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>
28 #include <attr/xattr.h>
36 #include "metastore.h"
37 #include "metaentry.h"
40 /* Free's a metaentry and all its parameters */
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
);
65 /* Allocates an empty metahash table */
66 static struct metahash
*
69 struct metahash
*mhash
;
70 mhash
= xmalloc(sizeof(struct metahash
));
71 memset(mhash
, 0, sizeof(struct metahash
));
75 /* Generates a hash key (using djb2) */
79 unsigned int hash
= 5381;
83 hash
= ((hash
<< 5) + hash
) + c
;
85 return hash
% HASH_INDEXES
;
88 /* Allocates an empty metaentry */
89 static struct metaentry
*
92 struct metaentry
*mentry
;
93 mentry
= xmalloc(sizeof(struct metaentry
));
94 memset(mentry
, 0, sizeof(struct metaentry
));
98 /* Does a bisect search for the closest match in a metaentry list */
100 mentry_find(const char *path
, struct metahash
*mhash
)
102 struct metaentry
*base
;
106 msg(MSG_ERROR
, "%s called with empty hash table\n", __FUNCTION__
);
111 for (base
= mhash
->bucket
[key
]; base
; base
= base
->next
) {
112 if (!strcmp(base
->path
, path
))
119 /* Inserts a metaentry into a metaentry list */
121 mentry_insert(struct metaentry
*mentry
, struct metahash
*mhash
)
125 key
= hash(mentry
->path
);
126 mentry
->next
= mhash
->bucket
[key
];
127 mhash
->bucket
[key
] = mentry
;
131 /* Prints a metaentry */
133 mentry_print(const struct metaentry
*mentry
)
137 if (!mentry
|| !mentry
->path
) {
139 "Incorrect meta entry passed to printmetaentry\n");
143 msg(MSG_DEBUG
, "===========================\n");
144 msg(MSG_DEBUG
, "Dump of metaentry %p\n", mentry
);
145 msg(MSG_DEBUG
, "===========================\n");
147 msg(MSG_DEBUG
, "path\t\t: %s\n", mentry
->path
);
148 msg(MSG_DEBUG
, "owner\t\t: %s\n", mentry
->owner
);
149 msg(MSG_DEBUG
, "group\t\t: %s\n", mentry
->group
);
150 msg(MSG_DEBUG
, "mtime\t\t: %ld\n", (unsigned long)mentry
->mtime
);
151 msg(MSG_DEBUG
, "mtimensec\t: %ld\n", (unsigned long)mentry
->mtimensec
);
152 msg(MSG_DEBUG
, "mode\t\t: %ld\n", (unsigned long)mentry
->mode
);
153 for (i
= 0; i
< mentry
->xattrs
; i
++) {
154 msg(MSG_DEBUG
, "xattr[%i]\t: %s=\"", i
, mentry
->xattr_names
[i
]);
155 binary_print(mentry
->xattr_values
[i
], mentry
->xattr_lvalues
[i
]);
156 msg(MSG_DEBUG
, "\"\n");
159 msg(MSG_DEBUG
, "===========================\n\n");
162 /* Prints all metaentries in a metaentry list */
164 mentries_print(const struct metahash
*mhash
)
166 const struct metaentry
*mentry
;
169 for (index
= 0; index
< HASH_INDEXES
; index
++)
170 for (mentry
= mhash
->bucket
[i
]; mentry
; mentry
= mentry
->next
)
171 mentry_print(mentry
);
173 msg(MSG_DEBUG
, "%i entries in total\n", mhash
->count
);
177 /* Creates a metaentry for the file/dir/etc at path */
178 static struct metaentry
*
179 mentry_create(const char *path
)
181 ssize_t lsize
, vsize
;
187 struct metaentry
*mentry
;
189 if (lstat(path
, &sbuf
)) {
190 msg(MSG_ERROR
, "lstat failed for %s: %s\n",
191 path
, strerror(errno
));
195 pbuf
= xgetpwuid(sbuf
.st_uid
);
197 msg(MSG_ERROR
, "getpwuid failed for %i: %s\n",
198 (int)sbuf
.st_uid
, strerror(errno
));
202 gbuf
= xgetgrgid(sbuf
.st_gid
);
204 msg(MSG_ERROR
, "getgrgid failed for %i: %s\n",
205 (int)sbuf
.st_gid
, strerror(errno
));
209 mentry
= mentry_alloc();
210 mentry
->path
= xstrdup(path
);
211 mentry
->owner
= xstrdup(pbuf
->pw_name
);
212 mentry
->group
= xstrdup(gbuf
->gr_name
);
213 mentry
->mode
= sbuf
.st_mode
& 0177777;
214 mentry
->mtime
= sbuf
.st_mtim
.tv_sec
;
215 mentry
->mtimensec
= sbuf
.st_mtim
.tv_nsec
;
217 /* symlinks have no xattrs */
218 if (S_ISLNK(mentry
->mode
))
221 lsize
= listxattr(path
, NULL
, 0);
223 msg(MSG_ERROR
, "listxattr failed for %s: %s\n",
224 path
, strerror(errno
));
228 list
= xmalloc(lsize
);
229 lsize
= listxattr(path
, list
, lsize
);
231 msg(MSG_ERROR
, "listxattr failed for %s: %s\n",
232 path
, strerror(errno
));
238 for (attr
= list
; attr
< list
+ lsize
; attr
= strchr(attr
, '\0') + 1) {
248 mentry
->xattr_names
= xmalloc(i
* sizeof(char *));
249 mentry
->xattr_values
= xmalloc(i
* sizeof(char *));
250 mentry
->xattr_lvalues
= xmalloc(i
* sizeof(ssize_t
));
253 for (attr
= list
; attr
< list
+ lsize
; attr
= strchr(attr
, '\0') + 1) {
257 mentry
->xattr_names
[i
] = xstrdup(attr
);
258 vsize
= getxattr(path
, attr
, NULL
, 0);
260 msg(MSG_ERROR
, "getxattr failed for %s: %s\n",
261 path
, strerror(errno
));
267 mentry
->xattr_lvalues
[i
] = vsize
;
268 mentry
->xattr_values
[i
] = xmalloc(vsize
);
270 vsize
= getxattr(path
, attr
, mentry
->xattr_values
[i
], vsize
);
272 msg(MSG_ERROR
, "getxattr failed for %s: %s\n",
273 path
, strerror(errno
));
285 /* Cleans up a path and makes it relative to current working dir unless it is absolute */
287 normalize_path(const char *orig
)
289 char *real
= canonicalize_file_name(orig
);
293 getcwd(cwd
, PATH_MAX
);
297 if (!strncmp(real
, cwd
, strlen(cwd
))) {
298 result
= xmalloc(strlen(real
) - strlen(cwd
) + 1 + 1);
301 strcat(result
, real
+ strlen(cwd
));
303 result
= xstrdup(real
);
310 /* Internal function for the recursive path walk */
312 mentries_recurse(const char *path
, struct metahash
*mhash
)
315 struct metaentry
*mentry
;
316 char tpath
[PATH_MAX
];
323 if (lstat(path
, &sbuf
)) {
324 msg(MSG_ERROR
, "lstat failed for %s: %s\n",
325 path
, strerror(errno
));
329 mentry
= mentry_create(path
);
333 mentry_insert(mentry
, mhash
);
335 if (S_ISDIR(sbuf
.st_mode
)) {
338 msg(MSG_ERROR
, "opendir failed for %s: %s\n",
339 path
, strerror(errno
));
343 while ((dent
= readdir(dir
))) {
344 if (!strcmp(dent
->d_name
, ".") ||
345 !strcmp(dent
->d_name
, "..") ||
346 !strcmp(dent
->d_name
, ".git"))
348 snprintf(tpath
, PATH_MAX
, "%s/%s", path
, dent
->d_name
);
349 tpath
[PATH_MAX
- 1] = '\0';
350 mentries_recurse(tpath
, mhash
);
357 /* Recurses opath and adds metadata entries to the metaentry list */
359 mentries_recurse_path(const char *opath
, struct metahash
**mhash
)
361 char *path
= normalize_path(opath
);
364 *mhash
= mhash_alloc();
365 mentries_recurse(path
, *mhash
);
369 /* Stores metaentries to a file */
371 mentries_tofile(const struct metahash
*mhash
, const char *path
)
374 const struct metaentry
*mentry
;
377 to
= fopen(path
, "w");
379 msg(MSG_CRITICAL
, "Failed to open %s: %s\n",
380 path
, strerror(errno
));
384 write_binary_string(SIGNATURE
, SIGNATURELEN
, to
);
385 write_binary_string(VERSION
, VERSIONLEN
, to
);
387 for (key
= 0; key
< HASH_INDEXES
; key
++) {
388 for (mentry
= mhash
->bucket
[key
]; mentry
; mentry
= mentry
->next
) {
389 write_string(mentry
->path
, to
);
390 write_string(mentry
->owner
, to
);
391 write_string(mentry
->group
, to
);
392 write_int((uint64_t)mentry
->mtime
, 8, to
);
393 write_int((uint64_t)mentry
->mtimensec
, 8, to
);
394 write_int((uint64_t)mentry
->mode
, 2, to
);
395 write_int(mentry
->xattrs
, 4, to
);
396 for (i
= 0; i
< mentry
->xattrs
; i
++) {
397 write_string(mentry
->xattr_names
[i
], to
);
398 write_int(mentry
->xattr_lvalues
[i
], 4, to
);
399 write_binary_string(mentry
->xattr_values
[i
],
400 mentry
->xattr_lvalues
[i
], to
);
408 /* Creates a metaentry list from a file */
410 mentries_fromfile(struct metahash
**mhash
, const char *path
)
412 struct metaentry
*mentry
;
421 *mhash
= mhash_alloc();
423 fd
= open(path
, O_RDONLY
);
425 msg(MSG_CRITICAL
, "Failed to open %s: %s\n",
426 path
, strerror(errno
));
430 if (fstat(fd
, &sbuf
)) {
431 msg(MSG_CRITICAL
, "Failed to stat %s: %s\n",
432 path
, strerror(errno
));
436 if (sbuf
.st_size
< (SIGNATURELEN
+ VERSIONLEN
)) {
437 msg(MSG_CRITICAL
, "File %s has an invalid size\n", path
);
441 mmapstart
= mmap(NULL
, (size_t)sbuf
.st_size
, PROT_READ
,
443 if (mmapstart
== MAP_FAILED
) {
444 msg(MSG_CRITICAL
, "Unable to mmap %s: %s\n",
445 path
, strerror(errno
));
449 max
= mmapstart
+ sbuf
.st_size
;
451 if (strncmp(ptr
, SIGNATURE
, SIGNATURELEN
)) {
452 msg(MSG_CRITICAL
, "Invalid signature for file %s\n", path
);
457 if (strncmp(ptr
, VERSION
, VERSIONLEN
)) {
458 msg(MSG_CRITICAL
, "Invalid version of file %s\n", path
);
463 while (ptr
< mmapstart
+ sbuf
.st_size
) {
465 msg(MSG_CRITICAL
, "Invalid characters in file %s\n",
470 mentry
= mentry_alloc();
471 mentry
->path
= read_string(&ptr
, max
);
472 mentry
->owner
= read_string(&ptr
, max
);
473 mentry
->group
= read_string(&ptr
, max
);
474 mentry
->mtime
= (time_t)read_int(&ptr
, 8, max
);
475 mentry
->mtimensec
= (time_t)read_int(&ptr
, 8, max
);
476 mentry
->mode
= (mode_t
)read_int(&ptr
, 2, max
);
477 mentry
->xattrs
= (unsigned int)read_int(&ptr
, 4, max
);
479 if (!mentry
->xattrs
) {
480 mentry_insert(mentry
, *mhash
);
484 mentry
->xattr_names
= xmalloc(mentry
->xattrs
*
486 mentry
->xattr_lvalues
= xmalloc(mentry
->xattrs
*
488 mentry
->xattr_values
= xmalloc(mentry
->xattrs
*
491 for (i
= 0; i
< mentry
->xattrs
; i
++) {
492 mentry
->xattr_names
[i
] = read_string(&ptr
, max
);
493 mentry
->xattr_lvalues
[i
] =
494 (int)read_int(&ptr
, 4, max
);
495 mentry
->xattr_values
[i
] =
496 read_binary_string(&ptr
,
497 mentry
->xattr_lvalues
[i
],
500 mentry_insert(mentry
, *mhash
);
504 munmap(mmapstart
, sbuf
.st_size
);
508 /* Searches haystack for an xattr matching xattr number n in needle */
510 mentry_find_xattr(struct metaentry
*haystack
, struct metaentry
*needle
, int n
)
514 for (i
= 0; i
< haystack
->xattrs
; i
++) {
515 if (strcmp(haystack
->xattr_names
[i
], needle
->xattr_names
[n
]))
517 if (haystack
->xattr_lvalues
[i
] != needle
->xattr_lvalues
[n
])
519 if (bcmp(haystack
->xattr_values
[i
], needle
->xattr_values
[n
],
520 needle
->xattr_lvalues
[n
]))
527 /* Returns zero if all xattrs in left and right match */
529 mentry_compare_xattr(struct metaentry
*left
, struct metaentry
*right
)
533 if (left
->xattrs
!= right
->xattrs
)
536 /* Make sure all xattrs in left are found in right and vice versa */
537 for (i
= 0; i
< left
->xattrs
; i
++) {
538 if (mentry_find_xattr(right
, left
, i
) < 0 ||
539 mentry_find_xattr(left
, right
, i
) < 0) {
547 /* Compares two metaentries and returns an int with a bitmask of differences */
549 mentry_compare(struct metaentry
*left
, struct metaentry
*right
, int do_mtime
)
551 int retval
= DIFF_NONE
;
553 if (!left
|| !right
) {
554 msg(MSG_ERROR
, "%s called with empty list\n", __FUNCTION__
);
558 if (strcmp(left
->path
, right
->path
))
561 if (strcmp(left
->owner
, right
->owner
))
562 retval
|= DIFF_OWNER
;
564 if (strcmp(left
->group
, right
->group
))
565 retval
|= DIFF_GROUP
;
567 if ((left
->mode
& 07777) != (right
->mode
& 07777))
570 if ((left
->mode
& S_IFMT
) != (right
->mode
& S_IFMT
))
573 if (do_mtime
&& strcmp(left
->path
, METAFILE
) &&
574 (left
->mtime
!= right
->mtime
||
575 left
->mtimensec
!= right
->mtimensec
))
576 retval
|= DIFF_MTIME
;
578 if (mentry_compare_xattr(left
, right
)) {
579 retval
|= DIFF_XATTR
;
586 /* Compares lists of real and stored metadata and calls pfunc for each */
588 mentries_compare(struct metahash
*mhashreal
,
589 struct metahash
*mhashstored
,
591 (struct metaentry
*real
, struct metaentry
*stored
, int do_mtime
),
594 struct metaentry
*real
, *stored
;
597 if (!mhashreal
|| !mhashstored
) {
598 msg(MSG_ERROR
, "%s called with empty list\n", __FUNCTION__
);
602 for (key
= 0; key
< HASH_INDEXES
; key
++) {
603 for (real
= mhashreal
->bucket
[key
]; real
; real
= real
->next
) {
604 stored
= mentry_find(real
->path
, mhashstored
);
607 pfunc(real
, NULL
, DIFF_ADDED
);
609 pfunc(real
, stored
, mentry_compare(real
, stored
, do_mtime
));
612 for (stored
= mhashstored
->bucket
[key
]; stored
; stored
= stored
->next
) {
613 real
= mentry_find(stored
->path
, mhashreal
);
616 pfunc(NULL
, stored
, DIFF_DELE
);