-
-
Save Theldus/2c7783831edc0df754ebac4fc360d803 to your computer and use it in GitHub Desktop.
Retrieve inode info from ext2,3,4 filesystems directly from disk (/dev/sdx)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* This is free and unencumbered software released into the public domain. | |
* Author: Davidson Francis, 2024-08-18 | |
*/ | |
#include <err.h> | |
#include <stddef.h> | |
#include <stdio.h> | |
#include <dirent.h> | |
#include <fcntl.h> | |
#include <sys/stat.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <inttypes.h> | |
#include <assert.h> | |
#include "sb.h" | |
static const char *file_types[] = { | |
"S_IFIFO (FIFO)", | |
"S_IFCHR (Character device)", | |
"S_IFDIR (Directory)", | |
"S_IFBLK (Block device)", | |
"S_IFREG (Regular file)", | |
"S_IFLNK (Symbolic link)" | |
}; | |
static int pread_all(int fd, void *buf, size_t count, off_t offset) | |
{ | |
char *ptr = buf; | |
size_t rem = count; | |
ssize_t ret = 0; | |
off_t off = offset; | |
while (rem) { | |
ret = pread(fd, ptr, rem, off); | |
if (ret < 0) | |
return ret; | |
ptr += ret; | |
rem -= ret; | |
off += ret; | |
} | |
return 1; | |
} | |
int stat_read_inode(struct stat_fs *st, e2_ino_t ino_num) | |
{ | |
u32 bg_desc_size; | |
u32 group_desc0; | |
u32 bg_desc_idx; | |
u32 block_size; | |
u32 ino_offset; | |
u32 inode_size; | |
u32 ino_block; | |
u32 ino_idx; | |
struct ext4_group_desc gd; | |
e2_blknr_t block_nr; | |
int ret; | |
bg_desc_size = 32; | |
if (has_feature_64bit(&st->sb)) | |
bg_desc_size = st->sb.bg_desc_size; | |
bg_desc_size &= ~7; | |
/* Check if SB is original format (0), or new format with dynamic inode | |
* size. */ | |
inode_size = 128; | |
if (st->sb.rev_level != 0) | |
inode_size = st->sb.inode_size; | |
st->ino_num = ino_num; | |
bg_desc_idx = (ino_num - 1) / st->sb.inodes_per_group; | |
ino_idx = (ino_num - 1) % st->sb.inodes_per_group; | |
block_size = (1024 << st->sb.log2_block_size); | |
ino_block = (ino_idx * inode_size) / block_size; | |
ino_offset = (ino_idx * inode_size) % block_size; | |
group_desc0 = 1; | |
if (block_size == 1024) | |
group_desc0 = 2; | |
group_desc0 *= block_size; /* Block Descriptor Table Idx 0. */ | |
ret = pread_all(st->disk_fd, &gd, bg_desc_size, | |
group_desc0 + (bg_desc_idx*bg_desc_size)); | |
if (ret < 0) | |
errx(1, "Unable to read block group descriptor table!\n"); | |
block_nr = gd.bg_inode_table_lo + ino_block; | |
/* physical disk offset of the block containing the inode ^.^ */ | |
block_nr *= block_size; | |
ret = pread_all(st->disk_fd, &st->ino, inode_size, block_nr + ino_offset); | |
if (ret < 0) | |
errx(1, "Unable to read block containing inode"); | |
return 0; | |
} | |
void stat_dump_detailed_inode_info(struct stat_fs *st) { | |
printf( | |
"Inode number: %" PRIu64 "\n" | |
"File size: %zu bytes\n" | |
"File type: %s\n" | |
"Inode flags: 0x%x (use extent?: %d)\n" | |
" First block at: %u (if !extent)\n", | |
st->ino_num, | |
((size_t)st->ino.size_hi << 32) | st->ino.size_lo, | |
file_types[st->ino.type >> 13], | |
st->ino.flags, !!(st->ino.flags & EXT4_EXTENTS_FLAG), | |
st->ino.blocks[0] | |
); | |
} | |
void stat_dump_brief_inode_info(struct stat_fs *st) { | |
printf( | |
"in: %8" PRIu64", fsize: %010zu %s\n", | |
st->ino_num, | |
((size_t)st->ino.size_hi << 32) | st->ino.size_lo, | |
((st->ino.type & EXT4_S_IFDIR) ? "// FOLDER" : "") | |
); | |
} | |
int stat_open_fs(struct stat_fs *st, const char *device) | |
{ | |
assert(sizeof(struct ext2_superblock) == EXT2_SB_SIZE); | |
assert(sizeof(struct ext2_inode) == EXT2_INODE_SIZE); | |
assert(sizeof(struct ext4_group_desc) == EXT4_GD_SIZE); | |
if (!st) | |
return -1; | |
st->disk_fd = open(device, O_RDONLY); | |
if (st->disk_fd < 0) | |
err(1, "Unable to open disk!"); | |
/* Read superblock. */ | |
if (pread_all(st->disk_fd, &st->sb, EXT2_SB_SIZE, EXT2_SB_OFFSET) < 0) | |
err(1, "Unable to read superblock!"); | |
/* Sanitize it. */ | |
if (st->sb.magic != EXT2_SB_MAGIC) | |
errx(1, "Wrong superblock magic number!"); | |
return 0; | |
} | |
#if 1 | |
int main(int argc, char **argv) | |
{ | |
struct stat_fs stat; | |
e2_ino_t ino; | |
if (argc < 3) | |
errx(1, "Usage: %s <inode> /dev/sdX", argv[0]); | |
ino = atoi(argv[1]); | |
if (stat_open_fs(&stat, argv[2]) < 0) | |
errx(1, "Unable to open filesystem!"); | |
if (stat_read_inode(&stat, ino) < 0) | |
errx(1, "Unable to retrieve inode info!"); | |
stat_dump_detailed_inode_info(&stat); | |
stat_close(&stat); | |
} | |
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* This is free and unencumbered software released into the public domain. | |
* Author: Davidson Francis, 2024-08-18 | |
*/ | |
#ifndef SB_H | |
#define SB_H | |
#include <unistd.h> | |
#include <inttypes.h> | |
typedef uint32_t u32; | |
typedef uint16_t u16; | |
typedef uint8_t u8; | |
typedef uint64_t u64; | |
typedef uint64_t e2_ino_t; | |
typedef uint64_t e2_blknr_t; | |
/* Superblock info. */ | |
#define EXT2_SB_OFFSET 1024 | |
#define EXT2_SB_SIZE 1024 | |
#define EXT2_SB_MAGIC 0xEF53 | |
/* Inode info. */ | |
#define EXT2_INODE_SIZE 256 | |
/* Ext4 group descriptor table info. */ | |
#define EXT4_GD_SIZE 64 | |
#define EXT4_EXTENTS_FLAG 0x00080000 | |
/* EXT4 modes. */ | |
#define EXT4_S_IFDIR 0x4000 | |
struct ext2_superblock { | |
/*00.*/ u32 total_inodes; | |
/*04.*/ u32 total_blocks; | |
/*08.*/ u32 meh1[4]; | |
/*24.*/ u32 log2_block_size; | |
/*28.*/ u32 meh2[3]; | |
/*40.*/ u32 inodes_per_group; | |
/*44.*/ u32 last_mount; /*epoch.*/ | |
/*48.*/ u32 last_written; /*epoch.*/ | |
/*52.*/ u16 meh3[2]; | |
/*56.*/ u16 magic; | |
/*60.*/ u32 meh4[4]; | |
/*76.*/ u32 rev_level; | |
/*80.*/ u32 meh5[2]; | |
/*88.*/ u16 inode_size; | |
/*90.*/ u8 meh6[6]; | |
/*96.*/ u32 feature_incompat; | |
/*100.*/ u8 meh7[154]; | |
/*254.*/ u16 bg_desc_size; | |
/*256.*/ u32 meh8[191]; | |
/*1020.*/u32 checksum; | |
}; | |
struct ext2_inode { | |
/*00.*/ u16 type; | |
/*02.*/ u16 permissions; | |
/*04.*/ u32 size_lo; | |
/*08.*/ u8 meh1[24]; | |
/*32.*/ u32 flags; | |
/*36.*/ u32 meh2; | |
/*40.*/ u32 blocks[15]; | |
/*100.*/ u8 meh3[8]; | |
/*108.*/u32 size_hi; | |
/*112.*/ u8 meh4[144]; | |
}; | |
struct ext4_group_desc { | |
u32 meh1[2]; | |
u32 bg_inode_table_lo; | |
u8 meh2[52]; | |
}; | |
struct stat_fs { | |
struct ext2_superblock sb; | |
struct ext2_inode ino; /* Current read inode structure. */ | |
e2_ino_t ino_num; /* Current read inode number. */ | |
int disk_fd; | |
}; | |
extern int stat_read_inode(struct stat_fs *st, e2_ino_t ino_num); | |
extern void stat_dump_detailed_inode_info(struct stat_fs *st); | |
extern void stat_dump_brief_inode_info(struct stat_fs *st); | |
extern int stat_open_fs(struct stat_fs *st, const char *device); | |
/* | |
* Borrowed from ext2fs. | |
* 0x80 Enable a filesystem size of 2^64 blocks (INCOMPAT_64BIT). | |
*/ | |
static inline int has_feature_64bit(struct ext2_superblock *sb) { | |
return (sb->feature_incompat & 0x0080) != 0; | |
} | |
static inline void stat_close(struct stat_fs *st) { | |
if (!st) | |
return; | |
close(st->disk_fd); | |
} | |
#endif /* SB_H. */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment