metastore.c: Remove unused parameters from functions.
[metastore.git] / metaentry.c
blobde8b6e0731b2a00f7bfc2e114bc66794c01c973d
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 version 2 of the License.
9 *
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.
21 #define _GNU_SOURCE
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <sys/xattr.h>
29 #include <limits.h>
30 #include <dirent.h>
31 #include <sys/mman.h>
32 #include <utime.h>
33 #include <fcntl.h>
34 #include <stdint.h>
35 #include <errno.h>
37 #include "metastore.h"
38 #include "metaentry.h"
39 #include "utils.h"
41 /* Free's a metaentry and all its parameters */
42 static void
43 mentry_free(struct metaentry *m)
45 unsigned i;
47 if (!m)
48 return;
50 free(m->path);
51 free(m->owner);
52 free(m->group);
54 for (i = 0; i < m->xattrs; i++) {
55 free(m->xattr_names[i]);
56 free(m->xattr_values[i]);
59 free(m->xattr_names);
60 free(m->xattr_values);
61 free(m->xattr_lvalues);
63 free(m);
66 /* Allocates an empty metahash table */
67 static struct metahash *
68 mhash_alloc()
70 struct metahash *mhash;
71 mhash = xmalloc(sizeof(struct metahash));
72 memset(mhash, 0, sizeof(struct metahash));
73 return mhash;
76 /* Generates a hash key (using djb2) */
77 static unsigned int
78 hash(const char *str)
80 unsigned int hash = 5381;
81 int c;
83 while ((c = *str++))
84 hash = ((hash << 5) + hash) + c;
86 return hash % HASH_INDEXES;
89 /* Allocates an empty metaentry */
90 static struct metaentry *
91 mentry_alloc()
93 struct metaentry *mentry;
94 mentry = xmalloc(sizeof(struct metaentry));
95 memset(mentry, 0, sizeof(struct metaentry));
96 return mentry;
99 /* Does a bisect search for the closest match in a metaentry list */
100 struct metaentry *
101 mentry_find(const char *path, struct metahash *mhash)
103 struct metaentry *base;
104 unsigned int key;
106 if (!mhash) {
107 msg(MSG_ERROR, "%s called with empty hash table\n", __FUNCTION__);
108 return NULL;
111 key = hash(path);
112 for (base = mhash->bucket[key]; base; base = base->next) {
113 if (!strcmp(base->path, path))
114 return base;
117 return NULL;
120 /* Inserts a metaentry into a metaentry list */
121 static void
122 mentry_insert(struct metaentry *mentry, struct metahash *mhash)
124 unsigned int key;
126 key = hash(mentry->path);
127 mentry->next = mhash->bucket[key];
128 mhash->bucket[key] = mentry;
131 #ifdef DEBUG
132 /* Prints a metaentry */
133 static void
134 mentry_print(const struct metaentry *mentry)
136 int i;
138 if (!mentry || !mentry->path) {
139 msg(MSG_DEBUG,
140 "Incorrect meta entry passed to printmetaentry\n");
141 return;
144 msg(MSG_DEBUG, "===========================\n");
145 msg(MSG_DEBUG, "Dump of metaentry %p\n", mentry);
146 msg(MSG_DEBUG, "===========================\n");
148 msg(MSG_DEBUG, "path\t\t: %s\n", mentry->path);
149 msg(MSG_DEBUG, "owner\t\t: %s\n", mentry->owner);
150 msg(MSG_DEBUG, "group\t\t: %s\n", mentry->group);
151 msg(MSG_DEBUG, "mtime\t\t: %ld\n", (unsigned long)mentry->mtime);
152 msg(MSG_DEBUG, "mtimensec\t: %ld\n", (unsigned long)mentry->mtimensec);
153 msg(MSG_DEBUG, "mode\t\t: %ld\n", (unsigned long)mentry->mode);
154 for (i = 0; i < mentry->xattrs; i++) {
155 msg(MSG_DEBUG, "xattr[%i]\t: %s=\"", i, mentry->xattr_names[i]);
156 binary_print(mentry->xattr_values[i], mentry->xattr_lvalues[i]);
157 msg(MSG_DEBUG, "\"\n");
160 msg(MSG_DEBUG, "===========================\n\n");
163 /* Prints all metaentries in a metaentry list */
164 static void
165 mentries_print(const struct metahash *mhash)
167 const struct metaentry *mentry;
168 int index;
170 for (index = 0; index < HASH_INDEXES; index++)
171 for (mentry = mhash->bucket[index]; mentry; mentry = mentry->next)
172 mentry_print(mentry);
174 msg(MSG_DEBUG, "%i entries in total\n", mhash->count);
176 #endif
178 /* Creates a metaentry for the file/dir/etc at path */
179 struct metaentry *
180 mentry_create(const char *path)
182 ssize_t lsize, vsize;
183 char *list, *attr;
184 struct stat sbuf;
185 struct passwd *pbuf;
186 struct group *gbuf;
187 int i;
188 struct metaentry *mentry;
190 if (lstat(path, &sbuf)) {
191 msg(MSG_ERROR, "lstat failed for %s: %s\n",
192 path, strerror(errno));
193 return NULL;
196 pbuf = xgetpwuid(sbuf.st_uid);
197 if (!pbuf) {
198 msg(MSG_ERROR, "getpwuid failed for %s: uid %i not found\n",
199 path, (int)sbuf.st_uid);
200 return NULL;
203 gbuf = xgetgrgid(sbuf.st_gid);
204 if (!gbuf) {
205 msg(MSG_ERROR, "getgrgid failed for %s: gid %i not found\n",
206 path, (int)sbuf.st_gid);
207 return NULL;
210 mentry = mentry_alloc();
211 mentry->path = xstrdup(path);
212 mentry->pathlen = strlen(mentry->path);
213 mentry->owner = xstrdup(pbuf->pw_name);
214 mentry->group = xstrdup(gbuf->gr_name);
215 mentry->mode = sbuf.st_mode & 0177777;
216 mentry->mtime = sbuf.st_mtim.tv_sec;
217 mentry->mtimensec = sbuf.st_mtim.tv_nsec;
219 /* symlinks have no xattrs */
220 if (S_ISLNK(mentry->mode))
221 return mentry;
223 lsize = listxattr(path, NULL, 0);
224 if (lsize < 0) {
225 /* Perhaps the FS doesn't support xattrs? */
226 if (errno == ENOTSUP)
227 return mentry;
229 msg(MSG_ERROR, "listxattr failed for %s: %s\n",
230 path, strerror(errno));
231 return NULL;
234 list = xmalloc(lsize);
235 lsize = listxattr(path, list, lsize);
236 if (lsize < 0) {
237 msg(MSG_ERROR, "listxattr failed for %s: %s\n",
238 path, strerror(errno));
239 free(list);
240 return NULL;
243 i = 0;
244 for (attr = list; attr < list + lsize; attr = strchr(attr, '\0') + 1) {
245 if (*attr == '\0')
246 continue;
247 i++;
250 if (i == 0)
251 return mentry;
253 mentry->xattrs = i;
254 mentry->xattr_names = xmalloc(i * sizeof(char *));
255 mentry->xattr_values = xmalloc(i * sizeof(char *));
256 mentry->xattr_lvalues = xmalloc(i * sizeof(ssize_t));
258 i = 0;
259 for (attr = list; attr < list + lsize; attr = strchr(attr, '\0') + 1) {
260 if (*attr == '\0')
261 continue;
263 mentry->xattr_names[i] = xstrdup(attr);
264 vsize = getxattr(path, attr, NULL, 0);
265 if (vsize < 0) {
266 msg(MSG_ERROR, "getxattr failed for %s: %s\n",
267 path, strerror(errno));
268 free(list);
269 mentry_free(mentry);
270 return NULL;
273 mentry->xattr_lvalues[i] = vsize;
274 mentry->xattr_values[i] = xmalloc(vsize);
276 vsize = getxattr(path, attr, mentry->xattr_values[i], vsize);
277 if (vsize < 0) {
278 msg(MSG_ERROR, "getxattr failed for %s: %s\n",
279 path, strerror(errno));
280 free(list);
281 mentry_free(mentry);
282 return NULL;
284 i++;
287 free(list);
288 return mentry;
291 /* Cleans up a path and makes it relative to current working dir unless it is absolute */
292 static char *
293 normalize_path(const char *orig)
295 char *real = realpath(orig, NULL);
296 char cwd[PATH_MAX];
297 char *result;
299 getcwd(cwd, PATH_MAX);
300 if (!real)
301 return NULL;
303 if (!strncmp(real, cwd, strlen(cwd))) {
304 result = xmalloc(strlen(real) - strlen(cwd) + 1 + 1);
305 result[0] = '\0';
306 strcat(result, ".");
307 strcat(result, real + strlen(cwd));
308 } else {
309 result = xstrdup(real);
312 free(real);
313 return result;
316 /* Internal function for the recursive path walk */
317 static void
318 mentries_recurse(const char *path, struct metahash *mhash, msettings *st)
320 struct stat sbuf;
321 struct metaentry *mentry;
322 char tpath[PATH_MAX];
323 DIR *dir;
324 struct dirent *dent;
326 if (!path)
327 return;
329 if (lstat(path, &sbuf)) {
330 msg(MSG_ERROR, "lstat failed for %s: %s\n",
331 path, strerror(errno));
332 return;
335 mentry = mentry_create(path);
336 if (!mentry)
337 return;
339 mentry_insert(mentry, mhash);
341 if (S_ISDIR(sbuf.st_mode)) {
342 dir = opendir(path);
343 if (!dir) {
344 msg(MSG_ERROR, "opendir failed for %s: %s\n",
345 path, strerror(errno));
346 return;
349 while ((dent = readdir(dir))) {
350 if (!strcmp(dent->d_name, ".") ||
351 !strcmp(dent->d_name, "..") ||
352 (!st->do_git && !strcmp(dent->d_name, ".git")))
353 continue;
354 snprintf(tpath, PATH_MAX, "%s/%s", path, dent->d_name);
355 tpath[PATH_MAX - 1] = '\0';
356 mentries_recurse(tpath, mhash, st);
359 closedir(dir);
363 /* Recurses opath and adds metadata entries to the metaentry list */
364 void
365 mentries_recurse_path(const char *opath, struct metahash **mhash, msettings *st)
367 char *path = normalize_path(opath);
369 if (!(*mhash))
370 *mhash = mhash_alloc();
371 mentries_recurse(path, *mhash, st);
372 free(path);
375 /* Stores metaentries to a file */
376 void
377 mentries_tofile(const struct metahash *mhash, const char *path)
379 FILE *to;
380 const struct metaentry *mentry;
381 int key;
382 unsigned i;
384 to = fopen(path, "w");
385 if (!to) {
386 msg(MSG_CRITICAL, "Failed to open %s: %s\n",
387 path, strerror(errno));
388 exit(EXIT_FAILURE);
391 write_binary_string(SIGNATURE, SIGNATURELEN, to);
392 write_binary_string(VERSION, VERSIONLEN, to);
394 for (key = 0; key < HASH_INDEXES; key++) {
395 for (mentry = mhash->bucket[key]; mentry; mentry = mentry->next) {
396 write_string(mentry->path, to);
397 write_string(mentry->owner, to);
398 write_string(mentry->group, to);
399 write_int((uint64_t)mentry->mtime, 8, to);
400 write_int((uint64_t)mentry->mtimensec, 8, to);
401 write_int((uint64_t)mentry->mode, 2, to);
402 write_int(mentry->xattrs, 4, to);
403 for (i = 0; i < mentry->xattrs; i++) {
404 write_string(mentry->xattr_names[i], to);
405 write_int(mentry->xattr_lvalues[i], 4, to);
406 write_binary_string(mentry->xattr_values[i],
407 mentry->xattr_lvalues[i], to);
412 fclose(to);
415 /* Creates a metaentry list from a file */
416 void
417 mentries_fromfile(struct metahash **mhash, const char *path)
419 struct metaentry *mentry;
420 char *mmapstart;
421 char *ptr;
422 char *max;
423 int fd;
424 struct stat sbuf;
425 unsigned i;
427 if (!(*mhash))
428 *mhash = mhash_alloc();
430 fd = open(path, O_RDONLY);
431 if (fd < 0) {
432 msg(MSG_CRITICAL, "Failed to open %s: %s\n",
433 path, strerror(errno));
434 exit(EXIT_FAILURE);
437 if (fstat(fd, &sbuf)) {
438 msg(MSG_CRITICAL, "Failed to stat %s: %s\n",
439 path, strerror(errno));
440 exit(EXIT_FAILURE);
443 if (sbuf.st_size < (SIGNATURELEN + VERSIONLEN)) {
444 msg(MSG_CRITICAL, "File %s has an invalid size\n", path);
445 exit(EXIT_FAILURE);
448 mmapstart = mmap(NULL, (size_t)sbuf.st_size, PROT_READ,
449 MAP_SHARED, fd, 0);
450 if (mmapstart == MAP_FAILED) {
451 msg(MSG_CRITICAL, "Unable to mmap %s: %s\n",
452 path, strerror(errno));
453 exit(EXIT_FAILURE);
455 ptr = mmapstart;
456 max = mmapstart + sbuf.st_size;
458 if (strncmp(ptr, SIGNATURE, SIGNATURELEN)) {
459 msg(MSG_CRITICAL, "Invalid signature for file %s\n", path);
460 goto out;
462 ptr += SIGNATURELEN;
464 if (strncmp(ptr, VERSION, VERSIONLEN)) {
465 msg(MSG_CRITICAL, "Invalid version of file %s\n", path);
466 goto out;
468 ptr += VERSIONLEN;
470 while (ptr < mmapstart + sbuf.st_size) {
471 if (*ptr == '\0') {
472 msg(MSG_CRITICAL, "Invalid characters in file %s\n",
473 path);
474 goto out;
477 mentry = mentry_alloc();
478 mentry->path = read_string(&ptr, max);
479 mentry->pathlen = strlen(mentry->path);
480 mentry->owner = read_string(&ptr, max);
481 mentry->group = read_string(&ptr, max);
482 mentry->mtime = (time_t)read_int(&ptr, 8, max);
483 mentry->mtimensec = (time_t)read_int(&ptr, 8, max);
484 mentry->mode = (mode_t)read_int(&ptr, 2, max);
485 mentry->xattrs = (unsigned int)read_int(&ptr, 4, max);
487 if (!mentry->xattrs) {
488 mentry_insert(mentry, *mhash);
489 continue;
492 mentry->xattr_names = xmalloc(mentry->xattrs *
493 sizeof(char *));
494 mentry->xattr_lvalues = xmalloc(mentry->xattrs *
495 sizeof(int));
496 mentry->xattr_values = xmalloc(mentry->xattrs *
497 sizeof(char *));
499 for (i = 0; i < mentry->xattrs; i++) {
500 mentry->xattr_names[i] = read_string(&ptr, max);
501 mentry->xattr_lvalues[i] =
502 (int)read_int(&ptr, 4, max);
503 mentry->xattr_values[i] =
504 read_binary_string(&ptr,
505 mentry->xattr_lvalues[i],
506 max);
508 mentry_insert(mentry, *mhash);
511 out:
512 munmap(mmapstart, sbuf.st_size);
513 close(fd);
516 /* Searches haystack for an xattr matching xattr number n in needle */
518 mentry_find_xattr(struct metaentry *haystack, struct metaentry *needle,
519 unsigned n)
521 unsigned i;
523 for (i = 0; i < haystack->xattrs; i++) {
524 if (strcmp(haystack->xattr_names[i], needle->xattr_names[n]))
525 continue;
526 if (haystack->xattr_lvalues[i] != needle->xattr_lvalues[n])
527 return -1;
528 if (bcmp(haystack->xattr_values[i], needle->xattr_values[n],
529 needle->xattr_lvalues[n]))
530 return -1;
531 return i;
533 return -1;
536 /* Returns zero if all xattrs in left and right match */
537 static int
538 mentry_compare_xattr(struct metaentry *left, struct metaentry *right)
540 unsigned i;
542 if (left->xattrs != right->xattrs)
543 return 1;
545 /* Make sure all xattrs in left are found in right and vice versa */
546 for (i = 0; i < left->xattrs; i++) {
547 if (mentry_find_xattr(right, left, i) < 0 ||
548 mentry_find_xattr(left, right, i) < 0) {
549 return 1;
553 return 0;
556 /* Compares two metaentries and returns an int with a bitmask of differences */
558 mentry_compare(struct metaentry *left, struct metaentry *right, msettings *st)
560 int retval = DIFF_NONE;
562 if (!left || !right) {
563 msg(MSG_ERROR, "%s called with empty list\n", __FUNCTION__);
564 return -1;
567 if (strcmp(left->path, right->path))
568 return -1;
570 if (strcmp(left->owner, right->owner))
571 retval |= DIFF_OWNER;
573 if (strcmp(left->group, right->group))
574 retval |= DIFF_GROUP;
576 if ((left->mode & 07777) != (right->mode & 07777))
577 retval |= DIFF_MODE;
579 if ((left->mode & S_IFMT) != (right->mode & S_IFMT))
580 retval |= DIFF_TYPE;
582 if (st->do_mtime && strcmp(left->path, st->metafile) &&
583 (left->mtime != right->mtime ||
584 left->mtimensec != right->mtimensec))
585 retval |= DIFF_MTIME;
587 if (mentry_compare_xattr(left, right)) {
588 retval |= DIFF_XATTR;
589 return retval;
592 return retval;
595 /* Compares lists of real and stored metadata and calls pfunc for each */
596 void
597 mentries_compare(struct metahash *mhashreal,
598 struct metahash *mhashstored,
599 void (*pfunc)
600 (struct metaentry *real, struct metaentry *stored, int cmp),
601 msettings *st)
603 struct metaentry *real, *stored;
604 int key;
606 if (!mhashreal || !mhashstored) {
607 msg(MSG_ERROR, "%s called with empty list\n", __FUNCTION__);
608 return;
611 for (key = 0; key < HASH_INDEXES; key++) {
612 for (real = mhashreal->bucket[key]; real; real = real->next) {
613 stored = mentry_find(real->path, mhashstored);
615 if (!stored)
616 pfunc(real, NULL, DIFF_ADDED);
617 else
618 pfunc(real, stored, mentry_compare(real, stored, st));
621 for (stored = mhashstored->bucket[key]; stored; stored = stored->next) {
622 real = mentry_find(stored->path, mhashreal);
624 if (!real)
625 pfunc(NULL, stored, DIFF_DELE);