Files
erofs-freebsd-out-tree/erofs_fs.h
T
2026-08-18 09:24:47 +02:00

497 lines
15 KiB
C

/* 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 <sys/types.h>
#include <sys/endian.h>
/* 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_FEATURE_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_ALL_FEATURE_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
/* Device table slot (128 bytes) */
struct erofs_deviceslot {
uint8_t tag[64];
__le32 blocks_lo;
__le32 uniaddr_lo;
__le16 blocks_hi;
__le16 uniaddr_hi;
uint8_t reserved[52];
} __packed;
#define EROFS_DEVT_SLOT_SIZE sizeof(struct erofs_deviceslot)
/* erofs on-disk super block (currently 144 bytes at maximum) */
struct erofs_super_block {
__le32 magic;
__le32 checksum;
__le32 feature_compat;
uint8_t blkszbits;
uint8_t sb_extslots;
union {
__le16 rootnid_2b;
__le16 blocks_hi;
} __packed rb;
__le64 inos;
__le64 epoch;
__le32 fixed_nsec;
__le32 blocks_lo;
__le32 meta_blkaddr;
__le32 xattr_blkaddr;
uint8_t uuid[16];
uint8_t volume_name[16];
__le32 feature_incompat;
union {
__le16 available_compr_algs;
__le16 lz4_max_distance;
} __packed u1;
__le16 extra_devices;
__le16 devt_slotoff;
uint8_t dirblkbits;
uint8_t xattr_prefix_count;
__le32 xattr_prefix_start;
__le64 packed_nid;
uint8_t xattr_filter_reserved;
uint8_t ishare_xattr_prefix_id;
uint8_t reserved[2];
__le32 build_time;
__le64 rootnid_8b;
__le64 reserved2;
__le64 metabox_nid;
__le64 reserved3;
} __packed;
/* 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
};
static inline bool
erofs_inode_is_data_compressed(unsigned int datamode)
{
return (datamode == EROFS_INODE_COMPRESSED_FULL ||
datamode == EROFS_INODE_COMPRESSED_COMPACT);
}
/* 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)
/* indicate chunk blkbits, thus 'chunksize = blocksize << chunk blkbits' */
#define EROFS_CHUNK_FORMAT_BLKBITS_MASK 0x001F
/* with chunk indexes or just a 4-byte block array */
#define EROFS_CHUNK_FORMAT_INDEXES 0x0020
#define EROFS_CHUNK_FORMAT_48BIT 0x0040
#define EROFS_CHUNK_FORMAT_ALL ((EROFS_CHUNK_FORMAT_48BIT << 1) - 1)
/* 32-byte and 64-byte on-disk inode record layouts. */
#define EROFS_INODE_LAYOUT_COMPACT 0
#define EROFS_INODE_LAYOUT_EXTENDED 1
#define EROFS_INODE_LAYOUT_PLAIN EROFS_INODE_FLAT_PLAIN
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 {
__le16 nlink; /* if EROFS_I_NLINK_1_BIT is unset */
__le16 blocks_hi; /* total blocks count MSB */
__le16 startblk_hi; /* starting block number MSB */
} __packed;
/* 32-byte reduced form of an ondisk inode */
struct erofs_inode_compact {
__le16 i_format; /* inode format hints */
__le16 i_xattr_icount;
__le16 i_mode;
union erofs_inode_i_nb i_nb;
__le32 i_size;
__le32 i_mtime;
union erofs_inode_i_u i_u;
__le32 i_ino; /* only used for 32-bit stat compatibility */
__le16 i_uid;
__le16 i_gid;
__le32 i_reserved;
} __packed;
/* 64-byte complete form of an ondisk inode */
struct erofs_inode_extended {
__le16 i_format; /* inode format hints */
__le16 i_xattr_icount;
__le16 i_mode;
union erofs_inode_i_nb i_nb;
__le64 i_size;
union erofs_inode_i_u i_u;
__le32 i_ino; /* only used for 32-bit stat compatibility */
__le32 i_uid;
__le32 i_gid;
__le64 i_mtime;
__le32 i_mtime_nsec;
__le32 i_nlink;
uint8_t i_reserved2[16];
} __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 {
__le32 h_name_filter; /* bit value 1 indicates not-present */
uint8_t h_shared_count;
uint8_t h_reserved2[7];
__le32 h_shared_xattrs[]; /* 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
#define EROFS_XATTR_FILTER_BITS 32
#define EROFS_XATTR_FILTER_DEFAULT UINT32_MAX
#define EROFS_XATTR_FILTER_SEED 0x25BBE08F
/* xattr entry (for both inline & shared xattrs) */
struct erofs_xattr_entry {
uint8_t e_name_len;
uint8_t e_name_index;
__le16 e_value_size;
char e_name[]; /* attribute name */
} __packed;
/* long xattr name prefix */
struct erofs_xattr_long_prefix {
uint8_t base_index; /* short xattr name prefix index */
char infix[]; /* infix apart from short prefix */
} __packed;
static inline unsigned int
erofs_xattr_ibody_size(__le16 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));
}
#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)));
}
/* represent a zeroed chunk (hole) */
#define EROFS_NULL_ADDR ((uint64_t)-1)
/* 4-byte block address array */
#define EROFS_BLOCK_MAP_ENTRY_SIZE sizeof(__le32)
/* 8-byte inode chunk index */
struct erofs_inode_chunk_index {
__le16 startblk_hi;
__le16 device_id;
__le32 startblk_lo;
} __packed;
#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)
/* dirent sorts in alphabet order, thus we can do binary search */
struct erofs_dirent {
__le64 nid;
__le16 nameoff;
uint8_t file_type;
uint8_t reserved;
} __packed;
/* 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
#define EROFS_NAME_LEN 255
#define Z_EROFS_PCLUSTER_MAX_SIZE (1024 * 1024)
#define Z_EROFS_PCLUSTER_MAX_DSIZE (12 * 1024 * 1024)
/* 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)
/* 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
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;
/* 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;
#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));
}
_Static_assert(sizeof(struct erofs_deviceslot) == 128,
"EROFS device slot ABI size");
_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 macro");
_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