fix documentation of --compare
[metastore.git] / metastore.c
bloba03e0ef9d3cceb4037224034cc1587f2b1e1704f
1 /*
2 * Main functions of the program.
4 * Copyright (C) 2007 David Härdeman <david@hardeman.nu>
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <getopt.h>
24 #include <utime.h>
25 #include <attr/xattr.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
30 #include "metastore.h"
31 #include "utils.h"
32 #include "metaentry.h"
34 /* Used to signal whether mtimes should be corrected */
35 static int do_mtime = 0;
38 * Prints differences between real and stored actual metadata
39 * - for use in mentries_compare
41 static void
42 compare_print(struct metaentry *real, struct metaentry *stored, int cmp)
44 if (!real && !stored) {
45 msg(MSG_ERROR, "%s called with incorrect arguments\n", __FUNCTION__);
46 return;
49 if (cmp == DIFF_NONE) {
50 msg(MSG_DEBUG, "%s:\tno difference\n", real->path);
51 return;
54 msg(MSG_QUIET, "%s:\t", real ? real->path : stored->path);
56 if (cmp & DIFF_ADDED)
57 msg(MSG_QUIET, "added ", real->path);
58 if (cmp & DIFF_DELE)
59 msg(MSG_QUIET, "removed ", stored->path);
60 if (cmp & DIFF_OWNER)
61 msg(MSG_QUIET, "owner ");
62 if (cmp & DIFF_GROUP)
63 msg(MSG_QUIET, "group ");
64 if (cmp & DIFF_MODE)
65 msg(MSG_QUIET, "mode ");
66 if (cmp & DIFF_TYPE)
67 msg(MSG_QUIET, "type ");
68 if (cmp & DIFF_MTIME)
69 msg(MSG_QUIET, "mtime ");
70 if (cmp & DIFF_XATTR)
71 msg(MSG_QUIET, "xattr ");
72 msg(MSG_QUIET, "\n");
76 * Tries to change the real metadata to match the stored one
77 * - for use in mentries_compare
79 static void
80 compare_fix(struct metaentry *real, struct metaentry *stored, int cmp)
82 struct group *group;
83 struct passwd *owner;
84 gid_t gid = -1;
85 uid_t uid = -1;
86 struct utimbuf tbuf;
87 int i;
89 if (!real && !stored) {
90 msg(MSG_ERROR, "%s called with incorrect arguments\n",
91 __FUNCTION__);
92 return;
95 if (!real) {
96 msg(MSG_NORMAL, "%s:\tremoved\n", stored->path);
97 return;
100 if (!stored) {
101 msg(MSG_NORMAL, "%s:\tadded\n", real->path);
102 return;
105 if (cmp == DIFF_NONE) {
106 msg(MSG_DEBUG, "%s:\tno difference\n", real->path);
107 return;
110 if (cmp & DIFF_TYPE) {
111 msg(MSG_NORMAL, "%s:\tnew type, will not change metadata\n",
112 real->path);
113 return;
116 msg(MSG_QUIET, "%s:\tchanging metadata\n", real->path);
118 while (cmp & (DIFF_OWNER | DIFF_GROUP)) {
119 if (cmp & DIFF_OWNER) {
120 msg(MSG_NORMAL, "\tchanging owner from %s to %s\n",
121 real->path, real->group, stored->group);
122 owner = xgetpwnam(stored->owner);
123 if (!owner) {
124 msg(MSG_DEBUG, "\tgetpwnam failed: %s\n",
125 strerror(errno));
126 break;
128 uid = owner->pw_uid;
131 if (cmp & DIFF_GROUP) {
132 msg(MSG_NORMAL, "\tchanging group from %s to %s\n",
133 real->path, real->group, stored->group);
134 group = xgetgrnam(stored->group);
135 if (!group) {
136 msg(MSG_DEBUG, "\tgetgrnam failed: %s\n",
137 strerror(errno));
138 break;
140 gid = group->gr_gid;
143 if (lchown(real->path, uid, gid)) {
144 msg(MSG_DEBUG, "\tlchown failed: %s\n",
145 strerror(errno));
146 break;
148 break;
151 if (cmp & DIFF_MODE) {
152 msg(MSG_NORMAL, "%s:\tchanging mode from 0%o to 0%o\n",
153 real->path, real->mode & 07777, stored->mode & 07777);
154 if (chmod(real->path, stored->mode & 07777))
155 msg(MSG_DEBUG, "\tchmod failed: %s\n", strerror(errno));
158 /* FIXME: Use utimensat here, or even better - lutimensat */
159 if ((cmp & DIFF_MTIME) && S_ISLNK(real->mode)) {
160 msg(MSG_NORMAL, "%s:\tsymlink, not changing mtime\n", real->path);
161 } else if (cmp & DIFF_MTIME) {
162 msg(MSG_NORMAL, "%s:\tchanging mtime from %ld to %ld\n",
163 real->path, real->mtime, stored->mtime);
164 tbuf.actime = stored->mtime;
165 tbuf.modtime = stored->mtime;
166 if (utime(real->path, &tbuf)) {
167 msg(MSG_DEBUG, "\tutime failed: %s\n", strerror(errno));
168 return;
172 if (cmp & DIFF_XATTR) {
173 for (i = 0; i < real->xattrs; i++) {
174 /* Any attrs to remove? */
175 if (mentry_find_xattr(stored, real, i) >= 0)
176 continue;
178 msg(MSG_NORMAL, "%s:\tremoving xattr %s\n",
179 real->path, real->xattr_names[i]);
180 if (lremovexattr(real->path, real->xattr_names[i]))
181 msg(MSG_DEBUG, "\tlremovexattr failed: %s\n",
182 strerror(errno));
185 for (i = 0; i < stored->xattrs; i++) {
186 /* Any xattrs to add? (on change they are removed above) */
187 if (mentry_find_xattr(real, stored, i) >= 0)
188 continue;
190 msg(MSG_NORMAL, "%s:\tadding xattr %s\n",
191 stored->path, stored->xattr_names[i]);
192 if (lsetxattr(stored->path, stored->xattr_names[i],
193 stored->xattr_values[i],
194 stored->xattr_lvalues[i], XATTR_CREATE))
195 msg(MSG_DEBUG, "\tlsetxattr failed: %s\n",
196 strerror(errno));
201 /* Prints usage message and exits */
202 static void
203 usage(const char *arg0, const char *message)
205 if (message)
206 msg(MSG_CRITICAL, "%s: %s\n\n", arg0, msg);
207 msg(MSG_CRITICAL, "Usage: %s ACTION [OPTION...] [PATH...]\n\n", arg0);
208 msg(MSG_CRITICAL, "Where ACTION is one of:\n"
209 " -c, --compare\tShow differences between stored and real metadata\n"
210 " -s, --save\tSave current metadata\n"
211 " -a, --apply\tApply stored metadata\n"
212 " -h, --help\tHelp message (this text)\n\n"
213 "Valid OPTIONS are (can be given more than once):\n"
214 " -v, --verbose\tPrint more verbose messages\n"
215 " -q, --quiet\tPrint less verbose messages\n"
216 " -m, --mtime\tAlso take mtime into account for diff or apply\n");
218 exit(message ? EXIT_FAILURE : EXIT_SUCCESS);
221 /* Options */
222 static struct option long_options[] = {
223 {"compare", 0, 0, 0},
224 {"save", 0, 0, 0},
225 {"apply", 0, 0, 0},
226 {"help", 0, 0, 0},
227 {"verbose", 0, 0, 0},
228 {"quiet", 0, 0, 0},
229 {"mtime", 0, 0, 0},
230 {0, 0, 0, 0}
233 /* Main function */
235 main(int argc, char **argv, char **envp)
237 int i, c;
238 struct metahash *real = NULL;
239 struct metahash *stored = NULL;
240 int action = 0;
242 /* Parse options */
243 i = 0;
244 while (1) {
245 int option_index = 0;
246 c = getopt_long(argc, argv, "csahvqm",
247 long_options, &option_index);
248 if (c == -1)
249 break;
250 switch (c) {
251 case 0:
252 if (!strcmp("verbose",
253 long_options[option_index].name)) {
254 adjust_verbosity(1);
255 } else if (!strcmp("quiet",
256 long_options[option_index].name)) {
257 adjust_verbosity(-1);
258 } else if (!strcmp("mtime",
259 long_options[option_index].name)) {
260 do_mtime = 1;
261 } else {
262 action |= (1 << option_index);
263 i++;
265 break;
266 case 'c':
267 action |= ACTION_DIFF;
268 i++;
269 break;
270 case 's':
271 action |= ACTION_SAVE;
272 i++;
273 break;
274 case 'a':
275 action |= ACTION_APPLY;
276 i++;
277 break;
278 case 'h':
279 action |= ACTION_HELP;
280 i++;
281 break;
282 case 'v':
283 adjust_verbosity(1);
284 break;
285 case 'q':
286 adjust_verbosity(-1);
287 break;
288 case 'm':
289 do_mtime = 1;
290 break;
291 default:
292 usage(argv[0], "unknown option");
296 /* Make sure only one action is specified */
297 if (i != 1)
298 usage(argv[0], "incorrect option(s)");
300 /* Perform action */
301 switch (action) {
302 case ACTION_DIFF:
303 mentries_fromfile(&stored, METAFILE);
304 if (!stored) {
305 msg(MSG_CRITICAL, "Failed to load metadata from %s\n",
306 METAFILE);
307 exit(EXIT_FAILURE);
310 if (optind < argc) {
311 while (optind < argc)
312 mentries_recurse_path(argv[optind++], &real);
313 } else {
314 mentries_recurse_path(".", &real);
317 if (!real) {
318 msg(MSG_CRITICAL,
319 "Failed to load metadata from file system\n");
320 exit(EXIT_FAILURE);
323 mentries_compare(real, stored, compare_print, do_mtime);
324 break;
326 case ACTION_SAVE:
327 if (optind < argc) {
328 while (optind < argc)
329 mentries_recurse_path(argv[optind++], &real);
330 } else {
331 mentries_recurse_path(".", &real);
334 if (!real) {
335 msg(MSG_CRITICAL,
336 "Failed to load metadata from file system\n");
337 exit(EXIT_FAILURE);
340 mentries_tofile(real, METAFILE);
341 break;
343 case ACTION_APPLY:
344 mentries_fromfile(&stored, METAFILE);
345 if (!stored) {
346 msg(MSG_CRITICAL, "Failed to load metadata from %s\n",
347 METAFILE);
348 exit(EXIT_FAILURE);
351 if (optind < argc) {
352 while (optind < argc)
353 mentries_recurse_path(argv[optind++], &real);
354 } else {
355 mentries_recurse_path(".", &real);
358 if (!real) {
359 msg(MSG_CRITICAL,
360 "Failed to load metadata from file system\n");
361 exit(EXIT_FAILURE);
364 mentries_compare(real, stored, compare_fix, do_mtime);
365 break;
367 case ACTION_HELP:
368 usage(argv[0], NULL);
371 exit(EXIT_SUCCESS);