/* * dpkg-statoverrides - override ownership and mode of files * * Copyright © 2000, 2001 Wichert Akkerman * Copyright © 2006-2009 Guillem Jover * * 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 of the License, 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 this program. If not, see . * */ #include #include #include #include #include #if HAVE_LOCALE_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #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.\n")); 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, _(" ...] \n" "\n"), thisname); printf(_( "Commands:\n" " --add \n" " add a new entry into the database.\n" " --remove remove file from the database.\n" " --list [] list current overrides in the database.\n" "\n")); printf(_( "Options:\n" " --admindir 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, _("")); 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) fprintf(out, "%s ", pw->pw_name); else fprintf(out, "#%d ", filestat->uid); gr = getgrgid(filestat->gid); if (gr) fprintf(out, "%s ", gr->gr_name); else fprintf(out, "#%d ", filestat->gid); fprintf(out, "%o %s\n", 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, 0 }, { "force", 0, 0, &opt_force, NULL, NULL, 1 }, { "update", 0, 0, &opt_update, NULL, NULL, 1 }, { "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; }