Oops, fix typo
[metastore.git] / metastore.c
blob35313b9705ae78ed4ce08635f71dfda2734a264e
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 <pwd.h>
24 #include <grp.h>
25 #include <getopt.h>
26 #include <utime.h>
27 #include <attr/xattr.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
32 #include "metastore.h"
33 #include "utils.h"
34 #include "metaentry.h"
36 /* Used to signal whether mtimes should be corrected */
37 static int do_mtime = 0;
40 * Prints differences between real and stored actual metadata
41 * - for use in mentries_compare
43 static void
44 compare_print(struct metaentry *real, struct metaentry *stored, int cmp)
46 if (!real) {
47 msg(MSG_QUIET, "%s:\tremoved\n", stored->path);
48 return;
51 if (!stored) {
52 msg(MSG_QUIET, "%s:\tadded\n", real->path);
53 return;
56 if (cmp == DIFF_NONE) {
57 msg(MSG_DEBUG, "%s:\tno difference\n", real->path);
58 return;
61 msg(MSG_QUIET, "%s:\t", real->path);
62 if (cmp & DIFF_OWNER)
63 msg(MSG_QUIET, "owner ");
64 if (cmp & DIFF_GROUP)
65 msg(MSG_QUIET, "group ");
66 if (cmp & DIFF_MODE)
67 msg(MSG_QUIET, "mode ");
68 if (cmp & DIFF_TYPE)
69 msg(MSG_QUIET, "type ");
70 if (cmp & DIFF_MTIME)
71 msg(MSG_QUIET, "mtime ");
72 if (cmp & DIFF_XATTR)
73 msg(MSG_QUIET, "xattr ");
74 msg(MSG_QUIET, "\n");
78 * Tries to change the real metadata to match the stored one
79 * - for use in mentries_compare
81 static void
82 compare_fix(struct metaentry *real, struct metaentry *stored, int cmp)
84 struct group *group;
85 struct passwd *owner;
86 gid_t gid = -1;
87 uid_t uid = -1;
88 struct utimbuf tbuf;
89 int i;
91 if (!real && !stored) {
92 msg(MSG_ERROR, "%s called with incorrect arguments\n",
93 __FUNCTION__);
94 return;
97 if (!real) {
98 msg(MSG_NORMAL, "%s:\tremoved\n", stored->path);
99 return;
102 if (!stored) {
103 msg(MSG_NORMAL, "%s:\tadded\n", real->path);
104 return;
107 if (cmp == DIFF_NONE) {
108 msg(MSG_DEBUG, "%s:\tno difference\n", real->path);
109 return;
112 if (cmp & DIFF_TYPE) {
113 msg(MSG_NORMAL, "%s:\tnew type, will not change metadata\n",
114 real->path);
115 return;
118 msg(MSG_QUIET, "%s:\tchanging metadata\n", real->path);
120 while (cmp & (DIFF_OWNER | DIFF_GROUP)) {
121 if (cmp & DIFF_OWNER) {
122 msg(MSG_NORMAL, "\tchanging owner from %s to %s\n",
123 real->path, real->group, stored->group);
124 owner = getpwnam(stored->owner);
125 if (!owner) {
126 msg(MSG_DEBUG, "\tgetpwnam failed: %s\n",
127 strerror(errno));
128 break;
130 uid = owner->pw_uid;
133 if (cmp & DIFF_GROUP) {
134 msg(MSG_NORMAL, "\tchanging group from %s to %s\n",
135 real->path, real->group, stored->group);
136 group = getgrnam(stored->group);
137 if (!group) {
138 msg(MSG_DEBUG, "\tgetgrnam failed: %s\n",
139 strerror(errno));
140 break;
142 gid = group->gr_gid;
145 if (lchown(real->path, uid, gid)) {
146 msg(MSG_DEBUG, "\tlchown failed: %s\n",
147 strerror(errno));
148 break;
150 break;
153 if (cmp & DIFF_MODE) {
154 msg(MSG_NORMAL, "%s:\tchanging mode from 0%o to 0%o\n",
155 real->path, real->mode & 07777, stored->mode & 07777);
156 if (chmod(real->path, stored->mode & 07777))
157 msg(MSG_DEBUG, "\tchmod failed: %s\n", strerror(errno));
160 /* FIXME: Use utimensat here, or even better - lutimensat */
161 if ((cmp & DIFF_MTIME) && S_ISLNK(real->mode)) {
162 msg(MSG_NORMAL, "%s:\tsymlink, not changing mtime", real->path);
163 } else if (cmp & DIFF_MTIME) {
164 msg(MSG_NORMAL, "%s:\tchanging mtime from %ld to %ld\n",
165 real->path, real->mtime, stored->mtime);
166 tbuf.actime = stored->mtime;
167 tbuf.modtime = stored->mtime;
168 if (utime(real->path, &tbuf)) {
169 msg(MSG_DEBUG, "\tutime failed: %s\n", strerror(errno));
170 return;
174 if (cmp & DIFF_XATTR) {
175 for (i = 0; i < real->xattrs; i++) {
176 /* Any attrs to remove? */
177 if (mentry_find_xattr(stored, real, i) >= 0)
178 continue;
180 msg(MSG_NORMAL, "%s:\tremoving xattr %s\n",
181 real->path, real->xattr_names[i]);
182 if (lremovexattr(real->path, real->xattr_names[i]))
183 msg(MSG_DEBUG, "\tlremovexattr failed: %s\n",
184 strerror(errno));
187 for (i = 0; i < stored->xattrs; i++) {
188 /* Any xattrs to add? (on change they are removed above) */
189 if (mentry_find_xattr(real, stored, i) >= 0)
190 continue;
192 msg(MSG_NORMAL, "%s:\tadding xattr %s\n",
193 stored->path, stored->xattr_names[i]);
194 if (lsetxattr(stored->path, stored->xattr_names[i],
195 stored->xattr_values[i],
196 stored->xattr_lvalues[i], XATTR_CREATE))
197 msg(MSG_DEBUG, "\tlsetxattr failed: %s\n",
198 strerror(errno));
203 /* Prints usage message and exits */
204 static void
205 usage(const char *arg0, const char *message)
207 if (message)
208 msg(MSG_CRITICAL, "%s: %s\n\n", arg0, msg);
209 msg(MSG_CRITICAL, "Usage: %s ACTION [OPTION...] [PATH...]\n\n", arg0);
210 msg(MSG_CRITICAL, "Where ACTION is one of:\n"
211 " -c, --compare\tShow differences between stored and real metadata\n"
212 " -s, --save\tSave current metadata\n"
213 " -a, --apply\tApply stored metadata\n"
214 " -h, --help\tHelp message (this text)\n\n"
215 "Valid OPTIONS are (can be given more than once):\n"
216 " -v, --verbose\tPrint more verbose messages\n"
217 " -q, --quiet\tPrint less verbose messages\n"
218 " -m, --mtime\tAlso take mtime into account for diff or apply\n");
220 exit(message ? EXIT_FAILURE : EXIT_SUCCESS);
223 /* Options */
224 static struct option long_options[] = {
225 {"compare", 0, 0, 0},
226 {"save", 0, 0, 0},
227 {"apply", 0, 0, 0},
228 {"help", 0, 0, 0},
229 {"verbose", 0, 0, 0},
230 {"quiet", 0, 0, 0},
231 {"mtime", 0, 0, 0},
232 {0, 0, 0, 0}
235 /* Main function */
237 main(int argc, char **argv, char **envp)
239 int i, c;
240 struct metaentry *lreal = NULL;
241 struct metaentry *lstored = NULL;
242 int action = 0;
244 /* Parse options */
245 i = 0;
246 while (1) {
247 int option_index = 0;
248 c = getopt_long(argc, argv, "csahvqm",
249 long_options, &option_index);
250 if (c == -1)
251 break;
252 switch (c) {
253 case 0:
254 if (!strcmp("verbose",
255 long_options[option_index].name)) {
256 adjust_verbosity(1);
257 } else if (!strcmp("quiet",
258 long_options[option_index].name)) {
259 adjust_verbosity(-1);
260 } else if (!strcmp("mtime",
261 long_options[option_index].name)) {
262 do_mtime = 1;
263 } else {
264 action |= (1 << option_index);
265 i++;
267 break;
268 case 'c':
269 action |= ACTION_DIFF;
270 i++;
271 break;
272 case 's':
273 action |= ACTION_SAVE;
274 i++;
275 break;
276 case 'a':
277 action |= ACTION_APPLY;
278 i++;
279 break;
280 case 'h':
281 action |= ACTION_HELP;
282 i++;
283 break;
284 case 'v':
285 adjust_verbosity(1);
286 break;
287 case 'q':
288 adjust_verbosity(-1);
289 break;
290 case 'm':
291 do_mtime = 1;
292 break;
293 default:
294 usage(argv[0], "unknown option");
298 /* Make sure only one action is specified */
299 if (i != 1)
300 usage(argv[0], "incorrect option(s)");
302 /* Perform action */
303 switch (action) {
304 case ACTION_DIFF:
305 mentries_fromfile(&lstored, METAFILE);
306 if (!lstored) {
307 msg(MSG_CRITICAL, "Failed to load metadata from %s\n",
308 METAFILE);
309 exit(EXIT_FAILURE);
312 if (optind < argc) {
313 while (optind < argc)
314 mentries_recurse_path(argv[optind++], &lreal);
315 } else {
316 mentries_recurse_path(".", &lreal);
319 if (!lreal) {
320 msg(MSG_CRITICAL,
321 "Failed to load metadata from file system\n");
322 exit(EXIT_FAILURE);
325 mentries_compare(lreal, lstored, compare_print, do_mtime);
326 break;
328 case ACTION_SAVE:
329 if (optind < argc) {
330 while (optind < argc)
331 mentries_recurse_path(argv[optind++], &lreal);
332 } else {
333 mentries_recurse_path(".", &lreal);
336 if (!lreal) {
337 msg(MSG_CRITICAL,
338 "Failed to load metadata from file system\n");
339 exit(EXIT_FAILURE);
342 mentries_tofile(lreal, METAFILE);
343 break;
345 case ACTION_APPLY:
346 mentries_fromfile(&lstored, METAFILE);
347 if (!lstored) {
348 msg(MSG_CRITICAL, "Failed to load metadata from %s\n",
349 METAFILE);
350 exit(EXIT_FAILURE);
353 if (optind < argc) {
354 while (optind < argc)
355 mentries_recurse_path(argv[optind++], &lreal);
356 } else {
357 mentries_recurse_path(".", &lreal);
360 if (!lreal) {
361 msg(MSG_CRITICAL,
362 "Failed to load metadata from file system\n");
363 exit(EXIT_FAILURE);
366 mentries_compare(lreal, lstored, compare_fix, do_mtime);
367 break;
369 case ACTION_HELP:
370 usage(argv[0], NULL);
373 exit(EXIT_SUCCESS);