Upload Tizen:Base source
[framework/base/util-linux-ng.git] / disk-utils / fsck.cramfs.c
1 /*
2  * cramfsck - check a cramfs file system
3  *
4  * Copyright (C) 2000-2002 Transmeta Corporation
5  *               2005 Adrian Bunk
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * 1999/12/03: Linus Torvalds (cramfs tester and unarchive program)
22  * 2000/06/03: Daniel Quinlan (CRC and length checking program)
23  * 2000/06/04: Daniel Quinlan (merged programs, added options, support
24  *                            for special files, preserve permissions and
25  *                            ownership, cramfs superblock v2, bogus mode
26  *                            test, pathname length test, etc.)
27  * 2000/06/06: Daniel Quinlan (support for holes, pretty-printing,
28  *                            symlink size test)
29  * 2000/07/11: Daniel Quinlan (file length tests, start at offset 0 or 512,
30  *                            fsck-compatible exit codes)
31  * 2000/07/15: Daniel Quinlan (initial support for block devices)
32  * 2002/01/10: Daniel Quinlan (additional checks, test more return codes,
33  *                            use read if mmap fails, standardize messages)
34  */
35
36 /* compile-time options */
37 //#define INCLUDE_FS_TESTS      /* include cramfs checking and extraction */
38
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <unistd.h>
42 #include <dirent.h>
43 #include <stdlib.h>
44 #include <errno.h>
45 #include <string.h>
46 #include <getopt.h>
47 #include <utime.h>
48 #include <fcntl.h>
49 #include <zlib.h>
50
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <sys/mman.h>
54 #include <sys/ioctl.h>
55 #include <sys/sysmacros.h>      /* for major, minor */
56
57 #include "cramfs.h"
58 #include "cramfs_common.h"
59 #include "nls.h"
60 #include "blkdev.h"
61
62 static const char *progname = "cramfsck";
63
64 static int fd;                  /* ROM image file descriptor */
65 static char *filename;          /* ROM image filename */
66 struct cramfs_super super;      /* just find the cramfs superblock once */
67 static int cramfs_is_big_endian = 0;    /* source is big endian */
68 static int opt_verbose = 0;     /* 1 = verbose (-v), 2+ = very verbose (-vv) */
69
70 char *extract_dir = ""; /* extraction directory (-x) */
71
72 /* Exit codes used by fsck-type programs */
73 #define FSCK_OK          0      /* No errors */
74 #define FSCK_NONDESTRUCT 1      /* File system errors corrected */
75 #define FSCK_REBOOT      2      /* System should be rebooted */
76 #define FSCK_UNCORRECTED 4      /* File system errors left uncorrected */
77 #define FSCK_ERROR       8      /* Operational error */
78 #define FSCK_USAGE       16     /* Usage or syntax error */
79 #define FSCK_LIBRARY     128    /* Shared library error */
80
81 #define PAD_SIZE 512
82
83 #ifdef INCLUDE_FS_TESTS
84
85 static int opt_extract = 0;     /* extract cramfs (-x) */
86
87 static uid_t euid;                     /* effective UID */
88
89 /* (cramfs_super + start) <= start_dir < end_dir <= start_data <= end_data */
90 static unsigned long start_dir = ~0UL; /* start of first non-root inode */
91 static unsigned long end_dir = 0;      /* end of the directory structure */
92 static unsigned long start_data = ~0UL;        /* start of the data (256 MB = max) */
93 static unsigned long end_data = 0;     /* end of the data */
94
95
96 /* Guarantee access to at least 8kB at a time */
97 #define ROMBUFFER_BITS  13
98 #define ROMBUFFERSIZE   (1 << ROMBUFFER_BITS)
99 #define ROMBUFFERMASK   (ROMBUFFERSIZE-1)
100 static char read_buffer[ROMBUFFERSIZE * 2];
101 static unsigned long read_buffer_block = ~0UL;
102
103 static z_stream stream;
104
105 /* Prototypes */
106 static void expand_fs(char *, struct cramfs_inode *);
107 #endif /* INCLUDE_FS_TESTS */
108
109 static char *outbuffer;
110
111 static size_t page_size;
112
113 /* Input status of 0 to print help and exit without an error. */
114 static void usage(int status)
115 {
116         FILE *stream = status ? stderr : stdout;
117
118         fprintf(stream, _("usage: %s [-hv] [-x dir] file\n"
119                 " -h         print this help\n"
120                 " -x dir     extract into dir\n"
121                 " -v         be more verbose\n"
122                 " file       file to test\n"), progname);
123
124         exit(status);
125 }
126
127 static void die(int status, int syserr, const char *fmt, ...)
128 {
129         va_list arg_ptr;
130         int save = errno;
131
132         fflush(0);
133         va_start(arg_ptr, fmt);
134         fprintf(stderr, "%s: ", progname);
135         vfprintf(stderr, fmt, arg_ptr);
136         if (syserr) {
137                 fprintf(stderr, ": %s", strerror(save));
138         }
139         fprintf(stderr, "\n");
140         va_end(arg_ptr);
141         exit(status);
142 }
143
144 int get_superblock_endianness(u32 magic)
145 {
146         if (magic == CRAMFS_MAGIC) {
147                 cramfs_is_big_endian = HOST_IS_BIG_ENDIAN;
148                 return 0;
149         }
150         else if (magic == u32_toggle_endianness(!HOST_IS_BIG_ENDIAN, CRAMFS_MAGIC)) {
151                 cramfs_is_big_endian = !HOST_IS_BIG_ENDIAN;
152                 return 0;
153         }
154         else {
155                 return -1;
156         }
157 }
158
159 static void test_super(int *start, size_t *length) {
160         struct stat st;
161
162         /* find the physical size of the file or block device */
163         if (stat(filename, &st) < 0) {
164                 die(FSCK_ERROR, 1, _("stat failed: %s"), filename);
165         }
166         fd = open(filename, O_RDONLY);
167         if (fd < 0) {
168                 die(FSCK_ERROR, 1, _("open failed: %s"), filename);
169         }
170         if (S_ISBLK(st.st_mode)) {
171                 unsigned long long bytes;
172                 if (blkdev_get_size(fd, &bytes)) {
173                         die(FSCK_ERROR, 1, _("ioctl failed: unable to determine device size: %s"), filename);
174                 }
175                 *length = bytes;
176         }
177         else if (S_ISREG(st.st_mode)) {
178                 *length = st.st_size;
179         }
180         else {
181                 die(FSCK_ERROR, 0, _("not a block device or file: %s"), filename);
182         }
183
184         if (*length < sizeof(struct cramfs_super)) {
185                 die(FSCK_UNCORRECTED, 0, _("file length too short"));
186         }
187
188         /* find superblock */
189         if (read(fd, &super, sizeof(super)) != sizeof(super)) {
190                 die(FSCK_ERROR, 1, _("read failed: %s"), filename);
191         }
192         if (get_superblock_endianness(super.magic) != -1) {
193                 *start = 0;
194         }
195         else if (*length >= (PAD_SIZE + sizeof(super))) {
196                 lseek(fd, PAD_SIZE, SEEK_SET);
197                 if (read(fd, &super, sizeof(super)) != sizeof(super)) {
198                         die(FSCK_ERROR, 1, _("read failed: %s"), filename);
199                 }
200                 if (get_superblock_endianness(super.magic) != -1) {
201                         *start = PAD_SIZE;
202                 }
203                 else {
204                         die(FSCK_UNCORRECTED, 0, "superblock magic not found");
205                 }
206         }
207         else {
208                 die(FSCK_UNCORRECTED, 0, _("superblock magic not found"));
209         }
210
211         if (opt_verbose) {
212                 printf("cramfs endianness is %s\n", cramfs_is_big_endian ? "big" : "little");
213         }
214
215         super_toggle_endianness(cramfs_is_big_endian, &super);
216         if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) {
217                 die(FSCK_ERROR, 0, _("unsupported filesystem features"));
218         }
219         if (super.size < page_size) {
220                 die(FSCK_UNCORRECTED, 0, _("superblock size (%d) too small"), super.size);
221         }
222         if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
223                 if (super.fsid.files == 0) {
224                         die(FSCK_UNCORRECTED, 0, _("zero file count"));
225                 }
226                 if (*length < super.size) {
227                         die(FSCK_UNCORRECTED, 0, _("file length too short"));
228                 }
229                 else if (*length > super.size) {
230                         fprintf(stderr, _("warning: file extends past end of filesystem\n"));
231                 }
232         }
233         else {
234                 fprintf(stderr, _("warning: old cramfs format\n"));
235         }
236 }
237
238 static void test_crc(int start)
239 {
240         void *buf;
241         u32 crc;
242
243         if (!(super.flags & CRAMFS_FLAG_FSID_VERSION_2)) {
244 #ifdef INCLUDE_FS_TESTS
245                 return;
246 #else /* not INCLUDE_FS_TESTS */
247                 die(FSCK_USAGE, 0, _("unable to test CRC: old cramfs format"));
248 #endif /* not INCLUDE_FS_TESTS */
249         }
250
251         crc = crc32(0L, Z_NULL, 0);
252
253         buf = mmap(NULL, super.size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
254         if (buf == MAP_FAILED) {
255                 buf = mmap(NULL, super.size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
256                 if (buf != MAP_FAILED) {
257                         lseek(fd, 0, SEEK_SET);
258                         if (read(fd, buf, super.size) < 0)
259                                 die(FSCK_ERROR, 1, _("read failed: %s"), filename);
260                 }
261         }
262         if (buf != MAP_FAILED) {
263                 ((struct cramfs_super *) (buf+start))->fsid.crc = crc32(0L, Z_NULL, 0);
264                 crc = crc32(crc, buf+start, super.size-start);
265                 munmap(buf, super.size);
266         }
267         else {
268                 int retval;
269                 size_t length = 0;
270
271                 buf = malloc(4096);
272                 if (!buf) {
273                         die(FSCK_ERROR, 1, _("malloc failed"));
274                 }
275                 lseek(fd, start, SEEK_SET);
276                 for (;;) {
277                         retval = read(fd, buf, 4096);
278                         if (retval < 0) {
279                                 die(FSCK_ERROR, 1, _("read failed: %s"), filename);
280                         }
281                         else if (retval == 0) {
282                                 break;
283                         }
284                         if (length == 0) {
285                                 ((struct cramfs_super *) buf)->fsid.crc = crc32(0L, Z_NULL, 0);
286                         }
287                         length += retval;
288                         if (length > (super.size-start)) {
289                                 crc = crc32(crc, buf, retval - (length - (super.size-start)));
290                                 break;
291                         }
292                         crc = crc32(crc, buf, retval);
293                 }
294                 free(buf);
295         }
296
297         if (crc != super.fsid.crc) {
298                 die(FSCK_UNCORRECTED, 0, _("crc error"));
299         }
300 }
301
302 #ifdef INCLUDE_FS_TESTS
303 static void print_node(char type, struct cramfs_inode *i, char *name)
304 {
305         char info[10];
306
307         if (S_ISCHR(i->mode) || (S_ISBLK(i->mode))) {
308                 /* major/minor numbers can be as high as 2^12 or 4096 */
309                 snprintf(info, 10, "%4d,%4d", major(i->size), minor(i->size));
310         }
311         else {
312                 /* size be as high as 2^24 or 16777216 */
313                 snprintf(info, 10, "%9d", i->size);
314         }
315
316         printf("%c %04o %s %5d:%-3d %s\n",
317                type, i->mode & ~S_IFMT, info, i->uid, i->gid,
318                !*name && type == 'd' ? "/" : name);
319 }
320
321 /*
322  * Create a fake "blocked" access
323  */
324 static void *romfs_read(unsigned long offset)
325 {
326         unsigned int block = offset >> ROMBUFFER_BITS;
327         if (block != read_buffer_block) {
328                 read_buffer_block = block;
329                 lseek(fd, block << ROMBUFFER_BITS, SEEK_SET);
330                 read(fd, read_buffer, ROMBUFFERSIZE * 2);
331         }
332         return read_buffer + (offset & ROMBUFFERMASK);
333 }
334
335 static struct cramfs_inode *cramfs_iget(struct cramfs_inode * i)
336 {
337         struct cramfs_inode *inode = malloc(sizeof(struct cramfs_inode));
338
339         if (!inode) {
340                 die(FSCK_ERROR, 1, _("malloc failed"));
341         }
342         inode_to_host(cramfs_is_big_endian, i, inode);
343         return inode;
344 }
345
346 static struct cramfs_inode *iget(unsigned int ino)
347 {
348         return cramfs_iget(romfs_read(ino));
349 }
350
351 static void iput(struct cramfs_inode *inode)
352 {
353         free(inode);
354 }
355
356 /*
357  * Return the offset of the root directory
358  */
359 static struct cramfs_inode *read_super(void)
360 {
361         struct cramfs_inode * root = cramfs_iget(&super.root);
362         unsigned long offset = root->offset << 2;
363
364         if (!S_ISDIR(root->mode))
365                 die(FSCK_UNCORRECTED, 0, _("root inode is not directory"));
366         if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
367             ((offset != sizeof(struct cramfs_super)) &&
368              (offset != PAD_SIZE + sizeof(struct cramfs_super))))
369         {
370                 die(FSCK_UNCORRECTED, 0, _("bad root offset (%lu)"), offset);
371         }
372         return root;
373 }
374
375 static int uncompress_block(void *src, int len)
376 {
377         int err;
378
379         stream.next_in = src;
380         stream.avail_in = len;
381
382         stream.next_out = (unsigned char *) outbuffer;
383         stream.avail_out = page_size*2;
384
385         inflateReset(&stream);
386
387         if (len > page_size*2) {
388                 die(FSCK_UNCORRECTED, 0, _("data block too large"));
389         }
390         err = inflate(&stream, Z_FINISH);
391         if (err != Z_STREAM_END) {
392                 die(FSCK_UNCORRECTED, 0, _("decompression error %p(%d): %s"),
393                     zError(err), src, len);
394         }
395         return stream.total_out;
396 }
397
398 #if !HAVE_LCHOWN
399 #define lchown chown
400 #endif
401 static void do_uncompress(char *path, int fd, unsigned long offset, unsigned long size)
402 {
403         unsigned long curr = offset + 4 * ((size + page_size - 1) / page_size);
404
405         do {
406                 unsigned long out = page_size;
407                 unsigned long next = u32_toggle_endianness(cramfs_is_big_endian, *(u32 *) romfs_read(offset));
408
409                 if (next > end_data) {
410                         end_data = next;
411                 }
412
413                 offset += 4;
414                 if (curr == next) {
415                         if (opt_verbose > 1) {
416                                 printf(_("  hole at %ld (%zd)\n"), curr, page_size);
417                         }
418                         if (size < page_size)
419                                 out = size;
420                         memset(outbuffer, 0x00, out);
421                 }
422                 else {
423                         if (opt_verbose > 1) {
424                                 printf(_("  uncompressing block at %ld to %ld (%ld)\n"), curr, next, next - curr);
425                         }
426                         out = uncompress_block(romfs_read(curr), next - curr);
427                 }
428                 if (size >= page_size) {
429                         if (out != page_size) {
430                                 die(FSCK_UNCORRECTED, 0, _("non-block (%ld) bytes"), out);
431                         }
432                 } else {
433                         if (out != size) {
434                                 die(FSCK_UNCORRECTED, 0, _("non-size (%ld vs %ld) bytes"), out, size);
435                         }
436                 }
437                 size -= out;
438                 if (opt_extract) {
439                         if (write(fd, outbuffer, out) < 0) {
440                                 die(FSCK_ERROR, 1, _("write failed: %s"), path);
441                         }
442                 }
443                 curr = next;
444         } while (size);
445 }
446
447 static void change_file_status(char *path, struct cramfs_inode *i)
448 {
449         struct utimbuf epoch = { 0, 0 };
450
451         if (euid == 0) {
452                 if (lchown(path, i->uid, i->gid) < 0) {
453                         die(FSCK_ERROR, 1, _("lchown failed: %s"), path);
454                 }
455                 if (S_ISLNK(i->mode))
456                         return;
457                 if ((S_ISUID | S_ISGID) & i->mode) {
458                         if (chmod(path, i->mode) < 0) {
459                                 die(FSCK_ERROR, 1, _("chown failed: %s"), path);
460                         }
461                 }
462         }
463         if (S_ISLNK(i->mode))
464                 return;
465         if (utime(path, &epoch) < 0) {
466                 die(FSCK_ERROR, 1, _("utime failed: %s"), path);
467         }
468 }
469
470 static void do_directory(char *path, struct cramfs_inode *i)
471 {
472         int pathlen = strlen(path);
473         int count = i->size;
474         unsigned long offset = i->offset << 2;
475         char *newpath = malloc(pathlen + 256);
476
477         if (!newpath) {
478                 die(FSCK_ERROR, 1, _("malloc failed"));
479         }
480         if (offset == 0 && count != 0) {
481                 die(FSCK_UNCORRECTED, 0, _("directory inode has zero offset and non-zero size: %s"), path);
482         }
483         if (offset != 0 && offset < start_dir) {
484                 start_dir = offset;
485         }
486         /* TODO: Do we need to check end_dir for empty case? */
487         memcpy(newpath, path, pathlen);
488         newpath[pathlen] = '/';
489         pathlen++;
490         if (opt_verbose) {
491                 print_node('d', i, path);
492         }
493         if (opt_extract) {
494                 if (mkdir(path, i->mode) < 0) {
495                         die(FSCK_ERROR, 1, _("mkdir failed: %s"), path);
496                 }
497                 change_file_status(path, i);
498         }
499         while (count > 0) {
500                 struct cramfs_inode *child = iget(offset);
501                 int size;
502                 int newlen = child->namelen << 2;
503
504                 size = sizeof(struct cramfs_inode) + newlen;
505                 count -= size;
506
507                 offset += sizeof(struct cramfs_inode);
508
509                 memcpy(newpath + pathlen, romfs_read(offset), newlen);
510                 newpath[pathlen + newlen] = 0;
511                 if (newlen == 0) {
512                         die(FSCK_UNCORRECTED, 0, _("filename length is zero"));
513                 }
514                 if ((pathlen + newlen) - strlen(newpath) > 3) {
515                         die(FSCK_UNCORRECTED, 0, _("bad filename length"));
516                 }
517                 expand_fs(newpath, child);
518
519                 offset += newlen;
520
521                 if (offset <= start_dir) {
522                         die(FSCK_UNCORRECTED, 0, _("bad inode offset"));
523                 }
524                 if (offset > end_dir) {
525                         end_dir = offset;
526                 }
527                 iput(child); /* free(child) */
528         }
529         free(newpath);
530 }
531
532 static void do_file(char *path, struct cramfs_inode *i)
533 {
534         unsigned long offset = i->offset << 2;
535         int fd = 0;
536
537         if (offset == 0 && i->size != 0) {
538                 die(FSCK_UNCORRECTED, 0, _("file inode has zero offset and non-zero size"));
539         }
540         if (i->size == 0 && offset != 0) {
541                 die(FSCK_UNCORRECTED, 0, _("file inode has zero size and non-zero offset"));
542         }
543         if (offset != 0 && offset < start_data) {
544                 start_data = offset;
545         }
546         if (opt_verbose) {
547                 print_node('f', i, path);
548         }
549         if (opt_extract) {
550                 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, i->mode);
551                 if (fd < 0) {
552                         die(FSCK_ERROR, 1, _("open failed: %s"), path);
553                 }
554         }
555         if (i->size) {
556                 do_uncompress(path, fd, offset, i->size);
557         }
558         if (opt_extract) {
559                 close(fd);
560                 change_file_status(path, i);
561         }
562 }
563
564 static void do_symlink(char *path, struct cramfs_inode *i)
565 {
566         unsigned long offset = i->offset << 2;
567         unsigned long curr = offset + 4;
568         unsigned long next = u32_toggle_endianness(cramfs_is_big_endian, *(u32 *) romfs_read(offset));
569         unsigned long size;
570
571         if (offset == 0) {
572                 die(FSCK_UNCORRECTED, 0, _("symbolic link has zero offset"));
573         }
574         if (i->size == 0) {
575                 die(FSCK_UNCORRECTED, 0, _("symbolic link has zero size"));
576         }
577
578         if (offset < start_data) {
579                 start_data = offset;
580         }
581         if (next > end_data) {
582                 end_data = next;
583         }
584
585         size = uncompress_block(romfs_read(curr), next - curr);
586         if (size != i->size) {
587                 die(FSCK_UNCORRECTED, 0, _("size error in symlink: %s"), path);
588         }
589         outbuffer[size] = 0;
590         if (opt_verbose) {
591                 char *str;
592
593                 asprintf(&str, "%s -> %s", path, outbuffer);
594                 print_node('l', i, str);
595                 if (opt_verbose > 1) {
596                         printf(_("  uncompressing block at %ld to %ld (%ld)\n"), curr, next, next - curr);
597                 }
598                 free(str);
599         }
600         if (opt_extract) {
601                 if (symlink(outbuffer, path) < 0) {
602                         die(FSCK_ERROR, 1, _("symlink failed: %s"), path);
603                 }
604                 change_file_status(path, i);
605         }
606 }
607
608 static void do_special_inode(char *path, struct cramfs_inode *i)
609 {
610         dev_t devtype = 0;
611         char type;
612
613         if (i->offset) {        /* no need to shift offset */
614                 die(FSCK_UNCORRECTED, 0, _("special file has non-zero offset: %s"), path);
615         }
616         if (S_ISCHR(i->mode)) {
617                 devtype = i->size;
618                 type = 'c';
619         }
620         else if (S_ISBLK(i->mode)) {
621                 devtype = i->size;
622                 type = 'b';
623         }
624         else if (S_ISFIFO(i->mode)) {
625                 if (i->size != 0) {
626                         die(FSCK_UNCORRECTED, 0, _("fifo has non-zero size: %s"), path);
627                 }
628                 type = 'p';
629         }
630         else if (S_ISSOCK(i->mode)) {
631                 if (i->size != 0) {
632                         die(FSCK_UNCORRECTED, 0, _("socket has non-zero size: %s"), path);
633                 }
634                 type = 's';
635         }
636         else {
637                 die(FSCK_UNCORRECTED, 0, _("bogus mode: %s (%o)"), path, i->mode);
638                 return;         /* not reached */
639         }
640
641         if (opt_verbose) {
642                 print_node(type, i, path);
643         }
644
645         if (opt_extract) {
646                 if (mknod(path, i->mode, devtype) < 0) {
647                         die(FSCK_ERROR, 1, _("mknod failed: %s"), path);
648                 }
649                 change_file_status(path, i);
650         }
651 }
652
653 static void expand_fs(char *path, struct cramfs_inode *inode)
654 {
655         if (S_ISDIR(inode->mode)) {
656                 do_directory(path, inode);
657         }
658         else if (S_ISREG(inode->mode)) {
659                 do_file(path, inode);
660         }
661         else if (S_ISLNK(inode->mode)) {
662                 do_symlink(path, inode);
663         }
664         else {
665                 do_special_inode(path, inode);
666         }
667 }
668
669 static void test_fs(int start)
670 {
671         struct cramfs_inode *root;
672
673         root = read_super();
674         umask(0);
675         euid = geteuid();
676         stream.next_in = NULL;
677         stream.avail_in = 0;
678         inflateInit(&stream);
679         expand_fs(extract_dir, root);
680         inflateEnd(&stream);
681         if (start_data != ~0UL) {
682                 if (start_data < (sizeof(struct cramfs_super) + start)) {
683                         die(FSCK_UNCORRECTED, 0, _("directory data start (%ld) < sizeof(struct cramfs_super) + start (%ld)"), start_data, sizeof(struct cramfs_super) + start);
684                 }
685                 if (end_dir != start_data) {
686                         die(FSCK_UNCORRECTED, 0, _("directory data end (%ld) != file data start (%ld)"), end_dir, start_data);
687                 }
688         }
689         if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
690                 if (end_data > super.size) {
691                         die(FSCK_UNCORRECTED, 0, _("invalid file data offset"));
692                 }
693         }
694         iput(root);             /* free(root) */
695 }
696 #endif /* INCLUDE_FS_TESTS */
697
698 int main(int argc, char **argv)
699 {
700         int c;                  /* for getopt */
701         int start = 0;
702         size_t length = 0;
703
704         setlocale(LC_MESSAGES, "");
705         bindtextdomain(PACKAGE, LOCALEDIR);
706         textdomain(PACKAGE);
707
708         page_size = getpagesize();
709
710         if (argc)
711                 progname = argv[0];
712
713         outbuffer = malloc(page_size * 2);
714         if (!outbuffer)
715                 die(FSCK_ERROR, 1, _("failed to allocate outbuffer"));
716
717         /* command line options */
718         while ((c = getopt(argc, argv, "hx:v")) != EOF) {
719                 switch (c) {
720                 case 'h':
721                         usage(FSCK_OK);
722                 case 'x':
723 #ifdef INCLUDE_FS_TESTS
724                         opt_extract = 1;
725                         extract_dir = optarg;
726                         break;
727 #else /* not INCLUDE_FS_TESTS */
728                         die(FSCK_USAGE, 0, _("compiled without -x support"));
729 #endif /* not INCLUDE_FS_TESTS */
730                 case 'v':
731                         opt_verbose++;
732                         break;
733                 }
734         }
735
736         if ((argc - optind) != 1)
737                 usage(FSCK_USAGE);
738         filename = argv[optind];
739
740         test_super(&start, &length);
741         test_crc(start);
742 #ifdef INCLUDE_FS_TESTS
743         test_fs(start);
744 #endif /* INCLUDE_FS_TESTS */
745
746         if (opt_verbose) {
747                 printf("%s: OK\n", filename);
748         }
749
750         exit(FSCK_OK);
751 }