Empty checkin to test git repo
[metastore.git] / metastore.c
blob3e0068cadac63e2d81a42a25566ed45529ce77ac
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <attr/xattr.h>
8 #include <string.h>
9 #include <pwd.h>
10 #include <grp.h>
11 #include <limits.h>
12 #include <dirent.h>
13 #include <sys/mman.h>
14 #include <fcntl.h>
15 #include <stdint.h>
16 #include <getopt.h>
17 #include <stdarg.h>
18 #include <utime.h>
20 #include "metastore.h"
21 #include "utils.h"
23 int verbosity = 0;
24 int do_mtime = 0;
26 int
27 msg(int level, const char *fmt, ...)
29 int ret;
30 va_list ap;
32 if (level > verbosity)
33 return 0;
35 va_start(ap, fmt);
36 ret = vfprintf(stderr, fmt, ap);
37 va_end(ap);
38 return ret;
41 void
42 mentry_free(struct metaentry *m)
44 int i;
46 if (!m)
47 return;
49 free(m->path);
50 free(m->owner);
51 free(m->group);
53 for (i = 0; i < m->xattrs; i++) {
54 free(m->xattr_names[i]);
55 free(m->xattr_values[i]);
58 free(m->xattr_names);
59 free(m->xattr_values);
60 free(m->xattr_lvalues);
62 free(m);
65 struct metaentry *
66 mentry_alloc()
68 struct metaentry *mentry;
69 mentry = xmalloc(sizeof(struct metaentry));
70 memset(mentry, 0, sizeof(struct metaentry));
71 return mentry;
74 void
75 mentry_insert(struct metaentry *mentry, struct metaentry **mhead)
77 struct metaentry *prev;
78 struct metaentry *curr;
79 int comp;
81 if (!(*mhead)) {
82 *mhead = mentry;
83 return;
86 if (strcmp(mentry->path, (*mhead)->path) < 0) {
87 mentry->next = *mhead;
88 *mhead = mentry;
89 return;
92 prev = *mhead;
93 for (curr = prev->next; curr; curr = curr->next) {
94 comp = strcmp(mentry->path, curr->path);
95 if (!comp)
96 /* Two matching paths */
97 return;
98 if (comp < 0)
99 break;
100 prev = curr;
103 if (curr)
104 mentry->next = curr;
105 prev->next = mentry;
108 void
109 mentry_print(const struct metaentry *mentry)
111 int i;
113 if (!mentry || !mentry->path) {
114 fprintf(stderr, "Incorrect meta entry passed to printmetaentry\n");
115 return;
118 printf("===========================\n");
119 printf("Dump of metaentry %p\n", mentry);
120 printf("===========================\n");
122 printf("path\t\t: %s\n", mentry->path);
123 printf("owner\t\t: %s\n", mentry->owner);
124 printf("group\t\t: %s\n", mentry->group);
125 printf("mtime\t\t: %ld\n", (unsigned long)mentry->mtime);
126 printf("mtimensec\t: %ld\n", (unsigned long)mentry->mtimensec);
127 printf("mode\t\t: %ld\n", (unsigned long)mentry->mode);
128 for (i = 0; i < mentry->xattrs; i++) {
129 printf("xattr[%i]\t: %s=\"", i, mentry->xattr_names[i]);
130 binary_print(mentry->xattr_values[i], mentry->xattr_lvalues[i]);
131 printf("\"\n");
134 printf("===========================\n\n");
137 void
138 mentries_print(const struct metaentry *mhead)
140 const struct metaentry *mentry;
141 int i;
143 for (mentry = mhead; mentry; mentry = mentry->next) {
144 i++;
145 mentry_print(mentry);
148 printf("%i entries in total\n", i);
151 struct metaentry *
152 mentry_create(const char *path)
154 ssize_t lsize, vsize;
155 char *list, *attr;
156 struct stat sbuf;
157 struct passwd *pbuf;
158 struct group *gbuf;
159 int i;
160 struct metaentry *mentry;
162 if (lstat(path, &sbuf)) {
163 perror("lstat");
164 return NULL;
167 pbuf = getpwuid(sbuf.st_uid);
168 if (!pbuf) {
169 perror("getpwuid");
170 return NULL;
173 gbuf = getgrgid(sbuf.st_gid);
174 if (!gbuf) {
175 perror("getgrgid");
176 return NULL;
179 mentry = mentry_alloc();
180 mentry->path = xstrdup(path);
181 mentry->owner = xstrdup(pbuf->pw_name);
182 mentry->group = xstrdup(gbuf->gr_name);
183 mentry->mode = sbuf.st_mode & 0177777;
184 mentry->mtime = sbuf.st_mtim.tv_sec;
185 mentry->mtimensec = sbuf.st_mtim.tv_nsec;
187 /* symlinks have no xattrs */
188 if (S_ISLNK(mentry->mode))
189 return mentry;
191 lsize = listxattr(path, NULL, 0);
192 if (lsize < 0) {
193 perror("listxattr");
194 return NULL;
197 list = xmalloc(lsize);
198 lsize = listxattr(path, list, lsize);
199 if (lsize < 0) {
200 perror("listxattr");
201 return NULL;
204 i = 0;
205 for (attr = list; attr < list + lsize; attr = strchr(attr, '\0') + 1) {
206 if (*attr == '\0')
207 continue;
208 i++;
211 if (i == 0)
212 return mentry;
214 mentry->xattrs = i;
215 mentry->xattr_names = xmalloc(i * sizeof(char *));
216 mentry->xattr_values = xmalloc(i * sizeof(char *));
217 mentry->xattr_lvalues = xmalloc(i * sizeof(ssize_t));
219 i = 0;
220 for (attr = list; attr < list + lsize; attr = strchr(attr, '\0') + 1) {
221 if (*attr == '\0')
222 continue;
224 mentry->xattr_names[i] = xstrdup(attr);
225 vsize = getxattr(path, attr, NULL, 0);
226 if (vsize < 0) {
227 perror("getxattr");
228 return NULL;
231 mentry->xattr_lvalues[i] = vsize;
232 mentry->xattr_values[i] = xmalloc(vsize);
234 vsize = getxattr(path, attr, mentry->xattr_values[i], vsize);
235 if (vsize < 0) {
236 perror("getxattr");
237 return NULL;
239 i++;
242 return mentry;
245 char *
246 normalize_path(const char *orig)
248 char *real = canonicalize_file_name(orig);
249 char cwd[PATH_MAX];
250 char *result;
252 getcwd(cwd, PATH_MAX);
253 if (!real)
254 return NULL;
256 if (!strncmp(real, cwd, strlen(cwd))) {
257 result = xmalloc(strlen(real) - strlen(cwd) + 1 + 1);
258 result[0] = '\0';
259 strcat(result, ".");
260 strcat(result, real + strlen(cwd));
261 } else {
262 result = xstrdup(real);
265 free(real);
266 return result;
269 void
270 mentries_recurse(const char *opath, struct metaentry **mhead)
272 struct stat sbuf;
273 struct metaentry *mentry;
274 char tpath[PATH_MAX];
275 DIR *dir;
276 struct dirent *dent;
277 char *path = normalize_path(opath);
279 if (!path)
280 return;
282 if (lstat(path, &sbuf)) {
283 printf("Failed to stat %s\n", path);
284 goto out;
287 mentry = mentry_create(path);
288 if (!mentry) {
289 printf("Failed to get metadata for %s\n", path);
290 goto out;
293 mentry_insert(mentry, mhead);
295 if (S_ISDIR(sbuf.st_mode)) {
296 dir = opendir(path);
297 if (!dir) {
298 printf("Failed to open dir %s\n", path);
299 return;
302 while ((dent = readdir(dir))) {
303 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
304 continue;
305 snprintf(tpath, PATH_MAX, "%s/%s", path, dent->d_name);
306 tpath[PATH_MAX - 1] = '\0';
307 mentries_recurse(tpath, mhead);
310 closedir(dir);
313 out:
314 free(path);
317 void
318 mentries_tofile(const struct metaentry *mhead, const char *path)
320 FILE *to;
321 const struct metaentry *mentry;
322 int i;
324 to = fopen(path, "w");
325 if (!to) {
326 perror("fopen");
327 exit(EXIT_FAILURE);
330 write_binary_string(SIGNATURE, SIGNATURELEN, to);
331 write_binary_string(VERSION, VERSIONLEN, to);
333 for (mentry = mhead; mentry; mentry = mentry->next) {
334 write_string(mentry->path, to);
335 write_string(mentry->owner, to);
336 write_string(mentry->group, to);
337 write_int((uint64_t)mentry->mtime, 8, to);
338 write_int((uint64_t)mentry->mtimensec, 8, to);
339 write_int((uint64_t)mentry->mode, 2, to);
340 write_int(mentry->xattrs, 4, to);
341 for (i = 0; i < mentry->xattrs; i++) {
342 write_string(mentry->xattr_names[i], to);
343 write_int(mentry->xattr_lvalues[i], 4, to);
344 write_binary_string(mentry->xattr_values[i], mentry->xattr_lvalues[i], to);
348 fclose(to);
351 void
352 mentries_fromfile(struct metaentry **mhead, const char *path)
354 struct metaentry *mentry;
355 char *mmapstart;
356 char *ptr;
357 char *max;
358 int fd;
359 struct stat sbuf;
360 int i;
362 fd = open(path, O_RDONLY);
363 if (fd < 0) {
364 perror("open");
365 exit(EXIT_FAILURE);
368 if (fstat(fd, &sbuf)) {
369 perror("fstat");
370 exit(EXIT_FAILURE);
373 if (sbuf.st_size < (SIGNATURELEN + VERSIONLEN)) {
374 fprintf(stderr, "Invalid size for file %s\n", path);
375 exit(EXIT_FAILURE);
378 mmapstart = mmap(NULL, (size_t)sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
379 if (mmapstart == MAP_FAILED) {
380 perror("mmap");
381 exit(EXIT_FAILURE);
383 ptr = mmapstart;
384 max = mmapstart + sbuf.st_size;
386 if (strncmp(ptr, SIGNATURE, SIGNATURELEN)) {
387 printf("Invalid signature for file %s\n", path);
388 goto out;
390 ptr += SIGNATURELEN;
392 if (strncmp(ptr, VERSION, VERSIONLEN)) {
393 printf("Invalid version for file %s\n", path);
394 goto out;
396 ptr += VERSIONLEN;
398 while (ptr < mmapstart + sbuf.st_size) {
399 if (*ptr == '\0') {
400 fprintf(stderr, "Invalid characters in file %s\n", path);
401 goto out;
404 mentry = mentry_alloc();
405 mentry->path = read_string(&ptr, max);
406 mentry->owner = read_string(&ptr, max);
407 mentry->group = read_string(&ptr, max);
408 mentry->mtime = (time_t)read_int(&ptr, 8, max);
409 mentry->mtimensec = (time_t)read_int(&ptr, 8, max);
410 mentry->mode = (mode_t)read_int(&ptr, 2, max);
411 mentry->xattrs = (unsigned int)read_int(&ptr, 4, max);
413 if (mentry->xattrs > 0) {
414 mentry->xattr_names = xmalloc(mentry->xattrs * sizeof(char *));
415 mentry->xattr_lvalues = xmalloc(mentry->xattrs * sizeof(int));
416 mentry->xattr_values = xmalloc(mentry->xattrs * sizeof(char *));
418 for (i = 0; i < mentry->xattrs; i++) {
419 mentry->xattr_names[i] = read_string(&ptr, max);
420 mentry->xattr_lvalues[i] = (int)read_int(&ptr, 4, max);
421 mentry->xattr_values[i] = read_binary_string(&ptr, mentry->xattr_lvalues[i], max);
424 mentry_insert(mentry, mhead);
427 out:
428 munmap(mmapstart, sbuf.st_size);
429 close(fd);
432 struct metaentry *
433 mentry_find(const char *path, struct metaentry *mhead)
435 struct metaentry *m;
437 /* FIXME - We can do a bisect search here instead */
438 for (m = mhead; m; m = m->next) {
439 if (!strcmp(path, m->path))
440 return m;
442 return NULL;
445 /* Returns xattr index in haystack which corresponds to xattr n in needle */
447 mentry_find_xattr(struct metaentry *haystack, struct metaentry *needle, int n)
449 int i;
451 for (i = 0; i < haystack->xattrs; i++) {
452 if (strcmp(haystack->xattr_names[i], needle->xattr_names[n]))
453 continue;
454 if (haystack->xattr_lvalues[i] != needle->xattr_lvalues[n])
455 return -1;
456 if (bcmp(haystack->xattr_values[i], needle->xattr_values[n], needle->xattr_lvalues[n]))
457 return -1;
458 return i;
460 return -1;
463 /* Returns zero if all xattrs in left and right match */
465 mentry_compare_xattr(struct metaentry *left, struct metaentry *right)
467 int i;
469 if (left->xattrs != right->xattrs)
470 return 1;
472 /* Make sure all xattrs in left are found in right and vice versa */
473 for (i = 0; i < left->xattrs; i++) {
474 if (mentry_find_xattr(right, left, i) < 0 ||
475 mentry_find_xattr(left, right, i) < 0) {
476 return 1;
480 return 0;
484 mentry_compare(struct metaentry *left, struct metaentry *right)
486 int retval = DIFF_NONE;
488 if (!left || !right) {
489 fprintf(stderr, "mentry_compare called with empty arguments\n");
490 return -1;
493 if (strcmp(left->path, right->path))
494 return -1;
496 if (strcmp(left->owner, right->owner))
497 retval |= DIFF_OWNER;
499 if (strcmp(left->group, right->group))
500 retval |= DIFF_GROUP;
502 if ((left->mode & 07777) != (right->mode & 07777))
503 retval |= DIFF_MODE;
505 if ((left->mode & S_IFMT) != (right->mode & S_IFMT))
506 retval |= DIFF_TYPE;
508 if (do_mtime && strcmp(left->path, METAFILE) &&
509 (left->mtime != right->mtime || left->mtimensec != right->mtimensec))
510 retval |= DIFF_MTIME;
512 if (mentry_compare_xattr(left, right)) {
513 retval |= DIFF_XATTR;
514 return retval;
517 return retval;
520 void
521 compare_print(struct metaentry *left, struct metaentry *right, int cmp)
523 if (!left) {
524 printf("Path %s: removed\n", right->path);
525 return;
528 if (!right) {
529 printf("Path %s: added\n", left->path);
530 return;
533 if (cmp != 0) {
534 printf("Path %s: ", left->path);
535 if (cmp & DIFF_OWNER)
536 printf("owner ");
537 if (cmp & DIFF_GROUP)
538 printf("group ");
539 if (cmp & DIFF_MODE)
540 printf("mode ");
541 if (cmp & DIFF_TYPE)
542 printf("type ");
543 if (cmp & DIFF_MTIME)
544 printf("mtime ");
545 if (cmp & DIFF_XATTR)
546 printf("xattr ");
547 printf("\n");
551 void
552 compare_fix(struct metaentry *left, struct metaentry *right, int cmp)
554 struct group *group;
555 struct passwd *owner;
556 gid_t gid = -1;
557 uid_t uid = -1;
558 struct utimbuf tbuf;
559 int i;
561 if (!left && !right) {
562 printf("%s called with incorrect arguments\n", __FUNCTION__);
563 return;
566 if (!left) {
567 printf("Path %s: removed\n", right->path);
568 return;
571 if (!right) {
572 printf("Path %s: added\n", left->path);
573 return;
576 if (cmp == DIFF_NONE) {
577 msg(MSG_DEBUG, "Path %s: no difference\n", left->path);
578 return;
581 if (cmp & DIFF_TYPE) {
582 printf("Path %s: new type, will not change metadata\n", left->path);
583 return;
586 if (cmp & (DIFF_OWNER | DIFF_GROUP)) {
587 if (cmp & DIFF_OWNER) {
588 printf("Path %s: fixing owner from %s to %s\n", left->path, left->group, right->group);
589 owner = getpwnam(right->owner);
590 if (!owner) {
591 perror("getpwnam");
592 return;
594 uid = owner->pw_uid;
597 if (cmp & DIFF_GROUP) {
598 printf("Path %s: fixing group from %s to %s\n", left->path, left->group, right->group);
599 group = getgrnam(right->group);
600 if (!group) {
601 perror("getgrnam");
602 return;
604 gid = group->gr_gid;
607 if (lchown(left->path, uid, gid)) {
608 perror("lchown");
609 return;
611 printf("Success\n");
614 if (cmp & DIFF_MODE) {
615 printf("Path %s: fixing mode from 0%o to 0%o\n", left->path, left->mode, right->mode);
616 if (chmod(left->path, left->mode)) {
617 perror("chmod");
618 return;
622 if (cmp & DIFF_MTIME) {
623 printf("Path %s: fixing mtime %ld to %ld\n", left->path, left->mtime, right->mtime);
624 /* FIXME: Use utimensat here */
625 tbuf.actime = right->mtime;
626 tbuf.modtime = right->mtime;
627 if (utime(left->path, &tbuf)) {
628 perror("utime");
629 return;
633 if (cmp & DIFF_XATTR) {
634 for (i = 0; i < left->xattrs; i++) {
635 /* Any attrs to remove? */
636 if (mentry_find_xattr(right, left, i) >= 0)
637 continue;
639 msg(MSG_NORMAL, "Path %s: removing xattr %s\n",
640 left->path, left->xattr_names[i]);
641 if (lremovexattr(left->path, left->xattr_names[i]))
642 perror("lremovexattr");
645 for (i = 0; i < right->xattrs; i++) {
646 /* Any xattrs to add? (on change they are removed above) */
647 if (mentry_find_xattr(left, right, i) >= 0)
648 continue;
650 msg(MSG_NORMAL, "Path %s: adding xattr %s\n",
651 right->path, right->xattr_names[i]);
652 if (lsetxattr(right->path, right->xattr_names[i],
653 right->xattr_values[i], right->xattr_lvalues[i], XATTR_CREATE))
654 perror("lsetxattr");
659 void
660 mentries_compare(struct metaentry *mheadleft,
661 struct metaentry *mheadright,
662 void (*printfunc)(struct metaentry *, struct metaentry *, int))
664 struct metaentry *left, *right;
665 int cmp;
667 if (!mheadleft || !mheadright) {
668 fprintf(stderr, "mentries_compare called with empty list\n");
669 return;
672 for (left = mheadleft; left; left = left->next) {
673 right = mentry_find(left->path, mheadright);
674 if (!right)
675 cmp = DIFF_ADDED;
676 else
677 cmp = mentry_compare(left, right);
678 printfunc(left, right, cmp);
681 for (right = mheadright; right; right = right->next) {
682 left = mentry_find(right->path, mheadleft);
683 if (!left)
684 printfunc(left, right, DIFF_DELE);
688 void
689 usage(const char *arg0, const char *msg)
691 if (msg)
692 fprintf(stderr, "%s: %s\n\n", arg0, msg);
693 fprintf(stderr, "Usage: %s ACTION [OPTIONS] [PATH]...\n\n", arg0);
694 fprintf(stderr, "Where ACTION is one of:\n"
695 " -d, --diff\tShow differences between stored and actual metadata\n"
696 " -s, --save\tSave current metadata\n"
697 " -a, --apply\tApply stored metadata\n"
698 " -h, --help\tHelp message (this text)\n\n"
699 "Valid OPTIONS are (can be given more than once):\n"
700 " -v, --verbose\tPrint more verbose messages\n"
701 " -q, --quiet\tPrint less verbose messages\n");
703 if (msg)
704 exit(EXIT_FAILURE);
705 exit(EXIT_SUCCESS);
708 static struct option long_options[] = {
709 {"compare", 0, 0, 0},
710 {"save", 0, 0, 0},
711 {"apply", 0, 0, 0},
712 {"help", 0, 0, 0},
713 {"verbose", 0, 0, 0},
714 {"quiet", 0, 0, 0},
715 {"mtime", 0, 0, 0},
716 {0, 0, 0, 0}
720 main(int argc, char **argv, char **envp)
722 int i, c;
723 struct metaentry *mhead = NULL;
724 struct metaentry *mfhead = NULL;
725 int action = 0;
727 i = 0;
728 while (1) {
729 int option_index = 0;
730 c = getopt_long(argc, argv, "csahvqm", long_options, &option_index);
731 if (c == -1)
732 break;
733 switch (c) {
734 case 0:
735 if (!strcmp("verbose", long_options[option_index].name)) {
736 verbosity++;
737 } else if (!strcmp("quiet", long_options[option_index].name)) {
738 verbosity--;
739 } else if (!strcmp("mtime", long_options[option_index].name)) {
740 do_mtime = 1;
741 } else {
742 action |= (1 << option_index);
743 i++;
745 break;
746 case 'c':
747 action |= ACTION_DIFF;
748 i++;
749 break;
750 case 's':
751 action |= ACTION_SAVE;
752 i++;
753 break;
754 case 'a':
755 action |= ACTION_APPLY;
756 i++;
757 break;
758 case 'h':
759 action |= ACTION_HELP;
760 i++;
761 break;
762 case 'v':
763 verbosity++;
764 break;
765 case 'q':
766 verbosity--;
767 break;
768 case 'm':
769 do_mtime = 1;
770 break;
771 default:
772 usage(argv[0], "unknown option");
776 if (i != 1)
777 usage(argv[0], "incorrect option(s)");
779 switch (action) {
780 case ACTION_DIFF:
781 mentries_fromfile(&mfhead, METAFILE);
782 if (!mfhead) {
783 fprintf(stderr, "Failed to load metadata from file\n");
784 exit(EXIT_FAILURE);
787 if (optind < argc)
788 while (optind < argc)
789 mentries_recurse(argv[optind++], &mhead);
790 else
791 mentries_recurse(".", &mhead);
792 if (!mhead) {
793 fprintf(stderr, "Failed to load metadata from fs\n");
794 exit(EXIT_FAILURE);
796 mentries_compare(mhead, mfhead, compare_print);
797 break;
798 case ACTION_SAVE:
799 if (optind < argc)
800 while (optind < argc)
801 mentries_recurse(argv[optind++], &mhead);
802 else
803 mentries_recurse(".", &mhead);
804 if (!mhead) {
805 fprintf(stderr, "Failed to load metadata from fs\n");
806 exit(EXIT_FAILURE);
808 mentries_tofile(mhead, METAFILE);
809 break;
810 case ACTION_APPLY:
811 mentries_fromfile(&mfhead, METAFILE);
812 if (!mfhead) {
813 fprintf(stderr, "Failed to load metadata from file\n");
814 exit(EXIT_FAILURE);
817 if (optind < argc)
818 while (optind < argc)
819 mentries_recurse(argv[optind++], &mhead);
820 else
821 mentries_recurse(".", &mhead);
822 if (!mhead) {
823 fprintf(stderr, "Failed to load metadata from fs\n");
824 exit(EXIT_FAILURE);
826 mentries_compare(mhead, mfhead, compare_fix);
827 break;
828 case ACTION_HELP:
829 usage(argv[0], NULL);
832 exit(EXIT_SUCCESS);