diff --git a/Makefile b/Makefile index f24b5f2..3d0f357 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ VMLINUXGZ = $(KSRC)/arch/alpha/boot/vmlinux.gz #TESTING = yes # for boot testing -#CFGDEFS = -DDEBUG_ISO -DDEBUG_ROCK -DDEBUG_EXT2 -DDEBUG +#CFGDEFS = -DDEBUG_ISO -DDEBUG_ROCK -DDEBUG_EXT2 -DDEBUG_XFS -DDEBUG # root, aka prefix root = @@ -57,7 +57,7 @@ override ASFLAGS += $(CPPFLAGS) $(CC) $(ASFLAGS) -D__ASSEMBLY__ -c -o $*.o $< NET_OBJS = net.o -DISK_OBJS = disk.o fs/ext2.o fs/ufs.o fs/dummy.o fs/iso.o +DISK_OBJS = disk.o fs/ext2.o fs/ufs.o fs/dummy.o fs/iso.o fs/xfs.o ifeq ($(TESTING),) ABOOT_OBJS = \ head.o aboot.o cons.o utils.o \ diff --git a/disk.c b/disk.c index 3cc451a..f86eb28 100644 --- a/disk.c +++ b/disk.c @@ -40,6 +40,7 @@ extern struct bootfs ext2fs; extern struct bootfs iso; extern struct bootfs ufs; extern struct bootfs dummyfs; +extern struct bootfs xfsfs; struct disklabel * label; int boot_part = -1; @@ -47,7 +48,8 @@ int boot_part = -1; static const struct bootfs *bootfs[] = { &ext2fs, &iso, - &ufs + &ufs, + &xfsfs }; /* @@ -326,23 +328,18 @@ mount_fs (long dev, int partition) return 0; } part = &label->d_partitions[partition - 1]; - for (i = 0; bootfs[i]->fs_type != part->p_fstype; ++i) { - if (i + 1 - >= (int) (sizeof(bootfs)/sizeof(bootfs[0]))) - { - printf("aboot: don't know how to mount " - "partition %d (filesystem type %d)\n", - partition, part->p_fstype); - return 0; + for (i = 0; i < (int)(sizeof(bootfs)/sizeof(bootfs[0])); i++) { + if (bootfs[i]->fs_type == part->p_fstype) { + fs = bootfs[i]; + if (!((*fs->mount)(dev, (long)(part->p_offset) * (long)(label->d_secsize), 1) + < 0)) + return fs; } } - fs = bootfs[i]; - if ((*fs->mount)(dev, (long)(part->p_offset) * (long)(label->d_secsize), 0) - < 0) { - printf("aboot: mount of partition %d failed\n", - partition); - return 0; - } + printf("aboot: don't know how to mount " + "partition %d (filesystem type %d)\n", + partition, part->p_fstype); + return 0; } return fs; } diff --git a/fs/ext2.c b/fs/ext2.c index fc66fed..d125497 100644 --- a/fs/ext2.c +++ b/fs/ext2.c @@ -15,9 +15,37 @@ #include "ext4.h" #include "utils.h" #include - +#include #define MAX_OPEN_FILES 5 +/* + * What this build of aboot can safely cope with. + * + * Keep these masks conservative; you can relax them as you implement + * more features. + */ + +#ifndef EXT4_FEATURE_INCOMPAT_SUPP +#define EXT4_FEATURE_INCOMPAT_SUPP \ + (EXT4_FEATURE_INCOMPAT_FILETYPE | \ + EXT4_FEATURE_INCOMPAT_RECOVER | \ + EXT4_FEATURE_INCOMPAT_EXTENTS | \ + EXT4_FEATURE_INCOMPAT_64BIT | \ + EXT4_FEATURE_INCOMPAT_FLEX_BG | \ + EXT4_FEATURE_INCOMPAT_MMP | \ + EXT4_FEATURE_INCOMPAT_CSUM_SEED) +#endif + +#ifndef EXT4_FEATURE_RO_COMPAT_SUPP +#define EXT4_FEATURE_RO_COMPAT_SUPP \ + (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER | \ + EXT4_FEATURE_RO_COMPAT_LARGE_FILE | \ + EXT4_FEATURE_RO_COMPAT_BTREE_DIR | \ + EXT4_FEATURE_RO_COMPAT_HUGE_FILE | \ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK | \ + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \ + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) +#endif extern struct bootfs ext2fs; static struct ext2_super_block sb; @@ -35,6 +63,7 @@ static int cached_diblkno = -1; static char *diblkbuf; static long dev = -1; static long partition_offset; +static unsigned int group_desc_size = 0; static struct inode_table_entry { struct ext2_inode inode; @@ -43,6 +72,77 @@ static struct inode_table_entry { unsigned short old_mode; } inode_table[MAX_OPEN_FILES]; +static int ext4_check_features(int quiet) +{ + /* sb is the global ext2_super_block already filled in by ext2_mount() */ +#ifdef DEBUG_EXT2 + uint32_t compat = sb.s_feature_compat; + printf("ext2/4: FS compat features: 0x%08x\n", compat); +#endif + uint32_t ro_compat = sb.s_feature_ro_compat; + uint32_t incompat = sb.s_feature_incompat; + uint32_t missing; + + /* First check incompatible features – these must all be understood */ + missing = incompat & ~EXT4_FEATURE_INCOMPAT_SUPP; + if (missing) { + if (!quiet) { + printf("ext2/4: unsupported INCOMPAT features: 0x%08x", missing); + + if (missing & EXT4_FEATURE_INCOMPAT_64BIT) + printf(" (64bit)"); + if (missing & EXT4_FEATURE_INCOMPAT_META_BG) + printf(" (meta_bg)"); + if (missing & EXT4_FEATURE_INCOMPAT_FLEX_BG) + printf(" (flex_bg)"); + if (missing & EXT4_FEATURE_INCOMPAT_MMP) + printf(" (mmp)"); + if (missing & EXT4_FEATURE_INCOMPAT_CSUM_SEED) + printf(" (csum_seed)"); + /* add more decodes here as you implement them */ + + printf("\n"); + printf(" Use an ext2/3/4 filesystem without these features\n"); + printf(" (for example: tune2fs -O ^64bit,^metadata_csum /dev/XXX\n"); + printf(" or create a small ext2/ext3 /boot partition).\n"); + } + return -1; + } + + /* + * Now check RO-compat features. + * In a read-only loader, unknown RO-compat features are theoretically + * safe, but some (e.g. metadata_csum) *do* affect on-disk layout, so + * we still reject unknown ones for now. + */ + missing = ro_compat & ~EXT4_FEATURE_RO_COMPAT_SUPP; + if (missing) { + if (!quiet) { + printf("ext2/4: unsupported RO_COMPAT features: 0x%08x", missing); + + if (missing & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) + printf(" (metadata_csum)"); + if (missing & EXT4_FEATURE_RO_COMPAT_BIGALLOC) + printf(" (bigalloc)"); + if (missing & EXT4_FEATURE_RO_COMPAT_QUOTA) + printf(" (quota)"); + /* add more decodes here as needed */ + + printf("\n"); + printf(" This aboot build cannot read this ext4 layout safely.\n"); + } + return -1; + } + +#ifdef DEBUG_EXT2 + if (!quiet) { + printf("ext2/4: features OK: compat=0x%08x ro_compat=0x%08x " + "incompat=0x%08x\n", + compat, ro_compat, incompat); + } +#endif + return 0; +} /* * Initialize an ext2 partition starting at offset P_OFFSET; this is @@ -54,6 +154,7 @@ static int ext2_mount(long cons_dev, long p_offset, long quiet) { long sb_block = 1; long sb_offset; + long gdt_start; int i; dev = cons_dev; @@ -82,23 +183,66 @@ static int ext2_mount(long cons_dev, long p_offset, long quiet) } return -1; } + /* + * ext4: group descriptor size. For classic ext2 this is either + * zero or 32. For ext4 with 64bit it is typically 64. + * + * We only care about the first sizeof(struct ext2_group_desc) + * bytes, which are laid out compatibly with ext2. + */ + group_desc_size = sb.s_desc_size; + if (group_desc_size == 0) + group_desc_size = sizeof(struct ext2_group_desc); + + if (group_desc_size < sizeof(struct ext2_group_desc)) { + printf("ext2/4: group descriptor size %u too small\n", + group_desc_size); + return -1; + } + if (ext4_check_features(quiet) < 0) + return -1; ngroups = (sb.s_blocks_count - sb.s_first_data_block + EXT2_BLOCKS_PER_GROUP(&sb) - 1) / EXT2_BLOCKS_PER_GROUP(&sb); + ext2fs.blocksize = EXT2_BLOCK_SIZE(&sb); + if (group_desc_size > ext2fs.blocksize) { + printf("ext2/4: group descriptor size %u > blocksize %u\n", + group_desc_size, ext2fs.blocksize); + return -1; + } + gds = malloc((size_t)(ngroups * sizeof(struct ext2_group_desc))); + if (!gds) { + printf("ext2: no memory for group descriptors\n"); + return -1; + } - ext2fs.blocksize = EXT2_BLOCK_SIZE(&sb); blkbuf = malloc(ext2fs.blocksize); iblkbuf = malloc(ext2fs.blocksize); diblkbuf = malloc(ext2fs.blocksize); /* read in the group descriptors (immediately follows superblock) */ - cons_read(dev, gds, ngroups * sizeof(struct ext2_group_desc), - partition_offset + - ext2fs.blocksize * (EXT2_MIN_BLOCK_SIZE/ext2fs.blocksize + 1)); + + gdt_start = partition_offset + + ext2fs.blocksize * + (EXT2_MIN_BLOCK_SIZE / ext2fs.blocksize + 1); + + for (i = 0; i < ngroups; i++) { + long off = gdt_start + (long)i * group_desc_size; + size_t toread = sizeof(struct ext2_group_desc); + + if (toread > group_desc_size) + toread = group_desc_size; + + if (cons_read(dev, &gds[i], toread, off) != (long)toread) { + printf("ext2/4: group descriptor %d read failed\n", i); + return -1; + } + } + /* * Calculate direct/indirect block limits for this file system * (blocksize dependent): @@ -299,50 +443,108 @@ static int ext2_blkno(struct ext2_inode *ip, int blkoff) static int ext4_breadi(struct ext2_inode *ip, long blkno, long nblks, char *buffer) { - long tot_bytes = 0; - - struct ext4_extent_header *hdr; - hdr = (struct ext4_extent_header *)&ip->i_block[0]; - - if (hdr->eh_magic != EXT4_EXT_MAGIC) { - printf("ext4_breadi: Extent header magic wrong.\n"); - return -1; - } - - if (hdr->eh_entries != 1) { - printf("ext4_breadi: Extent entries must be 1.\n"); - return -1; - } - - struct ext4_extent *ext; - ext = (struct ext4_extent *)&ip->i_block[3]; - - if (ext->ee_block != 0) { - printf("ext4_breadi: First logical block must be 0.\n"); - return -1; - } - - if (nblks > ext->ee_len) { - printf("ext4_breadi: Request nblks greater than extent len.\n"); - return -1; - } - - long ee_start = ((long)ext->ee_start_hi << 32) + ext->ee_start_lo; - - if (blkno + nblks > ext->ee_len) { - nblks = ext->ee_len - blkno; - } - - long offset = partition_offset + (ee_start + blkno) * ext2fs.blocksize; - long nbytes = nblks * ext2fs.blocksize; - - tot_bytes = cons_read(dev, buffer, nbytes, offset); - if (tot_bytes != nbytes) { - printf("ext4_breadi: cons_read failed.\n"); - return -1; - } - - return tot_bytes; + struct ext4_extent_header *hdr; + struct ext4_extent *ext_base; + int entries; + long cur_blk = blkno; /* logical block we are at */ + long blocks_left = nblks; /* blocks still to read */ + char *bufp = buffer; + long tot_bytes = 0; + + hdr = (struct ext4_extent_header *)&ip->i_block[0]; + + if (hdr->eh_magic != EXT4_EXT_MAGIC) { + printf("ext4_breadi: Extent header magic wrong.\n"); + return -1; + } + + /* For now we only handle leaf extents stored inline in the inode. */ + if (hdr->eh_depth != 0) { + printf("ext4_breadi: Extent tree depth %d not supported.\n", + hdr->eh_depth); + return -1; + } + + entries = hdr->eh_entries; + if (entries <= 0) { + printf("ext4_breadi: No extents.\n"); + return -1; + } + + /* --- honour file size like ext2_breadi() does --- */ + if ((blkno + nblks) * ext2fs.blocksize > ip->i_size) { + long maxblk = (ip->i_size + ext2fs.blocksize) / ext2fs.blocksize; + nblks = maxblk - blkno; + if (nblks <= 0) + return 0; /* nothing to read */ + } + cur_blk = blkno; + blocks_left = nblks; + + + /* Extents start right after the header in i_block[] */ + ext_base = (struct ext4_extent *)((char *)hdr + sizeof(*hdr)); + + while (blocks_left > 0) { + struct ext4_extent *ext = NULL; + int i; + + /* Find the extent that covers cur_blk */ + for (i = 0; i < entries; i++) { + struct ext4_extent *e = &ext_base[i]; + uint32_t first = e->ee_block; + uint32_t len = e->ee_len; + + if (len == 0) + continue; + + if ((uint32_t)cur_blk >= first && + (uint32_t)cur_blk < first + len) { + ext = e; + break; + } + } + + if (!ext) { + printf("ext4_breadi: logical block %ld not in any extent.\n", + cur_blk); + return -1; + } + + { + uint32_t first = ext->ee_block; + uint32_t len = ext->ee_len; + long within = cur_blk - first; /* offset inside this extent */ + long can_read = len - within; /* blocks available here */ + + if (can_read > blocks_left) + can_read = blocks_left; + + /* Calculate physical start block of this chunk */ + long ee_start = + ((long)ext->ee_start_hi << 32) + ext->ee_start_lo; + long phys_blk = ee_start + within; + + long offset = partition_offset + + phys_blk * ext2fs.blocksize; + long nbytes = can_read * ext2fs.blocksize; + long got; + + got = cons_read(dev, bufp, nbytes, offset); + if (got != nbytes) { + printf("ext4_breadi: cons_read failed (wanted %ld, got %ld).\n", + nbytes, got); + return -1; + } + + bufp += nbytes; + tot_bytes += nbytes; + cur_blk += can_read; + blocks_left -= can_read; + } + } + + return tot_bytes; } static int ext2_breadi(struct ext2_inode *ip, long blkno, long nblks, char *buffer) diff --git a/fs/xfs.c b/fs/xfs.c new file mode 100644 index 0000000..87a817c --- /dev/null +++ b/fs/xfs.c @@ -0,0 +1,1871 @@ +/* + * xfs.c - an implementation of the SGI XFS file system for aboot + * Jan-Jaap van der Heijden + * + * Based on fsys_xfs.c from GRUB -- GRand Unified Bootloader + * Copyright (C) 2001,2002,2004 Free Software Foundation, Inc. + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include "config.h" +#include "aboot.h" +#include "bootfs.h" +#include "cons.h" +#include "disklabel.h" +#include "utils.h" +#include "xfs.h" + +static int xfs_mount(long cons_dev, long p_offset, long quiet); +static int xfs_bread(int fd, long blkno, long nblks, char *buffer); +static int xfs_open(const char *dirname); +static void xfs_close(int fd); +static const char *xfs_readdir(int fd, int rewind); +static int xfs_fstat(int fd, struct stat* buf); +static char* first_dentry_dir3(xfs_ino_t *); +static char* first_dentry_dir2(xfs_ino_t *); +static char *next_dentry_dir3(xfs_ino_t *); +static char *next_dentry_dir2(xfs_ino_t *); +static char *next_dentry_local(xfs_ino_t *); +static xfs_ino_t xfs_dir2_sf_get_ino(xfs_dir2_sf_hdr_t *, xfs_dir2_inou_t *); + + +typedef struct { + uint32_t inumber_lo; + uint32_t inumber_hi; + uint8_t namelen; + uint8_t filetype; + uint8_t name[]; /* variable length */ +} xfs_dir3_data_entry_t; + +typedef union { + xfs_dir3_data_entry_t entry; + xfs_dir2_data_unused_t unused; +} xfs_dir3_data_union_t; + +struct bootfs xfsfs = { + FS_EXT2, + 0, + xfs_mount, + xfs_open, + xfs_bread, + xfs_close, + xfs_readdir, + xfs_fstat +}; + +typedef struct { + uint8_t namelen; + uint8_t offset; + char name[1]; /* name bytes follow */ +} xfs_dir3_sf_entry_t; + +static long dev = -1; +static long partition_offset; +static long filepos; +static long filemax; /* filelen */ + +static long xfs_read (void *buf, long len); + +#define isspace(c) ((c) == 0x10) + +/* + * Replacement for old Linux __swabXX() helpers. + * XFS metadata is big-endian on disk, Alpha CPU is little-endian. + */ +static inline uint16_t __swab16(uint16_t x) +{ + return (uint16_t)((x << 8) | (x >> 8)); +} + +static inline uint32_t __swab32(uint32_t x) +{ + return ((x & 0x000000FFU) << 24) | + ((x & 0x0000FF00U) << 8) | + ((x & 0x00FF0000U) >> 8) | + ((x & 0xFF000000U) >> 24); +} + +static inline uint64_t __swab64(uint64_t x) +{ + return ((uint64_t)__swab32((uint32_t)x) << 32) | + __swab32((uint32_t)(x >> 32)); +} + + +typedef struct xfs_dir2_leaf_entry { + uint32_t hashval; /* hash of name */ + uint32_t address; /* block/offset of data entry */ +} xfs_dir2_leaf_entry_t; + +typedef struct xfs_dir3_block_tail { + uint32_t bestcount; /* number of bestfree slots */ + uint32_t count; /* number of leaf entries */ + uint32_t stale; /* number of stale leaf entries */ +} xfs_dir3_block_tail_t; + + +static int +devread(long sector, long start, long length, void *buf) +{ + long pos = sector * SECT_SIZE; + pos += partition_offset + start; +#ifdef DEBUG_XFS2 + printf("Reading %ld bytes, starting at sector %ld, disk offset %ld\n", + length, sector, pos); +#endif + return cons_read(dev, buf, length, pos); +} + +#define MAXNAMELEN 256 +#define SECTOR_BITS 9 +#define MAX_LINK_COUNT 8 + +typedef struct xad { + xfs_fileoff_t offset; + xfs_fsblock_t start; + xfs_filblks_t len; +} xad_t; + +struct xfs_info { + int bsize; + int dirbsize; + int isize; + unsigned int agblocks; + int bdlog; + int blklog; + int inopblog; + int agblklog; + int agnolog; + unsigned int nextents; + xfs_daddr_t next; + xfs_daddr_t daddr; + xfs_dablk_t forw; + xfs_dablk_t dablk; + xfs_bmbt_rec_32_t *xt; + xfs_bmbt_ptr_t ptr0; + int btnode_ptr0_off; + int i8param; + int dirpos; + int dirmax; + int blkoff; + int fpos; + xfs_ino_t rootino; + xfs_ino_t new_ino; + int is_v5; + char* data_fork; +}; + + +static struct xfs_info xfs; + +/* On-disk v1/v2/v3 inode layout, used only to compute fork offsets. + * Matches current Linux struct xfs_dinode. Field order is important, + * but we only *use* di_version and the size/alignment. + */ +typedef struct xfs_dinode_disk { + uint16_t di_magic; /* XFS_DINODE_MAGIC */ + uint16_t di_mode; + uint8_t di_version; /* 1, 2 or 3 */ + uint8_t di_format; + uint16_t di_onlink; + uint32_t di_uid; + uint32_t di_gid; + uint32_t di_nlink; + uint16_t di_projid_lo; + uint16_t di_projid_hi; + uint8_t di_pad[6]; + uint16_t di_flushiter; + xfs_timestamp_t di_atime; + xfs_timestamp_t di_mtime; + xfs_timestamp_t di_ctime; + uint64_t di_size; + uint64_t di_nblocks; + uint32_t di_extsize; + uint32_t di_nextents; + uint16_t di_anextents; + uint8_t di_forkoff; + int8_t di_aformat; + uint32_t di_dmevmask; + uint16_t di_dmstate; + uint16_t di_flags; + uint32_t di_gen; + uint32_t di_next_unlinked; + + /* v3 (v5 filesystem) extra fields follow */ + uint32_t di_crc; + uint64_t di_changecount; + uint64_t di_lsn; + uint64_t di_flags2; + uint8_t di_pad2[16]; + xfs_timestamp_t di_crtime; + uint64_t di_ino; + uint8_t di_uuid[16]; +} xfs_dinode_disk_t; + + + +/* v5 (CRC-enabled) directory block header (48 bytes) */ +typedef struct xfs_dir3_blk_hdr { + uint32_t magic; /* XFS_DIR3_*_MAGIC */ + uint32_t crc; + uint64_t blkno; /* first block of the buffer */ + uint64_t lsn; /* sequence number of last write */ + uint8_t uuid[16]; /* filesystem we belong to */ + uint64_t owner; /* inode that owns the block */ +} xfs_dir3_blk_hdr_t; + +/* + * Full v5 directory data header (64 bytes). + * This is the v5 extension of xfs_dir2_data_hdr_t, so the + * bestfree[] array must be present and magic must still be + * at offset 0 (hdr.magic). + */ +typedef struct xfs_dir3_data_hdr { + xfs_dir3_blk_hdr_t hdr; /* 48 bytes */ + xfs_dir2_data_free_t bestfree[XFS_DIR2_DATA_FD_COUNT]; + uint32_t pad; /* 64-byte alignment */ +} xfs_dir3_data_hdr_t; + +#ifdef __alpha__ /* take care of alignment*/ + static long FSYS_BUF[32768/sizeof(long)]; + #define dirbuf ((long *)FSYS_BUF) + #define filebuf ((long *)FSYS_BUF + 4096/sizeof(long)) + #define inode ((xfs_dinode_t *)((long *)FSYS_BUF + 8192/sizeof(long))) +#else + static char FSYS_BUF[32768]; + #define dirbuf ((char *)FSYS_BUF) + #define filebuf ((char *)FSYS_BUF + 4096) + #define inode ((xfs_dinode_t *)((char *)FSYS_BUF + 8192)) +#endif + +#define icore (inode->di_core) +#define mask32lo(n) (((uint32_t)1 << (n)) - 1) + +#define XFS_INO_MASK(k) ((uint32_t)((1ULL << (k)) - 1)) +#define XFS_INO_OFFSET_BITS xfs.inopblog +#define XFS_INO_AGBNO_BITS xfs.agblklog +#define XFS_INO_AGINO_BITS (xfs.agblklog + xfs.inopblog) +#define XFS_INO_AGNO_BITS xfs.agnolog + + +/* Size of the core+next_unlinked for v1/v2 vs full inode for v3. */ +static inline int +xfs_dinode_size_disk(uint8_t version) +{ + if (version == 3) + return sizeof(xfs_dinode_disk_t); + /* v1/v2: data fork starts just before di_crc in the v3 layout */ + return offsetof(xfs_dinode_disk_t, di_crc); +} + +/* Pointer to the *data fork* (shortform dir / extents / etc). */ +static inline char * +xfs_dfork_dptr(void) +{ + xfs_dinode_disk_t *dip = (xfs_dinode_disk_t *)inode; + uint8_t version = dip->di_version; /* single byte, no swab needed */ + + return ((char *)dip) + xfs_dinode_size_disk(version); +} + +/* Pointer to shortform directory header. */ +static inline xfs_dir2_sf_t * +xfs_sf_dir(void) +{ + return (xfs_dir2_sf_t *)xfs_dfork_dptr(); +} + +/* Are we dealing with dir3-style shortform (v5 + ftype)? */ +static inline int +xfs_sf_is_dir3(void) +{ + /* Good enough approximation for aboot: v5 filesystem ⇒ dir3 shortform */ + return xfs.is_v5; /* or (xfs.is_v5 && icore.di_version >= 3) if you prefer */ +} + + +static inline xfs_agblock_t +agino2agbno (xfs_agino_t agino) +{ + return agino >> XFS_INO_OFFSET_BITS; +} + +static inline xfs_agnumber_t +ino2agno (xfs_ino_t ino) +{ + return ino >> XFS_INO_AGINO_BITS; +} + +static inline xfs_agino_t +ino2agino (xfs_ino_t ino) +{ + return ino & XFS_INO_MASK(XFS_INO_AGINO_BITS); +} + +static inline int +ino2offset (xfs_ino_t ino) +{ + return ino & XFS_INO_MASK(XFS_INO_OFFSET_BITS); +} + +/* XFS is big endian, alpha is little endian */ +#define le16(x) __swab16(x) +#define le32(x) __swab32(x) +#define le64(x) __swab64(x) + +static xfs_fsblock_t +xt_start (xfs_bmbt_rec_32_t *r) +{ + return (((xfs_fsblock_t)(le32 (r->l1) & mask32lo(9))) << 43) | + (((xfs_fsblock_t)le32 (r->l2)) << 11) | + (((xfs_fsblock_t)le32 (r->l3)) >> 21); +} + +static xfs_fileoff_t +xt_offset (xfs_bmbt_rec_32_t *r) +{ + return (((xfs_fileoff_t)le32 (r->l0) & + mask32lo(31)) << 23) | + (((xfs_fileoff_t)le32 (r->l1)) >> 9); +} + +static xfs_filblks_t +xt_len (xfs_bmbt_rec_32_t *r) +{ + return le32(r->l3) & mask32lo(21); +} + +static const char xfs_highbit[256] = { + -1, 0, 1, 1, 2, 2, 2, 2, /* 00 .. 07 */ + 3, 3, 3, 3, 3, 3, 3, 3, /* 08 .. 0f */ + 4, 4, 4, 4, 4, 4, 4, 4, /* 10 .. 17 */ + 4, 4, 4, 4, 4, 4, 4, 4, /* 18 .. 1f */ + 5, 5, 5, 5, 5, 5, 5, 5, /* 20 .. 27 */ + 5, 5, 5, 5, 5, 5, 5, 5, /* 28 .. 2f */ + 5, 5, 5, 5, 5, 5, 5, 5, /* 30 .. 37 */ + 5, 5, 5, 5, 5, 5, 5, 5, /* 38 .. 3f */ + 6, 6, 6, 6, 6, 6, 6, 6, /* 40 .. 47 */ + 6, 6, 6, 6, 6, 6, 6, 6, /* 48 .. 4f */ + 6, 6, 6, 6, 6, 6, 6, 6, /* 50 .. 57 */ + 6, 6, 6, 6, 6, 6, 6, 6, /* 58 .. 5f */ + 6, 6, 6, 6, 6, 6, 6, 6, /* 60 .. 67 */ + 6, 6, 6, 6, 6, 6, 6, 6, /* 68 .. 6f */ + 6, 6, 6, 6, 6, 6, 6, 6, /* 70 .. 77 */ + 6, 6, 6, 6, 6, 6, 6, 6, /* 78 .. 7f */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 80 .. 87 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 88 .. 8f */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 90 .. 97 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 98 .. 9f */ + 7, 7, 7, 7, 7, 7, 7, 7, /* a0 .. a7 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* a8 .. af */ + 7, 7, 7, 7, 7, 7, 7, 7, /* b0 .. b7 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* b8 .. bf */ + 7, 7, 7, 7, 7, 7, 7, 7, /* c0 .. c7 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* c8 .. cf */ + 7, 7, 7, 7, 7, 7, 7, 7, /* d0 .. d7 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* d8 .. df */ + 7, 7, 7, 7, 7, 7, 7, 7, /* e0 .. e7 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* e8 .. ef */ + 7, 7, 7, 7, 7, 7, 7, 7, /* f0 .. f7 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* f8 .. ff */ +}; + +static int +xfs_highbit32(uint32_t v) +{ + int i; + + if (v & 0xffff0000) + if (v & 0xff000000) + i = 24; + else + i = 16; + else if (v & 0x0000ffff) + if (v & 0x0000ff00) + i = 8; + else + i = 0; + else + return -1; + return i + xfs_highbit[(v >> i) & 0xff]; +} + +static int +isinxt (xfs_fileoff_t key, xfs_fileoff_t offset, xfs_filblks_t len) +{ + return (key >= offset) ? (key < offset + len ? 1 : 0) : 0; +} + +static xfs_daddr_t +agb2daddr (xfs_agnumber_t agno, xfs_agblock_t agbno) +{ + return ((xfs_fsblock_t)agno*xfs.agblocks + agbno) << xfs.bdlog; +} + +static xfs_daddr_t +fsb2daddr (xfs_fsblock_t fsbno) +{ + return agb2daddr ((xfs_agnumber_t)(fsbno >> xfs.agblklog), + (xfs_agblock_t)(fsbno & mask32lo(xfs.agblklog))); +} + +#undef offsetof +#define offsetof(t,m) ((int)&(((t *)0)->m)) + +static inline int +btroot_maxrecs (void) +{ + int tmp = icore.di_forkoff ? (icore.di_forkoff << 3) : xfs.isize; + + return (tmp - sizeof(xfs_bmdr_block_t) - offsetof(xfs_dinode_t, di_u)) / + (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t)); +} + + +/* Offsets of the data fork (literal area) in bytes. + * From XFS on-disk format documentation: + * - v2 inode: 0x64 (100) + * - v3 inode: 0xB0 (176) + */ +#define XFS_DINODE_DFORK_V2_OFF 100 +#define XFS_DINODE_DFORK_V3_OFF 176 + +static inline char * +xfs_dinode_data_fork(void) +{ + /* icore.di_version is already byte-swapped in the inode core */ + if (icore.di_version >= 3) + return (char *)inode + XFS_DINODE_DFORK_V3_OFF; + else + return (char *)inode + XFS_DINODE_DFORK_V2_OFF; +} + +/* Read inode from disk (v4/v5 compatible) */ + +static int +di_read (xfs_ino_t ino) +{ + xfs_agino_t agino; + xfs_agnumber_t agno; + xfs_agblock_t agbno; + xfs_daddr_t daddr; + int offset; + + /* ------------------------------- + * Locate the inode on disk + * ------------------------------- */ + agno = ino2agno (ino); + agino = ino2agino (ino); + agbno = agino2agbno (agino); + offset = ino2offset (ino); + daddr = agb2daddr (agno, agbno); + + /* Read the raw inode into FSYS_BUF-backed "inode" */ + devread (daddr, offset * xfs.isize, xfs.isize, (char *)inode); + +#ifdef DEBUG_XFS_2 + printf("di_read(): ino=%lu agno=%u agino=%u offset=%d isize=%d\n", + (unsigned long long) ino, + (unsigned int) agno, + (unsigned int) agino, + offset, + xfs.isize); + + printf("di_read(): magic=0x%x version=%d format=%d size=%lu flags=0x%x is_v5=%d\n", + le16(inode->di_core.di_magic), + inode->di_core.di_version, + inode->di_core.di_format, + (unsigned long long) le64(inode->di_core.di_size), + le16(inode->di_core.di_flags), + xfs.is_v5); +#endif + + /* ------------------------------- + * Basic sanity checks + * ------------------------------- */ + if (le16(inode->di_core.di_magic) != XFS_DINODE_MAGIC) { + printf("XFS: bad inode magic 0x%x for ino %lu\n", + le16(inode->di_core.di_magic), + (unsigned long long) ino); + return 0; + } + + /* v1 / v2 / v3 inodes are acceptable */ + if (inode->di_core.di_version != 1 && + inode->di_core.di_version != 2 && + inode->di_core.di_version != 3) { + printf("XFS: unsupported inode version %d for ino %lu\n", + inode->di_core.di_version, + (unsigned long long) ino); + return 0; + } + + /* + * NOTE: For v5 (di_version == 3), the fields we use: + * - di_format + * - di_forkoff + * - di_size + * - di_nextents + * - di_nblocks + * - timestamps, uid/gid, etc. + * are in the SAME places as for v1/v2 on disk. + * + * The extra v3 inode fields live after di_gen, so we do not + * need to special-case them here for our bootloader. + */ + + /* ------------------------------- + * Prime BTREE root pointer (ptr0) + * ------------------------------- + * init_extents() will: + * - for EXTENTS: use inode->di_u.di_bmx + * - for BTREE: follow xfs.ptr0, etc. + * + * xfs.ptr0 is only valid / needed if di_format == BTREE. + */ + if (inode->di_core.di_format == XFS_DINODE_FMT_BTREE) { + char *dfork = xfs_dfork_dptr(); /* start of data fork (v2/v3 aware) */ + + xfs.ptr0 = *(xfs_bmbt_ptr_t *)(dfork + + sizeof(xfs_bmdr_block_t) + + btroot_maxrecs() * sizeof(xfs_bmbt_key_t)); +#ifdef DEBUG_XFS + printf("di_read(): ino=%lu di_version=%u di_format=%u di_size=%lu di_nextents=%u is_v5=%d\n", + (unsigned long long)ino, + inode->di_core.di_version, + inode->di_core.di_format, + (unsigned long long)le64(inode->di_core.di_size), + le32(inode->di_core.di_nextents), + xfs.is_v5); +#endif + } + + return 1; +} + +/* On-disk header size (NOT sizeof the C struct) */ +static inline int +xfs_sf_hdr_size(uint8_t i8count) +{ + int size = 2; /* count + i8count */ + + if (i8count) + size += sizeof(xfs_dir2_ino8_t); + else + size += sizeof(xfs_dir2_ino4_t); + + return size; +} + +static inline xfs_dir2_sf_entry_t * +xfs_sf_firstentry(xfs_dir2_sf_t *sf) +{ + xfs_dir2_sf_hdr_t *hdr = &sf->hdr; + return (xfs_dir2_sf_entry_t *)((char *)hdr + xfs_sf_hdr_size(hdr->i8count)); +} + + + +static void +init_extents (void) +{ + xfs_bmbt_ptr_t ptr0; + xfs_btree_lblock_t h; + + switch (icore.di_format) { + case XFS_DINODE_FMT_EXTENTS: + /* Extent records start at the beginning of the data fork */ + //xfs.xt = (xfs_bmbt_rec_32_t *)xfs_dfork_dptr(); + //xfs.nextents = le32(icore.di_nextents); + xfs_dinode_disk_t *dip = (xfs_dinode_disk_t *)inode; + + /* This is what older XFS used for data extents (still correct + * on non-NREXT64 filesystems and v1/v2 inodes). + */ + uint32_t ne32 = le32(icore.di_nextents); + + /* On v5 filesystems with NREXT64, the 8 bytes where older + * layouts had (di_pad[6] + di_flushiter) are now a union that + * may contain di_big_nextents (64-bit data extent count). + * + * Our xfs_dinode_disk_t still has di_pad[6] + di_flushiter, + * but that's exactly the same 8-byte region, so we can just + * reinterpret it as a BE64 big extent counter. + */ + uint64_t be_big = 0; + memcpy(&be_big, dip->di_pad, sizeof(be_big)); /* di_pad[0..5] + di_flushiter */ + uint64_t big = le64(be_big); + + uint32_t ne; + if (big != 0) { + /* NREXT64 case: di_big_nextents is valid and non-zero. + * We only need 32 bits in the bootloader; directories on + * a boot partition won't have anywhere near 2^32 extents. + */ + ne = (uint32_t)big; + + } else { + /* Legacy case: use 32-bit di_nextents */ + ne = ne32; + } + xfs.xt = (xfs_bmbt_rec_32_t *)xfs_dfork_dptr(); + xfs.nextents = ne; + break; + case XFS_DINODE_FMT_BTREE: + ptr0 = xfs.ptr0; + for (;;) { + xfs.daddr = fsb2daddr (le64(ptr0)); + devread (xfs.daddr, 0, + sizeof(xfs_btree_lblock_t), (char *)&h); + if (!h.bb_level) { + xfs.nextents = le16(h.bb_numrecs); + xfs.next = fsb2daddr (le64(h.bb_rightsib)); + xfs.fpos = sizeof(xfs_btree_block_t); + return; + } + devread (xfs.daddr, xfs.btnode_ptr0_off, + sizeof(xfs_bmbt_ptr_t), (char *)&ptr0); + } + } +} + +static xad_t * +next_extent (void) +{ + static xad_t xad; + + switch (icore.di_format) { + case XFS_DINODE_FMT_EXTENTS: + if (xfs.nextents == 0) + return NULL; + break; + case XFS_DINODE_FMT_BTREE: + if (xfs.nextents == 0) { + xfs_btree_lblock_t h; + if (xfs.next == 0) + return NULL; + xfs.daddr = xfs.next; + devread (xfs.daddr, 0, sizeof(xfs_btree_lblock_t), (char *)&h); + xfs.nextents = le16(h.bb_numrecs); + xfs.next = fsb2daddr (le64(h.bb_rightsib)); + xfs.fpos = sizeof(xfs_btree_block_t); + } + /* Yeah, I know that's slow, but I really don't care */ + devread (xfs.daddr, xfs.fpos, sizeof(xfs_bmbt_rec_t), filebuf); + xfs.xt = (xfs_bmbt_rec_32_t *)filebuf; + xfs.fpos += sizeof(xfs_bmbt_rec_32_t); + } + xad.offset = xt_offset (xfs.xt); + xad.start = xt_start (xfs.xt); + xad.len = xt_len (xfs.xt); + ++xfs.xt; + --xfs.nextents; + + return &xad; +} + +/* + * Name lies - the function reads only first 100 bytes + */ +static void +xfs_dabread (void) +{ + xad_t *xad; + xfs_fileoff_t offset;; + + init_extents (); + while ((xad = next_extent ())) { + offset = xad->offset; + if (isinxt (xfs.dablk, offset, xad->len)) { + devread (fsb2daddr (xad->start + xfs.dablk - offset), + 0, 100, dirbuf); + break; + } + } +} + + +/* Get parent inode from shortform header (v4 & v5). */ + +static inline xfs_ino_t +sf_parent_ino(void) +{ + xfs_dir2_sf_t *sf = xfs_sf_dir(); + xfs_dir2_sf_hdr_t *hdr = &sf->hdr; + + return xfs_dir2_sf_get_ino(hdr, &hdr->parent); +} + + +static void debug_dump_sf_bytes(void) +{ + unsigned char *p = (unsigned char *)xfs_sf_dir(); + int i; + + printf("SF RAW DUMP (first 128 bytes):\n"); + + for (i = 0; i < 128; i++) { + printf("%02x ", p[i]); + if ((i % 16) == 15) + printf("\n"); + } + printf("\n"); +} +static inline int +roundup8 (int n) +{ + return ((n+7)&~7); +} + + +static int +xfs_count_dir3_entries(void) +{ + uint32_t magic; + int count = 0; + int has_ftype = xfs.is_v5; + xfs_dir3_block_tail_t tail; + int data_end; + + /* Start at beginning of block */ + filepos = 0; + xfs.blkoff = 0; + + /* Read block magic */ + if (xfs_read(&magic, sizeof(magic)) != sizeof(magic)) { + printf("xfs: failed to read dir3 block magic\n"); + return 0; + } + magic = le32(magic); + + if (magic != XFS_DIR3_DATA_MAGIC && magic != XFS_DIR3_BLOCK_MAGIC) { + printf("xfs: unsupported DIR3 block magic: 0x%x\n", magic); + return 0; + } + + /* Skip v5 data header */ + filepos = sizeof(xfs_dir3_data_hdr_t); + xfs.blkoff = filepos; + + /* --- Find block tail to know where the leaf area starts --- */ + { + long saved_pos = filepos; + long saved_off = xfs.blkoff; + + /* Tail is at end of block */ + filepos = xfs.dirbsize - sizeof(xfs_dir3_block_tail_t); + if (xfs_read(&tail, sizeof(tail)) != sizeof(tail)) { + printf("xfs: failed to read dir3 block tail\n"); + return 0; + } + + /* Convert endianness */ + uint32_t leaf_count = le32(tail.count); + uint32_t leaf_bytes = leaf_count * sizeof(xfs_dir2_leaf_entry_t); + + /* Leaf array starts just before the tail */ + data_end = xfs.dirbsize - sizeof(xfs_dir3_block_tail_t) - leaf_bytes; + + /* Restore position to start scanning data entries */ + filepos = saved_pos; + xfs.blkoff = saved_off; + } + + /* Walk entries only within [header .. data_end) */ + while (xfs.blkoff < data_end) { + xfs_dir2_data_union_t u; + + /* Read first 4 bytes of slot */ + if (xfs_read(&u, 4) != 4) + break; + + xfs.blkoff += 4; + + /* Free entry? */ + if (le16(u.unused.freetag) == XFS_DIR2_DATA_FREE_TAG) { + uint16_t len = le16(u.unused.length); + int skip = roundup8(len) - 4; /* 4 already read */ + if (skip < 0) skip = 0; + + filepos += skip; + xfs.blkoff += skip; + continue; + } + + /* Used entry: read remaining 4 bytes of inumber + namelen (5 bytes total) */ + if (xfs_read(((char *)&u) + 4, 5) != 5) + break; + + xfs.blkoff += 5; + + if (u.entry.namelen != 0) + count++; + + /* Compute full entry size */ + { + int base = 8 + 1 + u.entry.namelen + 2; + if (has_ftype) + base += 1; /* ftype byte */ + + int entsize = roundup8(base); + + /* We already consumed 9 bytes: 8 (ino) + 1 (namelen) */ + int skip = entsize - 9; + if (skip < 0) + skip = 0; + + filepos += skip; + xfs.blkoff += skip; + } + } + + /* Leave caller positioned at start of data region */ + filepos = sizeof(xfs_dir3_data_hdr_t); + xfs.blkoff = filepos; + + return count; +} + +static char * +next_dentry(xfs_ino_t *ino) +{ + +#ifdef DEBUG_XFS + printf("next_dentry(): di_format=%d dirpos=%d dirmax=%d is_v5=%d\n", + icore.di_format, xfs.dirpos, xfs.dirmax, xfs.is_v5); +#endif + /* Generic end-of-directory handling */ + if (icore.di_format == XFS_DINODE_FMT_LOCAL) { + /* shortform: dirpos includes -2 (.), -1 (..), then 0..dirmax-1 */ + if (xfs.dirpos >= xfs.dirmax) + return NULL; + } else { + /* block/leaf/btree formats */ + if (xfs.dirpos >= xfs.dirmax) { + + /* v5: we don’t chain leaf blocks yet → stop here */ + if (xfs.is_v5) { + return NULL; + } + + /* v4 (DIR2) leaf form: follow forward pointer */ + if (xfs.forw == 0) + return NULL; + + xfs.dablk = xfs.forw; + xfs_dabread(); + +#define h ((xfs_dir2_leaf_hdr_t *)dirbuf) + xfs.dirmax = le16(h->count) - le16(h->stale); + xfs.forw = le32(h->info.forw); +#undef h + xfs.dirpos = 0; + } + } + + switch (icore.di_format) { + case XFS_DINODE_FMT_LOCAL: + return next_dentry_local(ino); + + case XFS_DINODE_FMT_EXTENTS: + case XFS_DINODE_FMT_BTREE: + /* v4 → DIR2, v5 → DIR3 */ + return xfs.is_v5 ? next_dentry_dir3(ino) : next_dentry_dir2(ino); + + default: + printf("xfs: unsupported inode format %d\n", icore.di_format); + return NULL; + } +} + + +/* ------------------------------------------------------------ + * SHORTFORM DIRECTORY (LOCAL FORMAT) + * ------------------------------------------------------------ */ +static char * +first_dentry_local(xfs_ino_t *ino) +{ + xfs_dir2_sf_t *sf = xfs_sf_dir(); + xfs_dir2_sf_hdr_t *hdr = &sf->hdr; + + xfs.forw = 0; + xfs.dirmax = hdr->count; /* number of real entries (no . / ..) */ +#ifdef DEBUG_XFS + printf("sf->hdr.count=%d\n",xfs.dirmax); +#endif + /* Keep i8param for old helpers, though we no longer rely on it here. */ + xfs.i8param = hdr->i8count ? 0 : 4; + + /* + * dirpos: + * -2 = "." + * -1 = ".." + * 0 = first real entry in sf->list[] + */ + xfs.dirpos = -2; + +#ifdef DEBUG_XFS + printf("first_dentry(): ino=%lu version=%d format=%d size=%ld is_v5=%d\n", + (unsigned long long)*ino, + icore.di_version, + icore.di_format, + (long long)le64(icore.di_size), + xfs.is_v5); + printf("sf->hdr.count = %u\n", hdr->count); +#endif + + return next_dentry_local(ino); +} + + +static inline char * +xfs_sf_name(xfs_dir2_sf_entry_t *sfe) +{ + /* For both v4 and v5 shortform dirs, the name starts here. */ + return (char *)sfe->name; +} + + +/* Compute inumber for a shortform entry, from the same layout. */ + +static inline xfs_ino_t +xfs_sf_ino_from_entry(xfs_dir2_sf_entry_t *sfe) +{ + xfs_dir2_sf_t *sf = xfs_sf_dir(); + xfs_dir2_sf_hdr_t *hdr = &sf->hdr; + uint8_t *p; + xfs_ino_t ino = 0; + + /* Start at end of name */ + p = (uint8_t *)sfe->name + sfe->namelen; + + /* On v5/ftype=1 shortform dirs, name[namelen] is ftype, so skip it. */ + if (xfs.is_v5) + p++; + + if (hdr->i8count) { + /* 8-byte inode */ + memcpy(&ino, p, sizeof(xfs_dir2_ino8_t)); + ino = le64(ino) & 0x00ffffffffffffffULL; + } else { + uint32_t v32; + memcpy(&v32, p, sizeof(xfs_dir2_ino4_t)); + ino = le32(v32); + } + + return ino; +} + +/* Size of one shortform entry in a v4 (no ftype) shortform dir. + * + * On disk the layout is: + * [0] uint8_t namelen + * [1..] xfs_dir2_sf_off_t offset + * [..] name[namelen] + * [..] inode (4 or 8 bytes) + */ +/* v2 shortform entry size (no ftype stored) */ +static inline int +xfs_dir2_sf_entsize(xfs_dir2_sf_hdr_t *hdr, int namelen) +{ + int count = 0; + + count += 1; /* namelen */ + count += sizeof(xfs_dir2_sf_off_t); /* offset */ + count += namelen; /* name bytes */ + count += hdr->i8count + ? (int)sizeof(xfs_dir2_ino8_t) /* 8-byte inode (#s) */ + : (int)sizeof(xfs_dir2_ino4_t); /* 4-byte inode (#s) */ + + return count; +} +/* v5 shortform entry size: v2 + 1 byte for file type */ +static int +xfs_dir3_sf_entsize(xfs_dir2_sf_hdr_t *hdr, int namelen) +{ + return xfs_dir2_sf_entsize(hdr, namelen) + 1; +} + + + +/* Size of one shortform entry. + * + * On disk (v4): + * [0] u8 namelen + * [1..2] xfs_dir2_sf_off_t offset + * [3..] name[namelen] + * [..] inode (8 bytes for our purposes) + * + * On disk (v5 / ftype=1): + * [0] u8 namelen + * [1..2] xfs_dir2_sf_off_t offset + * [3..] name[namelen] + * [..] u8 ftype + * [..] inode (8 bytes) + */ + + +/* Version-agnostic helper */ +static inline int +xfs_sf_entsize(xfs_dir2_sf_hdr_t *hdr, int namelen) +{ + return xfs.is_v5 ? xfs_dir3_sf_entsize(hdr, namelen) + : xfs_dir2_sf_entsize(hdr, namelen); +} +/* Get parent inode from shortform header */ +static inline xfs_ino_t +sf_parent_ino_sf(void) +{ + xfs_dir2_sf_t *sf = xfs_sf_dir(); + xfs_dir2_sf_hdr_t *hdr = &sf->hdr; + uint8_t *p = (uint8_t *)&hdr->parent; + + if (hdr->i8count) + return (xfs_ino_t)le64(*(xfs_ino_t *)p); + else + return (xfs_ino_t)le32(*(uint32_t *)p); +} + + + +/* Decode an xfs_dir2_inou_t -> xfs_ino_t (v4/v5) */ +static inline xfs_ino_t +xfs_dir2_sf_get_ino(xfs_dir2_sf_hdr_t *hdr, xfs_dir2_inou_t *from) +{ + xfs_ino_t ino = 0; + + if (hdr->i8count) { + /* 64-bit inode stored big-endian in from->i8.i[8] */ + memcpy(&ino, &from->i8, sizeof(xfs_dir2_ino8_t)); + ino = le64(ino); /* swap from disk BE to CPU */ + ino &= 0x00ffffffffffffffULL; /* top byte reserved */ + } else { + uint32_t v = 0; + memcpy(&v, &from->i4, sizeof(xfs_dir2_ino4_t)); + ino = le32(v); + } + return ino; +} + +static char * +next_dentry_local(xfs_ino_t *ino) +{ + static xfs_dir2_sf_entry_t *sfe; + static char sf_name_buf[256]; /* XFS max namelen is 255 */ + + xfs_dir2_sf_t *sf = xfs_sf_dir(); + xfs_dir2_sf_hdr_t *hdr = &sf->hdr; + char *name = NULL; + int namelen; + + if (xfs.dirpos >= xfs.dirmax && xfs.dirpos >= 0) + return NULL; + + switch (xfs.dirpos) { + case -2: /* "." (synthetic) */ + *ino = 0; /* or current inode, depending on your design */ + name = "."; + namelen = 1; + break; + + case -1: /* ".." (from header parent) */ + *ino = sf_parent_ino(); /* using hdr + i8count */ + name = ".."; + namelen = 2; + + /* First on-disk entry starts here */ + sfe = xfs_sf_firstentry(sf); + break; + + default: + namelen = sfe->namelen; + if (namelen > 255) + namelen = 255; + + *ino = xfs_sf_ino_from_entry(sfe); + + memcpy(sf_name_buf, xfs_sf_name(sfe), namelen); + sf_name_buf[namelen] = '\0'; + name = sf_name_buf; + + /* Advance to the next shortform entry */ + sfe = (xfs_dir2_sf_entry_t *)((char *)sfe + + xfs_sf_entsize(hdr, sfe->namelen)); + break; + } + + xfs.dirpos++; + return name; +} + +static char * +first_dentry_dir3(xfs_ino_t *ino) +{ + xfs.forw = 0; + xfs.dirpos = 0; + + /* Count entries and position at first DIR3 data entry */ + xfs.dirmax = xfs_count_dir3_entries(); + if (xfs.dirmax <= 0) + return NULL; + + return next_dentry_dir3(ino); +} + + + +static char * +next_dentry_dir3(xfs_ino_t *ino) +{ + int has_ftype = xfs.is_v5; /* ftype present on your fs */ + +#define d2u ((xfs_dir2_data_union_t *)dirbuf) + + for (;;) { + /* At end of this DIR3 data block? Go to next block. */ + if (xfs.blkoff >= xfs.dirbsize) { + uint32_t magic; + + /* Align filepos down to the start of this block. */ + filepos &= ~(xfs.dirbsize - 1); + + /* Read block magic. */ + if (xfs_read(&magic, sizeof(magic)) != sizeof(magic)) { + printf("xfs: read error in DIR3 block header\n"); + return NULL; + } + magic = le32(magic); + + if (magic != XFS_DIR3_DATA_MAGIC && + magic != XFS_DIR3_BLOCK_MAGIC) { + printf("xfs: invalid DIR3 block magic: 0x%x\n", magic); + return NULL; + } + + /* Skip full v5 header. */ + xfs.blkoff = sizeof(xfs_dir3_data_hdr_t); + filepos |= xfs.blkoff; + } + /* Stop when we reach the tail */ +if (xfs.blkoff + sizeof(xfs_dir2_block_tail_t) >= xfs.dirbsize) + return NULL; + +/* Read first 4 bytes of the next slot */ +if (xfs_read(dirbuf, 4) != 4) { + /* This is end-of-dir, not an error */ + return NULL; +} + xfs.blkoff += 4; + + /* Free entry? */ + if (le16(d2u->unused.freetag) == XFS_DIR2_DATA_FREE_TAG) { + uint16_t len = le16(d2u->unused.length); + int skip = roundup8(len) - 4; /* 4 already read */ + + if (skip < 0) + skip = 0; + + filepos += skip; + xfs.blkoff += skip; + continue; /* look at next slot */ + } + + /* Used entry found. */ + break; + } + + /* Read rest of fixed header: remaining 4 bytes of inumber + namelen. */ + if (xfs_read((char *)dirbuf + 4, 5) != 5) { + printf("xfs: short read in dir3 entry header\n"); + return NULL; + } + xfs.blkoff += 5; + + *ino = le64(d2u->entry.inumber); + int namelen = d2u->entry.namelen; + + /* Skip bogus entries. */ + if (*ino == 0 || namelen == 0) { + xfs.dirpos++; + return next_dentry_dir3(ino); + } + + /* Compute remaining size of this entry. */ + { + int base = 8 + 1 + namelen + 2; + if (has_ftype) + base += 1; + + int entsize = roundup8(base); + int toread = entsize - 9; /* 9 bytes already consumed */ + + if (toread < 0) + toread = 0; + + if (toread && xfs_read(dirbuf, toread) != toread) { + printf("xfs: short read in dir3 filename\n"); + return NULL; + } + + xfs.blkoff += toread; + } + + /* dirbuf now starts with the filename bytes. */ + char *name = (char *)dirbuf; + name[namelen] = 0; /* overwrite filetype / padding / tag byte */ + + xfs.dirpos++; + +#undef d2u + return name; +} + + +/* + * Initialize reading entries from a classic XFS DIR2 directory + * (XFS v4, crc=0). Handles: + * - block format + * - leaf1 / leafn format + * + * Returns the first entry via next_dentry_dir2(). + */ +static char * +first_dentry_dir2(xfs_ino_t *ino) +{ + xfs.forw = 0; + filepos = 0; + +#ifdef DEBUG_XFS_2 + printf("first_dentry_dir2(): starting (di_format=%d)\n", + icore.di_format); +#endif + + /* ---------------------------------------------------------- + * SHORTFORM (small) directory + * ---------------------------------------------------------- */ + if (icore.di_format == XFS_DINODE_FMT_LOCAL) + { + xfs.dirmax = inode->di_u.di_dir2sf.hdr.count; + xfs.i8param = inode->di_u.di_dir2sf.hdr.i8count ? 0 : 4; + xfs.dirpos = -2; /* ".", "..", then real entries */ + +#ifdef DEBUG_XFS_2 + printf("DIR2 shortform: dirmax=%d i8param=%d\n", + xfs.dirmax, xfs.i8param); +#endif + return next_dentry_dir2(ino); + } + + /* ---------------------------------------------------------- + * EXTENTS or BTREE directory + * Read first data block header + * ---------------------------------------------------------- */ + if (xfs_read(dirbuf, sizeof(xfs_dir2_data_hdr_t)) != sizeof(xfs_dir2_data_hdr_t)) { + printf("xfs: failed to read dir2 data header\n"); + return NULL; + } + + uint32_t magic = le32(((xfs_dir2_data_hdr_t *)dirbuf)->magic); + +#ifdef DEBUG_XFS_2 + printf("DIR2 header magic = 0x%x\n", magic); +#endif + + /* ---------------------------------------------------------- + * CASE 1: BLOCK-FORMAT DIRECTORY (single block) + * ---------------------------------------------------------- */ + if (magic == XFS_DIR2_BLOCK_MAGIC) + { + xfs_dir2_block_tail_t *tail; + + /* Tail is located at end of block */ + filepos = xfs.dirbsize - sizeof(*tail); + + if (xfs_read(dirbuf, sizeof(*tail)) != sizeof(*tail)) { + printf("xfs: failed to read dir2 block tail\n"); + return NULL; + } + + tail = (xfs_dir2_block_tail_t *)dirbuf; + xfs.dirmax = le32(tail->count) - le32(tail->stale); + +#ifdef DEBUG_XFS_2 + printf("DIR2 block-format: dirmax=%d\n", xfs.dirmax); +#endif + } + + /* ---------------------------------------------------------- + * CASE 2: LEAF1 / LEAFN directory + * ---------------------------------------------------------- */ + else + { + /* Starting dablk for leaf / node search, per XFS rules */ + xfs.dablk = (1ULL << 35) >> xfs.blklog; + + for (;;) + { + xfs_dabread(); + + xfs_dir2_leaf_hdr_t *lh = (xfs_dir2_leaf_hdr_t *)dirbuf; + xfs_da_intnode_t *n = (xfs_da_intnode_t *)dirbuf; + + uint16_t m = le16(n->hdr.info.magic); + +#ifdef DEBUG_XFS_2 + printf("DIR2 leaf/node scan: magic=0x%x\n", m); +#endif + + if (m == XFS_DIR2_LEAF1_MAGIC || m == XFS_DIR2_LEAFN_MAGIC) + { + xfs.dirmax = le16(lh->count) - le16(lh->stale); + xfs.forw = le32(lh->info.forw); + +#ifdef DEBUG_XFS_2 + printf("DIR2 leaf-format: dirmax=%d forw=%u\n", + xfs.dirmax, xfs.forw); +#endif + break; + } + + /* Follow B-tree "before" pointer */ + xfs.dablk = le32(n->btree[0].before); + } + } + + /* -------------------------------------- + * Initialize data-block scan position + * -------------------------------------- */ + xfs.blkoff = sizeof(xfs_dir2_data_hdr_t); + filepos = xfs.blkoff; + xfs.dirpos = 0; + +#ifdef DEBUG_XFS_2 + printf("DIR2 start: blkoff=%d dirpos=%d dirmax=%d\n", + xfs.blkoff, xfs.dirpos, xfs.dirmax); +#endif + + return next_dentry_dir2(ino); +} + +static char * +next_dentry_dir2(xfs_ino_t *ino) +{ + int namelen; + int toread; + char *name; + +#define dau ((xfs_dir2_data_union_t *)dirbuf) + + /* Loop until we find a valid (non-free) entry */ + for (;;) { + + /* -------------------------------------------------------- + * If we hit the end of a DIR2 block, move to the next one + * -------------------------------------------------------- */ + if (xfs.blkoff >= xfs.dirbsize) { + + uint32_t magic; + + /* Align filepos to start of new block */ + filepos &= ~(xfs.dirbsize - 1); + + /* Read block magic */ + if (xfs_read(&magic, sizeof(magic)) != sizeof(magic)) { + printf("xfs: read error in DIR2 block header\n"); + return NULL; + } + magic = le32(magic); + + /* Valid DIR2 magic values */ + if (magic != XFS_DIR2_DATA_MAGIC && + magic != XFS_DIR2_BLOCK_MAGIC) + { + printf("xfs: invalid DIR2 block magic: 0x%x\n", magic); + return NULL; + } + + /* Reset pointer just after header */ + xfs.blkoff = sizeof(xfs_dir2_data_hdr_t); + filepos |= xfs.blkoff; + } + + /* -------------------------------------------------------- + * Read first 4 bytes of entry + * free-entry: dau->unused.freetag == FREE_TAG + * used-entry: beginning of inode number (LSB) + * -------------------------------------------------------- */ + if (xfs_read(dirbuf, 4) != 4) { + printf("xfs: short read in dir2 entry\n"); + return NULL; + } + xfs.blkoff += 4; + + /* Free entry? */ + if (dau->unused.freetag == XFS_DIR2_DATA_FREE_TAG) { + + uint32_t len = le16(dau->unused.length); + + /* Length includes the 4 bytes we already consumed */ + toread = roundup8(len) - 4; + + xfs.blkoff += toread; + filepos += toread; + + continue; /* skip free entry */ + } + + /* Otherwise this is a DIR2 data entry */ + break; + } + + /* -------------------------------------------------------- + * Read rest of entry header (5 bytes): + * - namelen (1) + * - name[0] (1) + * - tag (2) + * - padding (1) + * -------------------------------------------------------- */ + if (xfs_read((char *)dirbuf + 4, 5) != 5) { + printf("xfs: short read in dir2 entry header\n"); + return NULL; + } + + *ino = le64(dau->entry.inumber); + namelen = dau->entry.namelen; + + /* Skip bogus entries */ + if (*ino == 0 || namelen == 0) { + xfs.dirpos++; + return next_dentry_dir2(ino); + } + + /* -------------------------------------------------------- + * Read filename + padding + * -------------------------------------------------------- */ + toread = roundup8(namelen + 11) - 9; + + if (xfs_read(dirbuf, toread) != toread) { + printf("xfs: short read in dir2 filename\n"); + return NULL; + } + + name = (char *)dirbuf; + name[namelen] = 0; + + /* Advance */ + xfs.blkoff += toread + 5; + xfs.dirpos++; + +#undef dau + + return name; +} + +/* Dispatcher for first directory entry. + * Chooses between: + * - local directory (shortform) + * - dir2 block/leaf formats (XFS v4) + * - dir3 block/leaf formats (XFS v5) + */ + + +static char * +first_dentry(xfs_ino_t *ino) +{ + xfs.forw = 0; +#ifdef DEBUG_XFS + printf("first_dentry(): ino=%lu version=%d format=%d size=%lu is_v5=%d\n", + (unsigned long long)*ino, + inode->di_core.di_version, + inode->di_core.di_format, + (unsigned long long) le64(inode->di_core.di_size), + xfs.is_v5); +#endif + switch (icore.di_format) + { + /* ---------------------------------------------------- + * SHORTFORM (LOCAL) DIRECTORIES + * ---------------------------------------------------- */ + case XFS_DINODE_FMT_LOCAL: + return first_dentry_local(ino); + + /* ---------------------------------------------------- + * BLOCK / LEAF / B-TREE DIRECTORIES + * ---------------------------------------------------- */ + case XFS_DINODE_FMT_EXTENTS: + case XFS_DINODE_FMT_BTREE: + { + uint32_t magic; + + filepos = 0; + + /* Read the directory block header magic */ + if (xfs_read(&magic, sizeof(magic)) != sizeof(magic)) { + printf("xfs: failed to read directory block header\n"); + return NULL; + } + magic = le32(magic); + +#ifdef DEBUG_XFS_2 + printf("first_dentry(): directory block magic = 0x%x\n", magic); +#endif + + /* ----------------------------- + * DIR3 (XFS v5 CRC-enabled) + * ----------------------------- */ + if (magic == XFS_DIR3_DATA_MAGIC || + magic == XFS_DIR3_BLOCK_MAGIC || + magic == XFS_DIR3_LEAFN_MAGIC || + magic == XFS_DIR3_LEAF1_MAGIC) + { + return first_dentry_dir3(ino); + } + + /* ----------------------------- + * DIR2 (Classic XFS v4) + * ----------------------------- */ + if (magic == XFS_DIR2_DATA_MAGIC || + magic == XFS_DIR2_BLOCK_MAGIC || + magic == XFS_DIR2_LEAFN_MAGIC || + magic == XFS_DIR2_LEAF1_MAGIC) + { + return first_dentry_dir2(ino); + } + + printf("xfs: unsupported directory magic 0x%x\n", magic); + return NULL; + } + + default: + printf("xfs: unsupported inode format %d\n", icore.di_format); + return NULL; + } +} + + + +/* + * Initialize an XFS partition starting at offset P_OFFSET; this is + * sort-of the same idea as "mounting" it. Read in the relevant + * control structures and make them available to the user. Returns 0 + * if successful, -1 on failure. + */ +static int +xfs_mount(long cons_dev, long p_offset, long quiet) +{ + xfs_sb_t super; + + partition_offset = p_offset; + + if (cons_read (cons_dev, &super, sizeof(super), partition_offset) != sizeof(super)) { + printf("xfs_mount: read_disk_block failed!\n"); + return -1; + } else if (le32(super.sb_magicnum) != XFS_SB_MAGIC) { + printf("xfs_mount: Bad magic: %x\n", super.sb_magicnum); + return -1; + } else { + unsigned int ver = le16(super.sb_versionnum) & XFS_SB_VERSION_NUMBITS; + + /* Accept XFS v4 and v5 */ + if (ver == XFS_SB_VERSION_5) { +#ifdef DEBUG_XFS + printf("xfs_mount: Detected XFS v5 filesystem (CRC-enabled)\n"); +#endif + xfs.is_v5 = 1; /* new flag */ + } + else if (ver == XFS_SB_VERSION_4) { + xfs.is_v5 = 0; + } + else { + printf("xfs_mount: unsupported XFS version %u\n", ver); + return -1; + } + + + } + + xfs.bsize = le32 (super.sb_blocksize); + xfs.blklog = super.sb_blocklog; + xfs.bdlog = xfs.blklog - SECTOR_BITS; + xfs.rootino = le64 (super.sb_rootino); + xfs.isize = le16 (super.sb_inodesize); + xfs.agblocks = le32 (super.sb_agblocks); + xfs.dirbsize = xfs.bsize << super.sb_dirblklog; + + xfs.inopblog = super.sb_inopblog; + xfs.agblklog = super.sb_agblklog; + xfs.agnolog = xfs_highbit32 (le32(super.sb_agcount)); + + xfs.btnode_ptr0_off = + ((xfs.bsize - sizeof(xfs_btree_block_t)) / + (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t))) + * sizeof(xfs_bmbt_key_t) + sizeof(xfs_btree_block_t); + +#ifdef DEBUG_XFS + printf("XFS: version = %d\n",le16(super.sb_versionnum) & XFS_SB_VERSION_NUMBITS); + printf("XFS: blocksize = %d\n",xfs.bsize); +#endif + dev = cons_dev; + xfsfs.blocksize = xfs.bsize; + return 0; +} + +static long +xfs_read (void *buf, long len) +{ + xad_t *xad; + xfs_fileoff_t endofprev, endofcur, offset; + xfs_filblks_t xadlen; + int toread, startpos, endpos; + + if (icore.di_format == XFS_DINODE_FMT_LOCAL) { + char *dptr = xfs_dfork_dptr(); /* start of literal data/shortform dir */ + memmove(buf, dptr + filepos, len); + filepos += len; + return len; + } + startpos = filepos; + endpos = filepos + len; + endofprev = (xfs_fileoff_t)-1; + init_extents (); + while (len > 0 && (xad = next_extent ())) { + offset = xad->offset; + xadlen = xad->len; + if (isinxt (filepos >> xfs.blklog, offset, xadlen)) { + endofcur = (offset + xadlen) << xfs.blklog; + toread = (endofcur >= endpos) + ? len : (endofcur - filepos); + devread (fsb2daddr (xad->start), + filepos - (offset << xfs.blklog), toread, buf); + buf += toread; + len -= toread; + filepos += toread; + } else if (offset > endofprev) { + toread = ((offset << xfs.blklog) >= endpos) + ? len : ((offset - endofprev) << xfs.blklog); + len -= toread; + filepos += toread; + for (; toread; toread--) { + *(char*)buf++ = 0; + } + continue; + } + endofprev = offset + xadlen; + } + + return filepos - startpos; +} + +/* + * Read block number "blkno". + */ + +static int +xfs_bread(int fd, long blkno, long nblks, char *buffer) +{ + if (fd == 1) { + /* + * Duh. XFS doesn't read past EOF + * aboot does just that by trying to read nblks*blksize, + * where nblks*blksize > filesize + */ + memset(buffer,0,nblks*xfs.bsize); + long nbytes = xfs_read(buffer, nblks*xfs.bsize); + if (nbytes == le64(icore.di_size)) + return nblks*xfs.bsize; + return (int)nbytes; + } + printf("XFS error: bad file descriptor!\n"); + return -1; +} + +/* + * Unix-like open routine. Returns a small integer + * (does not care what file, we say it's OK) + */ +static int xfs_open(const char *dirname) +{ + xfs_ino_t ino, parent_ino; + xfs_fsize_t di_size; + int di_mode; + int cmp, n, link_count; + char linkbuf[xfs.bsize]; + char *rest, *name, ch; + char namebuf[MAXNAMELEN]; + strncpy(namebuf,dirname,MAXNAMELEN); + char *filename = namebuf; + + +#ifdef DEBUG_XFS + printf("xfs_open(): filename = %s\n", filename); +#endif + + + parent_ino = ino = xfs.rootino; + link_count = 0; + for (;;) { + di_read (ino); + di_size = le64 (icore.di_size); + di_mode = le16 (icore.di_mode); + +#ifdef DEBUG_XFS + printf("xfs_open(): di_mode = %o\n", di_mode); +#endif + if ((di_mode & IFMT) == IFLNK) { + if (++link_count > MAX_LINK_COUNT) { + printf("XFS error: symlink loop!\n"); + return 0; + } + if (di_size < xfs.bsize - 1) { + filepos = 0; + filemax = di_size; + n = xfs_read (linkbuf, filemax); + } else { + printf("XFS error: bad file length!\n"); + return 0; + } + + ino = (linkbuf[0] == '/') ? xfs.rootino : parent_ino; + while (n < (xfs.bsize - 1) && (linkbuf[n++] = *filename++)); + linkbuf[n] = 0; + filename = linkbuf; + continue; + } + + if (!*filename || isspace (*filename)) { + if (((di_mode & IFMT) != IFREG) + && ((di_mode & IFMT) != IFDIR)) { + printf("XFS error: bad file type!\n"); + return 0; + } + filepos = 0; + filemax = di_size; + return 1; + } + + if ((di_mode & IFMT) != IFDIR) { + printf("XFS error: bad file type!\n"); + return 0; + } + + for (; *filename == '/'; filename++); + + if (!strcmp(filename,"")) { + filepos = 0; + filemax = 0; + return 1; + } + + for (rest = filename; (ch = *rest) && !isspace (ch) && ch != '/'; rest++); + *rest = 0; +#ifdef DEBUG_XFS + printf("xfs_open(): looking for '%s' in current directory\n", filename); +#endif + + name = first_dentry (&xfs.new_ino); + for (;;) { +#ifdef DEBUG_XFS + if (name) + printf("xfs_open(): candidate='%s' looking_for='%s'\n", + name, filename); + else + printf("xfs_open(): candidate is NULL while looking_for='%s'\n", + filename); + + +#endif + cmp = (!*filename) ? -1 : strcmp (filename, name); + if (cmp == 0) { + parent_ino = ino; + if (xfs.new_ino) + ino = xfs.new_ino; + *(filename = rest) = ch; + break; + } + name = next_dentry (&xfs.new_ino); + if (name == NULL) { + *rest = ch; + return -1; + } + } + } +} + +/* + * Only one file is opened at any time, so close is a nop. + */ +static void xfs_close(int fd) +{ +} + +/* + * Return the next directory entry. + * Must have opened the directory with xfs_open() + */ +static const char * +xfs_readdir(int fd, int rewind) +{ +#ifdef DEBUG_XFS + printf("xfs_readdir(): fd=%d rewind=%d\n", fd, rewind); +#endif + + if (fd != 1) + return NULL; + if ((le16 (icore.di_mode) & IFMT) != IFDIR) { + printf("Not a directory!\n"); + return NULL; + } + if (rewind) { +#ifdef DEBUG_XFS + printf("xfs_readdir(): calling first_dentry()\n"); +#endif + return first_dentry (&xfs.new_ino); + } +#ifdef DEBUG_XFS + printf("xfs_readdir(): calling next_dentry()\n"); +#endif + return next_dentry (&xfs.new_ino); +} + +/* + * Get file status + */ +static int +xfs_fstat(int fd, struct stat* buf) +{ + if (fd != 1) + return -1; + + memset(buf, 0, sizeof(struct stat)); + buf->st_mode = le16(icore.di_mode); + //buf->st_flags = le16(icore.di_flags); + buf->st_nlink = le16(icore.di_onlink); + buf->st_uid = le32(icore.di_uid); + buf->st_gid = le32(icore.di_gid); + buf->st_size = le64(icore.di_size); + buf->st_blocks = le64(icore.di_nblocks); + buf->st_atime = le32(icore.di_atime.t_sec); + buf->st_mtime = le32(icore.di_mtime.t_sec); + buf->st_ctime = le32(icore.di_ctime.t_sec); + return 0; +} + + diff --git a/fs/xfs.h b/fs/xfs.h new file mode 100644 index 0000000..7f65e09 --- /dev/null +++ b/fs/xfs.h @@ -0,0 +1,566 @@ +/* + * xfs.h - an extraction from xfsprogs-1.3.5/include/xfs* into one file + * + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +typedef uint64_t xfs_ino_t; +typedef uint32_t xfs_agino_t; +typedef int64_t xfs_daddr_t; +typedef int64_t xfs_off_t; +typedef uint8_t uuid_t[16]; + + +/* those are from xfs_types.h */ + +typedef uint32_t xfs_agblock_t; /* blockno in alloc. group */ +typedef uint32_t xfs_extlen_t; /* extent length in blocks */ +typedef uint32_t xfs_agnumber_t; /* allocation group number */ +typedef int32_t xfs_extnum_t; /* # of extents in a file */ +typedef int16_t xfs_aextnum_t; /* # extents in an attribute fork */ +typedef int64_t xfs_fsize_t; /* bytes in a file */ + +typedef uint32_t xfs_dablk_t; /* dir/attr block number (in file) */ +typedef uint32_t xfs_dahash_t; /* dir/attr hash value */ + +/* + * Disk based types: + */ +typedef uint64_t xfs_dfsbno_t; /* blockno in filesystem (agno|agbno) */ +typedef uint64_t xfs_drfsbno_t; /* blockno in filesystem (raw) */ +typedef uint64_t xfs_drtbno_t; /* extent (block) in realtime area */ +typedef uint64_t xfs_dfiloff_t; /* block number in a file */ + +typedef uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */ +typedef uint64_t xfs_fileoff_t; /* block number in a file */ +typedef uint64_t xfs_filblks_t; /* number of blocks in a file */ + +#define XFS_DIR3_DATA_MAGIC 0x58444433 /* 'XDD3' - dir3 data (v5) */ +#define XFS_DIR2_BLOCK_MAGIC 0x58444242 /* 'XDBB' - dir2 block dirs */ +#define XFS_DIR3_BLOCK_MAGIC 0x58444233 /* 'XDB3' - dir3 block dirs */ +#define XFS_DIR2_DATA_MAGIC 0x58443244 /* 'XD2D' - dir2 data (v4) */ + +/* XFS directory block magic numbers */ + +/* ----- DIR2 (classic, v4) ----- */ +#define XFS_DIR2_LEAF1_MAGIC 0xd2f1 /* leaf block (version 2), type 1 */ +#define XFS_DIR2_LEAFN_MAGIC 0xd2ff /* leaf block (version 2), node type */ + +/* ----- DIR3 (CRC-enabled, v5) ----- */ +#define XFS_DIR3_LEAF1_MAGIC 0x3df1 /* leaf block (version 3), type 1 */ +#define XFS_DIR3_LEAFN_MAGIC 0x3dff /* leaf block (version 3), node type */ + +#define XFS_SB_MAGIC 0x58465342 /* 'XFSB'*/ +#define XFS_SB_VERSION_1 1 /* 5.3, 6.0.1, 6.1 */ +#define XFS_SB_VERSION_2 2 /* 6.2 - attributes */ +#define XFS_SB_VERSION_3 3 /* 6.2 - new inode version */ +#define XFS_SB_VERSION_4 4 /* 6.2+ - bitmask version */ +#define XFS_SB_VERSION_5 5 /* CRC enabled filesystem */ +#define XFS_SB_VERSION_NUMBITS 0x000f +#define XFS_SB_VERSION_ALLFBITS 0xfff0 +#define XFS_SB_VERSION_ATTRBIT 0x0010 +#define XFS_SB_VERSION_NLINKBIT 0x0020 +#define XFS_SB_VERSION_QUOTABIT 0x0040 +#define XFS_SB_VERSION_ALIGNBIT 0x0080 +#define XFS_SB_VERSION_DALIGNBIT 0x0100 +#define XFS_SB_VERSION_SHAREDBIT 0x0200 +#define XFS_SB_VERSION_LOGV2BIT 0x0400 +#define XFS_SB_VERSION_SECTORBIT 0x0800 +#define XFS_SB_VERSION_EXTFLGBIT 0x1000 +#define XFS_SB_VERSION_DIRV2BIT 0x2000 +#define XFS_SB_VERSION_BORGBIT 0x4000 /* ASCII only case-insens. */ +#define XFS_SB_VERSION_MOREBITSBIT 0x8000 +typedef struct xfs_sb +{ + uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */ + uint32_t sb_blocksize; /* logical block size, bytes */ + xfs_drfsbno_t sb_dblocks; /* number of data blocks */ + xfs_drfsbno_t sb_rblocks; /* number of realtime blocks */ + xfs_drtbno_t sb_rextents; /* number of realtime extents */ + uuid_t sb_uuid; /* file system unique id */ + xfs_dfsbno_t sb_logstart; /* starting block of log if internal */ + xfs_ino_t sb_rootino; /* root inode number */ + xfs_ino_t sb_rbmino; /* bitmap inode for realtime extents */ + xfs_ino_t sb_rsumino; /* summary inode for rt bitmap */ + xfs_agblock_t sb_rextsize; /* realtime extent size, blocks */ + xfs_agblock_t sb_agblocks; /* size of an allocation group */ + xfs_agnumber_t sb_agcount; /* number of allocation groups */ + xfs_extlen_t sb_rbmblocks; /* number of rt bitmap blocks */ + xfs_extlen_t sb_logblocks; /* number of log blocks */ + uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */ + uint16_t sb_sectsize; /* volume sector size, bytes */ + uint16_t sb_inodesize; /* inode size, bytes */ + uint16_t sb_inopblock; /* inodes per block */ + char sb_fname[12]; /* file system name */ + uint8_t sb_blocklog; /* log2 of sb_blocksize */ + uint8_t sb_sectlog; /* log2 of sb_sectsize */ + uint8_t sb_inodelog; /* log2 of sb_inodesize */ + uint8_t sb_inopblog; /* log2 of sb_inopblock */ + uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */ + uint8_t sb_rextslog; /* log2 of sb_rextents */ + uint8_t sb_inprogress; /* mkfs is in progress, don't mount */ + uint8_t sb_imax_pct; /* max % of fs for inode space */ + /* statistics */ + /* + * These fields must remain contiguous. If you really + * want to change their layout, make sure you fix the + * code in xfs_trans_apply_sb_deltas(). + */ + uint64_t sb_icount; /* allocated inodes */ + uint64_t sb_ifree; /* free inodes */ + uint64_t sb_fdblocks; /* free data blocks */ + uint64_t sb_frextents; /* free realtime extents */ + /* + * End contiguous fields. + */ + xfs_ino_t sb_uquotino; /* user quota inode */ + xfs_ino_t sb_gquotino; /* group quota inode */ + uint16_t sb_qflags; /* quota flags */ + uint8_t sb_flags; /* misc. flags */ + uint8_t sb_shared_vn; /* shared version number */ + xfs_extlen_t sb_inoalignmt; /* inode chunk alignment, fsblocks */ + uint32_t sb_unit; /* stripe or raid unit */ + uint32_t sb_width; /* stripe or raid width */ + uint8_t sb_dirblklog; /* log2 of dir block size (fsbs) */ + uint8_t sb_dummy[7]; /* padding */ +} xfs_sb_t; + + +/* those are from xfs_btree.h */ + +/* + * Long form header: bmap btrees. + */ +typedef struct xfs_btree_lblock +{ + uint32_t bb_magic; /* magic number for block type */ + uint16_t bb_level; /* 0 is a leaf */ + uint16_t bb_numrecs; /* current # of data records */ + xfs_dfsbno_t bb_leftsib; /* left sibling block or NULLDFSBNO */ + xfs_dfsbno_t bb_rightsib; /* right sibling block or NULLDFSBNO */ +} xfs_btree_lblock_t; + +/* + * Combined header and structure, used by common code. + */ +typedef struct xfs_btree_hdr +{ + uint32_t bb_magic; /* magic number for block type */ + uint16_t bb_level; /* 0 is a leaf */ + uint16_t bb_numrecs; /* current # of data records */ +} xfs_btree_hdr_t; + +typedef struct xfs_btree_block +{ + xfs_btree_hdr_t bb_h; /* header */ + union { + struct { + xfs_agblock_t bb_leftsib; + xfs_agblock_t bb_rightsib; + } s; /* short form pointers */ + struct { + xfs_dfsbno_t bb_leftsib; + xfs_dfsbno_t bb_rightsib; + } l; /* long form pointers */ + } bb_u; /* rest */ +} xfs_btree_block_t; + +/* those are from xfs_bmap_btree.h */ + +/* + * Bmap root header, on-disk form only. + */ +typedef struct xfs_bmdr_block +{ + uint16_t bb_level; /* 0 is a leaf */ + uint16_t bb_numrecs; /* current # of data records */ +} xfs_bmdr_block_t; + +/* + * Bmap btree record and extent descriptor. + * For 32-bit kernels, + * l0:31 is an extent flag (value 1 indicates non-normal). + * l0:0-30 and l1:9-31 are startoff. + * l1:0-8, l2:0-31, and l3:21-31 are startblock. + * l3:0-20 are blockcount. + * For 64-bit kernels, + * l0:63 is an extent flag (value 1 indicates non-normal). + * l0:9-62 are startoff. + * l0:0-8 and l1:21-63 are startblock. + * l1:0-20 are blockcount. + */ + +#define BMBT_USE_64 1 + +typedef struct xfs_bmbt_rec_32 +{ + uint32_t l0, l1, l2, l3; +} xfs_bmbt_rec_32_t; +typedef struct xfs_bmbt_rec_64 +{ + uint64_t l0, l1; +} xfs_bmbt_rec_64_t; + +#if BMBT_USE_64 +typedef uint64_t xfs_bmbt_rec_base_t; /* use this for casts */ +typedef xfs_bmbt_rec_64_t xfs_bmbt_rec_t, xfs_bmdr_rec_t; +#else /* !BMBT_USE_64 */ +typedef uint32_t xfs_bmbt_rec_base_t; /* use this for casts */ +typedef xfs_bmbt_rec_32_t xfs_bmbt_rec_t, xfs_bmdr_rec_t; +#endif /* BMBT_USE_64 */ + +/* + * Key structure for non-leaf levels of the tree. + */ +typedef struct xfs_bmbt_key +{ + xfs_dfiloff_t br_startoff; /* starting file offset */ +} xfs_bmbt_key_t, xfs_bmdr_key_t; + +typedef xfs_dfsbno_t xfs_bmbt_ptr_t, xfs_bmdr_ptr_t; /* btree pointer type */ + /* btree block header type */ +typedef struct xfs_btree_lblock xfs_bmbt_block_t; + + + +/* those are from xfs_dir2.h */ +/* + * Directory version 2. + * There are 4 possible formats: + * shortform + * single block - data with embedded leaf at the end + * multiple data blocks, single leaf+freeindex block + * data blocks, node&leaf blocks (btree), freeindex blocks + * + * The shortform format is in xfs_dir2_sf.h. + * The single block format is in xfs_dir2_block.h. + * The data block format is in xfs_dir2_data.h. + * The leaf and freeindex block formats are in xfs_dir2_leaf.h. + * Node blocks are the same as the other version, in xfs_da_btree.h. + */ + +/* + * Byte offset in data block and shortform entry. + */ +typedef uint16_t xfs_dir2_data_off_t; + +/* + * Byte offset in a directory. + */ +typedef xfs_off_t xfs_dir2_off_t; + +/* those are from xfs_da_btree.h */ +/*======================================================================== + * Directory Structure when greater than XFS_LBSIZE(mp) bytes. + *========================================================================*/ + +/* + * This structure is common to both leaf nodes and non-leaf nodes in the Btree. + * + * Is is used to manage a doubly linked list of all blocks at the same + * level in the Btree, and to identify which type of block this is. + */ +#define XFS_DIR2_LEAF1_MAGIC 0xd2f1 /* magic number: v2 dirlf single blks */ +#define XFS_DIR2_LEAFN_MAGIC 0xd2ff /* magic number: v2 dirlf multi blks */ + +typedef struct xfs_da_blkinfo { + xfs_dablk_t forw; /* previous block in list */ + xfs_dablk_t back; /* following block in list */ + uint16_t magic; /* validity check on block */ + uint16_t pad; /* unused */ +} xfs_da_blkinfo_t; + +/* + * This is the structure of the root and intermediate nodes in the Btree. + * The leaf nodes are defined above. + * + * Entries are not packed. + * + * Since we have duplicate keys, use a binary search but always follow + * all match in the block, not just the first match found. + */ + +typedef struct xfs_da_intnode { + struct xfs_da_node_hdr { /* constant-structure header block */ + xfs_da_blkinfo_t info; /* block type, links, etc. */ + uint16_t count; /* count of active entries */ + uint16_t level; /* level above leaves (leaf == 0) */ + } hdr; + struct xfs_da_node_entry { + xfs_dahash_t hashval; /* hash value for this descendant */ + xfs_dablk_t before; /* Btree block before this key */ + } btree[1]; /* variable sized array of keys */ +} xfs_da_intnode_t; + + +/* those are from xfs_dir2_data.h */ +/* + * Directory format 2, data block structures. + */ + +/* + * Constants. + */ +#define XFS_DIR2_DATA_FREE_TAG 0xffff +#define XFS_DIR2_DATA_FD_COUNT 3 + +/* + * Structures. + */ + +/* + * Describe a free area in the data block. + * The freespace will be formatted as a xfs_dir2_data_unused_t. + */ +typedef struct xfs_dir2_data_free { + xfs_dir2_data_off_t offset; /* start of freespace */ + xfs_dir2_data_off_t length; /* length of freespace */ +} xfs_dir2_data_free_t; + +/* + * Header for the data blocks. + * Always at the beginning of a directory-sized block. + * The code knows that XFS_DIR2_DATA_FD_COUNT is 3. + */ +typedef struct xfs_dir2_data_hdr { + uint32_t magic; /* XFS_DIR2_DATA_MAGIC */ + /* or XFS_DIR2_BLOCK_MAGIC */ + xfs_dir2_data_free_t bestfree[XFS_DIR2_DATA_FD_COUNT]; +} xfs_dir2_data_hdr_t; + +/* + * Active entry in a data block. Aligned to 8 bytes. + * Tag appears as the last 2 bytes. + */ +typedef struct xfs_dir2_data_entry { + xfs_ino_t inumber; /* inode number */ + uint8_t namelen; /* name length */ + uint8_t name[1]; /* name bytes, no null */ + /* variable offset */ + xfs_dir2_data_off_t tag; /* starting offset of us */ +} xfs_dir2_data_entry_t; + +/* + * Unused entry in a data block. Aligned to 8 bytes. + * Tag appears as the last 2 bytes. + */ +typedef struct xfs_dir2_data_unused { + uint16_t freetag; /* XFS_DIR2_DATA_FREE_TAG */ + xfs_dir2_data_off_t length; /* total free length */ + /* variable offset */ + xfs_dir2_data_off_t tag; /* starting offset of us */ +} xfs_dir2_data_unused_t; + +typedef union { + xfs_dir2_data_entry_t entry; + xfs_dir2_data_unused_t unused; +} xfs_dir2_data_union_t; + + +/* those are from xfs_dir2_leaf.h */ +/* + * Directory version 2, leaf block structures. + */ + +/* + * Leaf block header. + */ +typedef struct xfs_dir2_leaf_hdr { + xfs_da_blkinfo_t info; /* header for da routines */ + uint16_t count; /* count of entries */ + uint16_t stale; /* count of stale entries */ +} xfs_dir2_leaf_hdr_t; + + +/* those are from xfs_dir2_block.h */ +/* + * xfs_dir2_block.h + * Directory version 2, single block format structures + */ + +/* + * The single block format is as follows: + * xfs_dir2_data_hdr_t structure + * xfs_dir2_data_entry_t and xfs_dir2_data_unused_t structures + * xfs_dir2_leaf_entry_t structures + * xfs_dir2_block_tail_t structure + */ + +#define XFS_DIR2_BLOCK_MAGIC 0x58443242 /* XD2B: for one block dirs */ + +typedef struct xfs_dir2_block_tail { + uint32_t count; /* count of leaf entries */ + uint32_t stale; /* count of stale lf entries */ +} xfs_dir2_block_tail_t; + + +/* those are from xfs_dir2_sf.h */ + +/* + * Directory layout when stored internal to an inode. + * + * Small directories are packed as tightly as possible so as to + * fit into the literal area of the inode. + */ + +/* + * Inode number stored as 8 8-bit values. + */ +typedef struct { uint8_t i[8]; } xfs_dir2_ino8_t; + +/* + * Inode number stored as 4 8-bit values. + * Works a lot of the time, when all the inode numbers in a directory + * fit in 32 bits. + */ +typedef struct { uint8_t i[4]; } xfs_dir2_ino4_t; + +typedef union { + xfs_dir2_ino8_t i8; + xfs_dir2_ino4_t i4; +} xfs_dir2_inou_t; + +/* + * Normalized offset (in a data block) of the entry, really xfs_dir2_data_off_t. + * Only need 16 bits, this is the byte offset into the single block form. + */ +typedef struct { uint8_t i[2]; } xfs_dir2_sf_off_t; + +/* + * The parent directory has a dedicated field, and the self-pointer must + * be calculated on the fly. + * + * Entries are packed toward the top as tightly as possible. The header + * and the elements must be bcopy()'d out into a work area to get correct + * alignment for the inode number fields. + */ +typedef struct xfs_dir2_sf_hdr { + uint8_t count; /* count of entries */ + uint8_t i8count; /* count of 8-byte inode #s */ + xfs_dir2_inou_t parent; /* parent dir inode number */ +} xfs_dir2_sf_hdr_t; + +typedef struct xfs_dir2_sf_entry { + uint8_t namelen; /* actual name length */ + xfs_dir2_sf_off_t offset; /* saved offset */ + uint8_t name[1]; /* name, variable size */ + xfs_dir2_inou_t inumber; /* inode number, var. offset */ +} xfs_dir2_sf_entry_t; + +typedef struct xfs_dir2_sf { + xfs_dir2_sf_hdr_t hdr; /* shortform header */ + xfs_dir2_sf_entry_t list[1]; /* shortform entries */ +} xfs_dir2_sf_t; + +/* those are from xfs_dinode.h */ + + +#define XFS_DINODE_VERSION_1 1 +#define XFS_DINODE_VERSION_2 2 +#define XFS_DINODE_VERSION_3 3 /* v5 filesystem, CRC inode */ +#define XFS_DINODE_MAGIC 0x494e /* 'IN' */ + +/* + * Disk inode structure. + * This is just the header; the inode is expanded to fill a variable size + * with the last field expanding. It is split into the core and "other" + * because we only need the core part in the in-core inode. + */ +typedef struct xfs_timestamp { + int32_t t_sec; /* timestamp seconds */ + int32_t t_nsec; /* timestamp nanoseconds */ +} xfs_timestamp_t; + +/* + * Note: Coordinate changes to this structure with the XFS_DI_* #defines + * below and the offsets table in xfs_ialloc_log_di(). + */ +typedef struct xfs_dinode_core +{ + uint16_t di_magic; /* inode magic # = XFS_DINODE_MAGIC */ + uint16_t di_mode; /* mode and type of file */ + int8_t di_version; /* inode version */ + int8_t di_format; /* format of di_c data */ + uint16_t di_onlink; /* old number of links to file */ + uint32_t di_uid; /* owner's user id */ + uint32_t di_gid; /* owner's group id */ + uint32_t di_nlink; /* number of links to file */ + uint16_t di_projid; /* owner's project id */ + uint8_t di_pad[10]; /* unused, zeroed space */ + xfs_timestamp_t di_atime; /* time last accessed */ + xfs_timestamp_t di_mtime; /* time last modified */ + xfs_timestamp_t di_ctime; /* time created/inode modified */ + xfs_fsize_t di_size; /* number of bytes in file */ + xfs_drfsbno_t di_nblocks; /* # of direct & btree blocks used */ + xfs_extlen_t di_extsize; /* basic/minimum extent size for file */ + xfs_extnum_t di_nextents; /* number of extents in data fork */ + xfs_aextnum_t di_anextents; /* number of extents in attribute fork*/ + uint8_t di_forkoff; /* attr fork offs, <<3 for 64b align */ + int8_t di_aformat; /* format of attr fork's data */ + uint32_t di_dmevmask; /* DMIG event mask */ + uint16_t di_dmstate; /* DMIG state info */ + uint16_t di_flags; /* random flags, XFS_DIFLAG_... */ + uint32_t di_gen; /* generation number */ +} xfs_dinode_core_t; + +typedef struct xfs_dinode +{ + xfs_dinode_core_t di_core; + xfs_agino_t di_next_unlinked;/* agi unlinked list ptr */ + union { + xfs_bmdr_block_t di_bmbt; /* btree root block */ + xfs_bmbt_rec_32_t di_bmx[1]; /* extent list */ + xfs_dir2_sf_t di_dir2sf; /* shortform directory v2 */ + char di_c[1]; /* local contents */ + } di_u; +} xfs_dinode_t; + +/* + * Values for di_format + */ +typedef enum xfs_dinode_fmt +{ + XFS_DINODE_FMT_DEV, /* CHR, BLK: di_dev */ + XFS_DINODE_FMT_LOCAL, /* DIR, REG: di_c */ + /* LNK: di_symlink */ + XFS_DINODE_FMT_EXTENTS, /* DIR, REG, LNK: di_bmx */ + XFS_DINODE_FMT_BTREE, /* DIR, REG, LNK: di_bmbt */ + XFS_DINODE_FMT_UUID /* MNT: di_uuid */ +} xfs_dinode_fmt_t; + +/* + * File types (mode field) + */ +#define IFMT 0170000 /* type of file */ +#define IFDIR 0040000 /* directory */ +#define IFREG 0100000 /* regular */ +#define IFLNK 0120000 /* symbolic link */ + diff --git a/include/ext4.h b/include/ext4.h index 440a14d..e6504be 100644 --- a/include/ext4.h +++ b/include/ext4.h @@ -19,6 +19,68 @@ #ifndef _EXT4_H_ #define _EXT4_H_ + +/* --- ext4 superblock feature flags we care about --- */ + +/* Incompatible features (s_feature_incompat) */ +#ifndef EXT4_FEATURE_INCOMPAT_FILETYPE +#define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002 +#endif +#ifndef EXT4_FEATURE_INCOMPAT_RECOVER +#define EXT4_FEATURE_INCOMPAT_RECOVER 0x0004 +#endif +#ifndef EXT4_FEATURE_INCOMPAT_META_BG +#define EXT4_FEATURE_INCOMPAT_META_BG 0x0010 +#endif +#ifndef EXT4_FEATURE_INCOMPAT_EXTENTS +#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 +#endif +#ifndef EXT4_FEATURE_INCOMPAT_64BIT +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#endif +#ifndef EXT4_FEATURE_INCOMPAT_MMP +#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 +#endif +#ifndef EXT4_FEATURE_INCOMPAT_FLEX_BG +#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 +#endif + +/* RO-compat features (s_feature_ro_compat) */ +#ifndef EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER +#define EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#endif +#ifndef EXT4_FEATURE_RO_COMPAT_LARGE_FILE +#define EXT4_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#endif +#ifndef EXT4_FEATURE_RO_COMPAT_BTREE_DIR +#define EXT4_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 +#endif +#ifndef EXT4_FEATURE_RO_COMPAT_HUGE_FILE +#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 +#endif +#ifndef EXT4_FEATURE_RO_COMPAT_GDT_CSUM +#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 +#endif +#ifndef EXT4_FEATURE_RO_COMPAT_DIR_NLINK +#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 +#endif +#ifndef EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE +#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 +#endif +#ifndef EXT4_FEATURE_RO_COMPAT_BIGALLOC +#define EXT4_FEATURE_RO_COMPAT_BIGALLOC 0x0200 +#endif +#ifndef EXT4_FEATURE_RO_COMPAT_QUOTA +#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100 +#endif +#ifndef EXT4_FEATURE_RO_COMPAT_METADATA_CSUM +#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400 +#endif + +#ifndef EXT4_FEATURE_INCOMPAT_CSUM_SEED +#define EXT4_FEATURE_INCOMPAT_CSUM_SEED 0x2000 +#endif + /* * Inode flags */