/* SPDX-License-Identifier: MIT */ /* * EROFS (Enhanced ROM File System) on-disk format definition * * Copyright (C) 2017-2018 HUAWEI, Inc. * https://www.huawei.com/ * Copyright (C) 2021, Alibaba Cloud */ #ifndef __EROFS_FS_H #define __EROFS_FS_H #include #include /* FreeBSD compatibility - Linux-style little-endian types */ #ifndef __le16 typedef uint16_t __le16; typedef uint32_t __le32; typedef uint64_t __le64; typedef uint8_t __u8; #endif /* to allow for x86 boot sectors and other oddities. */ #define EROFS_SUPER_OFFSET 1024 #define EROFS_SUPER_MAGIC_V1 0xE0F5E1E2 #define EROFS_FEATURE_COMPAT_SB_CHKSUM 0x00000001 #define EROFS_FEATURE_COMPAT_MTIME 0x00000002 #define EROFS_FEATURE_COMPAT_XATTR_FILTER 0x00000004 #define EROFS_FEATURE_COMPAT_SHARED_EA_IN_METABOX 0x00000008 #define EROFS_FEATURE_COMPAT_PLAIN_XATTR_PFX 0x00000010 #define EROFS_FEATURE_COMPAT_ISHARE_XATTRS 0x00000020 /* * Any bits that aren't in EROFS_ALL_SUPPORTED_INCOMPAT should * be incompatible with this kernel version. */ #define EROFS_FEATURE_INCOMPAT_LZ4_0PADDING 0x00000001 #define EROFS_FEATURE_INCOMPAT_COMPR_CFGS 0x00000002 #define EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER 0x00000002 #define EROFS_FEATURE_INCOMPAT_CHUNKED_FILE 0x00000004 #define EROFS_FEATURE_INCOMPAT_DEVICE_TABLE 0x00000008 #define EROFS_FEATURE_INCOMPAT_COMPR_HEAD2 0x00000008 #define EROFS_FEATURE_INCOMPAT_ZTAILPACKING 0x00000010 #define EROFS_FEATURE_INCOMPAT_FRAGMENTS 0x00000020 #define EROFS_FEATURE_INCOMPAT_DEDUPE 0x00000020 #define EROFS_FEATURE_INCOMPAT_XATTR_PREFIXES 0x00000040 #define EROFS_FEATURE_INCOMPAT_48BIT 0x00000080 #define EROFS_FEATURE_INCOMPAT_METABOX 0x00000100 #define EROFS_DIRENT_NID_METABOX_BIT 63 #define EROFS_DIRENT_NID_METABOX (1ULL << EROFS_DIRENT_NID_METABOX_BIT) #define EROFS_DIRENT_NID_MASK ((1ULL << EROFS_DIRENT_NID_METABOX_BIT) - 1) #define EROFS_ALL_SUPPORTED_INCOMPAT \ (EROFS_FEATURE_INCOMPAT_LZ4_0PADDING | EROFS_FEATURE_INCOMPAT_48BIT | \ EROFS_FEATURE_INCOMPAT_COMPR_CFGS | \ EROFS_FEATURE_INCOMPAT_XATTR_PREFIXES | \ EROFS_FEATURE_INCOMPAT_ZTAILPACKING | \ EROFS_FEATURE_INCOMPAT_CHUNKED_FILE | \ EROFS_FEATURE_INCOMPAT_COMPR_HEAD2 | \ EROFS_FEATURE_INCOMPAT_FRAGMENTS | \ EROFS_FEATURE_INCOMPAT_METABOX) #define EROFS_SB_EXTSLOT_SIZE 16 #define EROFS_NAME_LEN 255 /* EROFS inode datalayout (i_format in on-disk inode) */ enum { EROFS_INODE_FLAT_PLAIN = 0, EROFS_INODE_COMPRESSED_FULL = 1, EROFS_INODE_FLAT_INLINE = 2, EROFS_INODE_COMPRESSED_COMPACT = 3, EROFS_INODE_CHUNK_BASED = 4, EROFS_INODE_DATALAYOUT_MAX }; /* bit definitions of inode i_format */ #define EROFS_I_VERSION_MASK 0x01 #define EROFS_I_DATALAYOUT_MASK 0x07 #define EROFS_I_VERSION_BIT 0 #define EROFS_I_DATALAYOUT_BIT 1 #define EROFS_I_NLINK_1_BIT 4 /* non-directory compact inodes only */ #define EROFS_I_DOT_OMITTED_BIT 4 /* (directories) omit the `.` dirent */ #define EROFS_I_ALL ((1 << (EROFS_I_NLINK_1_BIT + 1)) - 1) /* file type definitions in directory entries */ #define EROFS_FT_UNKNOWN 0 #define EROFS_FT_REG_FILE 1 #define EROFS_FT_DIR 2 #define EROFS_FT_CHRDEV 3 #define EROFS_FT_BLKDEV 4 #define EROFS_FT_FIFO 5 #define EROFS_FT_SOCK 6 #define EROFS_FT_SYMLINK 7 /* represent a zeroed chunk (hole) */ #define EROFS_NULL_ADDR ((uint64_t)-1) /* erofs on-disk super block (currently 144 bytes at maximum) */ struct erofs_super_block { uint32_t magic; uint32_t checksum; uint32_t feature_compat; uint8_t blkszbits; uint8_t sb_extslots; union { uint16_t rootnid_2b; uint16_t blocks_hi; } __packed rb; uint64_t inos; uint64_t epoch; uint32_t fixed_nsec; uint32_t blocks_lo; uint32_t meta_blkaddr; uint32_t xattr_blkaddr; uint8_t uuid[16]; uint8_t volume_name[16]; uint32_t feature_incompat; union { uint16_t available_compr_algs; uint16_t lz4_max_distance; } __packed u1; uint16_t extra_devices; uint16_t devt_slotoff; uint8_t dirblkbits; uint8_t xattr_prefix_count; uint32_t xattr_prefix_start; uint64_t packed_nid; uint8_t xattr_filter_reserved; uint8_t ishare_xattr_prefix_id; uint8_t reserved[2]; uint32_t build_time; uint64_t rootnid_8b; uint64_t reserved2; uint64_t metabox_nid; uint64_t reserved3; } __packed; struct erofs_inode_chunk_info { __le16 format; __le16 reserved; } __packed; union erofs_inode_i_u { __le32 blocks_lo; __le32 startblk_lo; __le32 rdev; struct erofs_inode_chunk_info c; }; union erofs_inode_i_nb { uint16_t nlink; /* if EROFS_I_NLINK_1_BIT is unset */ uint16_t blocks_hi; /* total blocks count MSB */ uint16_t startblk_hi; /* starting block number MSB */ } __packed; /* 32-byte reduced form of an ondisk inode */ struct erofs_inode_compact { uint16_t i_format; /* inode format hints */ uint16_t i_xattr_icount; uint16_t i_mode; union erofs_inode_i_nb i_nb; uint32_t i_size; uint32_t i_mtime; union erofs_inode_i_u i_u; uint32_t i_ino; /* only used for 32-bit stat compatibility */ uint16_t i_uid; uint16_t i_gid; uint32_t i_reserved; } __packed; /* 64-byte complete form of an ondisk inode */ struct erofs_inode_extended { uint16_t i_format; /* inode format hints */ uint16_t i_xattr_icount; uint16_t i_mode; union erofs_inode_i_nb i_nb; uint64_t i_size; union erofs_inode_i_u i_u; uint32_t i_ino; /* only used for 32-bit stat compatibility */ uint32_t i_uid; uint32_t i_gid; uint64_t i_mtime; uint32_t i_mtime_nsec; uint32_t i_nlink; uint8_t i_reserved2[16]; } __packed; /* dirent sorts in alphabet order, thus we can do binary search */ struct erofs_dirent { uint64_t nid; uint16_t nameoff; uint8_t file_type; uint8_t reserved; } __packed; /* * inline xattrs (n == i_xattr_icount): * erofs_xattr_ibody_header(1) + (n - 1) * 4 bytes * 12 bytes / \ * / \ * /-----------------------\ * | erofs_xattr_entries+ | * +-----------------------+ * inline xattrs must starts in erofs_xattr_ibody_header, * for read-only fs, no need to introduce h_refcount */ struct erofs_xattr_ibody_header { uint32_t h_name_filter; /* bit value 1 indicates not-present */ uint8_t h_shared_count; uint8_t h_reserved2[7]; uint32_t h_shared_xattrs[0]; /* shared xattr id array */ } __packed; /* Name indexes */ #define EROFS_XATTR_INDEX_USER 1 #define EROFS_XATTR_INDEX_POSIX_ACL_ACCESS 2 #define EROFS_XATTR_INDEX_POSIX_ACL_DEFAULT 3 #define EROFS_XATTR_INDEX_TRUSTED 4 #define EROFS_XATTR_INDEX_LUSTRE 5 #define EROFS_XATTR_INDEX_SECURITY 6 /* * bit 7 of e_name_index is set when it refers to a long xattr name prefix, * while the remained lower bits represent the index of the prefix. */ #define EROFS_XATTR_LONG_PREFIX 0x80 #define EROFS_XATTR_LONG_PREFIX_MASK 0x7f /* long xattr name prefix */ struct erofs_xattr_long_prefix { uint8_t base_index; /* short xattr name prefix index */ char infix[0]; /* infix apart from short prefix */ } __packed; /* xattr entry (for both inline & shared xattrs) */ struct erofs_xattr_entry { uint8_t e_name_len; uint8_t e_name_index; uint16_t e_value_size; char e_name[]; /* attribute name */ } __packed; #define EROFS_XATTR_ALIGN(size) \ (((size) + sizeof(struct erofs_xattr_entry) - 1) & \ ~(sizeof(struct erofs_xattr_entry) - 1)) static inline unsigned int erofs_xattr_entry_size(const struct erofs_xattr_entry *entry) { return (EROFS_XATTR_ALIGN( sizeof(*entry) + entry->e_name_len + le16toh(entry->e_value_size))); } static inline unsigned int erofs_xattr_ibody_size(uint16_t i_xattr_icount) { if (!i_xattr_icount) return 0; /* 1 header + n-1 * 4 bytes inline xattr to keep continuity */ return (sizeof(struct erofs_xattr_ibody_header) + sizeof(uint32_t) * (le16toh(i_xattr_icount) - 1)); } /* compression algorithm types (for h_algorithmtype) */ enum { Z_EROFS_COMPRESSION_LZ4 = 0, Z_EROFS_COMPRESSION_LZMA = 1, Z_EROFS_COMPRESSION_DEFLATE = 2, Z_EROFS_COMPRESSION_ZSTD = 3, Z_EROFS_COMPRESSION_MAX }; #define Z_EROFS_ALL_COMPR_ALGS ((1 << Z_EROFS_COMPRESSION_MAX) - 1) #define Z_EROFS_PCLUSTER_MAX_SIZE (1024 * 1024) #define Z_EROFS_PCLUSTER_MAX_DSIZE (12 * 1024 * 1024) /* 14 bytes (+ length field = 16 bytes) */ struct z_erofs_lz4_cfgs { __le16 max_distance; __le16 max_pclusterblks; uint8_t reserved[10]; } __packed; /* 14 bytes (+ length field = 16 bytes) */ struct z_erofs_lzma_cfgs { __le32 dict_size; __le16 format; uint8_t reserved[8]; } __packed; #define Z_EROFS_LZMA_MAX_DICT_SIZE (8 * Z_EROFS_PCLUSTER_MAX_SIZE) /* 6 bytes (+ length field = 8 bytes) */ struct z_erofs_deflate_cfgs { uint8_t windowbits; uint8_t reserved[5]; } __packed; /* 6 bytes (+ length field = 8 bytes) */ struct z_erofs_zstd_cfgs { uint8_t format; uint8_t windowlog; uint8_t reserved[4]; } __packed; #define Z_EROFS_ZSTD_MAX_DICT_SIZE Z_EROFS_PCLUSTER_MAX_SIZE /* z_advise flags */ #define Z_EROFS_ADVISE_COMPACTED_2B 0x0001 #define Z_EROFS_ADVISE_EXTENTS 0x0001 #define Z_EROFS_ADVISE_BIG_PCLUSTER_1 0x0002 #define Z_EROFS_ADVISE_BIG_PCLUSTER_2 0x0004 #define Z_EROFS_ADVISE_INLINE_PCLUSTER 0x0008 #define Z_EROFS_ADVISE_INTERLACED_PCLUSTER 0x0010 #define Z_EROFS_ADVISE_FRAGMENT_PCLUSTER 0x0020 #define Z_EROFS_ADVISE_EXTRECSZ_BIT 1 #define Z_EROFS_ADVISE_EXTRECSZ_MASK 0x3 #define Z_EROFS_FRAGMENT_INODE_BIT 7 /* Logical cluster types */ enum { Z_EROFS_LCLUSTER_TYPE_PLAIN = 0, Z_EROFS_LCLUSTER_TYPE_HEAD1 = 1, Z_EROFS_LCLUSTER_TYPE_NONHEAD = 2, Z_EROFS_LCLUSTER_TYPE_HEAD2 = 3, Z_EROFS_LCLUSTER_TYPE_MAX }; #define Z_EROFS_LI_LCLUSTER_TYPE_MASK (Z_EROFS_LCLUSTER_TYPE_MAX - 1) #define Z_EROFS_LI_PARTIAL_REF (1 << 15) #define Z_EROFS_LI_D0_CBLKCNT (1 << 11) /* Compression extent index structures */ struct z_erofs_lcluster_index { __le16 di_advise; __le16 di_clusterofs; union { __le32 blkaddr; __le16 delta[2]; } di_u; } __packed; struct z_erofs_map_header { union { __le32 h_fragmentoff; struct { __le16 h_reserved1; __le16 h_idata_size; }; __le32 h_extents_lo; }; __le16 h_advise; union { struct { uint8_t h_algorithmtype; uint8_t h_clusterbits; } __packed; __le16 h_extents_hi; } __packed; } __packed; #define Z_EROFS_MAP_HEADER_END(end) \ (roundup2((end), 8) + sizeof(struct z_erofs_map_header)) #define Z_EROFS_FULL_INDEX_START(end) (Z_EROFS_MAP_HEADER_END(end) + 8) #define Z_EROFS_EXTENT_PLEN_PARTIAL (1U << 27) #define Z_EROFS_EXTENT_PLEN_FMT_BIT 28 #define Z_EROFS_EXTENT_PLEN_MASK ((Z_EROFS_PCLUSTER_MAX_SIZE << 1) - 1) struct z_erofs_extent { __le32 plen; __le32 pstart_lo; __le32 pstart_hi; __le32 lstart_lo; __le32 lstart_hi; uint8_t reserved[12]; } __packed; static inline unsigned int z_erofs_extent_recsize(unsigned int advise) { return (4U << ((advise >> Z_EROFS_ADVISE_EXTRECSZ_BIT) & Z_EROFS_ADVISE_EXTRECSZ_MASK)); } /* Chunk-based file definitions */ #define EROFS_CHUNK_FORMAT_BLKBITS_MASK 0x001F #define EROFS_CHUNK_FORMAT_INDEXES 0x0020 #define EROFS_CHUNK_FORMAT_48BIT 0x0040 #define EROFS_CHUNK_FORMAT_ALL ((EROFS_CHUNK_FORMAT_48BIT << 1) - 1) #define EROFS_CHUNK_FORMAT_INDEXES_FLAG EROFS_CHUNK_FORMAT_INDEXES #define EROFS_BLOCK_MAP_ENTRY_SIZE sizeof(__le32) struct erofs_inode_chunk_index { __le16 startblk_hi; __le16 device_id; __le32 startblk_lo; } __packed; /* Device table slot (128 bytes) */ #define EROFS_DEVT_SLOT_SIZE 128 struct erofs_deviceslot { uint8_t tag[64]; __le32 blocks_lo; __le32 uniaddr_lo; __le16 blocks_hi; __le16 uniaddr_hi; uint8_t reserved[52]; } __packed; _Static_assert(sizeof(struct erofs_super_block) == 144, "EROFS super block ABI size"); _Static_assert(sizeof(struct erofs_inode_compact) == 32, "EROFS compact inode ABI size"); _Static_assert(sizeof(struct erofs_inode_extended) == 64, "EROFS extended inode ABI size"); _Static_assert(sizeof(struct erofs_xattr_ibody_header) == 12, "EROFS xattr ibody header ABI size"); _Static_assert(sizeof(struct erofs_xattr_entry) == 4, "EROFS xattr entry ABI size"); _Static_assert(sizeof(struct erofs_inode_chunk_info) == 4, "EROFS chunk info ABI size"); _Static_assert(sizeof(struct erofs_inode_chunk_index) == 8, "EROFS chunk index ABI size"); _Static_assert(sizeof(struct z_erofs_map_header) == 8, "EROFS zmap header ABI size"); _Static_assert(sizeof(struct z_erofs_lcluster_index) == 8, "EROFS lcluster index ABI size"); _Static_assert(sizeof(struct z_erofs_extent) == 32, "EROFS compression extent ABI size"); _Static_assert(sizeof(struct erofs_dirent) == 12, "EROFS dirent ABI size"); _Static_assert(sizeof(struct erofs_deviceslot) == EROFS_DEVT_SLOT_SIZE, "EROFS device slot ABI size"); _Static_assert(__builtin_offsetof(struct erofs_super_block, extra_devices) == 86, "EROFS extra device count ABI offset"); _Static_assert(__builtin_offsetof(struct erofs_super_block, devt_slotoff) == 88, "EROFS device table slot offset ABI offset"); _Static_assert(__builtin_offsetof(struct erofs_super_block, rootnid_8b) == 112, "EROFS 48-bit root nid ABI offset"); _Static_assert(__builtin_offsetof(struct erofs_super_block, metabox_nid) == 128, "EROFS metabox nid ABI offset"); _Static_assert(__builtin_offsetof(struct erofs_inode_compact, i_u) == 16, "EROFS compact inode union ABI offset"); _Static_assert(__builtin_offsetof(struct erofs_inode_extended, i_u) == 16, "EROFS extended inode union ABI offset"); _Static_assert(__builtin_offsetof(struct erofs_inode_extended, i_reserved2) == 48, "EROFS extended inode reserved ABI offset"); _Static_assert(__builtin_offsetof(struct erofs_inode_chunk_index, device_id) == 2, "EROFS chunk device id ABI offset"); _Static_assert(__builtin_offsetof(struct erofs_inode_chunk_index, startblk_lo) == 4, "EROFS chunk start block ABI offset"); _Static_assert(__builtin_offsetof(struct z_erofs_map_header, h_advise) == 4, "EROFS zmap advise ABI offset"); _Static_assert(__builtin_offsetof(struct erofs_deviceslot, blocks_lo) == 64, "EROFS device blocks ABI offset"); _Static_assert(__builtin_offsetof(struct erofs_deviceslot, uniaddr_lo) == 68, "EROFS device unified address ABI offset"); _Static_assert(__builtin_offsetof(struct erofs_deviceslot, blocks_hi) == 72, "EROFS device blocks high ABI offset"); _Static_assert(__builtin_offsetof(struct erofs_deviceslot, uniaddr_hi) == 74, "EROFS device unified address high ABI offset"); #endif