Edit

thodg/ext4fs/ext4fs.c

Branch :

  • ext4fs.c
  • /* kc3
     * Copyright 2025 kmx.io <contact@kmx.io>
     *
     * Permission is hereby granted to use this software granted the above
     * copyright notice and this permission paragraph are included in all
     * copies and substantial portions of this software.
     *
     * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
     * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
     * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
     * THIS SOFTWARE.
     */
    #define _DEFAULT_SOURCE 1
    #include <endian.h>
    #include <err.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <unistd.h>
    
    #include <linux/fs.h>
    
    #include <ext4fs.h>
    
    int ext4fs_block_bitmap (const struct ext4fs_super_block *sb,
                             const struct ext4fs_group_desc *gd,
                             uint64_t *dest)
    {
      if (! gd) {
        warnx("ext4fs_block_bitmap: NULL group descriptor");
        return -1;
      }
      if (! dest) {
        warnx("ext4fs_block_bitmap: NULL dest");
        return -1;
      }
      *dest = le32toh(gd->gd_block_bitmap_lo);
      if (sb->sb_feature_incompat & EXT4FS_FEATURE_INCOMPAT_64BIT)
        *dest |= ((uint64_t) le32toh(gd->gd_block_bitmap_hi) << 32);
      return 0;
    }
    
    int ext4fs_block_size (const struct ext4fs_super_block *sb,
                           uint32_t *dest)
    {
      if (! sb) {
        warnx("ext4fs_block_size: NULL super block");
        return -1;
      }
      if (! dest) {
        warnx("ext4fs_block_size: NULL dest");
        return -1;
      }
      *dest = 1 << (sb->sb_log_block_size + 10);
      return 0;
    }
    
    int ext4fs_blocks_count (const struct ext4fs_super_block *sb,
                             uint64_t *dest)
    {
      if (! sb) {
        warnx("ext4fs_blocks_count: NULL super block");
        return -1;
      }
      if (! dest) {
        warnx("ext4fs_blocks_count: NULL dest");
        return -1;
      }
      *dest = le32toh(sb->sb_blocks_count_lo);
      if (sb->sb_feature_incompat & EXT4FS_FEATURE_INCOMPAT_64BIT)
        *dest |= ((uint64_t) le32toh(sb->sb_blocks_count_hi) << 32);
      return 0;
    }
    
    struct ext4fs_group_desc *
    ext4fs_group_desc_read (struct ext4fs_group_desc *gd,
                            int fd,
                            const struct ext4fs_super_block *sb)
    {
      uint32_t block_size;
      ssize_t done;
      uint32_t offset;
      ssize_t r;
      ssize_t remaining;
      if (ext4fs_block_size(sb, &block_size))
        return NULL;
      offset = (EXT4FS_SUPER_BLOCK_OFFSET + EXT4FS_SUPER_BLOCK_SIZE +
                (block_size - 1)) / block_size * block_size;
      if (lseek(fd, offset, SEEK_SET) < 0) {
        warn("lseek %u", offset);
        return NULL;
      }
      done = 0;
      remaining = sizeof(struct ext4fs_group_desc);
      while (remaining > 0) {
        r = read(fd, (char *) gd + done, remaining);
        if (r < 0) {
          warn("read super block %ld", remaining);
          return NULL;
        }
        done += r;
        remaining -= r;
      }
      return gd;
    }
    
    int ext4fs_inode_bitmap (const struct ext4fs_super_block *sb,
                             const struct ext4fs_group_desc *gd,
                             uint64_t *dest)
    {
      if (! gd) {
        warnx("ext4fs_inode_bitmap: NULL group descriptor");
        return -1;
      }
      if (! dest) {
        warnx("ext4fs_inode_bitmap: NULL dest");
        return -1;
      }
      *dest = le32toh(gd->gd_inode_bitmap_lo);
      if (sb->sb_feature_incompat & EXT4FS_FEATURE_INCOMPAT_64BIT)
        *dest |= ((uint64_t) le32toh(gd->gd_inode_bitmap_hi) << 32);
      return 0;
    }
    
    int ext4fs_inode_table (const struct ext4fs_super_block *sb,
                            const struct ext4fs_group_desc *gd,
                            uint64_t *dest)
    {
      if (! gd) {
        warnx("ext4fs_inode_table: NULL group descriptor");
        return -1;
      }
      if (! dest) {
        warnx("ext4fs_inode_table: NULL dest");
        return -1;
      }
      *dest = le32toh(gd->gd_inode_table_lo);
      if (sb->sb_feature_incompat & EXT4FS_FEATURE_INCOMPAT_64BIT)
        *dest |= ((uint64_t) le32toh(gd->gd_inode_table_hi) << 32);
      return 0;
    }
    
    int ext4fs_inspect (int fd)
    {
      struct ext4fs_group_desc gd = {0};
      struct ext4fs_super_block sb = {0};
      uint64_t size = 0;
      if (ext4fs_size(fd, &size) ||
          ! size)
        return -1;
      printf("ext4fs_size: %lu\n", size);
      if (! ext4fs_super_block_read(&sb, fd))
        return -1;
      if (ext4fs_inspect_super_block(&sb))
        return -1;
      if (! ext4fs_group_desc_read(&gd, fd, &sb))
        return -1;
      if (ext4fs_inspect_group_desc(&sb, &gd))
        return -1;
      printf("EOF\n");
      return 0;
    }
        
    int ext4fs_inspect_group_desc (const struct ext4fs_super_block *sb,
                                   const struct ext4fs_group_desc *gd)
    {
      uint64_t block_bitmap;
      uint64_t inode_bitmap;
      uint64_t inode_table;
      if (ext4fs_block_bitmap(sb, gd, &block_bitmap) ||
          ext4fs_inode_bitmap(sb, gd, &inode_bitmap) ||
          ext4fs_inode_table(sb, gd, &inode_table))
        return -1;
      printf("%%Ext4fs.GroupDesc{gd_block_bitmap: %lu,\n"
             "                   gd_inode_bitmap: %lu,\n"
             "                   gd_inode_table: %lu}\n",
             block_bitmap,
             inode_bitmap,
             inode_table);
      return 0;
    }
    
    int ext4fs_inspect_super_block (const struct ext4fs_super_block *sb)
    {
      uint64_t blocks_count;
      s_enum *e;
      int first;
      if (ext4fs_blocks_count(sb, &blocks_count))
        return -1;
      printf("%%Ext4fs.SuperBlock{sb_inodes_count: %u,\n"
             "                   sb_blocks_count: %lu,\n"
             "                   sb_rev_level: %u,\n"
             "                   sb_rev_level_minor: %u,\n"
             "                   sb_feature_compat: ",
             le32toh(sb->sb_inodes_count),
             blocks_count,
             le32toh(sb->sb_rev_level),
             le32toh(sb->sb_rev_level_minor));
      e = ext4fs_feature_compat;
      first = 1;
      while (e->name) {
        if (sb->sb_feature_compat & e->value) {
          if (! first)
            printf("|");
          else
            first = 0;
          printf("%s", e->name);
        }
        e++;
      }
      printf(",\n"
             "                   sb_feature_incompat: ");
      e = ext4fs_feature_incompat;
      first = 1;
      while (e->name) {
        if (sb->sb_feature_incompat & e->value) {
          if (! first)
            printf("|");
          else
            first = 0;
          printf("%s", e->name);
        }
        e++;
      }
      printf(",\n"
             "                   sb_feature_ro_compat: ");
      e = ext4fs_feature_ro_compat;
      first = 1;
      while (e->name) {
        if (sb->sb_feature_ro_compat & e->value) {
          if (! first)
            printf("|");
          else
            first = 0;
          printf("%s", e->name);
        }
        e++;
      }
      printf("}\n");
      return 0;
    }
    
    int ext4fs_size (int fd, uint64_t *dest)
    {
      if (ioctl(fd, BLKGETSIZE64, dest) < 0) {
        warn("ioctl BLKGETSIZE64");
        return -1;
      }
      return 0;
    }
    
    struct ext4fs_super_block *
    ext4fs_super_block_read (struct ext4fs_super_block *sb,
                             int fd)
    {
      ssize_t done;
      ssize_t r;
      ssize_t remaining;
      if (lseek(fd, EXT4FS_SUPER_BLOCK_OFFSET, SEEK_SET) < 0) {
        warn("ext4fs_super_block_read: lseek 1024");
        return NULL;
      }
      done = 0;
      remaining = sizeof(struct ext4fs_super_block);
      while (remaining > 0) {
        r = read(fd, (char *) sb + done, remaining);
        if (r < 0) {
          warn("ext4fs_super_block_read: read super block %ld", remaining);
          return NULL;
        }
        done += r;
        remaining -= r;
      }
      return sb;
    }