source: trunk/minix/commands/simple/readfs.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 15.9 KB
Line 
1/* readfs - read a MINIX file system Author: Paul Polderman */
2
3/* Command: readfs - read and extract a MINIX filesystem.
4 *
5 * Syntax: readfs [-li] block-special [directory]
6 *
7 * Flags: -l: Extract files and dirs and produce a mkfs-listing on stdout
8 * -i: Information only: give the listing, but do not extract files.
9 * -d: Don't extract regular files, just the skeleton please.
10 *
11 * Examples: readfs /dev/fd1 # extract all files from /dev/fd1.
12 * readfs -i /dev/hd2 # see what's on /dev/hd2.
13 * readfs -l /dev/at0 rootfs # extract and list the filesystem
14 * # of /dev/at0 and put the tree
15 * # in the directory `rootfs'.
16 *
17 * Readfs reads a MINIX filesystem and extracts recursively all directories
18 * and files, and (optionally) produces a mkfs-listing of them on stdout.
19 * The root directory contents are placed in the current directory, unless
20 * a directory is given as argument, in which case the contents are put there.
21 * Readfs tries to restore the attributes (mode/uid/gid/time) of the files
22 * extracted to those of the original files.
23 * Special files are created as ordinary files, but the mkfs-listing
24 * enables mkfs to restore them to original.
25 */
26
27#include <sys/types.h>
28#include <sys/dir.h>
29#include <sys/stat.h>
30#include <sys/wait.h>
31#include <fcntl.h>
32#include <limits.h>
33#include <unistd.h>
34#include <stdlib.h>
35#include <string.h>
36#include <utime.h>
37#include <dirent.h>
38
39#define BLOCK_SIZE _STATIC_BLOCK_SIZE
40
41#include <minix/config.h>
42#include <minix/const.h>
43#include <minix/type.h>
44#include "../../servers/fs/const.h"
45#include "../../servers/fs/type.h"
46#include "../../servers/fs/buf.h"
47#include "../../servers/fs/super.h"
48
49#undef printf /* Definition used only in the kernel */
50#include <stdio.h>
51
52/* Compile with -I/user0/ast/minix
53 * (i.e. the directory containing the MINIX system sources)
54 *
55 * Author: Paul Polderman (polder@cs.vu.nl) April 1987
56 */
57
58char verbose = 0; /* give a mkfs-listing of the filesystem */
59 /* And extracts its contents. */
60char noaction = 0; /* just give a mkfs-listing, do not extract
61 * files. */
62char nofiles = 0; /* only extract the skeleton FS structure */
63
64struct super_block sb;
65char pathname[1024];
66int inodes_per_block;
67
68_PROTOTYPE(int main, (int argc, char **argv));
69_PROTOTYPE(void get_flags, (char *flags));
70_PROTOTYPE(void readfs, (char *special_file, char *directory));
71_PROTOTYPE(int get_inode, (int fd, Ino_t inum, d1_inode * ip));
72_PROTOTYPE(void dump_dir, (int special, d1_inode * ip, char *directory));
73_PROTOTYPE(int dump_file, (int special, d1_inode * ip, char *filename));
74_PROTOTYPE(int get_fileblock, (int special, d1_inode * ip, block_t b, struct buf * bp));
75_PROTOTYPE(int get_block, (int fd, block_t block, struct buf * bp, int type));
76_PROTOTYPE(int get_rawblock, (int special, block_t blockno, char *bufp));
77_PROTOTYPE(void restore, (char *name, d1_inode * ip));
78_PROTOTYPE(void show_info, (char *name, d1_inode * ip, char *path));
79_PROTOTYPE(void do_indent, (int i));
80_PROTOTYPE(int Mkdir, (char *directory));
81
82int main(argc, argv)
83int argc;
84char **argv;
85{
86 switch (argc) {
87 case 2:
88 pathname[0] = '\0';
89 readfs(argv[1], pathname);
90 break;
91 case 3:
92 if (argv[1][0] == '-') {
93 get_flags(&argv[1][1]);
94 pathname[0] = '\0';
95 readfs(argv[2], pathname);
96 } else {
97 strcpy(pathname, argv[2]);
98 readfs(argv[1], pathname);
99 }
100 break;
101 case 4:
102 if (argv[1][0] == '-') {
103 get_flags(&argv[1][1]);
104 strcpy(pathname, argv[3]);
105 readfs(argv[2], pathname);
106 break;
107 } /* else fall through .. */
108 default:
109 fprintf(stderr, "Usage: %s [-li] <special> [dirname]\n", argv[0]);
110 exit(1);
111 }
112 return(0);
113}
114
115void get_flags(flags)
116register char *flags;
117{
118 while (*flags) {
119 switch (*flags) {
120 case 'L':
121 case 'l': verbose = 1; break;
122 case 'I':
123 case 'i':
124 noaction = 1;
125 verbose = 1;
126 break;
127 case 'D':
128 case 'd': nofiles = 1; break;
129 default:
130 fprintf(stderr, "Bad flag: %c\n", *flags);
131 break;
132 }
133 flags++;
134 }
135}
136
137#define zone_shift (sb.s_log_zone_size) /* zone to block ratio */
138
139void readfs(special_file, directory)
140char *special_file, *directory;
141/* Readfs: opens the given special file (with MINIX filesystem),
142 * and extracts its contents into the given directory.
143 */
144{
145 d1_inode root_inode;
146 int special, magic;
147 off_t super_b;
148
149 umask(0);
150
151 /* Open the special file */
152 if ((special = open(special_file, O_RDONLY)) < 0) {
153 fprintf(stderr, "cannot open %s\n", special_file);
154 return;
155 }
156
157 /* Read the superblock */
158 super_b = (off_t) 1 *(off_t) BLOCK_SIZE;
159 if (lseek(special, super_b, SEEK_SET) != super_b) {
160 fprintf(stderr, "cannot seek to superblock\n");
161 return;
162 }
163 if (read(special, (char *) &sb, sizeof(struct super_block))
164 != sizeof(struct super_block)) {
165 fprintf(stderr, "cannot read superblock\n");
166 return;
167 }
168
169 /* The number of inodes in a block differs in V1 and V2. */
170 magic = sb.s_magic;
171 if (magic == SUPER_MAGIC || magic == SUPER_REV) {
172 inodes_per_block = V1_INODES_PER_BLOCK;
173 } else {
174 inodes_per_block = V2_INODES_PER_BLOCK(BLOCK_SIZE);
175 }
176
177 /* Is it really a MINIX filesystem ? */
178 if (magic != SUPER_MAGIC && magic != SUPER_V2) {
179 fprintf(stderr, "%s is not a valid MINIX filesystem\n", special_file);
180 return;
181 }
182
183 /* Fetch the inode of the root directory */
184 if (get_inode(special, (ino_t) ROOT_INODE, &root_inode) < 0) {
185 fprintf(stderr, "cannot get inode of root directory\n");
186 return;
187 }
188
189 /* Print number of blocks and inodes */
190 if (verbose) printf("boot\n%ld %d\n",
191 (block_t) sb.s_nzones << zone_shift, sb.s_ninodes);
192
193 /* Extract (recursively) the root directory */
194 dump_dir(special, &root_inode, directory);
195}
196
197/* Different type of blocks: (used in routine get_block for caching) */
198
199#define B_INODE 0 /* Cache #0 is the inode cache */
200#define B_INDIRECT 1 /* Cache #1 is the (dbl) indirect block cache */
201#define B_DATA 2 /* No cache for data blocks (only read once) */
202
203int get_inode(fd, inum, ip)
204int fd;
205ino_t inum;
206d1_inode *ip;
207
208/* Get inode `inum' from the MINIX filesystem. (Uses the inode-cache) */
209{
210 struct buf bp;
211 block_t block;
212 block_t ino_block;
213 unsigned short ino_offset;
214
215 /* Calculate start of i-list */
216 block = 1 + 1 + sb.s_imap_blocks + sb.s_zmap_blocks;
217
218 /* Calculate block with inode inum */
219 ino_block = ((inum - 1) / inodes_per_block);
220 ino_offset = ((inum - 1) % inodes_per_block);
221 block += ino_block;
222
223 /* Fetch the block */
224 if (get_block(fd, block, &bp, B_INODE) == 0) {
225 memcpy((void *) ip, (void *) &bp.b_v1_ino[ino_offset], sizeof(d1_inode));
226 return(0);
227 }
228
229 /* Oeps, foutje .. */
230 fprintf(stderr, "cannot find inode %d\n", inum);
231 return(-1);
232}
233
234static int indent = 0; /* current indent (used for mkfs-listing) */
235
236void dump_dir(special, ip, directory)
237int special;
238d1_inode *ip;
239char *directory;
240/* Make the given directory (if non-NULL),
241 * and recursively extract its contents.
242 */
243{
244 register struct direct *dp;
245 register int n_entries;
246 register char *name;
247 block_t b = 0;
248 d1_inode dip;
249 struct buf bp;
250
251 if (verbose) {
252 show_info(directory, ip, "");
253 indent++;
254 }
255 if (!noaction && *directory) {
256 /* Try to make the directory if not already there */
257 if (Mkdir(directory) != 0 || chdir(directory) < 0) {
258 fprintf(stderr, "Mkdir %s failed\n", directory);
259 return;
260 }
261 }
262 for (name = directory; *name; name++) /* Find end of pathname */
263 ;
264 *name++ = '/'; /* Add trailing slash */
265
266 n_entries = (int) (ip->d1_size / (off_t) sizeof(struct direct));
267 while (n_entries > 0) {
268
269 /* Read next block of the directory */
270 if (get_fileblock(special, ip, b, &bp) < 0) return;
271 dp = &bp.b_dir[0];
272 if (b++ == (block_t) 0) {
273 dp += 2; /* Skip "." and ".." */
274 n_entries -= 2;
275 }
276
277 /* Extract the files/directories listed in the block */
278 while (n_entries-- > 0 && dp < &bp.b_dir[NR_DIR_ENTRIES(BLOCK_SIZE)]) {
279 if (dp->d_ino != (ino_t) 0) {
280 if (get_inode(special, dp->d_ino, &dip) < 0) {
281 /* Bad luck */
282 dp++;
283 continue;
284 }
285
286 /* Add new pathname-component to `pathname'. */
287 strncpy(name, dp->d_name, (size_t) NAME_MAX);
288 name[NAME_MAX] = '\0';
289
290 /* Call the right routine */
291 if ((dip.d1_mode & I_TYPE) == I_DIRECTORY)
292 dump_dir(special, &dip, name);
293 else
294 dump_file(special, &dip, name);
295 }
296 dp++; /* Next entry, please. */
297 }
298 }
299 *--name = '\0'; /* Restore `pathname' to what it was. */
300 if (!noaction && *directory) {
301 chdir(".."); /* Go back up. */
302 restore(directory, ip); /* Restore mode/owner/accesstime */
303 }
304 if (verbose) {
305 do_indent(--indent); /* Let mkfs know we are done */
306 printf("$\n"); /* with this directory. */
307 }
308}
309
310int dump_file(special, ip, filename)
311int special;
312d1_inode *ip;
313char *filename;
314/* Extract given filename from the MINIX-filesystem,
315 * and store it on the local filesystem.
316 */
317{
318 int file;
319 block_t b = 0;
320 struct buf bp;
321 off_t size;
322
323 if (nofiles && (ip->d1_mode & I_TYPE) == I_REGULAR) return(0);
324
325 if (verbose) show_info(filename, ip, pathname);
326
327 if (noaction) return(0);
328
329 if (access(filename, 0) == 0) {
330 /* Should not happen, but just in case .. */
331 fprintf(stderr, "Will not create %s: file exists\n", filename);
332 return(-1);
333 }
334 if ((file = creat(filename, (ip->d1_mode & ALL_MODES))) < 0) {
335 fprintf(stderr, "cannot create %s\n", filename);
336 return(-1);
337 }
338
339 /* Don't try to extract /dev/hd0 */
340 if ((ip->d1_mode & I_TYPE) == I_REGULAR) {
341 size = ip->d1_size;
342 while (size > (off_t) 0) {
343 /* Get next block of file */
344 if (get_fileblock(special, ip, b++, &bp) < 0) {
345 close(file);
346 return(-1);
347 }
348
349 /* Write it to the file */
350 if (size > (off_t) BLOCK_SIZE)
351 write(file, bp.b_data, BLOCK_SIZE);
352 else
353 write(file, bp.b_data, (int) size);
354
355 size -= (off_t) BLOCK_SIZE;
356 }
357 }
358 close(file);
359 restore(filename, ip); /* Restore mode/owner/filetimes */
360 return(0);
361}
362
363int get_fileblock(special, ip, b, bp)
364int special;
365d1_inode *ip;
366block_t b;
367struct buf *bp;
368/* Read the `b'-th block from the file whose inode is `ip'. */
369{
370 zone_t zone, ind_zone;
371 block_t z, zone_index;
372 int r;
373
374 /* Calculate zone in which the datablock number is contained */
375 zone = (zone_t) (b >> zone_shift);
376
377 /* Calculate index of the block number in the zone */
378 zone_index = b - ((block_t) zone << zone_shift);
379
380 /* Go get the zone */
381 if (zone < (zone_t) V1_NR_DZONES) { /* direct block */
382 zone = ip->d1_zone[(int) zone];
383 z = ((block_t) zone << zone_shift) + zone_index;
384 r = get_block(special, z, bp, B_DATA);
385 return(r);
386 }
387
388 /* The zone is not a direct one */
389 zone -= (zone_t) V1_NR_DZONES;
390
391 /* Is it single indirect ? */
392 if (zone < (zone_t) V1_INDIRECTS) { /* single indirect block */
393 ind_zone = ip->d1_zone[V1_NR_DZONES];
394 } else { /* double indirect block */
395 /* Fetch the double indirect block */
396 ind_zone = ip->d1_zone[V1_NR_DZONES + 1];
397 z = (block_t) ind_zone << zone_shift;
398 r = get_block(special, z, bp, B_INDIRECT);
399 if (r < 0) return(r);
400
401 /* Extract the indirect zone number from it */
402 zone -= (zone_t) V1_INDIRECTS;
403
404 /* The next line assumes a V1 file system only! */
405 ind_zone = bp->b_v1_ind[(int) (zone / V1_INDIRECTS)];
406 zone %= (zone_t) V1_INDIRECTS;
407 }
408
409 /* Extract the datablock number from the indirect zone */
410 z = (block_t) ind_zone << zone_shift;
411 r = get_block(special, z, bp, B_INDIRECT);
412 if (r < 0) return(r);
413
414 /* The next line assumes a V1 file system only! */
415 zone = bp->b_v1_ind[(int) zone];
416
417 /* Calculate datablock number to be fetched */
418 z = ((block_t) zone << zone_shift) + zone_index;
419 r = get_block(special, z, bp, B_DATA);
420 return(r);
421}
422
423/* The following routines simulate a LRU block cache.
424 *
425 * Definition of a cache block:
426 */
427
428struct cache_block {
429 block_t b_block; /* block number of block */
430 long b_access; /* counter value of last access */
431 char b_buf[BLOCK_SIZE]; /* buffer for block */
432};
433
434#define NR_CACHES 2 /* total number of caches */
435#define NR_CBLOCKS 5 /* number of blocks in a cache */
436
437static struct cache_block cache[NR_CACHES][NR_CBLOCKS];
438static long counter = 0L; /* Counter used as a sense of time. */
439 /* Incremented after each cache operation. */
440
441int get_block(fd, block, bp, type)
442int fd;
443block_t block;
444struct buf *bp;
445int type;
446/* Get the requested block from the device with filedescriptor fd.
447 * If it is in the cache, no (floppy-) disk access is needed,
448 * if not, allocate a cache block and read the block into it.
449 */
450{
451 register int i;
452 register struct cache_block *cache_p, *cp;
453
454 if (block == (block_t) NO_ZONE) {
455 /* Should never happen in a good filesystem. */
456 fprintf(stderr, "get_block: NO_ZONE requested !\n");
457 return(-1);
458 }
459 if (type < 0 || type >= NR_CACHES) /* No cache for this type */
460 return(get_rawblock(fd, block, (char *) bp));
461
462 cache_p = cache[type];
463 cp = (struct cache_block *) 0;
464
465 /* First find out if block requested is in the cache */
466 for (i = 0; i < NR_CBLOCKS; i++) {
467 if (cache_p[i].b_block == block) { /* found right block */
468 cp = &cache_p[i];
469 break;
470 }
471 }
472
473 if (cp == (struct cache_block *) 0) { /* block is not in cache */
474 cp = cache_p; /* go find oldest buffer */
475 for (i = 0; i < NR_CBLOCKS; i++) {
476 if (cache_p[i].b_access < cp->b_access) cp = &cache_p[i];
477 }
478
479 /* Fill the buffer with the right block */
480 if (get_rawblock(fd, block, cp->b_buf) < 0) return(-1);
481 }
482
483 /* Update/store last access counter */
484 cp->b_access = ++counter;
485 cp->b_block = block;
486 memcpy((void *) bp, (void *) cp->b_buf, BLOCK_SIZE);
487 return(0);
488}
489
490int get_rawblock(special, blockno, bufp)
491int special;
492block_t blockno;
493char *bufp;
494/* Read a block from the disk. */
495{
496 off_t pos;
497
498 /* Calculate the position of the block on the disk */
499 pos = (off_t) blockno *(off_t) BLOCK_SIZE;
500
501 /* Read the block from the disk */
502 if (lseek(special, pos, SEEK_SET) == pos
503 && read(special, bufp, BLOCK_SIZE) == BLOCK_SIZE)
504 return(0);
505
506 /* Should never get here .. */
507 fprintf(stderr, "read block %d failed\n", blockno);
508 return(-1);
509}
510
511void restore(name, ip)
512char *name;
513d1_inode *ip;
514/* Restores given file's attributes.
515 * `ip' contains the attributes of the file on the MINIX filesystem,
516 * `name' is the filename of the extracted file on the local filesystem.
517 */
518{
519 long ttime[2];
520
521 chown(name, ip->d1_uid, ip->d1_gid); /* Fails if not superuser */
522 chmod(name, (ip->d1_mode & ALL_MODES));
523 ttime[0] = ttime[1] = ip->d1_mtime;
524 utime(name, (struct utimbuf *) ttime);
525}
526
527/* Characters to use as prefix to `mkfs' mode field */
528
529static char special_chars[] = {
530 '-', /* I_REGULAR */
531 'c', /* I_CHAR_SPECIAL */
532 'd', /* I_DIRECTORY */
533 'b' /* I_BLOCK_SPECIAL */
534};
535
536void show_info(name, ip, path)
537char *name;
538d1_inode *ip;
539char *path;
540/* Show information about the given file/dir in `mkfs'-format */
541{
542 char c1, c2, c3;
543
544 c1 = special_chars[(ip->d1_mode >> 13) & 03];
545 c2 = ((ip->d1_mode & ALL_MODES & ~RWX_MODES) == I_SET_UID_BIT) ? 'u' : '-';
546 c3 = ((ip->d1_mode & ALL_MODES & ~RWX_MODES) == I_SET_GID_BIT) ? 'g' : '-';
547
548 if (*name) {
549 do_indent(indent);
550 printf("%-14s ", name);
551 }
552 printf("%c%c%c%03o %d %d", c1, c2, c3,
553 (ip->d1_mode & RWX_MODES), ip->d1_uid, ip->d1_gid);
554
555 switch (ip->d1_mode & I_TYPE) {
556 case I_DIRECTORY:
557 break;
558 case I_CHAR_SPECIAL: /* Print major and minor dev numbers */
559 printf(" %d %d", (ip->d1_zone[0] >> MAJOR) & 0377,
560 (ip->d1_zone[0] >> MINOR) & 0377);
561 break;
562 case I_BLOCK_SPECIAL: /* Print major and minor dev numbers */
563 printf(" %d %d", (ip->d1_zone[0] >> MAJOR) & 0377,
564 (ip->d1_zone[0] >> MINOR) & 0377);
565 /* Also print the number of blocks on the device */
566 printf(" %ld", (ip->d1_size / (off_t) BLOCK_SIZE));
567 break;
568 default: /* Just print the pathname */
569 printf(" %s", path);
570 break;
571 }
572 putchar('\n');
573}
574
575#define INDENT_SIZE 4
576
577void do_indent(i)
578int i;
579{
580 i *= INDENT_SIZE;
581 while (i-- > 0) putchar(' ');
582}
583
584int Mkdir(directory)
585char *directory;
586/* Make a directory, return exit status.
587 * This routine is not necessary on systems that
588 * have a system call to make directories.
589 */
590{
591 int pid, status;
592
593 if ((pid = fork()) == 0) {
594 execl("/bin/Mkdir", "Mkdir", directory, (char *) 0);
595 execl("/usr/bin/Mkdir", "Mkdir", directory, (char *) 0);
596 exit(1);
597 } else if (pid < 0)
598 return(-1);
599 while (wait(&status) != pid);
600 return(status);
601}
Note: See TracBrowser for help on using the repository browser.