view ino_scan.c @ 18:c515cc303bed

cleanup copy data condition
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sat, 22 Mar 2008 18:43:58 -0400
parents c10bdd3261be
children 068a4a28bde6
line wrap: on
line source

/*
 * Copyright (c) 2008 Josef 'Jeff' Sipek
 *
 * Licensed under GPL version 2.
 */

#define _XOPEN_SOURCE 500
#define _LARGEFILE64_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

typedef unsigned int uint;
typedef unsigned short ushort;

#include <xfs/libxfs.h>

#define FNAME		"test.img"
#define DUMPDIR		"dump"
#define BLKSIZE		4096
#define BLKDEV_SIZE	(104857600 / BLKSIZE)
#define INOSIZE		256

/*
 * Should inode data be copied off the device into DUMPDIR?
 *
 * defined:	yes
 * undefined:	no
 */
//#define COPY_DATA

int fd;		/* device fd */
int ffd = 0;	/* file fd */

static inline __uint16_t swap16(__uint16_t in)
{
	return ((in & 0xff) << 8) | (in >> 8);
}

static inline __uint32_t swap32(__uint32_t in)
{
	return ((in & 0x000000ff) << 24)
	     | ((in & 0x0000ff00) << 8)
	     | ((in & 0x00ff0000) >> 8)
	     | ((in & 0xff000000) >> 24);
}

static inline __uint64_t swap64(__uint64_t in)
{
	return ((in & 0x00000000000000ffULL) << 56)
	     | ((in & 0x000000000000ff00ULL) << 40)
	     | ((in & 0x0000000000ff0000ULL) << 24)
	     | ((in & 0x00000000ff000000ULL) << 8)
	     | ((in & 0x000000ff00000000ULL) >> 8)
	     | ((in & 0x0000ff0000000000ULL) >> 24)
	     | ((in & 0x00ff000000000000ULL) >> 40)
	     | ((in & 0xff00000000000000ULL) >> 56);
}

#define die() do_die(__FILE__, __LINE__)
void do_die(char *file, int line)
{
	printf("fatal: %s:%d: %s\n", file, line, strerror(errno));
	fflush(stdout);
	exit(1);
}

