diff options
author | Guillem Jover <guillem@debian.org> | 2009-07-03 02:21:43 +0200 |
---|---|---|
committer | Guillem Jover <guillem@debian.org> | 2009-09-30 21:48:57 +0200 |
commit | 12a61afc463c74a40ac65ccc0cb6f75dee532f16 (patch) | |
tree | 782ffa288029cb052dd1c955331e40e6d82de323 | |
parent | d96bee65e139db050bd981a42e29c3763847ee77 (diff) | |
download | dpkg-12a61afc463c74a40ac65ccc0cb6f75dee532f16.tar.gz |
dpkg-statoverride: Rewrite in C
-rw-r--r-- | debian/changelog | 1 | ||||
-rw-r--r-- | po/POTFILES.in | 2 | ||||
-rw-r--r-- | scripts/Makefile.am | 2 | ||||
-rwxr-xr-x | scripts/dpkg-statoverride.pl | 247 | ||||
-rw-r--r-- | src/.gitignore | 1 | ||||
-rw-r--r-- | src/Makefile.am | 16 | ||||
-rw-r--r-- | src/statcmd.c | 437 |
7 files changed, 455 insertions, 251 deletions
diff --git a/debian/changelog b/debian/changelog index b48d89e99..fa8b5eb96 100644 --- a/debian/changelog +++ b/debian/changelog @@ -19,6 +19,7 @@ dpkg (1.15.5) UNRELEASED; urgency=low * Add C coding style document. * Make dpkg as strict as dpkg-statoverride on input when validating the parsed data from the statdb. + * Rewrite dpkg-statoverride in C. [ Raphaël Hertzog ] * Add versioned dependency on base-files (>= 5.0.0) to dpkg-dev to ensure diff --git a/po/POTFILES.in b/po/POTFILES.in index 1a63c5c17..259f14466 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -42,6 +42,7 @@ src/processarc.c src/query.c src/remove.c src/select.c +src/statcmd.c src/statdb.c src/trigcmd.c src/trigproc.c @@ -61,5 +62,4 @@ dpkg-split/split.c utils/start-stop-daemon.c scripts/dpkg-divert.pl -scripts/dpkg-statoverride.pl scripts/update-alternatives.pl diff --git a/scripts/Makefile.am b/scripts/Makefile.am index a1150aa80..9b287a0f1 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -16,7 +16,6 @@ bin_SCRIPTS = \ dpkg-scanpackages \ dpkg-scansources \ dpkg-shlibdeps \ - dpkg-statoverride \ dpkg-source \ dpkg-vendor \ update-alternatives @@ -41,7 +40,6 @@ EXTRA_DIST = \ dpkg-shlibdeps.pl \ dpkg-source.pl \ dpkg-divert.pl \ - dpkg-statoverride.pl \ dpkg-vendor.pl \ update-alternatives.pl \ changelog/debian.pl \ diff --git a/scripts/dpkg-statoverride.pl b/scripts/dpkg-statoverride.pl deleted file mode 100755 index 09b07b1d1..000000000 --- a/scripts/dpkg-statoverride.pl +++ /dev/null @@ -1,247 +0,0 @@ -#! /usr/bin/perl - -BEGIN { # Work-around for bug #479711 in perl - $ENV{PERL_DL_NONLAZY} = 1; -} - -use strict; -use warnings; - -use POSIX; -use POSIX qw(:errno_h :signal_h); -use Dpkg; -use Dpkg::Gettext; - -textdomain("dpkg"); - -my $verbose = 1; -my $doforce = 0; -my $doupdate = 0; -my $mode = ""; - -my %owner; -my %group; -my %mode; - -sub version { - printf _g("Debian %s version %s.\n"), $progname, $version; - - printf _g(" -Copyright (C) 2000 Wichert Akkerman."); - - printf _g(" -This is free software; see the GNU General Public Licence version 2 or -later for copying conditions. There is NO warranty. -"); -} - -sub usage { - printf _g( -"Usage: %s [<option> ...] <command> - -Commands: - --add <owner> <group> <mode> <file> - add a new entry into the database. - --remove <file> remove file from the database. - --list [<glob-pattern>] list current overrides in the database. - -Options: - --admindir <directory> set the directory with the statoverride file. - --update immediately update file permissions. - --force force an action even if a sanity check fails. - --quiet quiet operation, minimal output. - --help show this help message. - --version show the version. -"), $progname; -} - -sub CheckModeConflict { - return unless $mode; - badusage(sprintf(_g("two commands specified: %s and --%s"), $_, $mode)); -} - -while (@ARGV) { - $_=shift(@ARGV); - last if m/^--$/; - if (!m/^-/) { - unshift(@ARGV,$_); last; - } elsif (m/^--help$/) { - usage(); - exit(0); - } elsif (m/^--version$/) { - version(); - exit(0); - } elsif (m/^--update$/) { - $doupdate=1; - } elsif (m/^--quiet$/) { - $verbose=0; - } elsif (m/^--force$/) { - $doforce=1; - } elsif (m/^--admindir$/) { - @ARGV || badusage(sprintf(_g("--%s needs a <directory> argument"), "admindir")); - $admindir= shift(@ARGV); - } elsif (m/^--add$/) { - CheckModeConflict(); - $mode= 'add'; - } elsif (m/^--remove$/) { - CheckModeConflict(); - $mode= 'remove'; - } elsif (m/^--list$/) { - CheckModeConflict(); - $mode= 'list'; - } else { - badusage(sprintf(_g("unknown option \`%s'"), $_)); - } -} - -my $dowrite = 0; -my $exitcode = 0; - -badusage(_g("no mode specified")) unless $mode; -ReadOverrides(); - -if ($mode eq "add") { - @ARGV == 4 || badusage(_g("--add needs four arguments")); - - my $user = $ARGV[0]; - my $uid = 0; - my $gid = 0; - - if ($user =~ m/^#([0-9]+)$/) { - $uid=$1; - badusage(sprintf(_g("illegal user %s"), $user)) if ($uid < 0); - } else { - my ($name, $pw); - (($name, $pw, $uid) = getpwnam($user)) || - badusage(sprintf(_g("non-existing user %s"), $user)); - } - - my $group = $ARGV[1]; - if ($group =~ m/^#([0-9]+)$/) { - $gid=$1; - badusage(sprintf(_g("illegal group %s"), $group)) if ($gid < 0); - } else { - my ($name, $pw); - (($name, $pw, $gid) = getgrnam($group)) || - badusage(sprintf(_g("non-existing group %s"), $group)); - } - - my $mode = $ARGV[2]; - (($mode < 0) or (oct($mode) > 07777) or ($mode !~ m/\d+/)) && - badusage(sprintf(_g("illegal mode %s"), $mode)); - my $file = $ARGV[3]; - $file =~ m/\n/ && badusage(_g("file may not contain newlines")); - $file =~ s,/+$,, && print STDERR _g("stripping trailing /")."\n"; - - if (defined $owner{$file}) { - printf STDERR _g("An override for \"%s\" already exists, "), $file; - if ($doforce) { - print STDERR _g("but --force specified so will be ignored.")."\n"; - } else { - print STDERR _g("aborting")."\n"; - exit(3); - } - } - $owner{$file}=$user; - $group{$file}=$group; - $mode{$file}=$mode; - $dowrite=1; - - if ($doupdate) { - if (not -e $file) { - printf STDERR _g("warning: --update given but %s does not exist")."\n", $file; - } else { - chown ($uid,$gid,$file) || warn sprintf(_g("failed to chown %s: %s"), $file, $!)."\n"; - chmod (oct($mode),$file) || warn sprintf(_g("failed to chmod %s: %s"), $file, $!)."\n"; - } - } -} elsif ($mode eq "remove") { - @ARGV == 1 || badusage(sprintf(_g("--%s needs a single argument"), "remove")); - my $file = $ARGV[0]; - $file =~ s,/+$,, && print STDERR _g("stripping trailing /")."\n"; - if (not defined $owner{$file}) { - print STDERR _g("No override present.")."\n"; - exit(0) if ($doforce); - exit(2); - } - delete $owner{$file}; - delete $group{$file}; - delete $mode{$file}; - $dowrite=1; - print(STDERR _g("warning: --update is useless for --remove")."\n") if ($doupdate); -} elsif ($mode eq "list") { - my (@list, @ilist); - - @ilist= @ARGV ? @ARGV : ('*'); - while (defined($_=shift(@ilist))) { - s/\W/\\$&/g; - s/\\\?/./g; - s/\\\*/.*/g; - s,/+$,, && print STDERR _g("stripping trailing /")."\n"; - push(@list,"^$_\$"); - } - - my $pattern = join('|', @list); - $exitcode=1; - for my $file (keys %owner) { - next unless ($file =~ m/$pattern/o); - $exitcode=0; - print "$owner{$file} $group{$file} $mode{$file} $file\n"; - } -} - -WriteOverrides() if ($dowrite); - -exit($exitcode); - -sub ReadOverrides { - open(SO, "$admindir/statoverride") || - quit(sprintf(_g("cannot open statoverride: %s"), $!)); - while (<SO>) { - my ($owner,$group,$mode,$file); - chomp; - - ($owner,$group,$mode,$file)=split(' ', $_, 4); - die sprintf(_g("Multiple overrides for \"%s\", aborting"), $file) - if defined $owner{$file}; - $owner{$file}=$owner; - $group{$file}=$group; - $mode{$file}=$mode; - } - close(SO); -} - - -sub WriteOverrides { - my ($file); - - open(SO, ">$admindir/statoverride-new") || - quit(sprintf(_g("cannot open new statoverride file: %s"), $!)); - foreach $file (keys %owner) { - print SO "$owner{$file} $group{$file} $mode{$file} $file\n"; - } - close(SO); - chmod(0644, "$admindir/statoverride-new"); - unlink("$admindir/statoverride-old") || - $! == ENOENT || quit(sprintf(_g("error removing statoverride-old: %s"), $!)); - link("$admindir/statoverride","$admindir/statoverride-old") || - $! == ENOENT || quit(sprintf(_g("error creating new statoverride-old: %s"), $!)); - rename("$admindir/statoverride-new","$admindir/statoverride") - || quit(sprintf(_g("error installing new statoverride: %s"), $!)); -} - - -sub quit -{ - printf STDERR "%s: %s\n", $0, "@_"; - exit(2); -} - -sub badusage -{ - printf STDERR "%s: %s\n\n", $0, "@_"; - usage(); - exit(2); -} - -# vi: ts=8 sw=8 ai si cindent diff --git a/src/.gitignore b/src/.gitignore index 9b61029aa..10f60e91b 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,3 +1,4 @@ dpkg dpkg-query +dpkg-statoverride dpkg-trigger diff --git a/src/Makefile.am b/src/Makefile.am index 820cf1c59..a29b62910 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,7 +11,11 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/lib -bin_PROGRAMS = dpkg dpkg-query dpkg-trigger +bin_PROGRAMS = \ + dpkg \ + dpkg-query \ + dpkg-statoverride \ + dpkg-trigger dpkg_SOURCES = \ archives.c archives.h \ @@ -52,6 +56,16 @@ dpkg_query_LDADD = \ ../lib/compat/libcompat.a \ $(LIBINTL) +dpkg_statoverride_SOURCES = \ + filesdb.c filesdb.h \ + statdb.c \ + statcmd.c + +dpkg_statoverride_LDADD = \ + ../lib/dpkg/libdpkg.a \ + ../lib/compat/libcompat.a \ + $(LIBINTL) + dpkg_trigger_SOURCES = \ trigcmd.c diff --git a/src/statcmd.c b/src/statcmd.c new file mode 100644 index 000000000..2241302c2 --- /dev/null +++ b/src/statcmd.c @@ -0,0 +1,437 @@ +/* + * dpkg-statoverrides - override ownership and mode of files + * + * Copyright © 2000, 2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2006-2009 Guillem Jover <guillem@debian.org> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/i18n.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <signal.h> +#include <grp.h> +#include <pwd.h> +#include <fnmatch.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#if HAVE_LOCALE_H +#include <locale.h> +#endif + +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/path.h> +#include <dpkg/myopt.h> + +#include "main.h" +#include "filesdb.h" + +const char thisname[] = "dpkg-statoverrides"; +const char printforhelp[] = N_( +"Use --help for help about querying packages;\n" +"Use --license for copyright license and lack of warranty (GNU GPL)."); + +static void +printversion(const struct cmdinfo *cip, const char *value) +{ + printf(_("Debian %s version %s.\n"), thisname, DPKG_VERSION_ARCH); + + printf(_( +"Copyright (C) 2000, 2001 Wichert Akkerman.\n" +"Copyright (C) 2006-2009 Guillem Jover.")); + + printf(_( +"This is free software; see the GNU General Public Licence version 2 or\n" +"later for copying conditions. There is NO warranty.\n")); + + m_output(stdout, _("<standard output")); + + exit(0); +} + +static void +usage(const struct cmdinfo *cip, const char *value) +{ + printf(_( +"Usage: %s [<option> ...] <command>\n" +"\n"), thisname); + + printf(_( +"Commands:\n" +" --add <owner> <group> <mode> <file>\n" +" add a new entry into the database.\n" +" --remove <file> remove file from the database.\n" +" --list [<glob-pattern>] list current overrides in the database.\n" +"\n")); + + printf(_( +"Options:\n" +" --admindir <directory> set the directory with the statoverride file.\n" +" --update immediately update file permissions.\n" +" --force force an action even if a sanity check fails.\n" +" --quiet quiet operation, minimal output.\n" +" --help show this help message.\n" +" --version show the version.\n" +"\n")); + + m_output(stdout, _("<standard output")); + + exit(0); +} + +const struct cmdinfo *cipaction = NULL; +const char *admindir = ADMINDIR; + +static int opt_verbose = 1; +static int opt_force = 0; +static int opt_update = 0; + +static void +setaction(const struct cmdinfo *cip, const char *value) +{ + if (cipaction) + badusage(_("conflicting actions -%c (--%s) and -%c (--%s)"), + cip->oshort, cip->olong, + cipaction->oshort, cipaction->olong); + cipaction = cip; +} + +static char * +path_cleanup(const char *path) +{ + char *new_path = m_strdup(path); + + path_rtrim_slash_slashdot(new_path); + if (strcmp(path, new_path) != 0) + warning(_("stripping trailing /")); + + return new_path; +} + +static struct filestatoverride * +statdb_node_new(const char *user, const char *group, const char *mode) +{ + struct filestatoverride *filestat; + + filestat = nfmalloc(sizeof(*filestat)); + + filestat->uid = statdb_parse_uid(user); + filestat->gid = statdb_parse_gid(group); + filestat->mode = statdb_parse_mode(mode); + + return filestat; +} + +static struct filestatoverride ** +statdb_node_find(const char *filename) +{ + struct filenamenode *file; + + file = findnamenode(filename, 0); + + return &file->statoverride; +} + +static int +statdb_node_remove(const char *filename) +{ + struct filenamenode *file; + + file = findnamenode(filename, fnn_nonew); + if (!file || (file && !file->statoverride)) + return 0; + + file->statoverride = NULL; + + return 1; +} + +static void +statdb_node_apply(const char *filename, struct filestatoverride *filestat) +{ + if (access(filename, F_OK) != 0) { + warning(_("--update given but %s does not exist"), filename); + } else { + if (chown(filename, filestat->uid, filestat->gid) < 0) + warning(_("failed to chown %s: %s"), filename, + strerror(errno)); + if (chmod(filename, filestat->mode)) + warning(_("failed to chmod %s: %s"), filename, + strerror(errno)); + } +} + +static void +statdb_node_print(FILE *out, struct filenamenode *file) +{ + struct filestatoverride *filestat = file->statoverride; + struct passwd *pw; + struct group *gr; + + if (!filestat) + return; + + pw = getpwuid(filestat->uid); + if (pw == NULL) + ohshite(_("error getting user name for uid %d"), filestat->uid); + gr = getgrgid(filestat->gid); + if (gr == NULL) + ohshite(_("error getting group name for gid %d"), filestat->gid); + + fprintf(out, "%s %s %o %s\n", pw->pw_name, gr->gr_name, filestat->mode, + file->name); +} + +static void +statdb_write(void) +{ + FILE *dbfile; + struct fileiterator *i; + struct filenamenode *file; + struct varbuf dbname = VARBUF_INIT; + struct varbuf dbname_new = VARBUF_INIT; + struct varbuf dbname_old = VARBUF_INIT; + + varbufaddstr(&dbname, admindir); + varbufaddstr(&dbname, "/" STATOVERRIDEFILE); + varbufaddc(&dbname, '\0'); + + varbufaddstr(&dbname_new, dbname.buf); + varbufaddstr(&dbname_new, NEWDBEXT); + varbufaddc(&dbname_new, '\0'); + + varbufaddstr(&dbname_old, dbname.buf); + varbufaddstr(&dbname_old, OLDDBEXT); + varbufaddc(&dbname_old, '\0'); + + dbfile = fopen(dbname_new.buf, "w"); + if (!dbfile) + ohshite(_("cannot open new statoverride file")); + + i = iterfilestart(); + while ((file = iterfilenext(i))) + statdb_node_print(dbfile, file); + iterfileend(i); + + fclose(dbfile); + + chmod(dbname_new.buf, 0644); + if (unlink(dbname_old.buf) && errno != ENOENT) + ohshite(_("error removing statoverride-old")); + if (link(dbname.buf, dbname_old.buf) && errno != ENOENT) + ohshite(_("error creating new statoverride-old")); + if (rename(dbname_new.buf, dbname.buf)) + ohshite(_("error installing new statoverride")); + + varbuffree(&dbname); + varbuffree(&dbname_new); + varbuffree(&dbname_old); +} + +static int +statoverride_add(const char *const *argv) +{ + const char *user = argv[0]; + const char *group = argv[1]; + const char *mode = argv[2]; + const char *path = argv[3]; + char *filename; + struct filestatoverride **filestat; + + if (!user || !group || !mode || !path || argv[4]) + badusage(_("--add needs four arguments")); + + if (strchr(path, '\n')) + badusage(_("file may not contain newlines")); + + filename = path_cleanup(path); + + filestat = statdb_node_find(filename); + if (*filestat == NULL) { + if (opt_force) + warning(_("An override for '%s' already exists, " + "but --force specified so will be ignored."), + filename); + else + ohshit(_("An override for '%s' already exists, " + "aborting."), filename); + } + + *filestat = statdb_node_new(user, group, mode); + + if (opt_update) + statdb_node_apply(filename, *filestat); + + statdb_write(); + + free(filename); + + return 0; +} + +static int +statoverride_remove(const char *const *argv) +{ + const char *path = argv[0]; + char *filename; + + if (!path || argv[1]) + badusage(_("--%s needs a single argument"), "remove"); + + filename = path_cleanup(path); + + if (!statdb_node_remove(filename)) { + warning(_("No override present.")); + if (opt_force) + exit(0); + else + exit(2); + } + + if (opt_update) + warning(_("--update is useless for --remove")); + + statdb_write(); + + free(filename); + + return 0; +} + +struct glob_node { + struct glob_node *next; + char *pattern; +}; + +static void +glob_list_prepend(struct glob_node **list, char *pattern) +{ + struct glob_node *node; + + node = m_malloc(sizeof(*node)); + node->pattern = pattern; + node->next = *list; + *list = node; +} + +static void +glob_list_free(struct glob_node *head) +{ + while (head) { + struct glob_node *node = head; + + head = head->next; + free(node->pattern); + free(node); + } +} + +static int +statoverride_list(const char *const *argv) +{ + struct fileiterator *i; + struct filenamenode *file; + const char *thisarg; + struct glob_node *glob_list = NULL; + int ret = 1; + + while ((thisarg = *argv++)) { + char *pattern = path_cleanup(thisarg); + + glob_list_prepend(&glob_list, pattern); + } + if (glob_list == NULL) + glob_list_prepend(&glob_list, m_strdup("*")); + + i = iterfilestart(); + while ((file = iterfilenext(i))) { + struct glob_node *g; + + for (g = glob_list; g; g = g->next) { + if (fnmatch(g->pattern, file->name, 0) == 0) { + statdb_node_print(stdout, file); + ret = 0; + break; + } + } + } + iterfileend(i); + + glob_list_free(glob_list); + + return ret; +} + +#define ACTION(longopt, shortopt, code, function) \ + { longopt, shortopt, 0, 0, 0, setaction, code, 0, (voidfnp)function } + +static const struct cmdinfo cmdinfos[] = { + ACTION("add", 'L', act_listfiles, statoverride_add), + ACTION("remove", 's', act_status, statoverride_remove), + ACTION("list", 'p', act_printavail, statoverride_list), + + { "admindir", 0, 1, NULL, &admindir, NULL }, + { "quiet", 0, 0, &opt_verbose, NULL, NULL }, + { "force", 0, 0, &opt_force, NULL, NULL }, + { "update", 0, 0, &opt_update, NULL, NULL }, + { "help", 'h', 0, NULL, NULL, usage }, + { "version", 0, 0, NULL, NULL, printversion }, + /* UK spelling */ + { "licence", 0, 0, NULL, NULL, showcopyright }, + /* US spelling */ + { "license", 0, 0, NULL, NULL, showcopyright }, + { NULL, 0 } +}; + +int +main(int argc, const char *const *argv) +{ + jmp_buf ejbuf; + static int (*actionfunction)(const char *const *argv); + int ret; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + standard_startup(&ejbuf); + myopt(&argv, cmdinfos); + + if (!cipaction) + badusage(_("need an action option")); + + setvbuf(stdout, NULL, _IONBF, 0); + + filesdbinit(); + ensure_statoverrides(); + + actionfunction = (int (*)(const char *const *))cipaction->farg; + ret = actionfunction(argv); + + standard_shutdown(); + + return ret; +} + |