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