7 #include <attr/xattr.h>
20 #include "metastore.h"
27 msg(int level
, const char *fmt
, ...)
32 if (level
> verbosity
)
36 ret
= vfprintf(stderr
, fmt
, ap
);
42 mentry_free(struct metaentry
*m
)
53 for (i
= 0; i
< m
->xattrs
; i
++) {
54 free(m
->xattr_names
[i
]);
55 free(m
->xattr_values
[i
]);
59 free(m
->xattr_values
);
60 free(m
->xattr_lvalues
);
68 struct metaentry
*mentry
;
69 mentry
= xmalloc(sizeof(struct metaentry
));
70 memset(mentry
, 0, sizeof(struct metaentry
));
75 mentry_insert(struct metaentry
*mentry
, struct metaentry
**mhead
)
77 struct metaentry
*prev
;
78 struct metaentry
*curr
;
86 if (strcmp(mentry
->path
, (*mhead
)->path
) < 0) {
87 mentry
->next
= *mhead
;
93 for (curr
= prev
->next
; curr
; curr
= curr
->next
) {
94 comp
= strcmp(mentry
->path
, curr
->path
);
96 /* Two matching paths */
109 mentry_print(const struct metaentry
*mentry
)
113 if (!mentry
|| !mentry
->path
) {
114 fprintf(stderr
, "Incorrect meta entry passed to printmetaentry\n");
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
]);
134 printf("===========================\n\n");
138 mentries_print(const struct metaentry
*mhead
)
140 const struct metaentry
*mentry
;
143 for (mentry
= mhead
; mentry
; mentry
= mentry
->next
) {
145 mentry_print(mentry
);
148 printf("%i entries in total\n", i
);
152 mentry_create(const char *path
)
154 ssize_t lsize
, vsize
;
160 struct metaentry
*mentry
;
162 if (lstat(path
, &sbuf
)) {
167 pbuf
= getpwuid(sbuf
.st_uid
);
173 gbuf
= getgrgid(sbuf
.st_gid
);
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
))
191 lsize
= listxattr(path
, NULL
, 0);
197 list
= xmalloc(lsize
);
198 lsize
= listxattr(path
, list
, lsize
);
205 for (attr
= list
; attr
< list
+ lsize
; attr
= strchr(attr
, '\0') + 1) {
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
));
220 for (attr
= list
; attr
< list
+ lsize
; attr
= strchr(attr
, '\0') + 1) {
224 mentry
->xattr_names
[i
] = xstrdup(attr
);
225 vsize
= getxattr(path
, attr
, NULL
, 0);
231 mentry
->xattr_lvalues
[i
] = vsize
;
232 mentry
->xattr_values
[i
] = xmalloc(vsize
);
234 vsize
= getxattr(path
, attr
, mentry
->xattr_values
[i
], vsize
);
246 normalize_path(const char *orig
)
248 char *real
= canonicalize_file_name(orig
);
252 getcwd(cwd
, PATH_MAX
);
256 if (!strncmp(real
, cwd
, strlen(cwd
))) {
257 result
= xmalloc(strlen(real
) - strlen(cwd
) + 1 + 1);
260 strcat(result
, real
+ strlen(cwd
));
262 result
= xstrdup(real
);
270 mentries_recurse(const char *opath
, struct metaentry
**mhead
)
273 struct metaentry
*mentry
;
274 char tpath
[PATH_MAX
];
277 char *path
= normalize_path(opath
);
282 if (lstat(path
, &sbuf
)) {
283 printf("Failed to stat %s\n", path
);
287 mentry
= mentry_create(path
);
289 printf("Failed to get metadata for %s\n", path
);
293 mentry_insert(mentry
, mhead
);
295 if (S_ISDIR(sbuf
.st_mode
)) {
298 printf("Failed to open dir %s\n", path
);
302 while ((dent
= readdir(dir
))) {
303 if (!strcmp(dent
->d_name
, ".") || !strcmp(dent
->d_name
, ".."))
305 snprintf(tpath
, PATH_MAX
, "%s/%s", path
, dent
->d_name
);
306 tpath
[PATH_MAX
- 1] = '\0';
307 mentries_recurse(tpath
, mhead
);
318 mentries_tofile(const struct metaentry
*mhead
, const char *path
)
321 const struct metaentry
*mentry
;
324 to
= fopen(path
, "w");
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
);
352 mentries_fromfile(struct metaentry
**mhead
, const char *path
)
354 struct metaentry
*mentry
;
362 fd
= open(path
, O_RDONLY
);
368 if (fstat(fd
, &sbuf
)) {
373 if (sbuf
.st_size
< (SIGNATURELEN
+ VERSIONLEN
)) {
374 fprintf(stderr
, "Invalid size for file %s\n", path
);
378 mmapstart
= mmap(NULL
, (size_t)sbuf
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
379 if (mmapstart
== MAP_FAILED
) {
384 max
= mmapstart
+ sbuf
.st_size
;
386 if (strncmp(ptr
, SIGNATURE
, SIGNATURELEN
)) {
387 printf("Invalid signature for file %s\n", path
);
392 if (strncmp(ptr
, VERSION
, VERSIONLEN
)) {
393 printf("Invalid version for file %s\n", path
);
398 while (ptr
< mmapstart
+ sbuf
.st_size
) {
400 fprintf(stderr
, "Invalid characters in file %s\n", path
);
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
);
428 munmap(mmapstart
, sbuf
.st_size
);
433 mentry_find(const char *path
, struct metaentry
*mhead
)
437 /* FIXME - We can do a bisect search here instead */
438 for (m
= mhead
; m
; m
= m
->next
) {
439 if (!strcmp(path
, m
->path
))
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
)
451 for (i
= 0; i
< haystack
->xattrs
; i
++) {
452 if (strcmp(haystack
->xattr_names
[i
], needle
->xattr_names
[n
]))
454 if (haystack
->xattr_lvalues
[i
] != needle
->xattr_lvalues
[n
])
456 if (bcmp(haystack
->xattr_values
[i
], needle
->xattr_values
[n
], needle
->xattr_lvalues
[n
]))
463 /* Returns zero if all xattrs in left and right match */
465 mentry_compare_xattr(struct metaentry
*left
, struct metaentry
*right
)
469 if (left
->xattrs
!= right
->xattrs
)
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) {
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");
493 if (strcmp(left
->path
, right
->path
))
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))
505 if ((left
->mode
& S_IFMT
) != (right
->mode
& S_IFMT
))
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
;
521 compare_print(struct metaentry
*left
, struct metaentry
*right
, int cmp
)
524 printf("Path %s: removed\n", right
->path
);
529 printf("Path %s: added\n", left
->path
);
534 printf("Path %s: ", left
->path
);
535 if (cmp
& DIFF_OWNER
)
537 if (cmp
& DIFF_GROUP
)
543 if (cmp
& DIFF_MTIME
)
545 if (cmp
& DIFF_XATTR
)
552 compare_fix(struct metaentry
*left
, struct metaentry
*right
, int cmp
)
555 struct passwd
*owner
;
561 if (!left
&& !right
) {
562 printf("%s called with incorrect arguments\n", __FUNCTION__
);
567 printf("Path %s: removed\n", right
->path
);
572 printf("Path %s: added\n", left
->path
);
576 if (cmp
== DIFF_NONE
) {
577 msg(MSG_DEBUG
, "Path %s: no difference\n", left
->path
);
581 if (cmp
& DIFF_TYPE
) {
582 printf("Path %s: new type, will not change metadata\n", left
->path
);
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
);
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
);
607 if (lchown(left
->path
, uid
, gid
)) {
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
)) {
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
)) {
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)
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)
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
))
660 mentries_compare(struct metaentry
*mheadleft
,
661 struct metaentry
*mheadright
,
662 void (*printfunc
)(struct metaentry
*, struct metaentry
*, int))
664 struct metaentry
*left
, *right
;
667 if (!mheadleft
|| !mheadright
) {
668 fprintf(stderr
, "mentries_compare called with empty list\n");
672 for (left
= mheadleft
; left
; left
= left
->next
) {
673 right
= mentry_find(left
->path
, mheadright
);
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
);
684 printfunc(left
, right
, DIFF_DELE
);
689 usage(const char *arg0
, const char *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");
708 static struct option long_options
[] = {
709 {"compare", 0, 0, 0},
713 {"verbose", 0, 0, 0},
720 main(int argc
, char **argv
, char **envp
)
723 struct metaentry
*mhead
= NULL
;
724 struct metaentry
*mfhead
= NULL
;
729 int option_index
= 0;
730 c
= getopt_long(argc
, argv
, "csahvqm", long_options
, &option_index
);
735 if (!strcmp("verbose", long_options
[option_index
].name
)) {
737 } else if (!strcmp("quiet", long_options
[option_index
].name
)) {
739 } else if (!strcmp("mtime", long_options
[option_index
].name
)) {
742 action
|= (1 << option_index
);
747 action
|= ACTION_DIFF
;
751 action
|= ACTION_SAVE
;
755 action
|= ACTION_APPLY
;
759 action
|= ACTION_HELP
;
772 usage(argv
[0], "unknown option");
777 usage(argv
[0], "incorrect option(s)");
781 mentries_fromfile(&mfhead
, METAFILE
);
783 fprintf(stderr
, "Failed to load metadata from file\n");
788 while (optind
< argc
)
789 mentries_recurse(argv
[optind
++], &mhead
);
791 mentries_recurse(".", &mhead
);
793 fprintf(stderr
, "Failed to load metadata from fs\n");
796 mentries_compare(mhead
, mfhead
, compare_print
);
800 while (optind
< argc
)
801 mentries_recurse(argv
[optind
++], &mhead
);
803 mentries_recurse(".", &mhead
);
805 fprintf(stderr
, "Failed to load metadata from fs\n");
808 mentries_tofile(mhead
, METAFILE
);
811 mentries_fromfile(&mfhead
, METAFILE
);
813 fprintf(stderr
, "Failed to load metadata from file\n");
818 while (optind
< argc
)
819 mentries_recurse(argv
[optind
++], &mhead
);
821 mentries_recurse(".", &mhead
);
823 fprintf(stderr
, "Failed to load metadata from fs\n");
826 mentries_compare(mhead
, mfhead
, compare_fix
);
829 usage(argv
[0], NULL
);