Skip to content

Instantly share code, notes, and snippets.

@Theldus
Created August 19, 2024 00:06
Show Gist options
  • Save Theldus/2c7783831edc0df754ebac4fc360d803 to your computer and use it in GitHub Desktop.
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 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 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