void hexdump(unsigned char *p, int len)
{
	int i;
	for(i=0;i<len;i+=16,p+=16)
		printf("%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n",
			p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
			p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
}

void convert_extent_rec(xfs_bmbt_rec_t *rec, xfs_bmbt_irec_t *irec)
{
	__int64_t *p = (__int64_t*) rec;
	__int64_t tmp;

	tmp = swap64(*p);
	irec->br_state = (tmp) >> 63;
	irec->br_startoff = ((tmp) & 0x7fffffffffffffffULL) >> 9;
	irec->br_startblock = (tmp) << 55;

	p++;
	tmp = swap64(*p);

	irec->br_startblock |= (tmp) >> 21;
	irec->br_blockcount = (tmp) & 0x1fffffULL;
}

void copy_extent_data(struct xfs_bmbt_irec *irec)
{
	if (!ffd)
		return;

#ifdef COPY_DATA
	char buf[BLKSIZE];
	int ret;
	int i;

	for(i=0; i<irec->br_blockcount; i++) {
		ret = pread64(fd, buf, BLKSIZE, (irec->br_startblock + i) * BLKSIZE);
		if (ret == -1)
			die();

		ret = pwrite64(ffd, buf, BLKSIZE, (irec->br_startoff + i) * BLKSIZE);
		if (ret == -1)
			die();
	}
#endif
}

void process_reclist(xfs_bmbt_rec_t *rec, __uint32_t nexts)
{
	struct xfs_bmbt_irec irec;
	__uint32_t i;

	printf(">> [startoff,startblock,blockcount,extentflag]\n");
	for(i=0; i<nexts; i++, rec++) {
		convert_extent_rec(rec, &irec);
		printf(">> %u: [%llu,%llu,%llu,%d]\n",
				i, irec.br_startoff, irec.br_startblock,
				irec.br_blockcount, irec.br_state);

		copy_extent_data(&irec);
	}
}

void scan_lbtree(__uint64_t blk, __uint16_t level);

void scan_bmap(xfs_bmbt_block_t *block, __uint16_t level)
{
	xfs_bmbt_ptr_t *ptr;
	int i;

	if (!level) {
		process_reclist(XFS_BTREE_REC_ADDR(0, xfs_bmbt, block, 1, 0),
				swap16(block->bb_numrecs));
		return;
	}

	ptr = XFS_BTREE_PTR_ADDR(0, xfs_bmbt, block, 1,
			XFS_BTREE_BLOCK_MAXRECS(BLKSIZE, xfs_bmbt, level != 0));
	for(i=0; i<swap16(block->bb_numrecs); i++)
		scan_lbtree(swap64(ptr[i]), swap16(block->bb_level));
}

void scan_lbtree(__uint64_t blk, __uint16_t level)
{
	char buf[BLKSIZE];
	int ret;

	ret = pread64(fd, buf, BLKSIZE, blk * BLKSIZE);
	if (ret == -1)
		die();

	if (memcmp(buf, "BMAP", 4))
		die();

	scan_bmap((xfs_bmbt_block_t*) buf, level - 1);
}

static inline int get_dfork_size(struct xfs_dinode *inode)
{
	if (inode->di_core.di_forkoff)
		return ((int)inode->di_core.di_forkoff) << 3;
	return INOSIZE - sizeof(struct xfs_dinode_core) - sizeof(xfs_agino_t); 
}

void process_btinode(struct xfs_dinode *inode)
{
	xfs_bmdr_block_t *block;
	xfs_bmbt_ptr_t *ptr;
	int i;

	block = (void*)XFS_DFORK_PTR(inode, XFS_DATA_FORK);
	if (!swap16(block->bb_level)) {
		xfs_bmbt_rec_t *rec;

		rec = XFS_BTREE_REC_ADDR(0, xfs_bmdr, block, 1,
			XFS_BTREE_BLOCK_MAXRECS(get_dfork_size(inode), xfs_bmdr, 1));

		process_reclist(rec, swap16(block->bb_numrecs));
		return;
	}

	ptr = (void*)XFS_BTREE_PTR_ADDR(0, xfs_bmdr, block, 1,
			XFS_BTREE_BLOCK_MAXRECS(get_dfork_size(inode), xfs_bmdr, 0));
	for(i=0;i<swap16(block->bb_numrecs);i++)
		scan_lbtree(swap64(ptr[i]), swap16(block->bb_level));
}

int diformat_valid(__uint8_t fmt)
{
	switch(fmt) {
		case XFS_DINODE_FMT_DEV:
		case XFS_DINODE_FMT_LOCAL:
		case XFS_DINODE_FMT_EXTENTS:
		case XFS_DINODE_FMT_BTREE:
		case XFS_DINODE_FMT_UUID:
			return 1;
	}
	return 0;
}

char *diformat_str(__uint8_t fmt)
{
	switch(fmt) {
		case XFS_DINODE_FMT_DEV:
			return "XFS_DINODE_FMT_DEV";
		case XFS_DINODE_FMT_LOCAL:
			return "XFS_DINODE_FMT_LOCAL";
		case XFS_DINODE_FMT_EXTENTS:
			return "XFS_DINODE_FMT_EXTENTS";
		case XFS_DINODE_FMT_BTREE:
			return "XFS_DINODE_FMT_BTREE";
		case XFS_DINODE_FMT_UUID:
			return "XFS_DINODE_FMT_UUID";
	}
	return "(unknown format)";
}

void do_print_inode(struct xfs_dinode *inode)
{
	printf("> di_imode       %o\n", swap16(inode->di_core.di_mode));
	printf("> di_version     %u\n", inode->di_core.di_version);
	printf("> di_format      %u (%s)\n", inode->di_core.di_format,
				diformat_str(inode->di_core.di_format));
	printf("> di_forkoff     %u\n", inode->di_core.di_forkoff);
	printf("> di_uid         %u\n", swap32(inode->di_core.di_uid));
	printf("> di_gid         %u\n", swap32(inode->di_core.di_gid));
	printf("> di_nlink       %u\n", swap32(inode->di_core.di_nlink));
	printf("> di_size        %llu\n", swap64(inode->di_core.di_size));
	printf("> di_nextents    %u\n", swap32(inode->di_core.di_nextents));

	if (!swap32(inode->di_core.di_nextents)) {
		printf(">> Skipping nextents == 0\n");
		return;
	}

	if (!diformat_valid(inode->di_core.di_format) ||
	    (inode->di_core.di_version > 2)) {
		printf(">> Skipping format/version is invalid\n");
		return;
	}
	
	switch(inode->di_core.di_format) {
		case XFS_DINODE_FMT_EXTENTS:
			process_reclist((void*) XFS_DFORK_PTR(inode, XFS_DATA_FORK),
					swap32(inode->di_core.di_nextents));
			break;
		case XFS_DINODE_FMT_BTREE:
			process_btinode(inode);
			break;
		default:
			printf(">> Unknown data fork format %d\n",
				inode->di_core.di_format);
			break;
	}
}

void print_inode(loff_t blk)
{
#ifdef COPY_DATA
	char fname[PATH_MAX];
#endif
	unsigned char buf[INOSIZE];
	struct xfs_dinode *inode = (void*) buf;
	off_t off;
	int ret;

	printf("Found inode magic at block %llu\n", blk);
	
	for(off=0; off<(BLKSIZE/INOSIZE); off++) {
		ret = pread64(fd, buf, INOSIZE, blk * BLKSIZE + off * INOSIZE);
		if (ret == -1) {
			perror("pread64");
			return;
		}

		printf("Inode @ block %llu, inode within blk %lu\n", blk, off);

#ifdef COPY_DATA
		if (swap16(inode->di_core.di_mode) & S_IFREG) {
			snprintf(fname, PATH_MAX, "%s/%08llx-%08lx", DUMPDIR, blk, off);
			ffd = open(fname, O_CREAT | O_LARGEFILE | O_WRONLY, swap16(inode->di_core.di_mode) & 0777);
			if (ffd == -1)
				die();
		}
#endif

		do_print_inode(inode);

#ifdef COPY_DATA
		if (swap16(inode->di_core.di_mode) & S_IFREG) {
			ret = ftruncate(ffd, swap64(inode->di_core.di_size));
			if (ret == -1)
				die();

			ret = close(ffd);
			if (ret == -1)
				die();
			ffd = 0;
		}
#endif
	}
}

int main(int argc, char **argv)
{
	int ret;
	char buf[2];
	loff_t off;

	printf("Assuming inode size = %d, block size = %d\n", INOSIZE, BLKSIZE);

	fd = open(FNAME, O_LARGEFILE | O_RDONLY);
	if (fd == -1) {
		perror("open");
		return 1;
	}

	for(off = 0; off < BLKDEV_SIZE; off++) {
		ret = pread64(fd, buf, 2, off * BLKSIZE);
		if (ret == -1) {
			perror("pread64");
			return 1;
		}

		if (buf[0] == 'I' && buf[1] == 'N')
			print_inode(off);
	}

	return 0;
}