/* * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2014 * The President and Fellows of Harvard College. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include <sys/types.h> #include <stdint.h> #include <stdbool.h> #include <string.h> #include <stdio.h> #include <stdarg.h> #include <stdlib.h> #include <assert.h> #include <limits.h> #include <err.h> #include "support.h" #include "kern/sfs.h" #ifdef HOST /* * OS/161 runs natively on a big-endian platform, so we can * conveniently use the byteswapping functions for network byte order. */ #include <netinet/in.h> // for arpa/inet.h #include <arpa/inet.h> // for ntohl #include "hostcompat.h" #define SWAP64(x) ntohll(x) #define SWAP32(x) ntohl(x) #define SWAP16(x) ntohs(x) extern const char *hostcompat_progname; #else #define SWAP64(x) (x) #define SWAP32(x) (x) #define SWAP16(x) (x) #endif #include "disk.h" #define ARRAYCOUNT(a) (sizeof(a) / sizeof((a)[0])) #define DIVROUNDUP(a, b) (((a) + (b) - 1) / (b)) static bool dofiles, dodirs; static bool doindirect; static bool recurse; //////////////////////////////////////////////////////////// // printouts static unsigned dumppos; static void dumpval(const char *desc, const char *val) { size_t dlen, vlen, used; dlen = strlen(desc); vlen = strlen(val); printf(" "); printf("%s: %s", desc, val); used = dlen + 2 + vlen; for (; used < 36; used++) { putchar(' '); } if (dumppos % 2 == 1) { printf("\n"); } dumppos++; } static void dumpvalf(const char *desc, const char *valf, ...) { va_list ap; char buf[128]; va_start(ap, valf); vsnprintf(buf, sizeof(buf), valf, ap); va_end(ap); dumpval(desc, buf); } static void dumplval(const char *desc, const char *lval) { if (dumppos % 2 == 1) { printf("\n"); dumppos++; } printf(" %s: %s\n", desc, lval); dumppos += 2; } //////////////////////////////////////////////////////////// // fs structures static void dumpinode(uint32_t ino, const char *name); static uint32_t readsb(void) { struct sfs_superblock sb; diskread(&sb, SFS_SUPER_BLOCK); if (SWAP32(sb.sb_magic) != SFS_MAGIC) { errx(1, "Not an sfs filesystem"); } return SWAP32(sb.sb_nblocks); } static void dumpsb(void) { struct sfs_superblock sb; unsigned i; diskread(&sb, SFS_SUPER_BLOCK); sb.sb_volname[sizeof(sb.sb_volname)-1] = 0; printf("Superblock\n"); printf("----------\n"); dumpvalf("Magic", "0x%8x", SWAP32(sb.sb_magic)); dumpvalf("Size", "%u blocks", SWAP32(sb.sb_nblocks)); dumpvalf("Freemap size", "%u blocks", SFS_FREEMAPBLOCKS(SWAP32(sb.sb_nblocks))); dumpvalf("Block size", "%u bytes", SFS_BLOCKSIZE); dumplval("Volume name", sb.sb_volname); for (i=0; i<ARRAYCOUNT(sb.reserved); i++) { if (sb.reserved[i] != 0) { printf(" Word %u in reserved area: 0x%x\n", i, SWAP32(sb.reserved[i])); } } printf("\n"); } static void dumpfreemap(uint32_t fsblocks) { uint32_t freemapblocks = SFS_FREEMAPBLOCKS(fsblocks); uint32_t i, j, k, bn; uint8_t data[SFS_BLOCKSIZE], mask; char tmp[16]; printf("Free block bitmap\n"); printf("-----------------\n"); for (i=0; i<freemapblocks; i++) { diskread(data, SFS_FREEMAP_START+i); printf(" Freemap block #%u in disk block %u: blocks %u - %u" " (0x%x - 0x%x)\n", i, SFS_FREEMAP_START+i, i*SFS_BITSPERBLOCK, (i+1)*SFS_BITSPERBLOCK - 1, i*SFS_BITSPERBLOCK, (i+1)*SFS_BITSPERBLOCK - 1); for (j=0; j<SFS_BLOCKSIZE; j++) { if (j % 8 == 0) { snprintf(tmp, sizeof(tmp), "0x%x", i*SFS_BITSPERBLOCK + j*8); printf("%-7s ", tmp); } for (k=0; k<8; k++) { bn = i*SFS_BITSPERBLOCK + j*8 + k; mask = 1U << k; if (bn >= fsblocks) { if (data[j] & mask) { putchar('x'); } else { putchar('!'); } } else { if (data[j] & mask) { putchar('*'); } else { putchar('.'); } } } if (j % 8 == 7) { printf("\n"); } else { printf(" "); } } } printf("\n"); } static void dumpindirect(uint32_t block) { uint32_t ib[SFS_BLOCKSIZE/sizeof(uint32_t)]; char tmp[128]; unsigned i; if (block == 0) { return; } printf("Indirect block %u\n", block); diskread(ib, block); for (i=0; i<ARRAYCOUNT(ib); i++) { if (i % 4 == 0) { printf("@%-3u ", i); } snprintf(tmp, sizeof(tmp), "%u (0x%x)", SWAP32(ib[i]), SWAP32(ib[i])); printf(" %-16s", tmp); if (i % 4 == 3) { printf("\n"); } } } static uint32_t traverse_ib(uint32_t fileblock, uint32_t numblocks, uint32_t block, void (*doblock)(uint32_t, uint32_t)) { uint32_t ib[SFS_BLOCKSIZE/sizeof(uint32_t)]; unsigned i; if (block == 0) { memset(ib, 0, sizeof(ib)); } else { diskread(ib, block); } for (i=0; i<ARRAYCOUNT(ib) && fileblock < numblocks; i++) { doblock(fileblock++, SWAP32(ib[i])); } return fileblock; } static void traverse(const struct sfs_dinode *sfi, void (*doblock)(uint32_t, uint32_t)) { uint32_t fileblock; uint32_t numblocks; unsigned i; numblocks = DIVROUNDUP(SWAP32(sfi->sfi_size), SFS_BLOCKSIZE); fileblock = 0; for (i=0; i<SFS_NDIRECT && fileblock < numblocks; i++) { doblock(fileblock++, SWAP32(sfi->sfi_direct[i])); } if (fileblock < numblocks) { fileblock = traverse_ib(fileblock, numblocks, SWAP32(sfi->sfi_indirect), doblock); } assert(fileblock == numblocks); } static void dumpdirblock(uint32_t fileblock, uint32_t diskblock) { struct sfs_direntry sds[SFS_BLOCKSIZE/sizeof(struct sfs_direntry)]; int nsds = SFS_BLOCKSIZE/sizeof(struct sfs_direntry); int i; (void)fileblock; if (diskblock == 0) { printf(" [block %u - empty]\n", diskblock); return; } diskread(&sds, diskblock); printf(" [block %u]\n", diskblock); for (i=0; i<nsds; i++) { uint32_t ino = SWAP32(sds[i].sfd_ino); if (ino==SFS_NOINO) { printf(" [free entry]\n"); } else { sds[i].sfd_name[SFS_NAMELEN-1] = 0; /* just in case */ printf(" %u %s\n", ino, sds[i].sfd_name); } } } static void dumpdir(uint32_t ino, const struct sfs_dinode *sfi) { int nentries; nentries = SWAP32(sfi->sfi_size) / sizeof(struct sfs_direntry); if (SWAP32(sfi->sfi_size) % sizeof(struct sfs_direntry) != 0) { warnx("Warning: dir size is not a multiple of dir entry size"); } printf("Directory contents for inode %u: %d entries\n", ino, nentries); traverse(sfi, dumpdirblock); } static void recursedirblock(uint32_t fileblock, uint32_t diskblock) { struct sfs_direntry sds[SFS_BLOCKSIZE/sizeof(struct sfs_direntry)]; int nsds = SFS_BLOCKSIZE/sizeof(struct sfs_direntry); int i; (void)fileblock; if (diskblock == 0) { return; } diskread(&sds, diskblock); for (i=0; i<nsds; i++) { uint32_t ino = SWAP32(sds[i].sfd_ino); if (ino==SFS_NOINO) { continue; } sds[i].sfd_name[SFS_NAMELEN-1] = 0; /* just in case */ dumpinode(ino, sds[i].sfd_name); } } static void recursedir(uint32_t ino, const struct sfs_dinode *sfi) { int nentries; nentries = SWAP32(sfi->sfi_size) / sizeof(struct sfs_direntry); printf("Reading files in directory %u: %d entries\n", ino, nentries); traverse(sfi, recursedirblock); printf("Done with directory %u\n", ino); } static void dumpfileblock(uint32_t fileblock, uint32_t diskblock) { uint8_t data[SFS_BLOCKSIZE]; unsigned i, j; char tmp[128]; if (diskblock == 0) { printf(" 0x%6x [sparse]\n", fileblock * SFS_BLOCKSIZE); return; } diskread(data, diskblock); for (i=0; i<SFS_BLOCKSIZE; i++) { if (i % 16 == 0) { snprintf(tmp, sizeof(tmp), "0x%x", fileblock * SFS_BLOCKSIZE + i); printf("%8s", tmp); } if (i % 8 == 0) { printf(" "); } else { printf(" "); } printf("%02x", data[i]); if (i % 16 == 15) { printf(" "); for (j = i-15; j<=i; j++) { if (data[j] < 32 || data[j] > 126) { putchar('.'); } else { putchar(data[j]); } } printf("\n"); } } } static void dumpfile(uint32_t ino, const struct sfs_dinode *sfi) { printf("File contents for inode %u:\n", ino); traverse(sfi, dumpfileblock); } static void dumpinode(uint32_t ino, const char *name) { struct sfs_dinode sfi; const char *typename; char tmp[128]; unsigned i; diskread(&sfi, ino); printf("Inode %u", ino); if (name != NULL) { printf(" (%s)", name); } printf("\n"); printf("--------------\n"); switch (SWAP16(sfi.sfi_type)) { case SFS_TYPE_FILE: typename = "regular file"; break; case SFS_TYPE_DIR: typename = "directory"; break; default: typename = "invalid"; break; } dumpvalf("Type", "%u (%s)", SWAP16(sfi.sfi_type), typename); dumpvalf("Size", "%u", SWAP32(sfi.sfi_size)); dumpvalf("Link count", "%u", SWAP16(sfi.sfi_linkcount)); printf("\n"); printf(" Direct blocks:\n"); for (i=0; i<SFS_NDIRECT; i++) { if (i % 4 == 0) { printf("@%-2u ", i); } /* * Assume the disk size might be > 64K sectors (which * would be 32M) but is < 1024K sectors (512M) so we * need up to 5 hex digits for a block number. And * assume it's actually < 1 million sectors so we need * only up to 6 decimal digits. The complete block * number print then needs up to 16 digits. */ snprintf(tmp, sizeof(tmp), "%u (0x%x)", SWAP32(sfi.sfi_direct[i]), SWAP32(sfi.sfi_direct[i])); printf(" %-16s", tmp); if (i % 4 == 3) { printf("\n"); } } if (i % 4 != 0) { printf("\n"); } printf(" Indirect block: %u (0x%x)\n", SWAP32(sfi.sfi_indirect), SWAP32(sfi.sfi_indirect)); for (i=0; i<ARRAYCOUNT(sfi.sfi_waste); i++) { if (sfi.sfi_waste[i] != 0) { printf(" Word %u in waste area: 0x%x\n", i, SWAP32(sfi.sfi_waste[i])); } } if (doindirect) { dumpindirect(SWAP32(sfi.sfi_indirect)); } if (SWAP16(sfi.sfi_type) == SFS_TYPE_DIR && dodirs) { dumpdir(ino, &sfi); } if (SWAP16(sfi.sfi_type) == SFS_TYPE_FILE && dofiles) { dumpfile(ino, &sfi); } if (SWAP16(sfi.sfi_type) == SFS_TYPE_DIR && recurse) { recursedir(ino, &sfi); } } //////////////////////////////////////////////////////////// // main static void usage(void) { warnx("Usage: dumpsfs [options] device/diskfile"); warnx(" -s: dump superblock"); warnx(" -b: dump free block bitmap"); warnx(" -i ino: dump specified inode"); warnx(" -I: dump indirect blocks"); warnx(" -f: dump file contents"); warnx(" -d: dump directory contents"); warnx(" -r: recurse into directory contents"); warnx(" -a: equivalent to -sbdfr -i 1"); errx(1, " Default is -i 1"); } int main(int argc, char **argv) { bool dosb = false; bool dofreemap = false; uint32_t dumpino = 0; const char *dumpdisk = NULL; int i, j; uint32_t nblocks; #ifdef HOST /* Don't do this; it frobs the tty and you can't pipe to less */ /*hostcompat_init(argc, argv);*/ hostcompat_progname = argv[0]; #endif for (i=1; i<argc; i++) { if (argv[i][0] == '-') { for (j=1; argv[i][j]; j++) { switch (argv[i][j]) { case 's': dosb = true; break; case 'b': dofreemap = true; break; case 'i': if (argv[i][j+1] == 0) { dumpino = atoi(argv[++i]); } else { dumpino = atoi(argv[i]+j+1); j = strlen(argv[i]); } /* XXX ugly */ goto nextarg; case 'I': doindirect = true; break; case 'f': dofiles = true; break; case 'd': dodirs = true; break; case 'r': recurse = true; break; case 'a': dosb = true; dofreemap = true; dumpino = SFS_ROOTDIR_INO; doindirect = true; dofiles = true; dodirs = true; recurse = true; break; default: usage(); break; } } } else { if (dumpdisk != NULL) { usage(); } dumpdisk = argv[i]; } nextarg: ; } if (dumpdisk == NULL) { usage(); } if (!dosb && !dofreemap && dumpino == 0) { dumpino = SFS_ROOTDIR_INO; } opendisk(dumpdisk); nblocks = readsb(); if (dosb) { dumpsb(); } if (dofreemap) { dumpfreemap(nblocks); } if (dumpino != 0) { dumpinode(dumpino, NULL); } closedisk(); return 0; }