minix/commands/swapfs/swapfs.c
2010-05-12 16:28:54 +00:00

1048 lines
25 KiB
C

/* swapfs - swap a Minix file system Author: Niels C. Willems */
/* $Id$ */
/* Swapfs, a program to convert V1 or V2 Minix file systems from big endian
byte order to little endian and vv.
Some examples:
swapfs -v disk.01 ! only show verbose information.
swapfs /dev/fd0 | compress > fd0r.Z ! convert and compress filesystem.
swapfs -v fileA fileA ! read, convert and write the same filesystem.
This program uses one byte of heap memory for each data block (1Kbytes)
in the file system, so with Minix-PC 16-bit you can't swap file systems
bigger than about 32 Mbytes
Be careful with 'swapfs fileA fileA'. If the program aborts e.g. by
user interrupt, power failure or an inconsistent file system, you
better have a backup of fileA
This program only converts directories and indirect blocks of files
that are in use. Converting indirect blocks or directories of deleted
files is hard and not yet done.
If you have a (1.6.xx, xx < 18) version of Minix that supports the
mounting of reversed file systems always mount them read-only and
avoid any attemp to modify them (mkdir, open, creat) too!
These problems have been fixed in Minix 1.6.18.
In this version you can get some more information about the
file system with the -d (debug) flag.
Please send your bug reports or ideas to ncwille@cs.vu.nl
*/
#define _POSIX_SOURCE 1
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
#if __STDC__ == 1
#define _PROTOTYPE(function, params) function params
#else
#define _PROTOTYPE(function, params) function()
#endif
#define BLOCK_SIZE 1024
#define BOOT_BLOCK_OFF (blockn_t) 0
#define SUPER_BLOCK_OFF (blockn_t) 1
#define V1_MAGIC 0x137F
#define V2_MAGIC 0x2468
#define NINODES_OFFSET 0
#define V1_ZONES_OFFSET 2
#define IMAP_BLOCKS_OFFSET 4
#define ZMAP_BLOCKS_OFFSET 6
#define FIRSTDATAZONE_OFFSET 8
#define LOG_ZONE_SIZE_OFFSET 10
#define MAGIC_OFFSET 16
#define V2_ZONES_OFFSET 20
#define NR_DIRECT_ZONES 7
#define V1_NR_TZONES 9
#define V2_NR_TZONES 10
#define V1_INODE_SIZE 32
#define V2_INODE_SIZE 64
#define INODE1_MODE_OFF 0
#define INODE1_SIZE_OFF 4
#define INODE1_DIRECT_OFF 14
#define INODE1_IND1_OFF 28
#define INODE1_IND2_OFF 30
#define INODE2_MODE_OFF 0
#define INODE2_SIZE_OFF 8
#define INODE2_DIRECT_OFF 24
#define INODE2_IND1_OFF 52
#define INODE2_IND2_OFF 56
#define INODE2_IND3_OFF 60
#define INODE_MODE_MASK 0xf000 /* file type mask */
#define INODE_DIR_MODE 0x4000 /* directory */
#define INODE_BLK_SPECIAL_MODE 0x6000 /* block special */
#define INODE_CHR_SPECIAL_MODE 0x2000 /* character special */
#define T_MASK 0x1c
#define T_UNKNOWN 0x00
#define T_MAYBE_OLD_DIR 0x04
#define T_OLD_NON_DIR 0x08
#define T_DIR 0x0c
#define T_NON_DIR 0x10
#define INDIRECT_MASK 0x03
#define IND_PROCESSED_BIT 0x20 /* set when all blocks in ind block are
* marked */
#define IND_CONFLICT_BIT 0x40
#define TYPE_CONFLICT_BIT 0x80
#define DIR_ENTRY_SIZE 16
typedef enum {
Unused_zone, Old_zone, In_use_zone
} class_t;
typedef unsigned long blockn_t;
typedef unsigned int inodesn_t;
typedef struct {
inodesn_t ninodes; /* # usable inodes on the minor device */
blockn_t imap_blocks; /* # of blocks used by inode bit map */
blockn_t zmap_blocks; /* # of blocks used by zone bit map */
blockn_t firstdatazone; /* number of first data zone */
int log_zone_size; /* log2 of blocks/zone */
blockn_t zones; /* number of zones */
int version; /* file system version */
inodesn_t inodes_per_block;
blockn_t first_imap_block;
blockn_t first_zmap_block;
blockn_t first_inode_block; /* number of first block with inodes */
size_t dzmap_size; /* # of data zone blocks */
} super_t;
typedef struct { /* summary of inode */
long size; /* current file size in bytes */
blockn_t direct[NR_DIRECT_ZONES]; /* block numbers for direct,
* ind, ... */
blockn_t ind1; /* single indirect block number */
blockn_t ind2; /* double indirect block number */
blockn_t ind3; /* triple indirect block number */
int ztype; /* type of zones that belong to this inode */
} inode_t;
static char rcsid[] = "$Id$";
static int super_format[] = {2, 2, 2, 2, 2, 2, 4, 2, 2, 4, 0};
static int inode1_format[] = {2, 2, 4, 4, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0};
static int inode2_format[] = {2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 0};
static char *ind_str[4] = {"direct", "single indirect",
"double indirect", "triple indirect"};
static int big_endian_fs; /* set in init_super(), 1 iff file system has
* big endian byte order */
static int verbose_flag;
static int debug_flag;
static int test_flag;
typedef unsigned char *dzmap_t;
int _PROTOTYPE(main, (int argc, char *argv[]));
static void _PROTOTYPE(parse_args_init_io, (int argc, char *argv[]));
static void _PROTOTYPE(rw_boot, (void));
static void _PROTOTYPE(rw_init_super, (super_t * sp));
static void _PROTOTYPE(init_dzmap, (dzmap_t * dzmap_ptr, size_t dzmap_size));
static void _PROTOTYPE(rw_ibmap, (super_t super));
static void _PROTOTYPE(rw_zbmap, (super_t super));
static void _PROTOTYPE(print_stat, (dzmap_t dzmap, super_t super));
static void _PROTOTYPE(p1_rw_inodes, (dzmap_t dzmap, super_t super));
static void _PROTOTYPE(rd_indirects, (dzmap_t dzmap, super_t super, int ind,
class_t required_class));
static void _PROTOTYPE(rw_data_zones, (dzmap_t dzmap, super_t super));
static int _PROTOTYPE(read_block, (char *buf, blockn_t offset));
static void _PROTOTYPE(write_block, (char *buf));
static int _PROTOTYPE(convcpy, (char *dst, char *src, int *format));
static void _PROTOTYPE(conv2_blkcpy, (char *dst, char *src));
static void _PROTOTYPE(conv4_blkcpy, (char *dst, char *src));
static void _PROTOTYPE(conv2cpy, (char *dst, char *src));
static int _PROTOTYPE(inode_size, (int version));
static void _PROTOTYPE(init_super, (super_t * sp, char *buf));
static void _PROTOTYPE(get_inode, (inode_t * ip, char *buf, int version));
static int _PROTOTYPE(check_inode, (inode_t inode, super_t super));
static int _PROTOTYPE(was_blk_special, (inode_t inode));
static int _PROTOTYPE(check_blk_number, (blockn_t num, super_t super));
static void _PROTOTYPE(cw_inode_block, (char *buf, inodesn_t ninodes,
int version));
static void _PROTOTYPE(proc_ind, (dzmap_t dzmap, size_t curr_ind,
char *buf, super_t super));
static void _PROTOTYPE(cw_dir_block, (char *buf));
static void _PROTOTYPE(dzmap_add_inode, (dzmap_t dzmap, inode_t inode,
super_t super));
static void _PROTOTYPE(dz_update, (dzmap_t dzmap, blockn_t blknum,
int new_indnum, int new_ztype, super_t super));
static class_t _PROTOTYPE(ztype_class, (int ztype));
static unsigned int _PROTOTYPE(two_bytes, (char buf[2]));
static long _PROTOTYPE(four_bytes, (char buf[4]));
static void _PROTOTYPE(fail, (char *string));
static void _PROTOTYPE(usage, (char *arg0));
int main(argc, argv)
int argc;
char *argv[];
{
super_t super;
dzmap_t dzmap;
parse_args_init_io(argc, argv);
rw_boot();
rw_init_super(&super);
init_dzmap(&dzmap, super.dzmap_size);
rw_ibmap(super);
rw_zbmap(super);
p1_rw_inodes(dzmap, super);
rd_indirects(dzmap, super, 3, In_use_zone);
rd_indirects(dzmap, super, 2, In_use_zone);
rd_indirects(dzmap, super, 1, In_use_zone);
if (verbose_flag) putc('\n', stderr);
print_stat(dzmap, super);
rw_data_zones(dzmap, super);
return 0;
}
static void parse_args_init_io(argc, argv)
int argc;
char *argv[];
{
char *str;
struct stat buf;
ino_t src_ino;
int i;
debug_flag = 0;
verbose_flag = 0;
test_flag = 0;
for (i = 1; i < argc; i++) {
str = argv[i];
if (*str != '-') break;
switch (*++str) {
case 'v': verbose_flag = 1; break;
case 'd':
debug_flag = 1;
verbose_flag = 1;
break;
case 't': test_flag = 1; break;
default: usage(argv[0]);
}
}
if ((argc - i == 0 && isatty(0)) || (argc - i) > 2) usage(argv[0]);
if (argc - i > 0) {
(void) close(0);
if (open(argv[i], O_RDONLY) != 0) {
fprintf(stderr, "Can't open input file %s", argv[i]);
fail("");
}
}
if (isatty(1) || argc - i == 2) {
if (argc - i < 2)
test_flag = 1;
else {
i++;
(void) close(1);
(void) fstat(0, &buf);
src_ino = buf.st_ino;
if (stat(argv[i], &buf) == 0 && src_ino == buf.st_ino) {
/* Src and dest are the same */
if (open(argv[i], O_WRONLY) != 1) {
fprintf(stderr, "Can't open output file %s", argv[i]);
fail("");
}
} else if (creat(argv[i], 0644) != 1) {
fprintf(stderr, "Can't creat output file %s", argv[i]);
fail("");
}
}
}
}
static void rw_boot()
{
char buf[BLOCK_SIZE];
if (read_block(buf, BOOT_BLOCK_OFF) != BLOCK_SIZE)
fail("Can't read bootblock");
write_block(buf);
}
static void rw_init_super(sp)
super_t *sp;
{
char ibuf[BLOCK_SIZE], obuf[BLOCK_SIZE];
if (read_block(ibuf, SUPER_BLOCK_OFF) != BLOCK_SIZE)
fail("Can't read superblock");
init_super(sp, ibuf);
memcpy(obuf, ibuf, (size_t) BLOCK_SIZE); /* preserve 'unused' data */
(void) convcpy(obuf, ibuf, super_format);
write_block(obuf);
}
static void init_dzmap(dzmap_ptr, dzmap_size)
dzmap_t *dzmap_ptr;
size_t dzmap_size;
{
if ((*dzmap_ptr = (dzmap_t) malloc(dzmap_size)) == (dzmap_t) NULL)
fail("Not enough space for data zone map");
memset(*dzmap_ptr, '\0', (size_t) dzmap_size);
}
static void rw_ibmap(super)
super_t super;
{
char ibuf[BLOCK_SIZE], obuf[BLOCK_SIZE];
blockn_t i;
for (i = 0; i < super.imap_blocks; i++) {
if (read_block(ibuf, super.first_imap_block + i) != BLOCK_SIZE)
fail("Can't read inode bit map");
conv2_blkcpy(obuf, ibuf);
write_block(obuf);
}
}
static void rw_zbmap(super)
super_t super;
{
char ibuf[BLOCK_SIZE], obuf[BLOCK_SIZE];
blockn_t i;
for (i = 0; i < super.zmap_blocks; i++) {
if (read_block(ibuf, super.first_zmap_block + i) != BLOCK_SIZE)
fail("Can't read zone bit map");
conv2_blkcpy(obuf, ibuf);
write_block(obuf);
}
}
static void p1_rw_inodes(dzmap, super)
dzmap_t dzmap;
super_t super;
{
char buf[BLOCK_SIZE], *buf_ptr;
inodesn_t i, num_inodes;
blockn_t next_block;
inode_t inode;
next_block = super.first_inode_block;
for (i = 1; i <= super.ninodes; i++) {
if ((i - 1) % super.inodes_per_block == 0) {
if (read_block(buf, next_block) != BLOCK_SIZE)
fail("read failed in inode block");
buf_ptr = buf;
next_block++;
num_inodes = super.ninodes + 1 - i;
if (num_inodes > super.inodes_per_block)
num_inodes = super.inodes_per_block;
cw_inode_block(buf, num_inodes, super.version);
}
get_inode(&inode, buf_ptr, super.version);
dzmap_add_inode(dzmap, inode, super);
buf_ptr += inode_size(super.version);
}
}
static void print_stat(dzmap, super)
dzmap_t dzmap;
super_t super;
{
size_t i;
register unsigned char dz;
int both_conflict = 0, ind_conflict = 0, type_conflict = 0, unreferenced = 0;
int not_in_use = 0;
if (!verbose_flag) return;
for (i = 0; i < super.dzmap_size; i++) {
dz = dzmap[i];
if (dz & IND_CONFLICT_BIT && dz & TYPE_CONFLICT_BIT)
both_conflict++;
else if (dz & IND_CONFLICT_BIT)
ind_conflict++;
else if (dz & TYPE_CONFLICT_BIT)
type_conflict++;
if (dz == 0) unreferenced++;
if (ztype_class(dz & T_MASK) < In_use_zone) not_in_use++;
}
if (debug_flag) {
fprintf(stderr, "%5d zone blocks with conflicting indir.\n",
ind_conflict);
fprintf(stderr, "%5d zone blocks with conflicting types.\n",
type_conflict);
fprintf(stderr, "%5d zone blocks with conflicting types and indir.\n",
both_conflict);
fprintf(stderr, "%5d zone blocks never referenced.\n", unreferenced);
}
fprintf(stderr, "%5d zone blocks not in use.\n", not_in_use);
putc('\n', stderr);
}
static void rd_indirects(dzmap, super, ind, required_class)
dzmap_t dzmap;
super_t super;
int ind;
class_t required_class;
{
size_t i;
int ind_cnt;
off_t dz_offset;
char buf[BLOCK_SIZE];
dz_offset = super.firstdatazone;
ind_cnt = 0;
for (i = 0; i < super.dzmap_size; i++) {
if (ztype_class(dzmap[i] & T_MASK) != required_class ||
(dzmap[i] & INDIRECT_MASK) != ind ||
(dzmap[i] & IND_PROCESSED_BIT))
continue;
ind_cnt++;
if (read_block(buf, dz_offset + i) != BLOCK_SIZE) {
fprintf(stderr, "Can't read %s block", ind_str[ind]);
fail("");
}
proc_ind(dzmap, i, buf, super);
}
if ((verbose_flag && ind_cnt > 0) || debug_flag)
fprintf(stderr, "%5d %s zone blocks.\n", ind_cnt, ind_str[ind]);
}
static void rw_data_zones(dzmap, super)
dzmap_t dzmap;
super_t super;
{
char ibuf[BLOCK_SIZE], obuf[BLOCK_SIZE];
size_t i;
int ztype, ind, last_read;
off_t dz_offset;
dz_offset = super.firstdatazone;
for (i = 0; i < super.dzmap_size; i++) {
last_read = read_block(ibuf, dz_offset + i);
if (last_read != BLOCK_SIZE) break;
ind = dzmap[i] & INDIRECT_MASK;
if (ind == 0) {
ztype = dzmap[i] & T_MASK;
if (ztype == T_DIR)
cw_dir_block(ibuf);
else
write_block(ibuf);
} else {
if (super.version == 1)
conv2_blkcpy(obuf, ibuf);
else
conv4_blkcpy(obuf, ibuf);
write_block(obuf);
}
if (verbose_flag && i && i % 1024 == 0) {
fprintf(stderr, ".");
fflush(stderr);
}
}
if (verbose_flag && i > 1024) putc('\n', stderr);
if (last_read != BLOCK_SIZE) for (; i < super.dzmap_size; i++)
if (ztype_class(dzmap[i] & T_MASK) == In_use_zone)
fail("Can't read data zone");
}
static int read_block(buf, offset)
char *buf;
blockn_t offset;
{
static blockn_t curr_offset = 0;
int bytes;
if (offset != curr_offset) {
if (lseek(0, (off_t) offset * BLOCK_SIZE, 0) == -1)
fail("lseek failed on input file");
curr_offset = offset;
}
bytes = read(0, buf, BLOCK_SIZE);
if (bytes < 0) fail("read failed on input file");
curr_offset += bytes;
return bytes;
}
static void write_block(buf)
char *buf;
{
if (test_flag) return;
if (write(1, buf, BLOCK_SIZE) != BLOCK_SIZE)
fail("write failed on output file");
}
static int convcpy(dst, src, format)
char *dst;
char *src;
int *format;
{
char *old_src = src;
register char tmp;
int i;
for (i = 0; format[i] > 0; i++) {
switch (format[i]) {
case 1: *dst++ = *src++; break;
case 2:
tmp = *src++;
*dst++ = *src++;
*dst++ = tmp;
break;
case 4:
tmp = src[0];
dst[0] = src[3];
dst[3] = tmp;
tmp = src[1];
dst[1] = src[2];
dst[2] = tmp;
src += 4;
dst += 4;
break;
default:
fail("wrong format array for convcpy");
}
}
return(src - old_src);
}
static void conv2_blkcpy(dst, src)
char *dst;
char *src;
{
int i;
register char tmp;
for (i = 0; i < BLOCK_SIZE; i += 2) {
tmp = *src++;
*dst++ = *src++;
*dst++ = tmp;
}
}
static void conv4_blkcpy(dst, src)
char *dst;
char *src;
{
int i;
register char tmp;
for (i = 0; i < BLOCK_SIZE; i += 4) {
tmp = src[0];
dst[0] = src[3];
dst[3] = tmp;
tmp = src[1];
dst[1] = src[2];
dst[2] = tmp;
src += 4;
dst += 4;
}
}
static void conv2cpy(dst, src)
char *dst;
char *src;
{
register char tmp;
tmp = *src++;
*dst++ = *src++;
*dst++ = tmp;
}
static int inode_size(version)
int version;
{
return(version == 1) ? V1_INODE_SIZE : V2_INODE_SIZE;
}
static void init_super(sp, buf)
super_t *sp;
char *buf;
{
int magic;
long imapblks, zmapblks;
big_endian_fs = 0; /* guess the file system is little endian */
magic = two_bytes(buf + MAGIC_OFFSET);
if (magic != V1_MAGIC && magic != V2_MAGIC) {
big_endian_fs = 1;
magic = two_bytes(buf + MAGIC_OFFSET);
}
switch (magic) {
case V1_MAGIC: sp->version = 1; break;
case V2_MAGIC: sp->version = 2; break;
default: fail("Not a Minix file system");
}
if (verbose_flag) fprintf(stderr, "\nVersion = V%d, %s endian.\n",
sp->version, big_endian_fs ? "big" : "little");
sp->ninodes = two_bytes(buf + NINODES_OFFSET);
imapblks = two_bytes(buf + IMAP_BLOCKS_OFFSET);
sp->imap_blocks = imapblks;
zmapblks = two_bytes(buf + ZMAP_BLOCKS_OFFSET);
sp->zmap_blocks = zmapblks;
sp->firstdatazone = two_bytes(buf + FIRSTDATAZONE_OFFSET);
sp->log_zone_size = two_bytes(buf + LOG_ZONE_SIZE_OFFSET);
if (sp->version == 1)
sp->zones = two_bytes(buf + V1_ZONES_OFFSET);
else
sp->zones = four_bytes(buf + V2_ZONES_OFFSET);
sp->inodes_per_block = BLOCK_SIZE / inode_size(sp->version);
if (imapblks < 0 || zmapblks < 0 || sp->ninodes < 1 || sp->zones < 1)
fail("Bad superblock");
if (sp->log_zone_size != 0)
fail("Can't swap file systems with different zone and block sizes");
sp->first_imap_block = SUPER_BLOCK_OFF + 1;
sp->first_zmap_block = sp->first_imap_block + sp->imap_blocks;
sp->first_inode_block = sp->first_zmap_block + sp->zmap_blocks;
sp->dzmap_size = sp->zones - sp->firstdatazone;
if (verbose_flag) {
fprintf(stderr, "nzones = %ld, ", sp->zones);
fprintf(stderr, "ninodes = %u, ", sp->ninodes);
fprintf(stderr, "first data zone = %ld.\n\n", sp->firstdatazone);
}
}
static void get_inode(ip, buf, version)
inode_t *ip;
char *buf;
int version;
{
int i;
int mode;
if (version == 1) {
mode = two_bytes(buf + INODE1_MODE_OFF);
ip->size = four_bytes(buf + INODE1_SIZE_OFF);
ip->ind1 = two_bytes(buf + INODE1_IND1_OFF);
ip->ind2 = two_bytes(buf + INODE1_IND2_OFF);
ip->ind3 = 0;
for (i = 0; i < NR_DIRECT_ZONES; i++)
ip->direct[i] = two_bytes(buf + INODE1_DIRECT_OFF + 2 * i);
} else {
mode = two_bytes(buf + INODE2_MODE_OFF);
ip->size = four_bytes(buf + INODE2_SIZE_OFF);
ip->ind1 = four_bytes(buf + INODE2_IND1_OFF);
ip->ind2 = four_bytes(buf + INODE2_IND2_OFF);
ip->ind3 = four_bytes(buf + INODE2_IND3_OFF);
for (i = 0; i < NR_DIRECT_ZONES; i++)
ip->direct[i] = four_bytes(buf + INODE2_DIRECT_OFF + 4 * i);
}
if (mode == 0) {
if (ip->size % DIR_ENTRY_SIZE == 0)
ip->ztype = T_MAYBE_OLD_DIR;
else
ip->ztype = T_OLD_NON_DIR;
if (was_blk_special(*ip)) ip->size = 0;
} else {
mode = mode & INODE_MODE_MASK;
if (mode == INODE_BLK_SPECIAL_MODE || mode == INODE_CHR_SPECIAL_MODE)
ip->size = 0; /* prevent the use of the block numbers. */
ip->ztype = (mode == INODE_DIR_MODE) ? T_DIR : T_NON_DIR;
}
}
static int check_inode(inode, super)
inode_t inode;
super_t super;
{
int i;
for (i = 0; i < NR_DIRECT_ZONES; i++)
if (!check_blk_number(inode.direct[i], super)) return 0;
return(check_blk_number(inode.ind1, super) &&
check_blk_number(inode.ind2, super) &&
check_blk_number(inode.ind3, super));
}
static int check_blk_number(num, super)
blockn_t num;
super_t super;
{
if (num == 0 || (num >= super.firstdatazone && num < super.zones))
return 1;
fprintf(stderr, "warning bad block number %ld in inode.\n", num);
return 0;
}
static int was_blk_special(inode)
inode_t inode;
{
int i, result;
blockn_t block_size;
if (inode.size % BLOCK_SIZE || inode.ind1) return 0;
block_size = inode.size / BLOCK_SIZE;
for (i = NR_DIRECT_ZONES - 1; i >= 0; i--)
if (inode.direct[i] != 0) break;
result = (i < 1 && block_size > i + 1);
if (debug_flag && result) {
fprintf(stderr, "old block special file detected (slot = %d).\n", i);
}
return result;
}
static void cw_inode_block(buf, ninodes, version)
char *buf;
inodesn_t ninodes;
int version;
{
char output_buf[BLOCK_SIZE];
char *src, *dst;
inodesn_t i;
int cnt, free_bytes;
int *format;
src = buf;
dst = output_buf;
format = (version == 1) ? inode1_format : inode2_format;
for (i = 0; i < ninodes; i++) {
cnt = convcpy(dst, src, format);
src += cnt;
dst += cnt;
}
assert(cnt == inode_size(version));
free_bytes = BLOCK_SIZE - (src - buf);
assert(free_bytes >= 0);
if (verbose_flag && free_bytes > 0) {
/* There is a small change that the last free inode has no
* matching bit in the last inode bit map block: e.g. if
* sp->ninodes == 8191. */
fprintf(stderr, "%5d bytes (%d inodes) free in last inode block.\n",
free_bytes, free_bytes / inode_size(version));
memcpy(dst, src, (size_t) free_bytes);
}
write_block(output_buf);
}
static void proc_ind(dzmap, curr_ind, buf, super)
dzmap_t dzmap;
size_t curr_ind;
char *buf;
super_t super;
{
int indnum, i, ztype;
int word_size; /* size of zone block number in ind. block in
* bytes */
unsigned char dz, tmp_dz;
blockn_t blk, ind_blk;
int bad_range = 0, hidden_zero = 0, zero_flag = 0, expired = 0;
size_t blk_index;
dz = dzmap[curr_ind];
indnum = dz & INDIRECT_MASK;
ztype = dz & T_MASK;
ind_blk = curr_ind + super.firstdatazone;
word_size = (super.version == 1) ? 2 : 4;
assert(indnum > 0);
for (i = 0; i < BLOCK_SIZE; i += word_size) {
if (word_size == 2)
blk = two_bytes(buf + i);
else
blk = four_bytes(buf + i);
if (blk == 0)
zero_flag = 1;
else if (blk < super.firstdatazone || blk >= super.zones)
bad_range = 1;
else {
if (zero_flag) hidden_zero = 1;
blk_index = blk - super.firstdatazone;
tmp_dz = dzmap[blk_index];
if (ztype_class(tmp_dz & T_MASK) == In_use_zone) expired = 1;
}
}
if (ztype_class(ztype) == In_use_zone) {
if (bad_range) {
fprintf(stderr, "%s zone block contains ", ind_str[indnum]);
fail("illegal value");
}
if ((ztype == T_DIR || indnum > 1) && hidden_zero) {
fprintf(stderr, "WARNING: %s zone block %ld contains ",
ind_str[indnum], ind_blk);
fprintf(stderr, "unexpected zero block numbers\n");
}
} else {
if (expired) {
dzmap[curr_ind] &= ~(INDIRECT_MASK & IND_CONFLICT_BIT);
return;
}
/* Not yet implemented. :-( if (bad_range || (indnum > 1 &&
* hidden_zero) || equal_values(buf, super.version ) { } */
}
for (i = 0; i < BLOCK_SIZE; i += word_size) {
if (word_size == 2)
blk = two_bytes(buf + i);
else
blk = four_bytes(buf + i);
if (blk == 0) continue;
blk_index = blk - super.firstdatazone;
tmp_dz = dzmap[blk_index];
if (ztype_class(tmp_dz & T_MASK) == In_use_zone) { /* trouble */
if ((tmp_dz & INDIRECT_MASK) == indnum - 1 &&
(tmp_dz & T_MASK) == ztype)
fprintf(stderr, "WARNING: %s zone block %ld used more \
than once\n", ind_str[indnum - 1], blk);
else {
fprintf(stderr, "Block %ld used more than ", blk);
fail("once with different types");
}
}
dzmap[blk_index] = (dz & ~INDIRECT_MASK) | (indnum - 1);
}
dzmap[curr_ind] |= IND_PROCESSED_BIT;
}
static void cw_dir_block(buf)
char *buf;
{
char output_buf[BLOCK_SIZE];
int ino, i, old_ino_offset;
memcpy(output_buf, buf, BLOCK_SIZE);
for (i = 0; i < BLOCK_SIZE; i += DIR_ENTRY_SIZE) {
ino = two_bytes(buf + i);
if (ino == 0) {
old_ino_offset = i + DIR_ENTRY_SIZE - 2;
conv2cpy(output_buf + old_ino_offset, buf + old_ino_offset);
} else
conv2cpy(output_buf + i, buf + i);
}
write_block(output_buf);
}
static void dzmap_add_inode(dzmap, inode, super)
dzmap_t dzmap;
inode_t inode;
super_t super;
{
int i;
if (inode.size == 0 || !check_inode(inode, super)) return;
for (i = 0; i < NR_DIRECT_ZONES; i++)
dz_update(dzmap, inode.direct[i], 0, inode.ztype, super);
dz_update(dzmap, inode.ind1, 1, inode.ztype, super);
dz_update(dzmap, inode.ind2, 2, inode.ztype, super);
dz_update(dzmap, inode.ind3, 3, inode.ztype, super);
}
static void dz_update(dzmap, blknum, new_indnum, new_ztype, super)
dzmap_t dzmap;
blockn_t blknum;
int new_indnum;
int new_ztype;
super_t super;
{
size_t dznum;
int old_indnum;
int old_ztype;
unsigned char *dz;
char new_dz;
if (blknum == 0) return;
dznum = (size_t) (blknum - super.firstdatazone);
dz = &dzmap[dznum];
old_indnum = *dz & INDIRECT_MASK;
old_ztype = *dz & T_MASK;
new_dz = new_ztype | new_indnum;
if (ztype_class(new_ztype) > ztype_class(old_ztype)) {
*dz = new_dz;
return;
} else if (ztype_class(new_ztype) < ztype_class(old_ztype))
return;
/* Collision: old and new have the same class */
if (ztype_class(old_ztype) == In_use_zone) { /* trouble */
if (new_indnum == old_indnum && new_ztype == old_ztype) {
fprintf(stderr, "WARNING: file system corrupt, zone block %ld \
is used more than once.\n", blknum);
return;
}
fprintf(stderr, "ERROR: file system corrupt, zone block %ld is used \
more than once.\n", blknum);
fail("Can't determine its type");
}
assert(ztype_class(old_ztype) == Old_zone);
if (new_indnum != old_indnum) {
*dz |= IND_CONFLICT_BIT;
if (new_indnum > old_indnum) {
*dz &= ~INDIRECT_MASK;
*dz |= new_indnum;
}
}
if (new_ztype == T_MAYBE_OLD_DIR || old_ztype == T_MAYBE_OLD_DIR) {
*dz |= TYPE_CONFLICT_BIT;
*dz &= ~T_MASK;
*dz |= T_MAYBE_OLD_DIR;
}
}
static class_t ztype_class(ztype)
int ztype;
{
class_t class;
if (ztype == T_MAYBE_OLD_DIR || ztype == T_OLD_NON_DIR)
class = Old_zone;
else if (ztype == T_DIR || ztype == T_NON_DIR)
class = In_use_zone;
else
class = Unused_zone;
return class;
}
static void fail(str)
char *str;
{
fprintf(stderr, "%s\n", str);
exit(1);
}
static unsigned int two_bytes(buf)
char buf[2];
{
unsigned char *ubuf = (unsigned char *) buf;
if (big_endian_fs)
return(ubuf[0] << 8) | ubuf[1];
else
return(ubuf[1] << 8) | ubuf[0];
}
static long four_bytes(buf)
char buf[4];
{
unsigned char *ubuf = (unsigned char *) buf;
register int r1, r2;
if (big_endian_fs) {
r1 = (ubuf[0] << 8) | ubuf[1];
r2 = (ubuf[2] << 8) | ubuf[3];
} else {
r2 = (ubuf[1] << 8) | ubuf[0];
r1 = (ubuf[3] << 8) | ubuf[2];
}
return((long) r1 << 16) | r2;
}
static void usage(arg0)
char *arg0;
{
fprintf(stderr, "usage: %s [-v] srcfs [destfs]\n", arg0);
exit(2);
}