/* 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
