btrfs-progs: tests: fssum, drop unnecessray build defines
[platform/upstream/btrfs-progs.git] / tests / fssum.c
1 /*
2  * Copyright (C) 2012 STRATO AG.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License v2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public
14  * License along with this program; if not, write to the
15  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16  * Boston, MA 021110-1307, USA.
17  */
18
19 #define _BSD_SOURCE
20 #define _LARGEFILE64_SOURCE
21 #ifndef _GNU_SOURCE
22 #define _GNU_SOURCE
23 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <dirent.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <openssl/md5.h>
34 #include <netinet/in.h>
35 #include <inttypes.h>
36 #include <assert.h>
37
38 #define CS_SIZE 16
39 #define CHUNKS  128
40
41 #ifndef SEEK_DATA
42 #define SEEK_DATA 3
43 #define SEEK_HOLE 4
44 #endif
45
46 #if __BYTE_ORDER == __LITTLE_ENDIAN
47 #define htonll(x)     __bswap_64 (x)
48 #else
49 #define htonll(x)     (x)
50 #endif
51
52 /* TODO: add hardlink recognition */
53 /* TODO: add xattr/acl */
54
55 struct excludes {
56         char *path;
57         int len;
58 };
59
60 typedef struct _sum {
61         MD5_CTX         md5;
62         unsigned char   out[16];
63 } sum_t;
64
65 typedef int (*sum_file_data_t)(int fd, sum_t *dst);
66
67 int gen_manifest = 0;
68 int in_manifest = 0;
69 char *checksum = NULL;
70 struct excludes *excludes;
71 int n_excludes = 0;
72 int verbose = 0;
73 FILE *out_fp;
74 FILE *in_fp;
75
76 enum _flags {
77         FLAG_UID,
78         FLAG_GID,
79         FLAG_MODE,
80         FLAG_ATIME,
81         FLAG_MTIME,
82         FLAG_CTIME,
83         FLAG_DATA,
84         FLAG_OPEN_ERROR,
85         FLAG_STRUCTURE,
86         NUM_FLAGS
87 };
88
89 const char flchar[] = "ugoamcdes";
90 char line[65536];
91
92 int flags[NUM_FLAGS] = {1, 1, 1, 1, 1, 0, 1, 0, 0};
93
94 char *
95 getln(char *buf, int size, FILE *fp)
96 {
97         char *p;
98         int l;
99
100         p = fgets(buf, size, fp);
101         if (!p)
102                 return NULL;
103
104         l = strlen(p);
105         while(l > 0  && (p[l - 1] == '\n' || p[l - 1] == '\r'))
106                 p[--l] = 0;
107
108         return p;
109 }
110
111 void
112 parse_flag(int c)
113 {
114         int i;
115         int is_upper = 0;
116
117         if (c >= 'A' && c <= 'Z') {
118                 is_upper = 1;
119                 c += 'a' - 'A';
120         }
121         for (i = 0; flchar[i]; ++i) {
122                 if (flchar[i] == c) {
123                         flags[i] = is_upper ? 0 : 1;
124                         return;
125                 }
126         }
127         fprintf(stderr, "unrecognized flag %c\n", c);
128         exit(-1);
129 }
130
131 void
132 parse_flags(char *p)
133 {
134         while (*p)
135                 parse_flag(*p++);
136 }
137
138 void
139 usage(void)
140 {
141         fprintf(stderr, "usage: fssum <options> <path>\n");
142         fprintf(stderr, "  options:\n");
143         fprintf(stderr, "    -f          : write out a full manifest file\n");
144         fprintf(stderr, "    -w <file>   : send output to file\n");
145         fprintf(stderr, "    -v          : verbose mode (debugging only)\n");
146         fprintf(stderr,
147                 "    -r <file>   : read checksum or manifest from file\n");
148         fprintf(stderr, "    -[ugoamcde] : specify which fields to include in checksum calculation.\n");
149         fprintf(stderr, "         u      : include uid\n");
150         fprintf(stderr, "         g      : include gid\n");
151         fprintf(stderr, "         o      : include mode\n");
152         fprintf(stderr, "         m      : include mtime\n");
153         fprintf(stderr, "         a      : include atime\n");
154         fprintf(stderr, "         c      : include ctime\n");
155         fprintf(stderr, "         d      : include file data\n");
156         fprintf(stderr, "         e      : include open errors (aborts otherwise)\n");
157         fprintf(stderr, "         s      : include block structure (holes)\n");
158         fprintf(stderr, "    -[UGOAMCDES]: exclude respective field from calculation\n");
159         fprintf(stderr, "    -n          : reset all flags\n");
160         fprintf(stderr, "    -N          : set all flags\n");
161         fprintf(stderr, "    -x path     : exclude path when building checksum (multiple ok)\n");
162         fprintf(stderr, "    -h          : this help\n\n");
163         fprintf(stderr, "The default field mask is ugoamCdES. If the checksum/manifest is read from a\n");
164         fprintf(stderr, "file, the mask is taken from there and the values given on the command line\n");
165         fprintf(stderr, "are ignored.\n");
166         exit(-1);
167 }
168
169 static char buf[65536];
170
171 void *
172 alloc(size_t sz)
173 {
174         void *p = malloc(sz);
175
176         if (!p) {
177                 fprintf(stderr, "malloc failed\n");
178                 exit(-1);
179         }
180
181         return p;
182 }
183
184 void
185 sum_init(sum_t *cs)
186 {
187         MD5_Init(&cs->md5);
188 }
189
190 void
191 sum_fini(sum_t *cs)
192 {
193         MD5_Final(cs->out, &cs->md5);
194 }
195
196 void
197 sum_add(sum_t *cs, void *buf, int size)
198 {
199         MD5_Update(&cs->md5, buf, size);
200 }
201
202 void
203 sum_add_sum(sum_t *dst, sum_t *src)
204 {
205         sum_add(dst, src->out, sizeof(src->out));
206 }
207
208 void
209 sum_add_u64(sum_t *dst, uint64_t val)
210 {
211         uint64_t v = htonll(val);
212         sum_add(dst, &v, sizeof(v));
213 }
214
215 void
216 sum_add_time(sum_t *dst, time_t t)
217 {
218         sum_add_u64(dst, t);
219 }
220
221 char *
222 sum_to_string(sum_t *dst)
223 {
224         int i;
225         char *s = alloc(CS_SIZE * 2 + 1);
226
227         for (i = 0; i < CS_SIZE; ++i)
228                 sprintf(s + i * 2, "%02x", dst->out[i]);
229
230         return s;
231 }
232
233 int
234 sum_file_data_permissive(int fd, sum_t *dst)
235 {
236         int ret;
237         off_t pos;
238         off_t old;
239         int i;
240         uint64_t zeros = 0;
241
242         pos = lseek(fd, 0, SEEK_CUR);
243         if (pos == (off_t)-1)
244                 return errno == ENXIO ? 0 : -2;
245
246         while (1) {
247                 old = pos;
248                 pos = lseek(fd, pos, SEEK_DATA);
249                 if (pos == (off_t)-1) {
250                         if (errno == ENXIO) {
251                                 ret = 0;
252                                 pos = lseek(fd, 0, SEEK_END);
253                                 if (pos != (off_t)-1)
254                                         zeros += pos - old;
255                         } else {
256                                 ret = -2;
257                         }
258                         break;
259                 }
260                 ret = read(fd, buf, sizeof(buf));
261                 assert(ret); /* eof found by lseek */
262                 if (ret <= 0)
263                         break;
264                 if (old < pos) /* hole */
265                         zeros += pos - old;
266                 for (i = 0; i < ret; ++i) {
267                         for (old = i; buf[i] == 0 && i < ret; ++i)
268                                 ;
269                         if (old < i) /* code like a hole */
270                                 zeros += i - old;
271                         if (i == ret)
272                                 break;
273                         if (zeros) {
274                                 if (verbose >= 2)
275                                         fprintf(stderr,
276                                                 "adding %llu zeros to sum\n",
277                                                 (unsigned long long)zeros);
278                                 sum_add_u64(dst, 0);
279                                 sum_add_u64(dst, zeros);
280                                 zeros = 0;
281                         }
282                         for (old = i; buf[i] != 0 && i < ret; ++i)
283                                 ;
284                         if (verbose >= 2)
285                                 fprintf(stderr, "adding %u non-zeros to sum\n",
286                                         i - (int)old);
287                         sum_add(dst, buf + old, i - old);
288                 }
289                 pos += ret;
290         }
291
292         if (zeros) {
293                 if (verbose >= 2)
294                         fprintf(stderr,
295                                 "adding %llu zeros to sum (finishing)\n",
296                                 (unsigned long long)zeros);
297                 sum_add_u64(dst, 0);
298                 sum_add_u64(dst, zeros);
299         }
300
301         return ret;
302 }
303
304 int
305 sum_file_data_strict(int fd, sum_t *dst)
306 {
307         int ret;
308         off_t pos;
309
310         pos = lseek(fd, 0, SEEK_CUR);
311         if (pos == (off_t)-1)
312                 return errno == ENXIO ? 0 : -2;
313
314         while (1) {
315                 pos = lseek(fd, pos, SEEK_DATA);
316                 if (pos == (off_t)-1)
317                         return errno == ENXIO ? 0 : -2;
318                 ret = read(fd, buf, sizeof(buf));
319                 assert(ret); /* eof found by lseek */
320                 if (ret <= 0)
321                         return ret;
322                 if (verbose >= 2)
323                         fprintf(stderr,
324                                 "adding to sum at file offset %llu, %d bytes\n",
325                                 (unsigned long long)pos, ret);
326                 sum_add_u64(dst, (uint64_t)pos);
327                 sum_add(dst, buf, ret);
328                 pos += ret;
329         }
330 }
331
332 char *
333 escape(char *in)
334 {
335         char *out = alloc(strlen(in) * 3 + 1);
336         char *src = in;
337         char *dst = out;
338
339         for (; *src; ++src) {
340                 if (*src >= 32 && *src < 127 && *src != '\\') {
341                         *dst++ = *src;
342                 } else {
343                         sprintf(dst, "\\%02x", (unsigned char)*src);
344                         dst += 3;
345                 }
346         }
347         *dst = 0;
348
349         return out;
350 }
351
352 void
353 excess_file(const char *fn)
354 {
355         printf("only in local fs: %s\n", fn);
356 }
357
358 void
359 missing_file(const char *fn)
360 {
361         printf("only in remote fs: %s\n", fn);
362 }
363
364 int
365 pathcmp(const char *a, const char *b)
366 {
367         int len_a = strlen(a);
368         int len_b = strlen(b);
369
370         /*
371          * as the containing directory is sent after the files, it has to
372          * come out bigger in the comparison.
373          */
374         if (len_a < len_b && a[len_a - 1] == '/' && strncmp(a, b, len_a) == 0)
375                 return 1;
376         if (len_a > len_b && b[len_b - 1] == '/' && strncmp(a, b, len_b) == 0)
377                 return -1;
378
379         return strcmp(a, b);
380 }
381
382 void
383 check_match(char *fn, char *local_m, char *remote_m,
384             char *local_c, char *remote_c)
385 {
386         int match_m = !strcmp(local_m, remote_m);
387         int match_c = !strcmp(local_c, remote_c);
388
389         if (match_m && !match_c) {
390                 printf("data mismatch in %s\n", fn);
391         } else if (!match_m && match_c) {
392                 printf("metadata mismatch in %s\n", fn);
393         } else if (!match_m && !match_c) {
394                 printf("metadata and data mismatch in %s\n", fn);
395         }
396 }
397
398 char *prev_fn;
399 char *prev_m;
400 char *prev_c;
401 void
402 check_manifest(char *fn, char *m, char *c, int last_call)
403 {
404         char *rem_m;
405         char *rem_c;
406         char *l;
407         int cmp;
408
409         if (prev_fn) {
410                 if (last_call)
411                         cmp = -1;
412                 else
413                         cmp = pathcmp(prev_fn, fn);
414                 if (cmp > 0) {
415                         excess_file(fn);
416                         return;
417                 } else if (cmp < 0) {
418                         missing_file(prev_fn);
419                 } else {
420                         check_match(fn, m, prev_m, c, prev_c);
421                 }
422                 free(prev_fn);
423                 free(prev_m);
424                 free(prev_c);
425                 prev_fn = NULL;
426                 prev_m = NULL;
427                 prev_c = NULL;
428                 if (cmp == 0)
429                         return;
430         }
431         while ((l = getln(line, sizeof(line), in_fp))) {
432                 rem_c = strrchr(l, ' ');
433                 if (!rem_c) {
434                         /* final cs */
435                         checksum = strdup(l);
436                         break;
437                 }
438                 if (rem_c == l) {
439 malformed:
440                         fprintf(stderr, "malformed input\n");
441                         exit(-1);
442                 }
443                 *rem_c++ = 0;
444                 rem_m = strrchr(l, ' ');
445                 if (!rem_m)
446                         goto malformed;
447                 *rem_m++ = 0;
448
449                 if (last_call)
450                         cmp = -1;
451                 else
452                         cmp = pathcmp(l, fn);
453                 if (cmp == 0) {
454                         check_match(fn, m, rem_m, c, rem_c);
455                         return;
456                 } else if (cmp > 0) {
457                         excess_file(fn);
458                         prev_fn = strdup(l);
459                         prev_m = strdup(rem_m);
460                         prev_c = strdup(rem_c); 
461                         return;
462                 }
463                 missing_file(l);
464         }
465         if (!last_call)
466                 excess_file(fn);
467 }
468
469 int
470 namecmp(const void *aa, const void *bb)
471 {
472         char * const *a = aa;
473         char * const *b = bb;
474
475         return strcmp(*a, *b);
476 }
477
478 void
479 sum(int dirfd, int level, sum_t *dircs, char *path_prefix, char *path_in)
480 {
481         DIR *d;
482         struct dirent *de;
483         char **namelist = NULL;
484         int alloclen = 0;
485         int entries = 0;
486         int i;
487         int ret;
488         int fd;
489         int excl;
490         sum_file_data_t sum_file_data = flags[FLAG_STRUCTURE] ?
491                         sum_file_data_strict : sum_file_data_permissive;
492
493         d = fdopendir(dirfd);
494         if (!d) {
495                 perror("opendir");
496                 exit(-1);
497         }
498         while((de = readdir(d))) {
499                 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
500                         continue;
501                 if (entries == alloclen) {
502                         alloclen += CHUNKS;
503                         namelist = realloc(namelist,
504                                            alloclen * sizeof(*namelist));
505                         if (!namelist) {
506                                 fprintf(stderr, "malloc failed\n");
507                                 exit(-1);
508                         }
509                 }
510                 namelist[entries] = strdup(de->d_name);
511                 if (!namelist[entries]) {
512                         fprintf(stderr, "malloc failed\n");
513                         exit(-1);
514                 }
515                 ++entries;
516         }
517         qsort(namelist, entries, sizeof(*namelist), namecmp);
518         for (i = 0; i < entries; ++i) {
519                 struct stat64 st;
520                 sum_t cs;
521                 sum_t meta;
522                 char *path;
523
524                 sum_init(&cs);
525                 sum_init(&meta);
526                 path = alloc(strlen(path_in) + strlen(namelist[i]) + 3);
527                 sprintf(path, "%s/%s", path_in, namelist[i]);
528                 for (excl = 0; excl < n_excludes; ++excl) {
529                         if (strncmp(excludes[excl].path, path,
530                             excludes[excl].len) == 0)
531                                 goto next;
532                 }
533
534                 ret = fchdir(dirfd);
535                 if (ret == -1) {
536                         perror("fchdir");
537                         exit(-1);
538                 }
539                 ret = lstat64(namelist[i], &st);
540                 if (ret) {
541                         fprintf(stderr, "stat failed for %s/%s: %s\n",
542                                 path_prefix, path, strerror(errno));
543                         exit(-1);
544                 }
545                 sum_add_u64(&meta, level);
546                 sum_add(&meta, namelist[i], strlen(namelist[i]));
547                 if (!S_ISDIR(st.st_mode))
548                         sum_add_u64(&meta, st.st_nlink);
549                 if (flags[FLAG_UID])
550                         sum_add_u64(&meta, st.st_uid);
551                 if (flags[FLAG_GID])
552                         sum_add_u64(&meta, st.st_gid);
553                 if (flags[FLAG_MODE])
554                         sum_add_u64(&meta, st.st_mode);
555                 if (flags[FLAG_ATIME])
556                         sum_add_time(&meta, st.st_atime);
557                 if (flags[FLAG_MTIME])
558                         sum_add_time(&meta, st.st_mtime);
559                 if (flags[FLAG_CTIME])
560                         sum_add_time(&meta, st.st_ctime);
561                 if (S_ISDIR(st.st_mode)) {
562                         fd = openat(dirfd, namelist[i], 0);
563                         if (fd == -1 && flags[FLAG_OPEN_ERROR]) {
564                                 sum_add_u64(&meta, errno);
565                         } else if (fd == -1) {
566                                 fprintf(stderr, "open failed for %s/%s: %s\n",
567                                         path_prefix, path, strerror(errno));
568                                 exit(-1);
569                         } else {
570                                 sum(fd, level + 1, &cs, path_prefix, path);
571                                 close(fd);
572                         }
573                 } else if (S_ISREG(st.st_mode)) {
574                         sum_add_u64(&meta, st.st_size);
575                         if (flags[FLAG_DATA]) {
576                                 if (verbose)
577                                         fprintf(stderr, "file %s\n",
578                                                 namelist[i]);
579                                 fd = openat(dirfd, namelist[i], 0);
580                                 if (fd == -1 && flags[FLAG_OPEN_ERROR]) {
581                                         sum_add_u64(&meta, errno);
582                                 } else if (fd == -1) {
583                                         fprintf(stderr,
584                                                 "open failed for %s/%s: %s\n",
585                                                 path_prefix, path,
586                                                 strerror(errno));
587                                         exit(-1);
588                                 }
589                                 if (fd != -1) {
590                                         ret = sum_file_data(fd, &cs);
591                                         if (ret < 0) {
592                                                 fprintf(stderr,
593                                                         "read failed for "
594                                                         "%s/%s: %s\n",
595                                                         path_prefix, path,
596                                                         strerror(errno));
597                                                 exit(-1);
598                                         }
599                                         close(fd);
600                                 }
601                         }
602                 } else if (S_ISLNK(st.st_mode)) {
603                         ret = readlink(namelist[i], buf, sizeof(buf));
604                         if (ret == -1) {
605                                 perror("readlink");
606                                 exit(-1);
607                         }
608                         sum_add(&cs, buf, ret);
609                 } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
610                         sum_add_u64(&cs, major(st.st_rdev));
611                         sum_add_u64(&cs, minor(st.st_rdev));
612                 }
613                 sum_fini(&cs);
614                 sum_fini(&meta);
615                 if (gen_manifest || in_manifest) {
616                         char *fn;
617                         char *m;
618                         char *c;
619
620                         if (S_ISDIR(st.st_mode))
621                                 strcat(path, "/");
622                         fn = escape(path);
623                         m = sum_to_string(&meta);
624                         c = sum_to_string(&cs);
625
626                         if (gen_manifest)
627                                 fprintf(out_fp, "%s %s %s\n", fn, m, c);
628                         if (in_manifest)
629                                 check_manifest(fn, m, c, 0);
630                         free(c);
631                         free(m);
632                         free(fn);
633                 }
634                 sum_add_sum(dircs, &cs);
635                 sum_add_sum(dircs, &meta);
636 next:
637                 free(path);
638         }
639 }
640
641 int
642 main(int argc, char *argv[])
643 {
644         extern char *optarg;
645         extern int optind;
646         int     c;
647         char *path;
648         int fd;
649         sum_t cs;
650         char flagstring[sizeof(flchar)];
651         int i;
652         int plen;
653         int elen;
654         int n_flags = 0;
655         const char *allopts = "heEfuUgGoOaAmMcCdDsSnNw:r:vx:";
656
657         out_fp = stdout;
658         while ((c = getopt(argc, argv, allopts)) != EOF) {
659                 switch(c) {
660                 case 'f':
661                         gen_manifest = 1;
662                         break;
663                 case 'u':
664                 case 'U':
665                 case 'g':
666                 case 'G':
667                 case 'o':
668                 case 'O':
669                 case 'a':
670                 case 'A':
671                 case 'm':
672                 case 'M':
673                 case 'c':
674                 case 'C':
675                 case 'd':
676                 case 'D':
677                 case 'e':
678                 case 'E':
679                 case 's':
680                 case 'S':
681                         ++n_flags;
682                         parse_flag(c);
683                         break;
684                 case 'n':
685                         for (i = 0; i < NUM_FLAGS; ++i)
686                                 flags[i] = 0;
687                         break;
688                 case 'N':
689                         for (i = 0; i < NUM_FLAGS; ++i)
690                                 flags[i] = 1;
691                         break;
692                 case 'w':
693                         out_fp = fopen(optarg, "w");
694                         if (!out_fp) {
695                                 fprintf(stderr,
696                                         "failed to open output file: %s\n",
697                                         strerror(errno));
698                                 exit(-1);
699                         }
700                         break;
701                 case 'r':
702                         in_fp = fopen(optarg, "r");
703                         if (!in_fp) {
704                                 fprintf(stderr,
705                                         "failed to open input file: %s\n",
706                                         strerror(errno));
707                                 exit(-1);
708                         }
709                         break;
710                 case 'x':
711                         ++n_excludes;
712                         excludes = realloc(excludes,
713                                            sizeof(*excludes) * n_excludes);
714                         if (!excludes) {
715                                 fprintf(stderr,
716                                         "failed to alloc exclude space\n");
717                                 exit(-1);
718                         }
719                         excludes[n_excludes - 1].path = optarg;
720                         break;
721                 case 'v':
722                         ++verbose;
723                         break;
724                 case 'h':
725                 case '?':
726                         usage();
727                 }
728         }
729
730         if (optind + 1 != argc) {
731                 fprintf(stderr, "missing path\n");
732                 usage();
733         }
734
735         if (in_fp) {
736                 char *l = getln(line, sizeof(line), in_fp);
737                 char *p;
738
739                 if (l == NULL) {
740                         fprintf(stderr, "failed to read line from input\n");
741                         exit(-1);
742                 }
743                 if (strncmp(l, "Flags: ", 7) == 0) {
744                         l += 7;
745                         in_manifest = 1;
746                         parse_flags(l);
747                 } else if ((p = strchr(l, ':'))) {
748                         *p++ = 0;
749                         parse_flags(l);
750                         checksum = strdup(p);
751                 } else {
752                         fprintf(stderr, "invalid input file format\n");
753                         exit(-1);
754                 }
755                 if (n_flags)
756                         fprintf(stderr, "warning: "
757                                 "command line flags ignored in -r mode\n");
758         }
759         strcpy(flagstring, flchar);
760         for (i = 0; i < NUM_FLAGS; ++i) {
761                 if (flags[i] == 0)
762                         flagstring[i] -= 'a' - 'A';
763         }
764
765         path = argv[optind];
766         plen = strlen(path);
767         if (path[plen - 1] == '/') {
768                 --plen;
769                 path[plen] = '\0';
770         }
771
772         for (i = 0; i < n_excludes; ++i) {
773                 if (strncmp(path, excludes[i].path, plen) != 0)
774                         fprintf(stderr,
775                                 "warning: exclude %s outside of path %s\n",
776                                 excludes[i].path, path);
777                 else
778                         excludes[i].path += plen;
779                 elen = strlen(excludes[i].path);
780                 if (excludes[i].path[elen - 1] == '/')
781                         --elen;
782                 excludes[i].path[elen] = '\0';
783                 excludes[i].len = elen;
784         }
785
786         fd = open(path, O_RDONLY);
787         if (fd == -1) {
788                 fprintf(stderr, "failed to open %s: %s\n", path,
789                         strerror(errno));
790                 exit(-1);
791         }
792
793         if (gen_manifest)
794                 fprintf(out_fp, "Flags: %s\n", flagstring);
795
796         sum_init(&cs);
797         sum(fd, 1, &cs, path, "");
798         sum_fini(&cs);
799
800         close(fd);
801         if (in_manifest)
802                 check_manifest("", "", "", 1);
803
804         if (!checksum) {
805                 if (in_manifest) {
806                         fprintf(stderr, "malformed input\n");
807                         exit(-1);
808                 }
809                 if (!gen_manifest)
810                         fprintf(out_fp, "%s:", flagstring);
811
812                 fprintf(out_fp, "%s\n", sum_to_string(&cs));
813         } else {
814                 if (strcmp(checksum, sum_to_string(&cs)) == 0) {
815                         printf("OK\n");
816                         exit(0);
817                 } else {
818                         printf("FAIL\n");
819                         exit(1);
820                 }
821         }
822
823         exit(0);
824 }