2 * Various functions to work with meta entries.
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.
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.
25 #include <sys/types.h>
30 #include <attr/xattr.h>
38 #include "metastore.h"
39 #include "metaentry.h"
43 /* Free's a metaentry and all its parameters */
45 mentry_free(struct metaentry
*m
)
56 for (i
= 0; i
< m
->xattrs
; i
++) {
57 free(m
->xattr_names
[i
]);
58 free(m
->xattr_values
[i
]);
62 free(m
->xattr_values
);
63 free(m
->xattr_lvalues
);
69 /* Allocates an empty metaentry */
70 static struct metaentry
*
73 struct metaentry
*mentry
;
74 mentry
= xmalloc(sizeof(struct metaentry
));
75 memset(mentry
, 0, sizeof(struct metaentry
));
79 /* Inserts a metaentry into a metaentry list */
81 mentry_insert(struct metaentry
*mentry
, struct metaentry
**mlist
)
83 struct metaentry
*prev
;
84 struct metaentry
*curr
;
92 if (strcmp(mentry
->path
, (*mlist
)->path
) < 0) {
93 mentry
->next
= *mlist
;
99 for (curr
= prev
->next
; curr
; curr
= curr
->next
) {
100 comp
= strcmp(mentry
->path
, curr
->path
);
102 /* Two matching paths */
115 /* Prints a metaentry */
117 mentry_print(const struct metaentry
*mentry
)
121 if (!mentry
|| !mentry
->path
) {
123 "Incorrect meta entry passed to printmetaentry\n");
127 msg(MSG_DEBUG
, "===========================\n");
128 msg(MSG_DEBUG
, "Dump of metaentry %p\n", mentry
);
129 msg(MSG_DEBUG
, "===========================\n");
131 msg(MSG_DEBUG
, "path\t\t: %s\n", mentry
->path
);
132 msg(MSG_DEBUG
, "owner\t\t: %s\n", mentry
->owner
);
133 msg(MSG_DEBUG
, "group\t\t: %s\n", mentry
->group
);
134 msg(MSG_DEBUG
, "mtime\t\t: %ld\n", (unsigned long)mentry
->mtime
);
135 msg(MSG_DEBUG
, "mtimensec\t: %ld\n", (unsigned long)mentry
->mtimensec
);
136 msg(MSG_DEBUG
, "mode\t\t: %ld\n", (unsigned long)mentry
->mode
);
137 for (i
= 0; i
< mentry
->xattrs
; i
++) {
138 msg(MSG_DEBUG
, "xattr[%i]\t: %s=\"", i
, mentry
->xattr_names
[i
]);
139 binary_print(mentry
->xattr_values
[i
], mentry
->xattr_lvalues
[i
]);
140 msg(MSG_DEBUG
, "\"\n");
143 msg(MSG_DEBUG
, "===========================\n\n");
146 /* Prints all metaentries in a metaentry list */
148 mentries_print(const struct metaentry
*mlist
)
150 const struct metaentry
*mentry
;
153 for (mentry
= mlist
; mentry
; mentry
= mentry
->next
) {
155 mentry_print(mentry
);
158 msg(MSG_DEBUG
, "%i entries in total\n", i
);
162 /* Creates a metaentry for the file/dir/etc at path */
163 static struct metaentry
*
164 mentry_create(const char *path
)
166 ssize_t lsize
, vsize
;
172 struct metaentry
*mentry
;
174 if (lstat(path
, &sbuf
)) {
175 msg(MSG_ERROR
, "lstat failed for %s: %s\n",
176 path
, strerror(errno
));
180 pbuf
= getpwuid(sbuf
.st_uid
);
182 msg(MSG_ERROR
, "getpwuid failed for %i: %s\n",
183 (int)sbuf
.st_uid
, strerror(errno
));
187 gbuf
= getgrgid(sbuf
.st_gid
);
189 msg(MSG_ERROR
, "getgrgid failed for %i: %s\n",
190 (int)sbuf
.st_gid
, strerror(errno
));
194 mentry
= mentry_alloc();
195 mentry
->path
= xstrdup(path
);
196 mentry
->owner
= xstrdup(pbuf
->pw_name
);
197 mentry
->group
= xstrdup(gbuf
->gr_name
);
198 mentry
->mode
= sbuf
.st_mode
& 0177777;
199 mentry
->mtime
= sbuf
.st_mtim
.tv_sec
;
200 mentry
->mtimensec
= sbuf
.st_mtim
.tv_nsec
;
202 /* symlinks have no xattrs */
203 if (S_ISLNK(mentry
->mode
))
206 lsize
= listxattr(path
, NULL
, 0);
208 msg(MSG_ERROR
, "listxattr failed for %s: %s\n",
209 path
, strerror(errno
));
213 list
= xmalloc(lsize
);
214 lsize
= listxattr(path
, list
, lsize
);
216 msg(MSG_ERROR
, "listxattr failed for %s: %s\n",
217 path
, strerror(errno
));
222 for (attr
= list
; attr
< list
+ lsize
; attr
= strchr(attr
, '\0') + 1) {
232 mentry
->xattr_names
= xmalloc(i
* sizeof(char *));
233 mentry
->xattr_values
= xmalloc(i
* sizeof(char *));
234 mentry
->xattr_lvalues
= xmalloc(i
* sizeof(ssize_t
));
237 for (attr
= list
; attr
< list
+ lsize
; attr
= strchr(attr
, '\0') + 1) {
241 mentry
->xattr_names
[i
] = xstrdup(attr
);
242 vsize
= getxattr(path
, attr
, NULL
, 0);
244 msg(MSG_ERROR
, "getxattr failed for %s: %s\n",
245 path
, strerror(errno
));
249 mentry
->xattr_lvalues
[i
] = vsize
;
250 mentry
->xattr_values
[i
] = xmalloc(vsize
);
252 vsize
= getxattr(path
, attr
, mentry
->xattr_values
[i
], vsize
);
254 msg(MSG_ERROR
, "getxattr failed for %s: %s\n",
255 path
, strerror(errno
));
264 /* Cleans up a path and makes it relative to current working dir unless it is absolute */
266 normalize_path(const char *orig
)
268 char *real
= canonicalize_file_name(orig
);
272 getcwd(cwd
, PATH_MAX
);
276 if (!strncmp(real
, cwd
, strlen(cwd
))) {
277 result
= xmalloc(strlen(real
) - strlen(cwd
) + 1 + 1);
280 strcat(result
, real
+ strlen(cwd
));
282 result
= xstrdup(real
);
289 /* Internal function for the recursive path walk */
291 mentries_recurse(const char *path
, struct metaentry
**mlist
)
294 struct metaentry
*mentry
;
295 char tpath
[PATH_MAX
];
302 if (lstat(path
, &sbuf
)) {
303 msg(MSG_ERROR
, "lstat failed for %s: %s\n",
304 path
, strerror(errno
));
308 mentry
= mentry_create(path
);
312 mentry_insert(mentry
, mlist
);
314 if (S_ISDIR(sbuf
.st_mode
)) {
317 msg(MSG_ERROR
, "opendir failed for %s: %s\n",
318 path
, strerror(errno
));
322 while ((dent
= readdir(dir
))) {
323 if (!strcmp(dent
->d_name
, ".") ||
324 !strcmp(dent
->d_name
, "..") ||
325 !strcmp(dent
->d_name
, ".git"))
327 snprintf(tpath
, PATH_MAX
, "%s/%s", path
, dent
->d_name
);
328 tpath
[PATH_MAX
- 1] = '\0';
329 mentries_recurse(tpath
, mlist
);
336 /* Recurses opath and adds metadata entries to the metaentry list */
338 mentries_recurse_path(const char *opath
, struct metaentry
**mlist
)
340 char *path
= normalize_path(opath
);
341 mentries_recurse(path
, mlist
);
345 /* Stores a metaentry list to a file */
347 mentries_tofile(const struct metaentry
*mlist
, const char *path
)
350 const struct metaentry
*mentry
;
353 to
= fopen(path
, "w");
355 msg(MSG_CRITICAL
, "Failed to open %s: %s\n",
356 path
, strerror(errno
));
360 write_binary_string(SIGNATURE
, SIGNATURELEN
, to
);
361 write_binary_string(VERSION
, VERSIONLEN
, to
);
363 for (mentry
= mlist
; mentry
; mentry
= mentry
->next
) {
364 write_string(mentry
->path
, to
);
365 write_string(mentry
->owner
, to
);
366 write_string(mentry
->group
, to
);
367 write_int((uint64_t)mentry
->mtime
, 8, to
);
368 write_int((uint64_t)mentry
->mtimensec
, 8, to
);
369 write_int((uint64_t)mentry
->mode
, 2, to
);
370 write_int(mentry
->xattrs
, 4, to
);
371 for (i
= 0; i
< mentry
->xattrs
; i
++) {
372 write_string(mentry
->xattr_names
[i
], to
);
373 write_int(mentry
->xattr_lvalues
[i
], 4, to
);
374 write_binary_string(mentry
->xattr_values
[i
],
375 mentry
->xattr_lvalues
[i
], to
);
382 /* Creates a metaentry list from a file */
384 mentries_fromfile(struct metaentry
**mlist
, const char *path
)
386 struct metaentry
*mentry
;
394 fd
= open(path
, O_RDONLY
);
396 msg(MSG_CRITICAL
, "Failed to open %s: %s\n",
397 path
, strerror(errno
));
401 if (fstat(fd
, &sbuf
)) {
402 msg(MSG_CRITICAL
, "Failed to stat %s: %s\n",
403 path
, strerror(errno
));
407 if (sbuf
.st_size
< (SIGNATURELEN
+ VERSIONLEN
)) {
408 msg(MSG_CRITICAL
, "File %s has an invalid size\n", path
);
412 mmapstart
= mmap(NULL
, (size_t)sbuf
.st_size
, PROT_READ
,
414 if (mmapstart
== MAP_FAILED
) {
415 msg(MSG_CRITICAL
, "Unable to mmap %s: %s\n",
416 path
, strerror(errno
));
420 max
= mmapstart
+ sbuf
.st_size
;
422 if (strncmp(ptr
, SIGNATURE
, SIGNATURELEN
)) {
423 msg(MSG_CRITICAL
, "Invalid signature for file %s\n", path
);
428 if (strncmp(ptr
, VERSION
, VERSIONLEN
)) {
429 msg(MSG_CRITICAL
, "Invalid version of file %s\n", path
);
434 while (ptr
< mmapstart
+ sbuf
.st_size
) {
436 msg(MSG_CRITICAL
, "Invalid characters in file %s\n",
441 mentry
= mentry_alloc();
442 mentry
->path
= read_string(&ptr
, max
);
443 mentry
->owner
= read_string(&ptr
, max
);
444 mentry
->group
= read_string(&ptr
, max
);
445 mentry
->mtime
= (time_t)read_int(&ptr
, 8, max
);
446 mentry
->mtimensec
= (time_t)read_int(&ptr
, 8, max
);
447 mentry
->mode
= (mode_t
)read_int(&ptr
, 2, max
);
448 mentry
->xattrs
= (unsigned int)read_int(&ptr
, 4, max
);
450 if (!mentry
->xattrs
) {
451 mentry_insert(mentry
, mlist
);
455 mentry
->xattr_names
= xmalloc(mentry
->xattrs
*
457 mentry
->xattr_lvalues
= xmalloc(mentry
->xattrs
*
459 mentry
->xattr_values
= xmalloc(mentry
->xattrs
*
462 for (i
= 0; i
< mentry
->xattrs
; i
++) {
463 mentry
->xattr_names
[i
] = read_string(&ptr
, max
);
464 mentry
->xattr_lvalues
[i
] =
465 (int)read_int(&ptr
, 4, max
);
466 mentry
->xattr_values
[i
] =
467 read_binary_string(&ptr
,
468 mentry
->xattr_lvalues
[i
],
471 mentry_insert(mentry
, mlist
);
475 munmap(mmapstart
, sbuf
.st_size
);
479 /* Finds a metaentry matching path */
480 static struct metaentry
*
481 mentry_find(const char *path
, struct metaentry
*mlist
)
485 /* FIXME - We can do a bisect search here instead */
486 for (m
= mlist
; m
; m
= m
->next
) {
487 if (!strcmp(path
, m
->path
))
493 /* Searches haystack for an xattr matching xattr number n in needle */
495 mentry_find_xattr(struct metaentry
*haystack
, struct metaentry
*needle
, int n
)
499 for (i
= 0; i
< haystack
->xattrs
; i
++) {
500 if (strcmp(haystack
->xattr_names
[i
], needle
->xattr_names
[n
]))
502 if (haystack
->xattr_lvalues
[i
] != needle
->xattr_lvalues
[n
])
504 if (bcmp(haystack
->xattr_values
[i
], needle
->xattr_values
[n
],
505 needle
->xattr_lvalues
[n
]))
512 /* Returns zero if all xattrs in left and right match */
514 mentry_compare_xattr(struct metaentry
*left
, struct metaentry
*right
)
518 if (left
->xattrs
!= right
->xattrs
)
521 /* Make sure all xattrs in left are found in right and vice versa */
522 for (i
= 0; i
< left
->xattrs
; i
++) {
523 if (mentry_find_xattr(right
, left
, i
) < 0 ||
524 mentry_find_xattr(left
, right
, i
) < 0) {
532 /* Compares two metaentries and returns an int with a bitmask of differences */
534 mentry_compare(struct metaentry
*left
, struct metaentry
*right
, int do_mtime
)
536 int retval
= DIFF_NONE
;
538 if (!left
|| !right
) {
539 msg(MSG_ERROR
, "%s called with empty list\n", __FUNCTION__
);
543 if (strcmp(left
->path
, right
->path
))
546 if (strcmp(left
->owner
, right
->owner
))
547 retval
|= DIFF_OWNER
;
549 if (strcmp(left
->group
, right
->group
))
550 retval
|= DIFF_GROUP
;
552 if ((left
->mode
& 07777) != (right
->mode
& 07777))
555 if ((left
->mode
& S_IFMT
) != (right
->mode
& S_IFMT
))
558 if (do_mtime
&& strcmp(left
->path
, METAFILE
) &&
559 (left
->mtime
!= right
->mtime
||
560 left
->mtimensec
!= right
->mtimensec
))
561 retval
|= DIFF_MTIME
;
563 if (mentry_compare_xattr(left
, right
)) {
564 retval
|= DIFF_XATTR
;
571 /* Compares lists of real and stored metadata and calls pfunc for each */
573 mentries_compare(struct metaentry
*mlistreal
,
574 struct metaentry
*mliststored
,
576 (struct metaentry
*real
, struct metaentry
*stored
, int do_mtime
),
579 struct metaentry
*real
, *stored
;
582 if (!mlistreal
|| !mliststored
) {
583 msg(MSG_ERROR
, "%s called with empty list\n", __FUNCTION__
);
587 for (real
= mlistreal
; real
; real
= real
->next
) {
588 stored
= mentry_find(real
->path
, mliststored
);
592 cmp
= mentry_compare(real
, stored
, do_mtime
);
593 pfunc(real
, stored
, cmp
);
596 for (stored
= mliststored
; stored
; stored
= stored
->next
) {
597 real
= mentry_find(stored
->path
, mlistreal
);
599 pfunc(real
, stored
, DIFF_DELE
);