chiark / gitweb /
lib/dpkg/tarfn.c: Kludge `tar_header_decode' to handle spurious `errno'.
[dpkg] / src / statcmd.c
1 /*
2  * dpkg-statoverride - override ownership and mode of files
3  *
4  * Copyright © 2000, 2001 Wichert Akkerman <wakkerma@debian.org>
5  * Copyright © 2006-2015 Guillem Jover <guillem@debian.org>
6  *
7  * This is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 #include <config.h>
23 #include <compat.h>
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27
28 #include <errno.h>
29 #if HAVE_LOCALE_H
30 #include <locale.h>
31 #endif
32 #include <string.h>
33 #include <grp.h>
34 #include <pwd.h>
35 #include <fnmatch.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39
40 #include <dpkg/i18n.h>
41 #include <dpkg/dpkg.h>
42 #include <dpkg/dpkg-db.h>
43 #include <dpkg/path.h>
44 #include <dpkg/dir.h>
45 #include <dpkg/glob.h>
46 #include <dpkg/options.h>
47
48 #include "main.h"
49 #include "filesdb.h"
50
51 static const char printforhelp[] = N_(
52 "Use --help for help about overriding file stat information.");
53
54 static void DPKG_ATTR_NORET
55 printversion(const struct cmdinfo *cip, const char *value)
56 {
57         printf(_("Debian %s version %s.\n"), dpkg_get_progname(),
58                PACKAGE_RELEASE);
59
60         printf(_(
61 "This is free software; see the GNU General Public License version 2 or\n"
62 "later for copying conditions. There is NO warranty.\n"));
63
64         m_output(stdout, _("<standard output>"));
65
66         exit(0);
67 }
68
69 static void DPKG_ATTR_NORET
70 usage(const struct cmdinfo *cip, const char *value)
71 {
72         printf(_(
73 "Usage: %s [<option> ...] <command>\n"
74 "\n"), dpkg_get_progname());
75
76         printf(_(
77 "Commands:\n"
78 "  --add <owner> <group> <mode> <path>\n"
79 "                           add a new <path> entry into the database.\n"
80 "  --remove <path>          remove <path> from the database.\n"
81 "  --list [<glob-pattern>]  list current overrides in the database.\n"
82 "\n"));
83
84         printf(_(
85 "Options:\n"
86 "  --admindir <directory>   set the directory with the statoverride file.\n"
87 "  --update                 immediately update <path> permissions.\n"
88 "  --force                  force an action even if a sanity check fails.\n"
89 "  --quiet                  quiet operation, minimal output.\n"
90 "  --help                   show this help message.\n"
91 "  --version                show the version.\n"
92 "\n"));
93
94         m_output(stdout, _("<standard output>"));
95
96         exit(0);
97 }
98
99 static const char *admindir;
100
101 static int opt_verbose = 1;
102 static int opt_force = 0;
103 static int opt_update = 0;
104
105 static char *
106 path_cleanup(const char *path)
107 {
108         char *new_path = m_strdup(path);
109
110         path_trim_slash_slashdot(new_path);
111         if (opt_verbose && strcmp(path, new_path) != 0)
112                 warning(_("stripping trailing /"));
113
114         return new_path;
115 }
116
117 static struct file_stat *
118 statdb_node_new(const char *user, const char *group, const char *mode)
119 {
120         struct file_stat *filestat;
121
122         filestat = nfmalloc(sizeof(*filestat));
123
124         filestat->uid = statdb_parse_uid(user);
125         if (filestat->uid == (uid_t)-1)
126                 ohshit(_("user '%s' does not exist"), user);
127         filestat->uname = NULL;
128         filestat->gid = statdb_parse_gid(group);
129         if (filestat->gid == (gid_t)-1)
130                 ohshit(_("group '%s' does not exist"), group);
131         filestat->gname = NULL;
132         filestat->mode = statdb_parse_mode(mode);
133
134         return filestat;
135 }
136
137 static struct file_stat **
138 statdb_node_find(const char *filename)
139 {
140         struct filenamenode *file;
141
142         file = findnamenode(filename, 0);
143
144         return &file->statoverride;
145 }
146
147 static int
148 statdb_node_remove(const char *filename)
149 {
150         struct filenamenode *file;
151
152         file = findnamenode(filename, fnn_nonew);
153         if (!file || (file && !file->statoverride))
154                 return 0;
155
156         file->statoverride = NULL;
157
158         return 1;
159 }
160
161 static void
162 statdb_node_apply(const char *filename, struct file_stat *filestat)
163 {
164         if (chown(filename, filestat->uid, filestat->gid) < 0)
165                 ohshite(_("error setting ownership of '%.255s'"), filename);
166         if (chmod(filename, filestat->mode & ~S_IFMT))
167                 ohshite(_("error setting permissions of '%.255s'"), filename);
168
169         dpkg_selabel_load();
170         dpkg_selabel_set_context(filename, filename, filestat->mode);
171         dpkg_selabel_close();
172 }
173
174 static void
175 statdb_node_print(FILE *out, struct filenamenode *file)
176 {
177         struct file_stat *filestat = file->statoverride;
178         struct passwd *pw;
179         struct group *gr;
180
181         if (!filestat)
182                 return;
183
184         pw = getpwuid(filestat->uid);
185         if (pw)
186                 fprintf(out, "%s ", pw->pw_name);
187         else if (filestat->uname)
188                 fprintf(out, "%s ", filestat->uname);
189         else
190                 fprintf(out, "#%d ", filestat->uid);
191
192         gr = getgrgid(filestat->gid);
193         if (gr)
194                 fprintf(out, "%s ", gr->gr_name);
195         else if (filestat->gname)
196                 fprintf(out, "%s ", filestat->gname);
197         else
198                 fprintf(out, "#%d ", filestat->gid);
199
200         fprintf(out, "%o %s\n", filestat->mode & ~S_IFMT, file->name);
201 }
202
203 static void
204 statdb_write(void)
205 {
206         char *dbname;
207         struct atomic_file *dbfile;
208         struct fileiterator *iter;
209         struct filenamenode *file;
210
211         dbname = dpkg_db_get_path(STATOVERRIDEFILE);
212         dbfile = atomic_file_new(dbname, ATOMIC_FILE_BACKUP);
213         atomic_file_open(dbfile);
214
215         iter = files_db_iter_new();
216         while ((file = files_db_iter_next(iter)))
217                 statdb_node_print(dbfile->fp, file);
218         files_db_iter_free(iter);
219
220         atomic_file_sync(dbfile);
221         atomic_file_close(dbfile);
222         atomic_file_commit(dbfile);
223         atomic_file_free(dbfile);
224
225         dir_sync_path(dpkg_db_get_dir());
226
227         free(dbname);
228 }
229
230 static int
231 statoverride_add(const char *const *argv)
232 {
233         const char *user = argv[0];
234         const char *group = argv[1];
235         const char *mode = argv[2];
236         const char *path = argv[3];
237         char *filename;
238         struct file_stat **filestat;
239
240         if (!user || !group || !mode || !path || argv[4])
241                 badusage(_("--%s needs four arguments"), cipaction->olong);
242
243         if (strchr(path, '\n'))
244                 badusage(_("path may not contain newlines"));
245
246         filename = path_cleanup(path);
247
248         filestat = statdb_node_find(filename);
249         if (*filestat != NULL) {
250                 if (opt_force)
251                         warning(_("an override for '%s' already exists, "
252                                   "but --force specified so will be ignored"),
253                                 filename);
254                 else
255                         ohshit(_("an override for '%s' already exists; "
256                                  "aborting"), filename);
257         }
258
259         *filestat = statdb_node_new(user, group, mode);
260
261         if (opt_update) {
262                 struct stat st;
263
264                 if (stat(filename, &st) == 0) {
265                         (*filestat)->mode |= st.st_mode & S_IFMT;
266                         statdb_node_apply(filename, *filestat);
267                 } else if (opt_verbose) {
268                         warning(_("--update given but %s does not exist"),
269                                 filename);
270                 }
271         }
272
273         statdb_write();
274
275         free(filename);
276
277         return 0;
278 }
279
280 static int
281 statoverride_remove(const char *const *argv)
282 {
283         const char *path = argv[0];
284         char *filename;
285
286         if (!path || argv[1])
287                 badusage(_("--%s needs a single argument"), "remove");
288
289         filename = path_cleanup(path);
290
291         if (!statdb_node_remove(filename)) {
292                 if (opt_verbose)
293                         warning(_("no override present"));
294                 if (opt_force)
295                         return 0;
296                 else
297                         return 2;
298         }
299
300         if (opt_update && opt_verbose)
301                 warning(_("--update is useless for --remove"));
302
303         statdb_write();
304
305         free(filename);
306
307         return 0;
308 }
309
310 static int
311 statoverride_list(const char *const *argv)
312 {
313         struct fileiterator *iter;
314         struct filenamenode *file;
315         const char *thisarg;
316         struct glob_node *glob_list = NULL;
317         int ret = 1;
318
319         while ((thisarg = *argv++)) {
320                 char *pattern = path_cleanup(thisarg);
321
322                 glob_list_prepend(&glob_list, pattern);
323         }
324         if (glob_list == NULL)
325                 glob_list_prepend(&glob_list, m_strdup("*"));
326
327         iter = files_db_iter_new();
328         while ((file = files_db_iter_next(iter))) {
329                 struct glob_node *g;
330
331                 for (g = glob_list; g; g = g->next) {
332                         if (fnmatch(g->pattern, file->name, 0) == 0) {
333                                 statdb_node_print(stdout, file);
334                                 ret = 0;
335                                 break;
336                         }
337                 }
338         }
339         files_db_iter_free(iter);
340
341         glob_list_free(glob_list);
342
343         return ret;
344 }
345
346 static const struct cmdinfo cmdinfos[] = {
347         ACTION("add",    0, act_install,   statoverride_add),
348         ACTION("remove", 0, act_remove,    statoverride_remove),
349         ACTION("list",   0, act_listfiles, statoverride_list),
350
351         { "admindir",   0,   1,  NULL,         &admindir, NULL          },
352         { "quiet",      0,   0,  &opt_verbose, NULL,      NULL, 0       },
353         { "force",      0,   0,  &opt_force,   NULL,      NULL, 1       },
354         { "update",     0,   0,  &opt_update,  NULL,      NULL, 1       },
355         { "help",       '?', 0,  NULL,         NULL,      usage         },
356         { "version",    0,   0,  NULL,         NULL,      printversion  },
357         {  NULL,        0                                               }
358 };
359
360 int
361 main(int argc, const char *const *argv)
362 {
363         int ret;
364
365         dpkg_locales_init(PACKAGE);
366         dpkg_program_init("dpkg-statoverride");
367         dpkg_options_parse(&argv, cmdinfos, printforhelp);
368
369         admindir = dpkg_db_set_dir(admindir);
370
371         if (!cipaction)
372                 badusage(_("need an action option"));
373
374         filesdbinit();
375         ensure_statoverrides(STATDB_PARSE_LAX);
376
377         ret = cipaction->action(argv);
378
379         dpkg_program_done();
380
381         return ret;
382 }