/*---------------------------------------------------------------------------- 130-line Turbo C program to dump the root directory of an ext2 partition in a hard disk or a disk image file. Chris Giese http://SisAndHappy.com/ChrisGiese/ This code is public domain (no copyright). You can do whatever you want with it. 27 Apr 2017: automatically setting partition size and start from MBR 25 Apr 2017: initial release ----------------------------------------------------------------------------*/ #define BYTES_PER_DISK_SECTOR 512 #include #include /* ANSI C if you comment out this #include and use only FILEDISK code below */ #include #if 0 #include #else typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned long uint32_t; #endif #define read_le16(X) *(uint16_t *)(X) /* little-endian (x86) CPU only */ #define read_le32(X) *(uint32_t *)(X) /* these initial values let us read only sector 0 of the disk */ static uint32_t g_part_start = 0, g_part_size = 1; static int read_sector(void *buf, uint32_t lba) { #if 0 /* FILEDISK */ static const char *img_file = "/apps/bochs/dlxlinux/hd10meg.img"; static FILE *f; if(lba >= g_part_size) return +1; lba += g_part_start; if(f == NULL) { f = fopen(img_file, "rb"); if(f == NULL) { printf("Error: can't open disk image file %s\n", img_file); exit(1); }} fseek(f, lba * BYTES_PER_DISK_SECTOR, SEEK_SET); return (fread(buf, 1, BYTES_PER_DISK_SECTOR, f) == BYTES_PER_DISK_SECTOR) ? 0 : -1; } #else /* INT 0x13 HARD DISK ACCESS (LEGACY BIOS) */ static const unsigned char int13_drive_num = 0x80; struct REGPACK regs; #pragma pack(1) struct { /* INT 13h AH=42h/AH=43h command packet */ uint8_t pkt_len; uint8_t res0; uint8_t num_sectors; uint8_t res1; uint16_t buf_off; uint16_t buf_seg; uint32_t lba31_0; uint32_t lba63_32; } lba_cmd_pkt = { 0 }; if(lba >= g_part_size) return +1; lba += g_part_start; lba_cmd_pkt.pkt_len = sizeof(lba_cmd_pkt); lba_cmd_pkt.num_sectors = 1; lba_cmd_pkt.buf_off = FP_OFF(buf); lba_cmd_pkt.buf_seg = FP_SEG(buf); lba_cmd_pkt.lba31_0 = lba; regs.r_ds = FP_SEG(&lba_cmd_pkt); regs.r_si = FP_OFF(&lba_cmd_pkt); regs.r_dx = int13_drive_num; regs.r_ax = 0x4200; intr(0x13, ®s); return (regs.r_flags & 0x01) ? -1 : 0; } #endif #define BPERL 16 /* bytes displayed per line of the dump */ void dump(const void *data0, unsigned count) { const unsigned char *data = data0; unsigned j, k; while(count != 0) { k = (count < BPERL) ? count : BPERL; for(j = 0; j < k; j++) printf("%02X ", data[j]); printf("\t"); for(j = 0; j < k; j++) putchar((data[j] < ' ') ? '_' : data[j]); printf("\n"); data = &data[k]; count -= k; }} int main(void) { static unsigned char buf[BYTES_PER_DISK_SECTOR]; unsigned bytes_per_block, sectors_per_block, j, k; uint32_t blk, root_dir_block[15]; /* read MBR-type partition table from sector 0 of disk */ if(read_sector(buf, 0) != 0) return 1; /* can't read sector 0 */ if(buf[510] != 0x55 || buf[511] != 0xAA) return 1; /* invalid MBR */ for(j = 0; j < 4; j++) { if(buf[446 + 16 * j + 4] == 0x83) break; } if(j >= 4) return 1; /* no ext2 partition found */ g_part_start = read_le32(&buf[446 + 16 * j + 8]); g_part_size = read_le32(&buf[446 + 16 * j + 12]); /* superblock at offset 1024 always, regardless of sector or block size */ blk = 1024 / BYTES_PER_DISK_SECTOR; j = 1024 % BYTES_PER_DISK_SECTOR; if(read_sector(buf, blk)) return 1; /* can't read superblock */ if(read_le16(&buf[j + 56]) != 0xEF53) return 2; /* ext2 magic value */ bytes_per_block = 1024 << buf[j + 24]; sectors_per_block = bytes_per_block / BYTES_PER_DISK_SECTOR; /* total_blocks = read_le32(&buf[j + 4]); blocks_per_group = read_le32(&buf[j + 32]); inodes_per_group = read_le32(&buf[j + 40]); group descriptors are 32 bytes each -- read #0; just after superblock */ blk = (bytes_per_block <= 1024) ? 2 : 1; if(read_sector(buf, blk * sectors_per_block)) return 1; blk = read_le32(&buf[8]); /* starting block of inode table */ /* Inodes are 128 bytes each -- read #1; which is the root inode. WARNING: inodes might be larger in newer versions of ext2/3/4 -- get size from superblock. */ if(read_sector(buf, blk * sectors_per_block)) return 1; j = 128 * 1; /* root_dir_len = read_le32(&buf[j + 4]); */ for(k = 0; k < 15; k++) root_dir_block[k] = read_le32(&buf[j + 40 + 4 * k]); /* Read and dump only the first block of the root dir. If you add on to this code, note that root_dir_block[12], [13], and [14] are the singly-, doubly-, and triply-indirect block pointers. */ blk = root_dir_block[0]; if(read_sector(buf, blk * sectors_per_block)) return 1; dump(buf, 256); return 0; }