metaentry.c: Fix underallocation for xattr_lvalues.
[metastore.git] / src / metaentry.c
blob2e2d7a497b356251a0cd7b92a435ec593db44e9e
1 /*
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; only version 2 of the License is applicable.
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.
20 #define _GNU_SOURCE
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #include <sys/xattr.h>
28 #include <limits.h>
29 #include <dirent.h>
30 #include <sys/mman.h>
31 #include <utime.h>
32 #include <fcntl.h>
33 #include <stdint.h>
34 #include <errno.h>
35 #include <time.h>
37 #include <sys/param.h>
38 #ifndef BSD
39 # include <bsd/string.h>
40 #endif
42 #include "metastore.h"
43 #include "metaentry.h"
44 #include "utils.h"
46 /* Free's a metaentry and all its parameters */
47 static void
48 mentry_free(struct metaentry *m)
50 unsigned i;
52 if (!m)
53 return;
55 free(m->path);
56 free(m->owner);
57 free(m->group);
59 for (i = 0; i < m->xattrs; i++) {
60 free(m->xattr_names[i]);
61 free(m->xattr_values[i]);
64 free(m->xattr_names);
65 free(m->xattr_values);
66 free(m->xattr_lvalues);
68 free(m);
71 /* Allocates an empty metahash table */
72 static struct metahash *
73 mhash_alloc()
75 struct metahash *mhash;
76 mhash = xmalloc(sizeof(struct metahash));
77 memset(mhash, 0, sizeof(struct metahash));
78 return mhash;
81 /* Generates a hash key (using djb2) */
82 static unsigned
83 hash(const char *str)
85 unsigned hash = 5381;
86 int c;
88 while ((c = *str++))
89 hash = ((hash << 5) + hash) + c;
91 return hash % HASH_INDEXES;
94 /* Allocates an empty metaentry */
95 static struct metaentry *
96 mentry_alloc()
98 struct metaentry *mentry;
99 mentry = xmalloc(sizeof(struct metaentry));
100 memset(mentry, 0, sizeof(struct metaentry));
101 return mentry;
104 /* Does a bisect search for the closest match in a metaentry list */
105 struct metaentry *
106 mentry_find(const char *path, struct metahash *mhash)
108 struct metaentry *base;
109 unsigned key;
111 if (!mhash) {
112 msg(MSG_ERROR, "%s called with empty hash table\n", __func__);
113 return NULL;
116 key = hash(path);
117 for (base = mhash->bucket[key]; base; base = base->next) {
118 if (!strcmp(base->path, path))
119 return base;
122 return NULL;
125 /* Inserts a metaentry into a metaentry list */
126 static void
127 mentry_insert(struct metaentry *mentry, struct metahash *mhash)
129 unsigned key;
131 key = hash(mentry->path);
132 mentry->next = mhash->bucket[key];
133 mhash->bucket[key] = mentry;
136 #ifdef DEBUG
137 /* Prints a metaentry */
138 static void
139 mentry_print(const struct metaentry *mentry)
141 int i;
143 if (!mentry || !mentry->path) {
144 msg(MSG_DEBUG,
145 "Incorrect meta entry passed to printmetaentry\n");
146 return;
149 msg(MSG_DEBUG, "===========================\n");
150 msg(MSG_DEBUG, "Dump of metaentry %p\n", mentry);
151 msg(MSG_DEBUG, "===========================\n");
153 msg(MSG_DEBUG, "path\t\t: %s\n", mentry->path);
154 msg(MSG_DEBUG, "owner\t\t: %s\n", mentry->owner);
155 msg(MSG_DEBUG, "group\t\t: %s\n", mentry->group);
156 msg(MSG_DEBUG, "mtime\t\t: %ld\n", (unsigned long)mentry->mtime);
157 msg(MSG_DEBUG, "mtimensec\t: %ld\n", (unsigned long)mentry->mtimensec);
158 msg(MSG_DEBUG, "mode\t\t: %ld\n", (unsigned long)mentry->mode);
159 for (i = 0; i < mentry->xattrs; i++) {
160 msg(MSG_DEBUG, "xattr[%i]\t: %s=\"", i, mentry->xattr_names[i]);
161 binary_print(mentry->xattr_values[i], mentry->xattr_lvalues[i]);
162 msg(MSG_DEBUG, "\"\n");
165 msg(MSG_DEBUG, "===========================\n\n");
168 /* Prints all metaentries in a metaentry list */
169 static void
170 mentries_print(const struct metahash *mhash)
172 const struct metaentry *mentry;
173 int index;
175 for (index = 0; index < HASH_INDEXES; index++)
176 for (mentry = mhash->bucket[index]; mentry; mentry = mentry->next)
177 mentry_print(mentry);
179 msg(MSG_DEBUG, "%i entries in total\n", mhash->count);
181 #endif
183 /* Creates a metaentry for the file/dir/etc at path */
184 struct metaentry *
185 mentry_create(const char *path)
187 ssize_t lsize, vsize;
188 char *list, *attr;
189 struct stat sbuf;
190 struct passwd *pbuf;
191 struct group *gbuf;
192 int i;
193 struct metaentry *mentry;
195 if (lstat(path, &sbuf)) {
196 msg(MSG_ERROR, "lstat failed for %s: %s\n",
197 path, strerror(errno));
198 return NULL;
201 pbuf = xgetpwuid(sbuf.st_uid);
202 if (!pbuf) {
203 msg(MSG_ERROR, "getpwuid failed for %s: uid %i not found\n",
204 path, (int)sbuf.st_uid);
205 return NULL;
208 gbuf = xgetgrgid(sbuf.st_gid);
209 if (!gbuf) {
210 msg(MSG_ERROR, "getgrgid failed for %s: gid %i not found\n",
211 path, (int)sbuf.st_gid);
212 return NULL;
215 mentry = mentry_alloc();
216 mentry->path = xstrdup(path);
217 mentry->pathlen = strlen(mentry->path);
218 mentry->owner = xstrdup(pbuf->pw_name);
219 mentry->group = xstrdup(gbuf->gr_name);
220 mentry->mode = sbuf.st_mode & 0177777;
221 mentry->mtime = sbuf.st_mtim.tv_sec;
222 mentry->mtimensec = sbuf.st_mtim.tv_nsec;
224 /* symlinks have no xattrs */
225 if (S_ISLNK(mentry->mode))
226 return mentry;
228 lsize = listxattr(path, NULL, 0);
229 if (lsize < 0) {
230 /* Perhaps the FS doesn't support xattrs? */
231 if (errno == ENOTSUP)
232 return mentry;
234 msg(MSG_ERROR, "listxattr failed for %s: %s\n",
235 path, strerror(errno));
236 return NULL;
239 list = xmalloc(lsize);
240 lsize = listxattr(path, list, lsize);
241 if (lsize < 0) {
242 msg(MSG_ERROR, "listxattr failed for %s: %s\n",
243 path, strerror(errno));
244 free(list);
245 return NULL;
248 i = 0;
249 for (attr = list; attr < list + lsize; attr = strchr(attr, '\0') + 1) {
250 if (*attr == '\0')
251 continue;
252 i++;
255 if (i == 0)
256 return mentry;
258 mentry->xattrs = i;
259 mentry->xattr_names = xmalloc(i * sizeof(char *));
260 mentry->xattr_values = xmalloc(i * sizeof(char *));
261 mentry->xattr_lvalues = xmalloc(i * sizeof(ssize_t));
263 i = 0;
264 for (attr = list; attr < list + lsize; attr = strchr(attr, '\0') + 1) {
265 if (*attr == '\0')
266 continue;
268 mentry->xattr_names[i] = xstrdup(attr);
269 mentry->xattr_values[i] = NULL;
271 vsize = getxattr(path, attr, NULL, 0);
272 if (vsize < 0) {
273 msg(MSG_ERROR, "getxattr failed for %s: %s\n",
274 path, strerror(errno));
275 free(list);
276 mentry->xattrs = i + 1;
277 mentry_free(mentry);
278 return NULL;
281 mentry->xattr_lvalues[i] = vsize;
282 mentry->xattr_values[i] = xmalloc(vsize);
284 vsize = getxattr(path, attr, mentry->xattr_values[i], vsize);
285 if (vsize < 0) {
286 msg(MSG_ERROR, "getxattr failed for %s: %s\n",
287 path, strerror(errno));
288 free(list);
289 mentry->xattrs = i + 1;
290 mentry_free(mentry);
291 return NULL;
293 i++;
296 free(list);
297 return mentry;
300 /* Cleans up a path and makes it relative to cwd unless it is absolute */
301 static char *
302 normalize_path(const char *orig)
304 char *real = realpath(orig, NULL);
305 char cwd[PATH_MAX];
306 char *result;
308 getcwd(cwd, PATH_MAX);
309 if (!real)
310 return NULL;
312 if (!strncmp(real, cwd, strlen(cwd))) {
313 result = xmalloc(strlen(real) - strlen(cwd) + 1 + 1);
314 result[0] = '\0';
315 strcat(result, ".");
316 strcat(result, real + strlen(cwd));
317 } else {
318 result = xstrdup(real);
321 free(real);
322 return result;
325 /* Internal function for the recursive path walk */
326 static void
327 mentries_recurse(const char *path, struct metahash *mhash, msettings *st)
329 struct stat sbuf;
330 struct metaentry *mentry;
331 char tpath[PATH_MAX];
332 DIR *dir;
333 struct dirent *dent;
335 if (!path)
336 return;
338 if (lstat(path, &sbuf)) {
339 msg(MSG_ERROR, "lstat failed for %s: %s\n",
340 path, strerror(errno));
341 return;
344 mentry = mentry_create(path);
345 if (!mentry)
346 return;
348 mentry_insert(mentry, mhash);
350 if (S_ISDIR(sbuf.st_mode)) {
351 dir = opendir(path);
352 if (!dir) {
353 msg(MSG_ERROR, "opendir failed for %s: %s\n",
354 path, strerror(errno));
355 return;
358 while ((dent = readdir(dir))) {
359 if (!strcmp(dent->d_name, ".") ||
360 !strcmp(dent->d_name, "..") ||
361 (!st->do_git && !strcmp(dent->d_name, ".git"))
363 continue;
364 snprintf(tpath, PATH_MAX, "%s/%s", path, dent->d_name);
365 tpath[PATH_MAX - 1] = '\0';
366 mentries_recurse(tpath, mhash, st);
369 closedir(dir);
373 /* Recurses opath and adds metadata entries to the metaentry list */
374 void
375 mentries_recurse_path(const char *opath, struct metahash **mhash, msettings *st)
377 char *path = normalize_path(opath);
379 if (!(*mhash))
380 *mhash = mhash_alloc();
381 mentries_recurse(path, *mhash, st);
382 free(path);
385 /* Stores metaentries to a file */
386 void
387 mentries_tofile(const struct metahash *mhash, const char *path)
389 FILE *to;
390 const struct metaentry *mentry;
391 int key;
392 unsigned i;
394 to = fopen(path, "w");
395 if (!to) {
396 msg(MSG_CRITICAL, "Failed to open %s: %s\n",
397 path, strerror(errno));
398 exit(EXIT_FAILURE);
401 write_binary_string(SIGNATURE, SIGNATURELEN, to);
402 write_binary_string(VERSION, VERSIONLEN, to);
404 for (key = 0; key < HASH_INDEXES; key++) {
405 for (mentry = mhash->bucket[key]; mentry; mentry = mentry->next) {
406 write_string(mentry->path, to);
407 write_string(mentry->owner, to);
408 write_string(mentry->group, to);
409 write_int((uint64_t)mentry->mtime, 8, to);
410 write_int((uint64_t)mentry->mtimensec, 8, to);
411 write_int((uint64_t)mentry->mode, 2, to);
412 write_int(mentry->xattrs, 4, to);
413 for (i = 0; i < mentry->xattrs; i++) {
414 write_string(mentry->xattr_names[i], to);
415 write_int(mentry->xattr_lvalues[i], 4, to);
416 write_binary_string(mentry->xattr_values[i],
417 mentry->xattr_lvalues[i], to);
422 fclose(to);
425 /* Creates a metaentry list from a file */
426 void
427 mentries_fromfile(struct metahash **mhash, const char *path)
429 struct metaentry *mentry;
430 char *mmapstart;
431 char *ptr;
432 char *max;
433 int fd;
434 struct stat sbuf;
435 unsigned i;
437 if (!(*mhash))
438 *mhash = mhash_alloc();
440 fd = open(path, O_RDONLY);
441 if (fd < 0) {
442 msg(MSG_CRITICAL, "Failed to open %s: %s\n",
443 path, strerror(errno));
444 exit(EXIT_FAILURE);
447 if (fstat(fd, &sbuf)) {
448 msg(MSG_CRITICAL, "Failed to stat %s: %s\n",
449 path, strerror(errno));
450 exit(EXIT_FAILURE);
453 if (sbuf.st_size < (SIGNATURELEN + VERSIONLEN)) {
454 msg(MSG_CRITICAL, "File %s has an invalid size\n", path);
455 exit(EXIT_FAILURE);
458 mmapstart = mmap(NULL, (size_t)sbuf.st_size, PROT_READ,
459 MAP_SHARED, fd, 0);
460 if (mmapstart == MAP_FAILED) {
461 msg(MSG_CRITICAL, "Unable to mmap %s: %s\n",
462 path, strerror(errno));
463 exit(EXIT_FAILURE);
465 ptr = mmapstart;
466 max = mmapstart + sbuf.st_size;
468 if (strncmp(ptr, SIGNATURE, SIGNATURELEN)) {
469 msg(MSG_CRITICAL, "Invalid signature for file %s\n", path);
470 goto out;
472 ptr += SIGNATURELEN;
474 if (strncmp(ptr, VERSION, VERSIONLEN)) {
475 msg(MSG_CRITICAL, "Invalid version of file %s\n", path);
476 goto out;
478 ptr += VERSIONLEN;
480 while (ptr < mmapstart + sbuf.st_size) {
481 if (*ptr == '\0') {
482 msg(MSG_CRITICAL, "Invalid characters in file %s\n",
483 path);
484 goto out;
487 mentry = mentry_alloc();
488 mentry->path = read_string(&ptr, max);
489 mentry->pathlen = strlen(mentry->path);
490 mentry->owner = read_string(&ptr, max);
491 mentry->group = read_string(&ptr, max);
492 mentry->mtime = (time_t)read_int(&ptr, 8, max);
493 mentry->mtimensec = (time_t)read_int(&ptr, 8, max);
494 mentry->mode = (mode_t)read_int(&ptr, 2, max);
495 mentry->xattrs = (unsigned)read_int(&ptr, 4, max);
497 if (!mentry->xattrs) {
498 mentry_insert(mentry, *mhash);
499 continue;
502 mentry->xattr_names = xmalloc(mentry->xattrs * sizeof(char *));
503 mentry->xattr_lvalues = xmalloc(mentry->xattrs * sizeof(ssize_t));
504 mentry->xattr_values = xmalloc(mentry->xattrs * sizeof(char *));
506 for (i = 0; i < mentry->xattrs; i++) {
507 mentry->xattr_names[i] = read_string(&ptr, max);
508 mentry->xattr_lvalues[i] = (int)read_int(&ptr, 4, max);
509 mentry->xattr_values[i] = read_binary_string(
510 &ptr,
511 mentry->xattr_lvalues[i],
515 mentry_insert(mentry, *mhash);
518 out:
519 munmap(mmapstart, sbuf.st_size);
520 close(fd);
523 /* Searches haystack for an xattr matching xattr number n in needle */
525 mentry_find_xattr(struct metaentry *haystack, struct metaentry *needle,
526 unsigned n)
528 unsigned i;
530 for (i = 0; i < haystack->xattrs; i++) {
531 if (strcmp(haystack->xattr_names[i], needle->xattr_names[n]))
532 continue;
533 if (haystack->xattr_lvalues[i] != needle->xattr_lvalues[n])
534 return -1;
535 if (bcmp(haystack->xattr_values[i], needle->xattr_values[n],
536 needle->xattr_lvalues[n])
538 return -1;
539 return i;
541 return -1;
544 /* Returns zero if all xattrs in left and right match */
545 static int
546 mentry_compare_xattr(struct metaentry *left, struct metaentry *right)
548 unsigned i;
550 if (left->xattrs != right->xattrs)
551 return 1;
553 /* Make sure all xattrs in left are found in right and vice versa */
554 for (i = 0; i < left->xattrs; i++) {
555 if ( mentry_find_xattr(right, left, i) < 0
556 || mentry_find_xattr(left, right, i) < 0
558 return 1;
562 return 0;
565 /* Compares two metaentries and returns an int with a bitmask of differences */
567 mentry_compare(struct metaentry *left, struct metaentry *right, msettings *st)
569 int retval = DIFF_NONE;
571 if (!left || !right) {
572 msg(MSG_ERROR, "%s called with empty list\n", __func__);
573 return -1;
576 if (strcmp(left->path, right->path))
577 return -1;
579 if (strcmp(left->owner, right->owner))
580 retval |= DIFF_OWNER;
582 if (strcmp(left->group, right->group))
583 retval |= DIFF_GROUP;
585 if ((left->mode & 07777) != (right->mode & 07777))
586 retval |= DIFF_MODE;
588 if ((left->mode & S_IFMT) != (right->mode & S_IFMT))
589 retval |= DIFF_TYPE;
591 if (st->do_mtime && strcmp(left->path, st->metafile) &&
592 ( left->mtime != right->mtime
593 || left->mtimensec != right->mtimensec)
595 retval |= DIFF_MTIME;
597 if (mentry_compare_xattr(left, right)) {
598 retval |= DIFF_XATTR;
599 return retval;
602 return retval;
605 /* Compares lists of real and stored metadata and calls pfunc for each */
606 void
607 mentries_compare(struct metahash *mhashreal,
608 struct metahash *mhashstored,
609 void (*pfunc)
610 (struct metaentry *real, struct metaentry *stored, int cmp),
611 msettings *st)
613 struct metaentry *real, *stored;
614 int key;
616 if (!mhashreal || !mhashstored) {
617 msg(MSG_ERROR, "%s called with empty list\n", __func__);
618 return;
621 for (key = 0; key < HASH_INDEXES; key++) {
622 for (real = mhashreal->bucket[key]; real; real = real->next) {
623 stored = mentry_find(real->path, mhashstored);
625 if (!stored)
626 pfunc(real, NULL, DIFF_ADDED);
627 else
628 pfunc(real, stored, mentry_compare(real, stored, st));
631 for (stored = mhashstored->bucket[key]; stored; stored = stored->next) {
632 real = mentry_find(stored->path, mhashreal);
634 if (!real)
635 pfunc(NULL, stored, DIFF_DELE);
640 /* Dumps given metadata */
641 void
642 mentries_dump(struct metahash *mhash)
644 const struct metaentry *mentry;
645 char mode[11 + 1] = "";
646 char date[12 + 2 + 2 + 2*1 + 1 + 2 + 2 + 2 + 2*1 + 1] = "";
647 char zone[5 + 1] = "";
648 struct tm cal;
650 for (int key = 0; key < HASH_INDEXES; key++) {
651 for (mentry = mhash->bucket[key]; mentry; mentry = mentry->next) {
652 strmode(mentry->mode, mode);
653 localtime_r(&mentry->mtime, &cal);
654 strftime(date, sizeof(date), "%F %T", &cal);
655 strftime(zone, sizeof(zone), "%z", &cal);
656 printf("%s\t%s\t%s\t%s.%09ld %s\t%s%s\n",
657 mode,
658 mentry->owner, mentry->group,
659 date, mentry->mtimensec, zone,
660 mentry->path, S_ISDIR(mentry->mode) ? "/" : "");
661 for (unsigned i = 0; i < mentry->xattrs; i++) {
662 printf("\t\t\t\t%s%s\t%s=",
663 mentry->path, S_ISDIR(mentry->mode) ? "/" : "",
664 mentry->xattr_names[i]);
665 ssize_t p = 0;
666 for (; p < mentry->xattr_lvalues[i]; p++) {
667 const char ch = mentry->xattr_values[i][p];
668 if ((unsigned)(ch - 32) > 126 - 32) {
669 p = -1;
670 break;
673 if (p >= 0)
674 printf("\"%.*s\"\n",
675 (int)mentry->xattr_lvalues[i],
676 mentry->xattr_values[i]);
677 else {
678 printf("0x");
679 for (p = 0; p < mentry->xattr_lvalues[i]; p++)
680 printf("%02hhx", (char)mentry->xattr_values[i][p]);
681 printf("\n");