⛏️ index : haiku.git

/*
 * Copyright 2005, Axel DΓΆrfler, axeld@pinc-software.de. All rights reserved.
 * Distributed under the terms of the MIT License.
 */


#include "bfs.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>


extern const char *__progname;
static const char *sProgramName = __progname;

static bool sDumpBlocks = false;


bool
disk_super_block::IsValid()
{
	if (Magic1() != (int32)SUPER_BLOCK_MAGIC1
		|| Magic2() != (int32)SUPER_BLOCK_MAGIC2
		|| Magic3() != (int32)SUPER_BLOCK_MAGIC3
		|| (int32)block_size != inode_size
		|| ByteOrder() != SUPER_BLOCK_FS_LENDIAN
		|| (1UL << BlockShift()) != BlockSize()
		|| AllocationGroups() < 1
		|| AllocationGroupShift() < 1
		|| BlocksPerAllocationGroup() < 1
		|| NumBlocks() < 10
		|| AllocationGroups() != divide_roundup(NumBlocks(),
			1L << AllocationGroupShift()))
		return false;

	return true;
}


off_t
toBlock(disk_super_block &superBlock, block_run run)
{
	return ((((off_t)run.AllocationGroup()) << superBlock.AllocationGroupShift()) | (off_t)run.Start());
}


off_t
toOffset(disk_super_block &superBlock, block_run run)
{
	return toBlock(superBlock, run) << superBlock.BlockShift();
}


void
dumpBlock(const char *buffer, int size)
{
	const int blockSize = 16;

	printf("\t");

	for (int i = 0; i < size;) {
		int start = i;

		for (; i < start + blockSize; i++) {
			if (!(i % 4))
				printf(" ");

			if (i >= size)
				printf("  ");
			else
				printf("%02x", *(unsigned char *)(buffer+i));
		}
		printf("  ");

		for (i = start; i < start + blockSize; i++) {
			if (i < size) {
				char c = *(buffer+i);

				if (c < 30)
					printf(".");
				else
					printf("%c", c);
			}
			else
				break;
		}

		if (i < size)
			printf("\n\t");
		else
			printf("\n");
	}
}


void
dumpLogEntry(int device, disk_super_block &superBlock, int32 &start, uint8 *block)
{
	int32 blockSize = superBlock.BlockSize();
	int32 entry = 0;
	off_t count = 1;
	int32 arrayBlocks = 1;
	off_t blockStart = start;
	bool first = true;
	int32 indexOffset = -1;
	int32 index = 0;
	off_t dataStart = blockStart;
	off_t bitmapSize = superBlock.AllocationGroups() * superBlock.BlocksPerAllocationGroup();

	uint8 *data = NULL;
	if (sDumpBlocks)
		data = (uint8 *)malloc(blockSize);

	for (int32 i = 0; i < arrayBlocks; i++) {
		off_t blockNumber = toBlock(superBlock, superBlock.log_blocks)
			+ blockStart++ % superBlock.log_blocks.Length();
		if (read_pos(device, blockNumber << superBlock.BlockShift(),
				block, blockSize) != blockSize) {
			fprintf(stderr, "%s: could not read block %lld.\n", sProgramName, blockNumber);
			exit(-1);
		}

		off_t *array = (off_t *)block;
		int32 arraySize = blockSize / sizeof(off_t);

		if (first) {
			count = array[0];
			arrayBlocks = ((count + 1) * sizeof(off_t) + blockSize - 1) / blockSize;
			arraySize--;
			dataStart += arrayBlocks;

			start += arrayBlocks + count;

			printf("Entry %ld contains %lld blocks (entry starts at block %lld):\n", entry, count, blockNumber);
			first = false;
		} else
			indexOffset += arraySize;

		for (; index < count; index++) {
			int32 arrayIndex = index - indexOffset;
			if (arrayIndex >= arraySize)
				break;

			off_t value = array[arrayIndex];
			printf("%7ld: %lld%s\n", index, value, value == 0 ? " (superblock)" :
				value < bitmapSize + 1 ? " (bitmap block)" : "");

			if (data != NULL) {
				off_t blockNumber = toBlock(superBlock, superBlock.log_blocks)
					+ ((dataStart + index) % superBlock.log_blocks.Length());
				if (read_pos(device, blockNumber << superBlock.BlockShift(),
						data, blockSize) != blockSize) {
					fprintf(stderr, "%s: could not read block %lld.\n", sProgramName, blockNumber);
				} else
					dumpBlock((char *)data, blockSize);
			}
		}
	}

	free(data);
}


void
usage(void)
{
	fprintf(stderr, "usage: %s [-d] bfs-volume\n"
		"    -d\tdump blocks in log\n", sProgramName);
	exit(0);
}


int
main(int argc, char **argv)
{
	if (argc < 2 || !strcmp(argv[1], "--help"))
		usage();

	while (*++argv) {
		char *arg = *argv;
		if (*arg == '-') {
			while (*++arg && isalpha(*arg)) {
				switch (*arg) {
					case 'd':
						sDumpBlocks = true;
						break;
					default:
						usage();
				}
			}
		}
		else
			break;
	}

	char *devicePath = argv[0];
	int device = open(devicePath, O_RDONLY);
	if (device < 0) {
		fprintf(stderr, "%s: could not open device %s: %s\n",
			sProgramName, devicePath, strerror(errno));
		return -1;
	}

	disk_super_block superBlock;
	if (read_pos(device, 512, &superBlock, sizeof(disk_super_block)) < (ssize_t)sizeof(disk_super_block)) {
		fprintf(stderr, "%s: could not read superblock.\n", sProgramName);
		return -1;
	}

	if (!superBlock.IsValid()) {
		fprintf(stderr, "%s: invalid superblock!\n", sProgramName);
		return -1;
	}

	if (superBlock.Flags() == SUPER_BLOCK_DISK_CLEAN) {
		printf("volume is clean; log is empty.\n");
		return 0;
	}

	off_t bitmapSize = superBlock.AllocationGroups() * superBlock.BlocksPerAllocationGroup();
	printf("Log area = (%lld - %lld), current start = %lld, end = %lld, %lld bitmap blocks\n",
		toBlock(superBlock, superBlock.log_blocks),
		toBlock(superBlock, superBlock.log_blocks) + superBlock.log_blocks.Length(),
		superBlock.LogStart(), superBlock.LogEnd(), bitmapSize);

	uint8 *block = (uint8 *)malloc(superBlock.BlockSize());
	if (block == NULL) {
		fprintf(stderr, "%s: could not allocate block!\n", sProgramName);
		return -1;
	}

	int32 start = superBlock.LogStart();
	int32 lastStart = -1;

	while (true) {
		if (start == superBlock.LogEnd())
			break;
		if (start == lastStart)
			break;

		lastStart = start;	
		dumpLogEntry(device, superBlock, start, block);
	}

	close(device);
	return 0;
}