Created
April 18, 2022 07:22
-
-
Save Theldus/cd0da7539ebaebbcdc4cc8d74ce448aa to your computer and use it in GitHub Desktop.
Read directory entries through ext4 filesystem layout, without opendir/readdir
This file contains 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
/* | |
* 2022 April, 18 | |
* The author disclaims copyright to this source code, and it is | |
* release to the public domain. | |
* | |
* This code serves and has been published for study purposes | |
* only, I am not responsible for any damage caused by it. | |
*/ | |
#include <stdio.h> | |
#include <string.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <inttypes.h> | |
#include <sys/ioctl.h> | |
#include <linux/fs.h> | |
/* | |
* Iterate over all dir entries for a given folder | |
* using the ext4 layout, instead of opendir/readdir... | |
* | |
* Why? why not? | |
* | |
* Limitations: | |
* May not work for every folder out of the sun, since the folder | |
* may use a hash tree approach, for faster look up and stuff... | |
* I'm too lazy to implement this. | |
* | |
* Refer to 'Hash Tree Directories' in: | |
* https://www.kernel.org/doc/html/latest/filesystems/ext4/directory.html | |
* for more info. | |
* | |
* Also useful: | |
* https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout | |
*/ | |
/* File types to string. */ | |
const char *ft2str[] = | |
{ | |
"Unknown", | |
"Regular File", | |
"Directory", | |
"Char Device", | |
"Block Device", | |
"FIFO", | |
"Socket", | |
"Symbolic Link" | |
}; | |
/* Ext4 dirent. */ | |
#define EXT4_NAME_LEN 255 | |
/* v2. */ | |
struct ext4_dir_entry_2 | |
{ | |
/* Inode number */ | |
uint32_t inode; | |
/* Directory entry length */ | |
uint16_t rec_len; | |
/* Name length */ | |
uint8_t name_len; | |
/* See file type macros EXT4_FT_* below */ | |
uint8_t file_type; | |
/* File name */ | |
char name[EXT4_NAME_LEN]; | |
}; | |
struct fiemap_extent | |
{ | |
/* | |
* Logical offset in bytes for the start of | |
* the extent from the beginning of the file. | |
*/ | |
uint64_t fe_logical; | |
/* | |
* Physical offset in bytes for the start | |
* of the extent from the beginning of the disk. | |
*/ | |
uint64_t fe_physical; | |
/* Length in bytes for this extent. */ | |
uint64_t fe_length; | |
uint64_t fe_reserved64[2]; | |
/* FIEMAP_EXTENT_* flags for this extent. */ | |
uint32_t fe_flags; | |
uint32_t fe_reserved[3]; | |
}; | |
struct fiemap | |
{ | |
/* | |
* Logical offset (inclusive) at | |
* which to start mapping (in). | |
*/ | |
uint64_t fm_start; | |
/* | |
* Logical length of mapping which | |
* userspace wants (in). | |
*/ | |
uint64_t fm_length; | |
/* FIEMAP_FLAG_* flags for request (in/out). */ | |
uint32_t fm_flags; | |
/* Number of extents that were mapped (out). */ | |
uint32_t fm_mapped_extents; | |
/* Size of fm_extents array (in). */ | |
uint32_t fm_extent_count; | |
uint32_t fm_reserved; | |
/* Array of mapped extents (out). */ | |
struct fiemap_extent fm_extents[0]; | |
}; | |
/* | |
* @brief Get first physical byte of the given | |
* file descriptor fd. | |
* | |
* @param fd File to be retrieved the phys position. | |
* | |
* @return Returns the physical position on success, | |
* 0 otherwise. | |
*/ | |
static uint64_t get_first_phys_byte(int fd) | |
{ | |
uint8_t buf[128] = {0}; /* Fiemap/extent buffer. */ | |
int rc; /* Return code. */ | |
struct fiemap *fiemap = (struct fiemap *)buf; | |
struct fiemap_extent *fm_ext = &fiemap->fm_extents[0]; | |
int count = (sizeof(buf) - sizeof(*fiemap)) / | |
sizeof(struct fiemap_extent); | |
fiemap->fm_length = ~0ULL; | |
fiemap->fm_flags = 0; | |
fiemap->fm_extent_count = count; | |
rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap); | |
if (rc < 0) | |
{ | |
fprintf(stderr, "Unable to FIEMAP fd...\n"); | |
return (0); | |
} | |
return (fm_ext[0].fe_physical); | |
} | |
/* | |
* @brief For a given path folder, get position of the first physical | |
* byte on disk. | |
* | |
* @param path Folder path | |
* @param fd Returned fd. | |
* | |
* @return Returns the first physical byte. | |
*/ | |
static uint64_t get_phys_byte_folder(const char *path, int *fd) | |
{ | |
uint64_t phys; | |
*fd = open(path, O_RDONLY); | |
if (*fd < 0) { | |
fprintf(stderr, "Unable to get size: %s\n", path); | |
perror("reason "); | |
} | |
phys = get_first_phys_byte(*fd); | |
close(*fd); | |
return (phys); | |
} | |
/** | |
* @brief Dump a single dirent. | |
* | |
* @param dp Dirent to be dumped on screen. | |
*/ | |
static void dump_ext4dirent(const struct ext4_dir_entry_2 *dp) | |
{ | |
printf("inode: %d\n", dp->inode); | |
printf("rec_len: %d\n", dp->rec_len); | |
printf("name_len: %d\n", dp->name_len); | |
printf("file_type: %s\n", ft2str[dp->file_type]); | |
printf("name: %.*s\n", dp->name_len, dp->name); | |
} | |
/** | |
* @brief Loop through all dirents of a opened disk @p fd, | |
* starting at @p seek with at most @p max_dirents entry. | |
* | |
* @param fd Opened disk (read-only) to be read. | |
* @param seek First physical byte. | |
* @param max_dirents Max dirents to be read. | |
* | |
* @return Returns 0 if success, a negative number otherwise. | |
*/ | |
static int loop_dirents(int fd, off_t seek, int max_dirents) | |
{ | |
struct ext4_dir_entry_2 dp = {0}; | |
ssize_t ret_read; | |
int i = 0; | |
do | |
{ | |
/* Seek right position. */ | |
seek = lseek(fd, seek + dp.rec_len, SEEK_SET); | |
if (seek < 0) { | |
fprintf(stderr, "Unable to seek\n"); | |
return (-1); | |
} | |
memset(&dp, 0, sizeof dp); | |
/* Read entire dirent. */ | |
ret_read = read(fd, (void*)&dp, sizeof dp); | |
if (ret_read <= 0) { | |
fprintf(stderr, "Unable to read dirent!, %d\n", (int)ret_read); | |
return (-1); | |
} | |
printf("dirent #%d:\n", i); | |
dump_ext4dirent(&dp); | |
printf("---------\n"); | |
} while (++i < max_dirents && dp.inode); | |
return (0); | |
} | |
/* Main program. */ | |
int main(int argc, char **argv) | |
{ | |
uint64_t phys_fold; | |
int fd; | |
if (argc < 2) { | |
fprintf(stderr, "Usage: %s <path-to-folder>\n", argv[0]); | |
return (1); | |
} | |
phys_fold = get_phys_byte_folder(argv[1], &fd); | |
if (fd < 0) | |
return (1); | |
printf("Phys: %" PRId64 "\n\n", phys_fold); | |
fd = open("/dev/sdb2", O_RDONLY); | |
if (fd < 0) { | |
fprintf(stderr, "Unable to open disk\n"); | |
perror("reason "); | |
} | |
loop_dirents(fd, phys_fold, 1000); | |
close(fd); | |
return (0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment