]> git.ipfire.org Git - thirdparty/util-linux.git/blame - disk-utils/fsck.cramfs.c
tests: check also for /dev/loop/X
[thirdparty/util-linux.git] / disk-utils / fsck.cramfs.c
CommitLineData
63cccae4
KZ
1/*
2 * cramfsck - check a cramfs file system
3 *
19922f22
KZ
4 * Copyright (C) 2000-2002 Transmeta Corporation
5 * 2005 Adrian Bunk
63cccae4
KZ
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)
19922f22
KZ
32 * 2002/01/10: Daniel Quinlan (additional checks, test more return codes,
33 * use read if mmap fails, standardize messages)
63cccae4
KZ
34 */
35
36/* compile-time options */
19922f22 37//#define INCLUDE_FS_TESTS /* include cramfs checking and extraction */
63cccae4
KZ
38
39#include <stdio.h>
19922f22 40#include <stdarg.h>
63cccae4
KZ
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"
fbaec83b 58#include "cramfs_common.h"
63cccae4 59#include "nls.h"
098fa6b1 60#include "blkdev.h"
63cccae4
KZ
61
62static const char *progname = "cramfsck";
63
64static int fd; /* ROM image file descriptor */
65static char *filename; /* ROM image filename */
19922f22 66struct cramfs_super super; /* just find the cramfs superblock once */
fbaec83b 67static int cramfs_is_big_endian = 0; /* source is big endian */
63cccae4
KZ
68static int opt_verbose = 0; /* 1 = verbose (-v), 2+ = very verbose (-vv) */
69
63cccae4
KZ
70char *extract_dir = NULL; /* extraction directory (-x) */
71
19922f22
KZ
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 */
63cccae4
KZ
80
81#define PAD_SIZE 512
b22550fa 82
19922f22
KZ
83#ifdef INCLUDE_FS_TESTS
84
85static int opt_extract = 0; /* extract cramfs (-x) */
86
87static uid_t euid; /* effective UID */
88
89/* (cramfs_super + start) <= start_dir < end_dir <= start_data <= end_data */
90static unsigned long start_dir = ~0UL; /* start of first non-root inode */
91static unsigned long end_dir = 0; /* end of the directory structure */
92static unsigned long start_data = ~0UL; /* start of the data (256 MB = max) */
93static unsigned long end_data = 0; /* end of the data */
94
63cccae4
KZ
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)
100static char read_buffer[ROMBUFFERSIZE * 2];
101static unsigned long read_buffer_block = ~0UL;
102
19922f22 103static z_stream stream;
63cccae4 104
19922f22
KZ
105/* Prototypes */
106static void expand_fs(char *, struct cramfs_inode *);
63cccae4
KZ
107#endif /* INCLUDE_FS_TESTS */
108
19922f22
KZ
109static char *outbuffer;
110
111static size_t page_size;
112
63cccae4
KZ
113/* Input status of 0 to print help and exit without an error. */
114static 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
19922f22
KZ
127static 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
fbaec83b
SRP
144int 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
19922f22
KZ
159static 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) {
e56644ca 164 die(FSCK_ERROR, 1, _("stat failed: %s"), filename);
19922f22
KZ
165 }
166 fd = open(filename, O_RDONLY);
167 if (fd < 0) {
e56644ca 168 die(FSCK_ERROR, 1, _("open failed: %s"), filename);
19922f22
KZ
169 }
170 if (S_ISBLK(st.st_mode)) {
098fa6b1
ST
171 unsigned long long bytes;
172 if (blkdev_get_size(fd, &bytes)) {
e56644ca 173 die(FSCK_ERROR, 1, _("ioctl failed: unable to determine device size: %s"), filename);
19922f22 174 }
098fa6b1 175 *length = bytes;
19922f22
KZ
176 }
177 else if (S_ISREG(st.st_mode)) {
178 *length = st.st_size;
179 }
180 else {
e56644ca 181 die(FSCK_ERROR, 0, _("not a block device or file: %s"), filename);
19922f22
KZ
182 }
183
184 if (*length < sizeof(struct cramfs_super)) {
e56644ca 185 die(FSCK_UNCORRECTED, 0, _("file length too short"));
19922f22
KZ
186 }
187
188 /* find superblock */
189 if (read(fd, &super, sizeof(super)) != sizeof(super)) {
e56644ca 190 die(FSCK_ERROR, 1, _("read failed: %s"), filename);
19922f22 191 }
fbaec83b 192 if (get_superblock_endianness(super.magic) != -1) {
19922f22
KZ
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)) {
e56644ca 198 die(FSCK_ERROR, 1, _("read failed: %s"), filename);
19922f22 199 }
fbaec83b 200 if (get_superblock_endianness(super.magic) != -1) {
19922f22
KZ
201 *start = PAD_SIZE;
202 }
fbaec83b
SRP
203 else {
204 die(FSCK_UNCORRECTED, 0, "superblock magic not found");
205 }
19922f22 206 }
fbaec83b 207 else {
e56644ca 208 die(FSCK_UNCORRECTED, 0, _("superblock magic not found"));
19922f22 209 }
fbaec83b
SRP
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);
19922f22 216 if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) {
e56644ca 217 die(FSCK_ERROR, 0, _("unsupported filesystem features"));
19922f22
KZ
218 }
219 if (super.size < page_size) {
e56644ca 220 die(FSCK_UNCORRECTED, 0, _("superblock size (%d) too small"), super.size);
19922f22
KZ
221 }
222 if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
223 if (super.fsid.files == 0) {
e56644ca 224 die(FSCK_UNCORRECTED, 0, _("zero file count"));
19922f22
KZ
225 }
226 if (*length < super.size) {
e56644ca 227 die(FSCK_UNCORRECTED, 0, _("file length too short"));
19922f22
KZ
228 }
229 else if (*length > super.size) {
e56644ca 230 fprintf(stderr, _("warning: file extends past end of filesystem\n"));
19922f22
KZ
231 }
232 }
233 else {
e56644ca 234 fprintf(stderr, _("warning: old cramfs format\n"));
19922f22
KZ
235 }
236}
237
238static 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 */
e56644ca 247 die(FSCK_USAGE, 0, _("unable to test CRC: old cramfs format"));
19922f22
KZ
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);
5f841647 258 if (read(fd, buf, super.size) < 0)
e56644ca 259 die(FSCK_ERROR, 1, _("read failed: %s"), filename);
19922f22
KZ
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) {
e56644ca 273 die(FSCK_ERROR, 1, _("malloc failed"));
19922f22
KZ
274 }
275 lseek(fd, start, SEEK_SET);
276 for (;;) {
277 retval = read(fd, buf, 4096);
278 if (retval < 0) {
e56644ca 279 die(FSCK_ERROR, 1, _("read failed: %s"), filename);
19922f22
KZ
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) {
e56644ca 298 die(FSCK_UNCORRECTED, 0, _("crc error"));
19922f22
KZ
299 }
300}
301
63cccae4
KZ
302#ifdef INCLUDE_FS_TESTS
303static 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, name);
318}
319
320/*
321 * Create a fake "blocked" access
322 */
323static void *romfs_read(unsigned long offset)
324{
325 unsigned int block = offset >> ROMBUFFER_BITS;
326 if (block != read_buffer_block) {
327 read_buffer_block = block;
328 lseek(fd, block << ROMBUFFER_BITS, SEEK_SET);
329 read(fd, read_buffer, ROMBUFFERSIZE * 2);
330 }
331 return read_buffer + (offset & ROMBUFFERMASK);
332}
333
334static struct cramfs_inode *cramfs_iget(struct cramfs_inode * i)
335{
336 struct cramfs_inode *inode = malloc(sizeof(struct cramfs_inode));
19922f22
KZ
337
338 if (!inode) {
e56644ca 339 die(FSCK_ERROR, 1, _("malloc failed"));
19922f22 340 }
fbaec83b 341 inode_to_host(cramfs_is_big_endian, i, inode);
63cccae4
KZ
342 return inode;
343}
344
345static struct cramfs_inode *iget(unsigned int ino)
346{
347 return cramfs_iget(romfs_read(ino));
348}
349
63cccae4
KZ
350static void iput(struct cramfs_inode *inode)
351{
352 free(inode);
353}
63cccae4
KZ
354
355/*
19922f22 356 * Return the offset of the root directory
63cccae4
KZ
357 */
358static struct cramfs_inode *read_super(void)
359{
fbaec83b
SRP
360 struct cramfs_inode * root = cramfs_iget(&super.root);
361 unsigned long offset = root->offset << 2;
19922f22 362
fbaec83b 363 if (!S_ISDIR(root->mode))
e56644ca 364 die(FSCK_UNCORRECTED, 0, _("root inode is not directory"));
19922f22
KZ
365 if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
366 ((offset != sizeof(struct cramfs_super)) &&
367 (offset != PAD_SIZE + sizeof(struct cramfs_super))))
368 {
e56644ca 369 die(FSCK_UNCORRECTED, 0, _("bad root offset (%lu)"), offset);
19922f22 370 }
fbaec83b 371 return root;
63cccae4
KZ
372}
373
374static int uncompress_block(void *src, int len)
375{
376 int err;
377
378 stream.next_in = src;
379 stream.avail_in = len;
380
381 stream.next_out = (unsigned char *) outbuffer;
19922f22 382 stream.avail_out = page_size*2;
63cccae4
KZ
383
384 inflateReset(&stream);
385
19922f22 386 if (len > page_size*2) {
e56644ca 387 die(FSCK_UNCORRECTED, 0, _("data block too large"));
19922f22 388 }
63cccae4
KZ
389 err = inflate(&stream, Z_FINISH);
390 if (err != Z_STREAM_END) {
e56644ca 391 die(FSCK_UNCORRECTED, 0, _("decompression error %p(%d): %s"),
19922f22 392 zError(err), src, len);
63cccae4
KZ
393 }
394 return stream.total_out;
395}
396
48d7b13a
KZ
397#if !HAVE_LCHOWN
398#define lchown chown
63cccae4 399#endif
19922f22
KZ
400static void do_uncompress(char *path, int fd, unsigned long offset, unsigned long size)
401{
402 unsigned long curr = offset + 4 * ((size + page_size - 1) / page_size);
403
404 do {
405 unsigned long out = page_size;
fbaec83b 406 unsigned long next = u32_toggle_endianness(cramfs_is_big_endian, *(u32 *) romfs_read(offset));
19922f22
KZ
407
408 if (next > end_data) {
409 end_data = next;
410 }
411
412 offset += 4;
413 if (curr == next) {
414 if (opt_verbose > 1) {
fbaec83b 415 printf(_(" hole at %ld (%zd)\n"), curr, page_size);
19922f22
KZ
416 }
417 if (size < page_size)
418 out = size;
419 memset(outbuffer, 0x00, out);
420 }
421 else {
422 if (opt_verbose > 1) {
e56644ca 423 printf(_(" uncompressing block at %ld to %ld (%ld)\n"), curr, next, next - curr);
19922f22
KZ
424 }
425 out = uncompress_block(romfs_read(curr), next - curr);
426 }
427 if (size >= page_size) {
428 if (out != page_size) {
e56644ca 429 die(FSCK_UNCORRECTED, 0, _("non-block (%ld) bytes"), out);
19922f22
KZ
430 }
431 } else {
432 if (out != size) {
e56644ca 433 die(FSCK_UNCORRECTED, 0, _("non-size (%ld vs %ld) bytes"), out, size);
19922f22
KZ
434 }
435 }
436 size -= out;
437 if (opt_extract) {
438 if (write(fd, outbuffer, out) < 0) {
e56644ca 439 die(FSCK_ERROR, 1, _("write failed: %s"), path);
19922f22
KZ
440 }
441 }
442 curr = next;
443 } while (size);
444}
63cccae4
KZ
445
446static void change_file_status(char *path, struct cramfs_inode *i)
447{
448 struct utimbuf epoch = { 0, 0 };
449
450 if (euid == 0) {
48d7b13a 451 if (lchown(path, i->uid, i->gid) < 0) {
e56644ca 452 die(FSCK_ERROR, 1, _("lchown failed: %s"), path);
63cccae4
KZ
453 }
454 if (S_ISLNK(i->mode))
455 return;
456 if ((S_ISUID | S_ISGID) & i->mode) {
457 if (chmod(path, i->mode) < 0) {
e56644ca 458 die(FSCK_ERROR, 1, _("chown failed: %s"), path);
63cccae4
KZ
459 }
460 }
461 }
462 if (S_ISLNK(i->mode))
463 return;
464 if (utime(path, &epoch) < 0) {
e56644ca 465 die(FSCK_ERROR, 1, _("utime failed: %s"), path);
19922f22
KZ
466 }
467}
468
469static void do_directory(char *path, struct cramfs_inode *i)
470{
471 int pathlen = strlen(path);
472 int count = i->size;
473 unsigned long offset = i->offset << 2;
474 char *newpath = malloc(pathlen + 256);
475
476 if (!newpath) {
e56644ca 477 die(FSCK_ERROR, 1, _("malloc failed"));
19922f22
KZ
478 }
479 if (offset == 0 && count != 0) {
e56644ca 480 die(FSCK_UNCORRECTED, 0, _("directory inode has zero offset and non-zero size: %s"), path);
19922f22
KZ
481 }
482 if (offset != 0 && offset < start_dir) {
483 start_dir = offset;
484 }
485 /* TODO: Do we need to check end_dir for empty case? */
486 memcpy(newpath, path, pathlen);
487 newpath[pathlen] = '/';
488 pathlen++;
489 if (opt_verbose) {
490 print_node('d', i, path);
491 }
492 if (opt_extract) {
493 if (mkdir(path, i->mode) < 0) {
e56644ca 494 die(FSCK_ERROR, 1, _("mkdir failed: %s"), path);
19922f22
KZ
495 }
496 change_file_status(path, i);
497 }
498 while (count > 0) {
499 struct cramfs_inode *child = iget(offset);
500 int size;
501 int newlen = child->namelen << 2;
502
503 size = sizeof(struct cramfs_inode) + newlen;
504 count -= size;
505
506 offset += sizeof(struct cramfs_inode);
507
508 memcpy(newpath + pathlen, romfs_read(offset), newlen);
509 newpath[pathlen + newlen] = 0;
510 if (newlen == 0) {
e56644ca 511 die(FSCK_UNCORRECTED, 0, _("filename length is zero"));
19922f22
KZ
512 }
513 if ((pathlen + newlen) - strlen(newpath) > 3) {
e56644ca 514 die(FSCK_UNCORRECTED, 0, _("bad filename length"));
19922f22
KZ
515 }
516 expand_fs(newpath, child);
517
518 offset += newlen;
519
520 if (offset <= start_dir) {
e56644ca 521 die(FSCK_UNCORRECTED, 0, _("bad inode offset"));
19922f22
KZ
522 }
523 if (offset > end_dir) {
524 end_dir = offset;
525 }
526 iput(child); /* free(child) */
527 }
528 free(newpath);
529}
530
531static void do_file(char *path, struct cramfs_inode *i)
532{
533 unsigned long offset = i->offset << 2;
534 int fd = 0;
535
536 if (offset == 0 && i->size != 0) {
e56644ca 537 die(FSCK_UNCORRECTED, 0, _("file inode has zero offset and non-zero size"));
19922f22
KZ
538 }
539 if (i->size == 0 && offset != 0) {
e56644ca 540 die(FSCK_UNCORRECTED, 0, _("file inode has zero size and non-zero offset"));
19922f22
KZ
541 }
542 if (offset != 0 && offset < start_data) {
543 start_data = offset;
544 }
545 if (opt_verbose) {
546 print_node('f', i, path);
547 }
548 if (opt_extract) {
549 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, i->mode);
550 if (fd < 0) {
e56644ca 551 die(FSCK_ERROR, 1, _("open failed: %s"), path);
19922f22
KZ
552 }
553 }
554 if (i->size) {
555 do_uncompress(path, fd, offset, i->size);
556 }
557 if (opt_extract) {
558 close(fd);
559 change_file_status(path, i);
63cccae4
KZ
560 }
561}
562
563static void do_symlink(char *path, struct cramfs_inode *i)
564{
565 unsigned long offset = i->offset << 2;
566 unsigned long curr = offset + 4;
fbaec83b 567 unsigned long next = u32_toggle_endianness(cramfs_is_big_endian, *(u32 *) romfs_read(offset));
63cccae4
KZ
568 unsigned long size;
569
19922f22 570 if (offset == 0) {
e56644ca 571 die(FSCK_UNCORRECTED, 0, _("symbolic link has zero offset"));
19922f22
KZ
572 }
573 if (i->size == 0) {
e56644ca 574 die(FSCK_UNCORRECTED, 0, _("symbolic link has zero size"));
19922f22
KZ
575 }
576
577 if (offset < start_data) {
578 start_data = offset;
579 }
63cccae4
KZ
580 if (next > end_data) {
581 end_data = next;
582 }
583
584 size = uncompress_block(romfs_read(curr), next - curr);
585 if (size != i->size) {
e56644ca 586 die(FSCK_UNCORRECTED, 0, _("size error in symlink: %s"), path);
63cccae4
KZ
587 }
588 outbuffer[size] = 0;
589 if (opt_verbose) {
590 char *str;
591
19922f22 592 asprintf(&str, "%s -> %s", path, outbuffer);
63cccae4
KZ
593 print_node('l', i, str);
594 if (opt_verbose > 1) {
e56644ca 595 printf(_(" uncompressing block at %ld to %ld (%ld)\n"), curr, next, next - curr);
63cccae4 596 }
19922f22 597 free(str);
63cccae4
KZ
598 }
599 if (opt_extract) {
19922f22 600 if (symlink(outbuffer, path) < 0) {
e56644ca 601 die(FSCK_ERROR, 1, _("symlink failed: %s"), path);
19922f22 602 }
63cccae4
KZ
603 change_file_status(path, i);
604 }
605}
606
607static void do_special_inode(char *path, struct cramfs_inode *i)
608{
609 dev_t devtype = 0;
610 char type;
611
19922f22 612 if (i->offset) { /* no need to shift offset */
e56644ca 613 die(FSCK_UNCORRECTED, 0, _("special file has non-zero offset: %s"), path);
19922f22 614 }
63cccae4
KZ
615 if (S_ISCHR(i->mode)) {
616 devtype = i->size;
617 type = 'c';
618 }
619 else if (S_ISBLK(i->mode)) {
620 devtype = i->size;
621 type = 'b';
622 }
19922f22
KZ
623 else if (S_ISFIFO(i->mode)) {
624 if (i->size != 0) {
e56644ca 625 die(FSCK_UNCORRECTED, 0, _("fifo has non-zero size: %s"), path);
19922f22 626 }
63cccae4 627 type = 'p';
19922f22
KZ
628 }
629 else if (S_ISSOCK(i->mode)) {
630 if (i->size != 0) {
e56644ca 631 die(FSCK_UNCORRECTED, 0, _("socket has non-zero size: %s"), path);
19922f22 632 }
63cccae4 633 type = 's';
19922f22 634 }
63cccae4 635 else {
e56644ca 636 die(FSCK_UNCORRECTED, 0, _("bogus mode: %s (%o)"), path, i->mode);
19922f22 637 return; /* not reached */
63cccae4
KZ
638 }
639
640 if (opt_verbose) {
641 print_node(type, i, path);
642 }
643
644 if (opt_extract) {
645 if (mknod(path, i->mode, devtype) < 0) {
e56644ca 646 die(FSCK_ERROR, 1, _("mknod failed: %s"), path);
63cccae4
KZ
647 }
648 change_file_status(path, i);
649 }
650}
651
19922f22 652static void expand_fs(char *path, struct cramfs_inode *inode)
63cccae4 653{
19922f22
KZ
654 if (S_ISDIR(inode->mode)) {
655 do_directory(path, inode);
656 }
657 else if (S_ISREG(inode->mode)) {
658 do_file(path, inode);
659 }
660 else if (S_ISLNK(inode->mode)) {
661 do_symlink(path, inode);
662 }
663 else {
664 do_special_inode(path, inode);
665 }
63cccae4
KZ
666}
667
19922f22 668static void test_fs(int start)
63cccae4 669{
19922f22 670 struct cramfs_inode *root;
63cccae4 671
19922f22
KZ
672 root = read_super();
673 umask(0);
674 euid = geteuid();
675 stream.next_in = NULL;
676 stream.avail_in = 0;
677 inflateInit(&stream);
678 expand_fs(extract_dir, root);
679 inflateEnd(&stream);
680 if (start_data != ~0UL) {
681 if (start_data < (sizeof(struct cramfs_super) + start)) {
e56644ca 682 die(FSCK_UNCORRECTED, 0, _("directory data start (%ld) < sizeof(struct cramfs_super) + start (%ld)"), start_data, sizeof(struct cramfs_super) + start);
63cccae4 683 }
19922f22 684 if (end_dir != start_data) {
e56644ca 685 die(FSCK_UNCORRECTED, 0, _("directory data end (%ld) != file data start (%ld)"), end_dir, start_data);
63cccae4 686 }
63cccae4 687 }
19922f22
KZ
688 if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
689 if (end_data > super.size) {
e56644ca 690 die(FSCK_UNCORRECTED, 0, _("invalid file data offset"));
63cccae4 691 }
63cccae4 692 }
19922f22 693 iput(root); /* free(root) */
63cccae4
KZ
694}
695#endif /* INCLUDE_FS_TESTS */
696
697int main(int argc, char **argv)
698{
63cccae4
KZ
699 int c; /* for getopt */
700 int start = 0;
293889d0 701 size_t length = 0;
19922f22 702
e56644ca
PR
703 setlocale(LC_MESSAGES, "");
704 bindtextdomain(PACKAGE, LOCALEDIR);
705 textdomain(PACKAGE);
706
d144727b 707 page_size = getpagesize();
63cccae4
KZ
708
709 if (argc)
710 progname = argv[0];
711
19922f22
KZ
712 outbuffer = malloc(page_size * 2);
713 if (!outbuffer)
e56644ca 714 die(FSCK_ERROR, 1, _("failed to allocate outbuffer"));
19922f22 715
63cccae4
KZ
716 /* command line options */
717 while ((c = getopt(argc, argv, "hx:v")) != EOF) {
718 switch (c) {
719 case 'h':
19922f22 720 usage(FSCK_OK);
63cccae4
KZ
721 case 'x':
722#ifdef INCLUDE_FS_TESTS
723 opt_extract = 1;
19922f22 724 extract_dir = optarg;
63cccae4 725 break;
19922f22 726#else /* not INCLUDE_FS_TESTS */
e56644ca 727 die(FSCK_USAGE, 0, _("compiled without -x support"));
63cccae4
KZ
728#endif /* not INCLUDE_FS_TESTS */
729 case 'v':
730 opt_verbose++;
731 break;
732 }
733 }
734
735 if ((argc - optind) != 1)
19922f22 736 usage(FSCK_USAGE);
63cccae4
KZ
737 filename = argv[optind];
738
19922f22
KZ
739 test_super(&start, &length);
740 test_crc(start);
63cccae4 741#ifdef INCLUDE_FS_TESTS
19922f22
KZ
742 test_fs(start);
743#endif /* INCLUDE_FS_TESTS */
63cccae4 744
19922f22
KZ
745 if (opt_verbose) {
746 printf("%s: OK\n", filename);
63cccae4 747 }
63cccae4 748
19922f22 749 exit(FSCK_OK);
63cccae4 750}