/*
 * srchfs (Search file system superblocks)
 *
 * $Id: srchfs.c 37 2004-11-29 07:43:38Z guillem $
 *
 * Copyright (C) 2001, 2004 Guillem Jover <guillem@hadrons.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 */

#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64

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

void
printsect(uint8_t *sector)
{
	int i, j;

	for (j = 0; j < 0x200; j += 0x10) {
		printf("%010x ", j);
		for (i = 0; i < 0x10; i++)
			printf("%02x ", sector[j + i]);
		for (i = 0; i < 0x10; i++) {
			uint8_t byte = sector[j + i];

			printf("%c", isprint(byte) ? byte: '.');
		}
		printf("\n");
	}
}

int
is_dos_part_table(uint8_t *sector)
{
	if (sector[510] == 0x55 && sector[511] == 0xaa)
		return 1;

	return 0;
}

int
is_linux_swap(uint8_t *sector)
{
	const char *swap_magic = "SWAP-SPACE";

	return !memcmp(sector + 502, swap_magic, sizeof(swap_magic));
}

int
is_ext2fs(uint8_t *sector, char *label, int label_size)
{
	if ((sector[0x38] == 0x53) && (sector[0x39] == 0xef)) {
		int i;

		memcpy(label, sector + 0x78, label_size - 1);
		label[label_size] = 0;
		for (i = 0; i < label_size && label[i]; i++) {
			if (!isprint(label[i]))
				return 0;
		}

		return 1;
	}

	return 0;
}

int
main(int argc, char **argv)
{
	int hd;
	ssize_t read_size;
	off_t sectn = 0;
	uint8_t sector[512];
	char label[15];

	if (argc < 2) {
		fprintf(stderr, "error: param too few [%i]\n", argc);
		exit(1);
	}

	hd = open(argv[1], O_RDONLY);
	if (hd < 0) {
		fprintf(stderr, "error: opening[%s] %s\n", argv[1],
			strerror(errno));
		exit(1);
	}

	if (argc > 2) {
		off_t hd_offs, hd_seek_offs;

		sectn = strtoll(argv[2], NULL, 10);
		hd_seek_offs = sectn * sizeof(sector);

		hd_offs = lseek(hd, hd_seek_offs, SEEK_SET);
		if (hd_offs < 0) {
			printf("%llu\n", hd_offs);
			fprintf(stderr, "error: seeking at sector[%lld] %s\n",
				sectn, strerror(errno));
			exit(1);
		}
	}

	do {
		read_size = read(hd, sector, sizeof(sector));
		switch (read_size) {
		case sizeof(sector):
			if (is_ext2fs(sector, label, sizeof(label)))
				printf("fs: Linux ext2fs label[%s] sector[%lld]\n",
				       label, sectn);
			else if (is_linux_swap(sector))
				printf("fs: Linux swap file sector[%lld]\n",
				       sectn);
			else if (is_dos_part_table(sector)) {
				printf("fs: Partition table at sector[%lld]\n",
				       sectn);
				printsect(sector);
			}

			sectn++;
			break;
		case -1:
			fprintf(stderr, "error: reading %s\n",
				strerror(errno));
			exit(1);
		case 0:
		default:
			fprintf(stderr, "EOF at sector[%lld]\n", sectn);
			close(hd);
			exit(1);
		}

	} while (1);

	return 0;
}

