2015-12-23 00:50:04 +00:00

608 lines
13 KiB
C

/*
* 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;
if (dumpino == 0) {
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;
}