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>
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 */
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
62 char nofiles
= 0; /* only extract the skeleton FS structure */
64 struct super_block sb
;
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
));
89 readfs(argv
[1], pathname
);
92 if (argv
[1][0] == '-') {
93 get_flags(&argv
[1][1]);
95 readfs(argv
[2], pathname
);
97 strcpy(pathname
, argv
[2]);
98 readfs(argv
[1], pathname
);
102 if (argv
[1][0] == '-') {
103 get_flags(&argv
[1][1]);
104 strcpy(pathname
, argv
[3]);
105 readfs(argv
[2], pathname
);
107 } /* else fall through .. */
109 fprintf(stderr
, "Usage: %s [-li] <special> [dirname]\n", argv
[0]);
115 void get_flags(flags
)
116 register char *flags
;
121 case 'l': verbose
= 1; break;
128 case 'd': nofiles
= 1; break;
130 fprintf(stderr
, "Bad flag: %c\n", *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.
151 /* Open the special file */
152 if ((special
= open(special_file
, O_RDONLY
)) < 0) {
153 fprintf(stderr
, "cannot open %s\n", special_file
);
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");
163 if (read(special
, (char *) &sb
, sizeof(struct super_block
))
164 != sizeof(struct super_block
)) {
165 fprintf(stderr
, "cannot read superblock\n");
169 /* The number of inodes in a block differs in V1 and V2. */
171 if (magic
== SUPER_MAGIC
|| magic
== SUPER_REV
) {
172 inodes_per_block
= V1_INODES_PER_BLOCK
;
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
);
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");
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
)
208 /* Get inode `inum' from the MINIX filesystem. (Uses the inode-cache) */
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
);
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
));
229 /* Oeps, foutje .. */
230 fprintf(stderr
, "cannot find inode %d\n", inum
);
234 static int indent
= 0; /* current indent (used for mkfs-listing) */
236 void dump_dir(special
, ip
, 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
;
252 show_info(directory
, ip
, "");
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
);
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;
272 if (b
++ == (block_t
) 0) {
273 dp
+= 2; /* Skip "." and ".." */
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) {
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
);
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 */
305 do_indent(--indent
); /* Let mkfs know we are done */
306 printf("$\n"); /* with this directory. */
310 int dump_file(special
, ip
, filename
)
314 /* Extract given filename from the MINIX-filesystem,
315 * and store it on the local filesystem.
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
);
334 if ((file
= creat(filename
, (ip
->d1_mode
& ALL_MODES
))) < 0) {
335 fprintf(stderr
, "cannot create %s\n", filename
);
339 /* Don't try to extract /dev/hd0 */
340 if ((ip
->d1_mode
& I_TYPE
) == I_REGULAR
) {
342 while (size
> (off_t
) 0) {
343 /* Get next block of file */
344 if (get_fileblock(special
, ip
, b
++, &bp
) < 0) {
349 /* Write it to the file */
350 if (size
> (off_t
) BLOCK_SIZE
)
351 write(file
, bp
.b_data
, BLOCK_SIZE
);
353 write(file
, bp
.b_data
, (int) size
);
355 size
-= (off_t
) BLOCK_SIZE
;
359 restore(filename
, ip
); /* Restore mode/owner/filetimes */
363 int get_fileblock(special
, ip
, b
, 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
;
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
);
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
);
423 /* The following routines simulate a LRU block cache.
425 * Definition of a 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
)
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.
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");
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 */
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
;
486 memcpy((void *) bp
, (void *) cp
->b_buf
, BLOCK_SIZE
);
490 int get_rawblock(special
, blockno
, bufp
)
494 /* Read a block from the disk. */
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
)
506 /* Should never get here .. */
507 fprintf(stderr
, "read block %d failed\n", blockno
);
511 void restore(name
, 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.
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
[] = {
531 'c', /* I_CHAR_SPECIAL */
532 'd', /* I_DIRECTORY */
533 'b' /* I_BLOCK_SPECIAL */
536 void show_info(name
, ip
, path
)
540 /* Show information about the given file/dir in `mkfs'-format */
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' : '-';
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
) {
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);
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
));
568 default: /* Just print the pathname */
575 #define INDENT_SIZE 4
581 while (i
-- > 0) putchar(' ');
586 /* Make a directory, return exit status.
587 * This routine is not necessary on systems that
588 * have a system call to make directories.
593 if ((pid
= fork()) == 0) {
594 execl("/bin/Mkdir", "Mkdir", directory
, (char *) 0);
595 execl("/usr/bin/Mkdir", "Mkdir", directory
, (char *) 0);
599 while (wait(&status
) != pid
);