608 lines
13 KiB
C
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;
|
|
}
|