forget difference between big and small commands - obsolete with vm.
[minix.git] / commands / simple / readfs.c
blob77f4d8cd7052b28adfcaae86550647c4a1263d64
1 /* readfs - read a MINIX file system Author: Paul Polderman */
3 /* Command: readfs - read and extract a MINIX filesystem.
5 * Syntax: readfs [-li] block-special [directory]
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.
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'.
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.
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>
39 #define BLOCK_SIZE _STATIC_BLOCK_SIZE
41 #include <minix/config.h>
42 #include <minix/const.h>
43 #include <minix/type.h>
44 #include "../../servers/mfs/const.h"
45 #include "../../servers/mfs/type.h"
46 #include "../../servers/mfs/buf.h"
47 #include "../../servers/mfs/super.h"
49 #undef printf /* Definition used only in the kernel */
50 #include <stdio.h>
52 /* Compile with -I/user0/ast/minix
53 * (i.e. the directory containing the MINIX system sources)
55 * Author: Paul Polderman (polder@cs.vu.nl) April 1987
58 char verbose = 0; /* give a mkfs-listing of the filesystem */
59 /* And extracts its contents. */
60 char noaction = 0; /* just give a mkfs-listing, do not extract
61 * files. */
62 char nofiles = 0; /* only extract the skeleton FS structure */
64 struct super_block sb;
65 char pathname[1024];
66 int inodes_per_block;
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));
82 int main(argc, argv)
83 int argc;
84 char **argv;
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);
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);
112 return(0);
115 void get_flags(flags)
116 register char *flags;
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;
133 flags++;
137 #define zone_shift (sb.s_log_zone_size) /* zone to block ratio */
139 void readfs(special_file, directory)
140 char *special_file, *directory;
141 /* Readfs: opens the given special file (with MINIX filesystem),
142 * and extracts its contents into the given directory.
145 d1_inode root_inode;
146 int special, magic;
147 off_t super_b;
149 umask(0);
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;
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;
163 if (read(special, (char *) &sb, sizeof(struct super_block))
164 != sizeof(struct super_block)) {
165 fprintf(stderr, "cannot read superblock\n");
166 return;
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);
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;
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;
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);
193 /* Extract (recursively) the root directory */
194 dump_dir(special, &root_inode, directory);
197 /* Different type of blocks: (used in routine get_block for caching) */
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) */
203 int get_inode(fd, inum, ip)
204 int fd;
205 ino_t inum;
206 d1_inode *ip;
208 /* Get inode `inum' from the MINIX filesystem. (Uses the inode-cache) */
210 struct buf bp;
211 block_t block;
212 block_t ino_block;
213 unsigned short ino_offset;
215 /* Calculate start of i-list */
216 block = 1 + 1 + sb.s_imap_blocks + sb.s_zmap_blocks;
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;
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);
229 /* Oeps, foutje .. */
230 fprintf(stderr, "cannot find inode %d\n", inum);
231 return(-1);
234 static int indent = 0; /* current indent (used for mkfs-listing) */
236 void dump_dir(special, ip, directory)
237 int special;
238 d1_inode *ip;
239 char *directory;
240 /* Make the given directory (if non-NULL),
241 * and recursively extract its contents.
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;
251 if (verbose) {
252 show_info(directory, ip, "");
253 indent++;
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;
262 for (name = directory; *name; name++) /* Find end of pathname */
264 *name++ = '/'; /* Add trailing slash */
266 n_entries = (int) (ip->d1_size / (off_t) sizeof(struct direct));
267 while (n_entries > 0) {
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;
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;
286 /* Add new pathname-component to `pathname'. */
287 strncpy(name, dp->d_name, (size_t) NAME_MAX);
288 name[NAME_MAX] = '\0';
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);
296 dp++; /* Next entry, please. */
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 */
304 if (verbose) {
305 do_indent(--indent); /* Let mkfs know we are done */
306 printf("$\n"); /* with this directory. */
310 int dump_file(special, ip, filename)
311 int special;
312 d1_inode *ip;
313 char *filename;
314 /* Extract given filename from the MINIX-filesystem,
315 * and store it on the local filesystem.
318 int file;
319 block_t b = 0;
320 struct buf bp;
321 off_t size;
323 if (nofiles && (ip->d1_mode & I_TYPE) == I_REGULAR) return(0);
325 if (verbose) show_info(filename, ip, pathname);
327 if (noaction) return(0);
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);
334 if ((file = creat(filename, (ip->d1_mode & ALL_MODES))) < 0) {
335 fprintf(stderr, "cannot create %s\n", filename);
336 return(-1);
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);
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);
355 size -= (off_t) BLOCK_SIZE;
358 close(file);
359 restore(filename, ip); /* Restore mode/owner/filetimes */
360 return(0);
363 int get_fileblock(special, ip, b, bp)
364 int special;
365 d1_inode *ip;
366 block_t b;
367 struct buf *bp;
368 /* Read the `b'-th block from the file whose inode is `ip'. */
370 zone_t zone, ind_zone;
371 block_t z, zone_index;
372 int r;
374 /* Calculate zone in which the datablock number is contained */
375 zone = (zone_t) (b >> zone_shift);
377 /* Calculate index of the block number in the zone */
378 zone_index = b - ((block_t) zone << zone_shift);
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);
388 /* The zone is not a direct one */
389 zone -= (zone_t) V1_NR_DZONES;
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);
401 /* Extract the indirect zone number from it */
402 zone -= (zone_t) V1_INDIRECTS;
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;
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);
414 /* The next line assumes a V1 file system only! */
415 zone = bp->b_v1_ind[(int) zone];
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);
423 /* The following routines simulate a LRU block cache.
425 * Definition of a cache block:
428 struct 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 */
434 #define NR_CACHES 2 /* total number of caches */
435 #define NR_CBLOCKS 5 /* number of blocks in a cache */
437 static struct cache_block cache[NR_CACHES][NR_CBLOCKS];
438 static long counter = 0L; /* Counter used as a sense of time. */
439 /* Incremented after each cache operation. */
441 int get_block(fd, block, bp, type)
442 int fd;
443 block_t block;
444 struct buf *bp;
445 int 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.
451 register int i;
452 register struct cache_block *cache_p, *cp;
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);
459 if (type < 0 || type >= NR_CACHES) /* No cache for this type */
460 return(get_rawblock(fd, block, (char *) bp));
462 cache_p = cache[type];
463 cp = (struct cache_block *) 0;
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;
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];
479 /* Fill the buffer with the right block */
480 if (get_rawblock(fd, block, cp->b_buf) < 0) return(-1);
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);
490 int get_rawblock(special, blockno, bufp)
491 int special;
492 block_t blockno;
493 char *bufp;
494 /* Read a block from the disk. */
496 off_t pos;
498 /* Calculate the position of the block on the disk */
499 pos = (off_t) blockno *(off_t) BLOCK_SIZE;
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);
506 /* Should never get here .. */
507 fprintf(stderr, "read block %d failed\n", blockno);
508 return(-1);
511 void restore(name, ip)
512 char *name;
513 d1_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.
519 long ttime[2];
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);
527 /* Characters to use as prefix to `mkfs' mode field */
529 static char special_chars[] = {
530 '-', /* I_REGULAR */
531 'c', /* I_CHAR_SPECIAL */
532 'd', /* I_DIRECTORY */
533 'b' /* I_BLOCK_SPECIAL */
536 void show_info(name, ip, path)
537 char *name;
538 d1_inode *ip;
539 char *path;
540 /* Show information about the given file/dir in `mkfs'-format */
542 char c1, c2, c3;
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' : '-';
548 if (*name) {
549 do_indent(indent);
550 printf("%-14s ", name);
552 printf("%c%c%c%03o %d %d", c1, c2, c3,
553 (ip->d1_mode & RWX_MODES), ip->d1_uid, ip->d1_gid);
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;
572 putchar('\n');
575 #define INDENT_SIZE 4
577 void do_indent(i)
578 int i;
580 i *= INDENT_SIZE;
581 while (i-- > 0) putchar(' ');
584 int Mkdir(directory)
585 char *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.
591 int pid, status;
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);