80-col align code, add license headers
[metastore.git] / metaentry.c
blobd8c7cb8e1a3e0abba9af91c588996203db072216
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 <pwd.h>
29 #include <grp.h>
30 #include <attr/xattr.h>
31 #include <limits.h>
32 #include <dirent.h>
33 #include <sys/mman.h>
34 #include <utime.h>
35 #include <fcntl.h>
36 #include <stdint.h>
38 #include "metastore.h"
39 #include "metaentry.h"
40 #include "utils.h"
42 #if 0
43 static void
44 mentry_free(struct metaentry *m)
46 int i;
48 if (!m)
49 return;
51 free(m->path);
52 free(m->owner);
53 free(m->group);
55 for (i = 0; i < m->xattrs; i++) {
56 free(m->xattr_names[i]);
57 free(m->xattr_values[i]);
60 free(m->xattr_names);
61 free(m->xattr_values);
62 free(m->xattr_lvalues);
64 free(m);
66 #endif
68 static struct metaentry *
69 mentry_alloc()
71 struct metaentry *mentry;
72 mentry = xmalloc(sizeof(struct metaentry));
73 memset(mentry, 0, sizeof(struct metaentry));
74 return mentry;
77 static void
78 mentry_insert(struct metaentry *mentry, struct metaentry **mhead)
80 struct metaentry *prev;
81 struct metaentry *curr;
82 int comp;
84 if (!(*mhead)) {
85 *mhead = mentry;
86 return;
89 if (strcmp(mentry->path, (*mhead)->path) < 0) {
90 mentry->next = *mhead;
91 *mhead = mentry;
92 return;
95 prev = *mhead;
96 for (curr = prev->next; curr; curr = curr->next) {
97 comp = strcmp(mentry->path, curr->path);
98 if (!comp)
99 /* Two matching paths */
100 return;
101 if (comp < 0)
102 break;
103 prev = curr;
106 if (curr)
107 mentry->next = curr;
108 prev->next = mentry;
111 #ifdef DEBUG
112 static void
113 mentry_print(const struct metaentry *mentry)
115 int i;
117 if (!mentry || !mentry->path) {
118 msg(MSG_DEBUG,
119 "Incorrect meta entry passed to printmetaentry\n");
120 return;
123 msg(MSG_DEBUG, "===========================\n");
124 msg(MSG_DEBUG, "Dump of metaentry %p\n", mentry);
125 msg(MSG_DEBUG, "===========================\n");
127 msg(MSG_DEBUG, "path\t\t: %s\n", mentry->path);
128 msg(MSG_DEBUG, "owner\t\t: %s\n", mentry->owner);
129 msg(MSG_DEBUG, "group\t\t: %s\n", mentry->group);
130 msg(MSG_DEBUG, "mtime\t\t: %ld\n", (unsigned long)mentry->mtime);
131 msg(MSG_DEBUG, "mtimensec\t: %ld\n", (unsigned long)mentry->mtimensec);
132 msg(MSG_DEBUG, "mode\t\t: %ld\n", (unsigned long)mentry->mode);
133 for (i = 0; i < mentry->xattrs; i++) {
134 msg(MSG_DEBUG, "xattr[%i]\t: %s=\"", i, mentry->xattr_names[i]);
135 binary_print(mentry->xattr_values[i], mentry->xattr_lvalues[i]);
136 msg(MSG_DEBUG, "\"\n");
139 msg(MSG_DEBUG, "===========================\n\n");
142 static void
143 mentries_print(const struct metaentry *mhead)
145 const struct metaentry *mentry;
146 int i;
148 for (mentry = mhead; mentry; mentry = mentry->next) {
149 i++;
150 mentry_print(mentry);
153 msg(MSG_DEBUG, "%i entries in total\n", i);
155 #endif
157 static struct metaentry *
158 mentry_create(const char *path)
160 ssize_t lsize, vsize;
161 char *list, *attr;
162 struct stat sbuf;
163 struct passwd *pbuf;
164 struct group *gbuf;
165 int i;
166 struct metaentry *mentry;
168 if (lstat(path, &sbuf)) {
169 msg(MSG_ERROR, "lstat failed for %s: %s\n",
170 path, strerror(errno));
171 return NULL;
174 pbuf = getpwuid(sbuf.st_uid);
175 if (!pbuf) {
176 msg(MSG_ERROR, "getpwuid failed for %i: %s\n",
177 (int)sbuf.st_uid, strerror(errno));
178 return NULL;
181 gbuf = getgrgid(sbuf.st_gid);
182 if (!gbuf) {
183 msg(MSG_ERROR, "getgrgid failed for %i: %s\n",
184 (int)sbuf.st_gid, strerror(errno));
185 return NULL;
188 mentry = mentry_alloc();
189 mentry->path = xstrdup(path);
190 mentry->owner = xstrdup(pbuf->pw_name);
191 mentry->group = xstrdup(gbuf->gr_name);
192 mentry->mode = sbuf.st_mode & 0177777;
193 mentry->mtime = sbuf.st_mtim.tv_sec;
194 mentry->mtimensec = sbuf.st_mtim.tv_nsec;
196 /* symlinks have no xattrs */
197 if (S_ISLNK(mentry->mode))
198 return mentry;
200 lsize = listxattr(path, NULL, 0);
201 if (lsize < 0) {
202 msg(MSG_ERROR, "listxattr failed for %s: %s\n",
203 path, strerror(errno));
204 return NULL;
207 list = xmalloc(lsize);
208 lsize = listxattr(path, list, lsize);
209 if (lsize < 0) {
210 msg(MSG_ERROR, "listxattr failed for %s: %s\n",
211 path, strerror(errno));
212 return NULL;
215 i = 0;
216 for (attr = list; attr < list + lsize; attr = strchr(attr, '\0') + 1) {
217 if (*attr == '\0')
218 continue;
219 i++;
222 if (i == 0)
223 return mentry;
225 mentry->xattrs = i;
226 mentry->xattr_names = xmalloc(i * sizeof(char *));
227 mentry->xattr_values = xmalloc(i * sizeof(char *));
228 mentry->xattr_lvalues = xmalloc(i * sizeof(ssize_t));
230 i = 0;
231 for (attr = list; attr < list + lsize; attr = strchr(attr, '\0') + 1) {
232 if (*attr == '\0')
233 continue;
235 mentry->xattr_names[i] = xstrdup(attr);
236 vsize = getxattr(path, attr, NULL, 0);
237 if (vsize < 0) {
238 msg(MSG_ERROR, "getxattr failed for %s: %s\n",
239 path, strerror(errno));
240 return NULL;
243 mentry->xattr_lvalues[i] = vsize;
244 mentry->xattr_values[i] = xmalloc(vsize);
246 vsize = getxattr(path, attr, mentry->xattr_values[i], vsize);
247 if (vsize < 0) {
248 msg(MSG_ERROR, "getxattr failed for %s: %s\n",
249 path, strerror(errno));
250 return NULL;
252 i++;
255 return mentry;
258 static char *
259 normalize_path(const char *orig)
261 char *real = canonicalize_file_name(orig);
262 char cwd[PATH_MAX];
263 char *result;
265 getcwd(cwd, PATH_MAX);
266 if (!real)
267 return NULL;
269 if (!strncmp(real, cwd, strlen(cwd))) {
270 result = xmalloc(strlen(real) - strlen(cwd) + 1 + 1);
271 result[0] = '\0';
272 strcat(result, ".");
273 strcat(result, real + strlen(cwd));
274 } else {
275 result = xstrdup(real);
278 free(real);
279 return result;
282 static void
283 mentries_recurse(const char *path, struct metaentry **mhead)
285 struct stat sbuf;
286 struct metaentry *mentry;
287 char tpath[PATH_MAX];
288 DIR *dir;
289 struct dirent *dent;
291 if (!path)
292 return;
294 if (lstat(path, &sbuf)) {
295 msg(MSG_ERROR, "lstat failed for %s: %s\n",
296 path, strerror(errno));
297 return;
300 mentry = mentry_create(path);
301 if (!mentry)
302 return;
304 mentry_insert(mentry, mhead);
306 if (S_ISDIR(sbuf.st_mode)) {
307 dir = opendir(path);
308 if (!dir) {
309 msg(MSG_ERROR, "opendir failed for %s: %s\n",
310 path, strerror(errno));
311 return;
314 while ((dent = readdir(dir))) {
315 if (!strcmp(dent->d_name, ".") ||
316 !strcmp(dent->d_name, "..") ||
317 !strcmp(dent->d_name, ".git"))
318 continue;
319 snprintf(tpath, PATH_MAX, "%s/%s", path, dent->d_name);
320 tpath[PATH_MAX - 1] = '\0';
321 mentries_recurse(tpath, mhead);
324 closedir(dir);
328 void
329 mentries_recurse_path(const char *opath, struct metaentry **mhead)
331 char *path = normalize_path(opath);
332 mentries_recurse(path, mhead);
333 free(path);
336 void
337 mentries_tofile(const struct metaentry *mhead, const char *path)
339 FILE *to;
340 const struct metaentry *mentry;
341 int i;
343 to = fopen(path, "w");
344 if (!to) {
345 perror("fopen");
346 exit(EXIT_FAILURE);
349 write_binary_string(SIGNATURE, SIGNATURELEN, to);
350 write_binary_string(VERSION, VERSIONLEN, to);
352 for (mentry = mhead; mentry; mentry = mentry->next) {
353 write_string(mentry->path, to);
354 write_string(mentry->owner, to);
355 write_string(mentry->group, to);
356 write_int((uint64_t)mentry->mtime, 8, to);
357 write_int((uint64_t)mentry->mtimensec, 8, to);
358 write_int((uint64_t)mentry->mode, 2, to);
359 write_int(mentry->xattrs, 4, to);
360 for (i = 0; i < mentry->xattrs; i++) {
361 write_string(mentry->xattr_names[i], to);
362 write_int(mentry->xattr_lvalues[i], 4, to);
363 write_binary_string(mentry->xattr_values[i],
364 mentry->xattr_lvalues[i], to);
368 fclose(to);
371 void
372 mentries_fromfile(struct metaentry **mhead, const char *path)
374 struct metaentry *mentry;
375 char *mmapstart;
376 char *ptr;
377 char *max;
378 int fd;
379 struct stat sbuf;
380 int i;
382 fd = open(path, O_RDONLY);
383 if (fd < 0) {
384 perror("open");
385 exit(EXIT_FAILURE);
388 if (fstat(fd, &sbuf)) {
389 perror("fstat");
390 exit(EXIT_FAILURE);
393 if (sbuf.st_size < (SIGNATURELEN + VERSIONLEN)) {
394 msg(MSG_CRITICAL, "File %s has an invalid size\n", path);
395 exit(EXIT_FAILURE);
398 mmapstart = mmap(NULL, (size_t)sbuf.st_size, PROT_READ,
399 MAP_SHARED, fd, 0);
400 if (mmapstart == MAP_FAILED) {
401 msg(MSG_CRITICAL, "Unable to mmap %s: %s\n",
402 path, strerror(errno));
403 exit(EXIT_FAILURE);
405 ptr = mmapstart;
406 max = mmapstart + sbuf.st_size;
408 if (strncmp(ptr, SIGNATURE, SIGNATURELEN)) {
409 msg(MSG_CRITICAL, "Invalid signature for file %s\n", path);
410 goto out;
412 ptr += SIGNATURELEN;
414 if (strncmp(ptr, VERSION, VERSIONLEN)) {
415 msg(MSG_CRITICAL, "Invalid version of file %s\n", path);
416 goto out;
418 ptr += VERSIONLEN;
420 while (ptr < mmapstart + sbuf.st_size) {
421 if (*ptr == '\0') {
422 msg(MSG_CRITICAL, "Invalid characters in file %s\n",
423 path);
424 goto out;
427 mentry = mentry_alloc();
428 mentry->path = read_string(&ptr, max);
429 mentry->owner = read_string(&ptr, max);
430 mentry->group = read_string(&ptr, max);
431 mentry->mtime = (time_t)read_int(&ptr, 8, max);
432 mentry->mtimensec = (time_t)read_int(&ptr, 8, max);
433 mentry->mode = (mode_t)read_int(&ptr, 2, max);
434 mentry->xattrs = (unsigned int)read_int(&ptr, 4, max);
436 if (!mentry->xattrs) {
437 mentry_insert(mentry, mhead);
438 continue;
441 mentry->xattr_names = xmalloc(mentry->xattrs *
442 sizeof(char *));
443 mentry->xattr_lvalues = xmalloc(mentry->xattrs *
444 sizeof(int));
445 mentry->xattr_values = xmalloc(mentry->xattrs *
446 sizeof(char *));
448 for (i = 0; i < mentry->xattrs; i++) {
449 mentry->xattr_names[i] = read_string(&ptr, max);
450 mentry->xattr_lvalues[i] =
451 (int)read_int(&ptr, 4, max);
452 mentry->xattr_values[i] =
453 read_binary_string(&ptr,
454 mentry->xattr_lvalues[i],
455 max);
457 mentry_insert(mentry, mhead);
460 out:
461 munmap(mmapstart, sbuf.st_size);
462 close(fd);
465 static struct metaentry *
466 mentry_find(const char *path, struct metaentry *mhead)
468 struct metaentry *m;
470 /* FIXME - We can do a bisect search here instead */
471 for (m = mhead; m; m = m->next) {
472 if (!strcmp(path, m->path))
473 return m;
475 return NULL;
478 /* Returns xattr index in haystack which corresponds to xattr n in needle */
480 mentry_find_xattr(struct metaentry *haystack, struct metaentry *needle, int n)
482 int i;
484 for (i = 0; i < haystack->xattrs; i++) {
485 if (strcmp(haystack->xattr_names[i], needle->xattr_names[n]))
486 continue;
487 if (haystack->xattr_lvalues[i] != needle->xattr_lvalues[n])
488 return -1;
489 if (bcmp(haystack->xattr_values[i], needle->xattr_values[n],
490 needle->xattr_lvalues[n]))
491 return -1;
492 return i;
494 return -1;
497 /* Returns zero if all xattrs in left and right match */
498 static int
499 mentry_compare_xattr(struct metaentry *left, struct metaentry *right)
501 int i;
503 if (left->xattrs != right->xattrs)
504 return 1;
506 /* Make sure all xattrs in left are found in right and vice versa */
507 for (i = 0; i < left->xattrs; i++) {
508 if (mentry_find_xattr(right, left, i) < 0 ||
509 mentry_find_xattr(left, right, i) < 0) {
510 return 1;
514 return 0;
517 static int
518 mentry_compare(struct metaentry *left, struct metaentry *right)
520 int retval = DIFF_NONE;
522 if (!left || !right) {
523 msg(MSG_ERROR, "%s called with empty list\n", __FUNCTION__);
524 return -1;
527 if (strcmp(left->path, right->path))
528 return -1;
530 if (strcmp(left->owner, right->owner))
531 retval |= DIFF_OWNER;
533 if (strcmp(left->group, right->group))
534 retval |= DIFF_GROUP;
536 if ((left->mode & 07777) != (right->mode & 07777))
537 retval |= DIFF_MODE;
539 if ((left->mode & S_IFMT) != (right->mode & S_IFMT))
540 retval |= DIFF_TYPE;
542 if (do_mtime && strcmp(left->path, METAFILE) &&
543 (left->mtime != right->mtime ||
544 left->mtimensec != right->mtimensec))
545 retval |= DIFF_MTIME;
547 if (mentry_compare_xattr(left, right)) {
548 retval |= DIFF_XATTR;
549 return retval;
552 return retval;
555 void
556 mentries_compare(struct metaentry *mheadleft,
557 struct metaentry *mheadright,
558 void (*printfunc)(struct metaentry *, struct metaentry *, int))
560 struct metaentry *left, *right;
561 int cmp;
563 if (!mheadleft || !mheadright) {
564 msg(MSG_ERROR, "%s called with empty list\n", __FUNCTION__);
565 return;
568 for (left = mheadleft; left; left = left->next) {
569 right = mentry_find(left->path, mheadright);
570 if (!right)
571 cmp = DIFF_ADDED;
572 else
573 cmp = mentry_compare(left, right);
574 printfunc(left, right, cmp);
577 for (right = mheadright; right; right = right->next) {
578 left = mentry_find(right->path, mheadleft);
579 if (!left)
580 printfunc(left, right, DIFF_DELE);