Files
Ernesto A. Fernández 1158bd03a6 Use s_mount_opt for all kernel versions
I'm trying to unify mount option processing across kernel versions as
much as possible. Instead of calling parse_options_set_flags() directly
from parse_options() whenever possible, always set the new s_mount_opt
and use it to call parse_options_set_flags() later.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2026-04-03 19:26:43 -03:00

1347 lines
45 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2018 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
*/
#ifndef _APFS_H
#define _APFS_H
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/types.h>
#include <linux/version.h>
/*
* Redhat kernels regularly break the api, so they need their own version
* checks. Make sure they always fail for non-rhel kernels.
*
* By the way, if rhel keeps picking up breaking patches for old majors after
* a new one is out, this check could break down and things could get more
* complicated. I don't think they do that though...
*/
#ifdef RHEL_RELEASE
#define RHEL_VERSION_GE(a, b) (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(a, b))
#else
#define RHEL_VERSION_GE(a, b) 0
#endif
#include "apfs_raw.h"
#define EFSBADCRC EBADMSG /* Bad CRC detected */
#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) /* SB_RDONLY came in 4.14 */
#define SB_RDONLY MS_RDONLY
#define SB_SILENT MS_SILENT
#define SB_NOSEC MS_NOSEC
#define SB_ACTIVE MS_ACTIVE
static inline bool sb_rdonly(const struct super_block *sb) { return sb->s_flags & SB_RDONLY; }
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0)
#define lockdep_assert_held_write(l) ((void)(l))
#endif
/* Compatibility wrapper around submit_bh() */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) || RHEL_VERSION_GE(9, 2)
#define apfs_submit_bh(op, op_flags, bh) submit_bh(op | op_flags, bh)
#else
#define apfs_submit_bh(op, op_flags, bh) submit_bh(op, op_flags, bh)
#endif
/*
* Parameter for the snapshot creation ioctl
*/
struct apfs_ioctl_snap_name {
char name[APFS_SNAP_MAX_NAMELEN + 1];
};
#define APFS_IOC_SET_DFLT_PFK _IOW('@', 0x80, struct apfs_wrapped_crypto_state)
#define APFS_IOC_SET_DIR_CLASS _IOW('@', 0x81, u32)
#define APFS_IOC_SET_PFK _IOW('@', 0x82, struct apfs_wrapped_crypto_state)
#define APFS_IOC_GET_CLASS _IOR('@', 0x83, u32)
#define APFS_IOC_GET_PFK _IOR('@', 0x84, struct apfs_wrapped_crypto_state)
#define APFS_IOC_TAKE_SNAPSHOT _IOW('@', 0x85, struct apfs_ioctl_snap_name)
/*
* In-memory representation of an APFS object
*/
struct apfs_object {
struct super_block *sb;
u64 block_nr;
u64 oid;
/*
* Buffer head containing the one block of the object, may be NULL if
* the object is only in memory. TODO: support objects with more than
* one block.
*/
struct buffer_head *o_bh;
char *data; /* The raw object */
bool ephemeral; /* Is this an ephemeral object? */
};
/* Constants used in managing the size of a node's table of contents */
#define APFS_BTREE_TOC_ENTRY_INCREMENT 8
#define APFS_BTREE_TOC_ENTRY_MAX_UNUSED (2 * BTREE_TOC_ENTRY_INCREMENT)
/*
* In-memory representation of an APFS node
*/
struct apfs_node {
u32 tree_type; /* Tree type (subtype of the node object) */
u16 flags; /* Node flags */
u32 records; /* Number of records in the node */
int key; /* Offset of the key area in the block */
int free; /* Offset of the free area in the block */
int val; /* Offset of the value area in the block */
int key_free_list_len; /* Length of the fragmented free key space */
int val_free_list_len; /* Length of the fragmented free value space */
struct apfs_object object; /* Object holding the node */
};
/**
* apfs_node_is_leaf - Check if a b-tree node is a leaf
* @node: the node to check
*/
static inline bool apfs_node_is_leaf(struct apfs_node *node)
{
return (node->flags & APFS_BTNODE_LEAF) != 0;
}
/**
* apfs_node_is_root - Check if a b-tree node is the root
* @node: the node to check
*/
static inline bool apfs_node_is_root(struct apfs_node *node)
{
return (node->flags & APFS_BTNODE_ROOT) != 0;
}
/**
* apfs_node_has_fixed_kv_size - Check if a b-tree node has fixed key/value
* sizes
* @node: the node to check
*/
static inline bool apfs_node_has_fixed_kv_size(struct apfs_node *node)
{
return (node->flags & APFS_BTNODE_FIXED_KV_SIZE) != 0;
}
struct apfs_ip_bitmap_block_info {
bool dirty; /* Do we need to commit this block to disk? */
void *block; /* In-memory address of the bitmap block */
};
/*
* Space manager data in memory.
*/
struct apfs_spaceman {
struct apfs_spaceman_phys *sm_raw; /* On-disk spaceman structure */
struct apfs_nxsb_info *sm_nxi; /* Container superblock */
u32 sm_size; /* Size of @sm_raw in bytes */
u32 sm_blocks_per_chunk; /* Blocks covered by a bitmap block */
u32 sm_chunks_per_cib; /* Chunk count in a chunk-info block */
u64 sm_block_count; /* Block count for the container */
u64 sm_chunk_count; /* Number of bitmap blocks */
u32 sm_cib_count; /* Number of chunk-info blocks */
u64 sm_free_count; /* Number of free blocks */
u32 sm_addr_offset; /* Offset of cib addresses in @sm_raw */
u64 sm_main_fq_nodes; /* Number of nodes in the main fq */
/*
* A range of freed blocks not yet put in the free queue. Extend this as
* much as possible before creating an actual record.
*/
u64 sm_free_cache_base;
u64 sm_free_cache_blkcnt;
/* Shift to match an ip block with its bitmap in the array */
int sm_ip_bmaps_shift;
/* Mask to find an ip block's offset inside its ip bitmap */
u32 sm_ip_bmaps_mask;
/* Number of ip bitmaps */
u32 sm_ip_bmaps_count;
/* List of ip bitmaps, in order */
struct apfs_ip_bitmap_block_info sm_ip_bmaps[];
};
#define APFS_TRANS_MAIN_QUEUE_MAX 10000
#define APFS_TRANS_BUFFERS_MAX 65536
#define APFS_TRANS_STARTS_MAX 65536
/* Possible states for the container transaction structure */
#define APFS_NX_TRANS_FORCE_COMMIT 1 /* Commit guaranteed */
#define APFS_NX_TRANS_DEFER_COMMIT 2 /* Commit banned right now */
#define APFS_NX_TRANS_COMMITTING 4 /* Commit ongoing */
#define APFS_NX_TRANS_INCOMPLETE_BLOCK 8 /* A data block is not written in full */
/*
* ENOSPC checks before transactions are more or less coarse depending on this.
*/
enum apfs_trans_kind {
APFS_TRANS_REG, /* Most transactions */
APFS_TRANS_DEL, /* Transactions that usually free space */
APFS_TRANS_SYNC, /* Sync (or unmount) transaction */
};
/* The space requirements for each kind of transaction, in blocks */
#define APFS_REG_ROOM 128
#define APFS_DEL_ROOM 20
#define APFS_SYNC_ROOM 6
/*
* Structure that keeps track of a container transaction.
*/
struct apfs_nx_transaction {
unsigned int t_state;
struct delayed_work t_work; /* Work task for transaction commits */
struct super_block *t_work_sb; /* sb that last queued a commit */
struct list_head t_inodes; /* List of inodes in the transaction */
struct list_head t_buffers; /* List of buffers in the transaction */
size_t t_buffers_count; /* Count of items on the list */
int t_starts_count; /* Count of starts for transaction */
};
/* State bits for buffer heads in a transaction */
#define BH_TRANS BH_PrivateStart /* Attached to a transaction */
#define BH_CSUM (BH_PrivateStart + 1) /* Requires checksum update */
BUFFER_FNS(TRANS, trans);
BUFFER_FNS(CSUM, csum);
/*
* Additional information for a buffer in a transaction.
*/
struct apfs_bh_info {
struct buffer_head *bh; /* The buffer head */
struct list_head list; /* List of buffers in the transaction */
};
/*
* List entry for an in-memory ephemeral object
*/
struct apfs_ephemeral_object_info {
u64 oid; /* Ephemeral object id */
u32 size; /* Size of the object in bytes */
void *object; /* In-memory address of the object */
};
/*
* We allocate a fixed space for the list of ephemeral objects. I don't
* actually know how big this should be allowed to get, but all the objects
* must be written down with each transaction commit, so probably not too big.
*/
#define APFS_EPHEMERAL_LIST_SIZE 32768
#define APFS_EPHEMERAL_LIST_LIMIT (APFS_EPHEMERAL_LIST_SIZE / sizeof(struct apfs_ephemeral_object_info))
/* Mount option flags for a container */
#define APFS_CHECK_NODES 1
#define APFS_READWRITE 2
/*
* Mount options for a container are decided on its first mount. This flag lets
* future mounts know that has happened already.
*/
#define APFS_FLAGS_SET 4
/*
* Wrapper around block devices for portability.
*/
struct apfs_blkdev_info {
struct block_device *blki_bdev;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0) || RHEL_VERSION_GE(9, 5)
struct file *blki_bdev_file;
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
struct bdev_handle *blki_bdev_handle;
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0) && !RHEL_VERSION_GE(9, 4)
fmode_t blki_mode;
#endif
/*
* For tier 2, we need to remember the mount path for show_options().
* We don't need this for the main device of course, but better to
* keep things consistent.
*/
char *blki_path;
};
/*
* Container superblock data in memory
*/
struct apfs_nxsb_info {
/* Block device info for the container */
struct apfs_blkdev_info *nx_blkdev_info;
struct apfs_blkdev_info *nx_tier2_info;
u64 nx_tier2_bno; /* Offset for tier 2 block numbers */
struct apfs_nx_superblock *nx_raw; /* On-disk main sb */
u64 nx_bno; /* Current block number for the checkpoint superblock */
u64 nx_xid; /* Latest transaction id */
/* List of ephemeral objects in memory (except the superblock) */
struct apfs_ephemeral_object_info *nx_eph_list;
int nx_eph_count;
struct list_head vol_list; /* List of mounted volumes in container */
unsigned int nx_flags; /* Mount options shared by all volumes */
unsigned int nx_refcnt; /* Number of mounted volumes in container */
/* TODO: handle block sizes above the maximum of PAGE_SIZE? */
unsigned long nx_blocksize;
unsigned char nx_blocksize_bits;
struct apfs_spaceman *nx_spaceman;
struct apfs_nx_transaction nx_transaction;
int nx_trans_buffers_max;
/* For now, a single semaphore for every operation */
struct rw_semaphore nx_big_sem;
/* List of currently mounted containers */
struct list_head nx_list;
};
extern struct mutex nxs_mutex;
/*
* Omap mapping in memory.
* TODO: could this and apfs_omap_rec be the same struct?
*/
struct apfs_omap_map {
u64 xid;
u64 bno;
u32 flags;
};
/*
* Omap record data in memory
*/
struct apfs_omap_rec {
u64 oid;
u64 bno;
};
#define APFS_OMAP_CACHE_SLOTS 128
#define APFS_OMAP_CACHE_SLOT_MASK (APFS_OMAP_CACHE_SLOTS - 1)
/**
* Cache of omap records
*/
struct apfs_omap_cache {
struct apfs_omap_rec recs[APFS_OMAP_CACHE_SLOTS];
bool disabled;
spinlock_t lock;
};
/*
* Omap structure shared by all snapshots for the same volume.
*/
struct apfs_omap {
struct apfs_node *omap_root;
struct apfs_omap_cache omap_cache;
/* Transaction id for most recent snapshot */
u64 omap_latest_snap;
/* Number of snapshots sharing this omap */
unsigned int omap_refcnt;
};
/*
* Volume superblock data in memory
*/
struct apfs_sb_info {
struct apfs_nxsb_info *s_nxi; /* In-memory container sb for volume */
struct list_head list; /* List of mounted volumes in container */
struct apfs_superblock *s_vsb_raw; /* On-disk volume sb */
dev_t s_anon_dev; /* Anonymous device for this volume-snapshot */
char *s_tier2_path; /* Path to the tier 2 device */
char *s_snap_name; /* Label for the mounted snapshot */
u64 s_snap_xid; /* Transaction id for mounted snapshot */
struct apfs_node *s_cat_root; /* Root of the catalog tree */
struct apfs_omap *s_omap; /* The object map */
struct apfs_object s_vobject; /* Volume superblock object */
/* Mount options */
unsigned int s_vol_nr; /* Index of the volume in the sb list */
kuid_t s_uid; /* uid to override on-disk uid */
kgid_t s_gid; /* gid to override on-disk gid */
unsigned int s_mount_opt;
struct apfs_crypto_state_val *s_dflt_pfk; /* default per-file key */
struct inode *s_private_dir; /* Inode for the private directory */
struct work_struct s_orphan_cleanup_work;
atomic_t s_orphan_cleanup_err; /* Error from last orphan cleanup */
};
static inline struct apfs_sb_info *APFS_SB(struct super_block *sb)
{
return sb->s_fs_info;
}
static inline bool apfs_is_sealed(struct super_block *sb)
{
u64 flags = le64_to_cpu(APFS_SB(sb)->s_vsb_raw->apfs_incompatible_features);
return flags & APFS_INCOMPAT_SEALED_VOLUME;
}
/**
* apfs_vol_is_encrypted - Check if a volume is encrypting files
* @sb: superblock
*/
static inline bool apfs_vol_is_encrypted(struct super_block *sb)
{
struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw;
return (vsb_raw->apfs_fs_flags & cpu_to_le64(APFS_FS_UNENCRYPTED)) == 0;
}
/**
* APFS_NXI - Get the shared container info for a volume's superblock
* @sb: superblock structure
*/
static inline struct apfs_nxsb_info *APFS_NXI(struct super_block *sb)
{
return APFS_SB(sb)->s_nxi;
}
/**
* APFS_SM - Get the shared spaceman struct for a volume's superblock
* @sb: superblock structure
*/
static inline struct apfs_spaceman *APFS_SM(struct super_block *sb)
{
return APFS_NXI(sb)->nx_spaceman;
}
static inline bool apfs_is_case_insensitive(struct super_block *sb)
{
return (APFS_SB(sb)->s_vsb_raw->apfs_incompatible_features &
cpu_to_le64(APFS_INCOMPAT_CASE_INSENSITIVE)) != 0;
}
static inline bool apfs_is_normalization_insensitive(struct super_block *sb)
{
struct apfs_sb_info *sbi = APFS_SB(sb);
u64 flags = le64_to_cpu(sbi->s_vsb_raw->apfs_incompatible_features);
if (apfs_is_case_insensitive(sb))
return true;
if (flags & APFS_INCOMPAT_NORMALIZATION_INSENSITIVE)
return true;
return false;
}
/**
* apfs_max_maps_per_block - Find the maximum map count for a mapping block
* @sb: superblock structure
*/
static inline int apfs_max_maps_per_block(struct super_block *sb)
{
unsigned long maps_size;
maps_size = (sb->s_blocksize - sizeof(struct apfs_checkpoint_map_phys));
return maps_size / sizeof(struct apfs_checkpoint_mapping);
}
/*
* In-memory representation of a key, as relevant for a b-tree query.
*/
struct apfs_key {
u64 id;
u64 number; /* Extent offset, name hash or transaction id */
const char *name; /* On-disk name string */
u8 type; /* Record type (0 for the omap) */
};
/**
* apfs_init_free_queue_key - Initialize an in-memory key for a free queue query
* @xid: transaction id
* @paddr: block number
* @key: apfs_key structure to initialize
*/
static inline void apfs_init_free_queue_key(u64 xid, u64 paddr,
struct apfs_key *key)
{
key->id = xid;
key->type = 0;
key->number = paddr;
key->name = NULL;
}
/**
* apfs_init_omap_key - Initialize an in-memory key for an omap query
* @oid: object id
* @xid: latest transaction id
* @key: apfs_key structure to initialize
*/
static inline void apfs_init_omap_key(u64 oid, u64 xid, struct apfs_key *key)
{
key->id = oid;
key->type = 0;
key->number = xid;
key->name = NULL;
}
/**
* apfs_init_extent_key - Initialize an in-memory key for an extentref query
* @bno: physical block number for the start of the extent
* @key: apfs_key structure to initialize
*/
static inline void apfs_init_extent_key(u64 bno, struct apfs_key *key)
{
key->id = bno;
key->type = APFS_TYPE_EXTENT;
key->number = 0;
key->name = NULL;
}
/**
* apfs_init_inode_key - Initialize an in-memory key for an inode query
* @ino: inode number
* @key: apfs_key structure to initialize
*/
static inline void apfs_init_inode_key(u64 ino, struct apfs_key *key)
{
key->id = ino;
key->type = APFS_TYPE_INODE;
key->number = 0;
key->name = NULL;
}
/**
* apfs_init_file_extent_key - Initialize an in-memory key for an extent query
* @id: extent id
* @offset: logical address (0 for a multiple query)
* @key: apfs_key structure to initialize
*/
static inline void apfs_init_file_extent_key(u64 id, u64 offset,
struct apfs_key *key)
{
key->id = id;
key->type = APFS_TYPE_FILE_EXTENT;
key->number = offset;
key->name = NULL;
}
static inline void apfs_init_fext_key(u64 id, u64 offset, struct apfs_key *key)
{
key->id = id;
key->type = 0;
key->number = offset;
key->name = NULL;
}
/**
* apfs_init_dstream_id_key - Initialize an in-memory key for a dstream query
* @id: data stream id
* @key: apfs_key structure to initialize
*/
static inline void apfs_init_dstream_id_key(u64 id, struct apfs_key *key)
{
key->id = id;
key->type = APFS_TYPE_DSTREAM_ID;
key->number = 0;
key->name = NULL;
}
/**
* apfs_init_crypto_state_key - Initialize an in-memory key for a crypto query
* @id: crypto state id
* @key: apfs_key structure to initialize
*/
static inline void apfs_init_crypto_state_key(u64 id, struct apfs_key *key)
{
key->id = id;
key->type = APFS_TYPE_CRYPTO_STATE;
key->number = 0;
key->name = NULL;
}
/**
* apfs_init_sibling_link_key - Initialize an in-memory key for a sibling query
* @ino: inode number
* @id: sibling id
* @key: apfs_key structure to initialize
*/
static inline void apfs_init_sibling_link_key(u64 ino, u64 id,
struct apfs_key *key)
{
key->id = ino;
key->type = APFS_TYPE_SIBLING_LINK;
key->number = id; /* Only guessing (TODO) */
key->name = NULL;
}
/**
* apfs_init_sibling_map_key - Initialize in-memory key for a sibling map query
* @id: sibling id
* @key: apfs_key structure to initialize
*/
static inline void apfs_init_sibling_map_key(u64 id, struct apfs_key *key)
{
key->id = id;
key->type = APFS_TYPE_SIBLING_MAP;
key->number = 0;
key->name = NULL;
}
extern void apfs_init_drec_key(struct super_block *sb, u64 ino, const char *name,
unsigned int name_len, struct apfs_key *key);
/**
* apfs_init_xattr_key - Initialize an in-memory key for a xattr query
* @ino: inode number of the parent file
* @name: xattr name (NULL for a multiple query)
* @key: apfs_key structure to initialize
*/
static inline void apfs_init_xattr_key(u64 ino, const char *name,
struct apfs_key *key)
{
key->id = ino;
key->type = APFS_TYPE_XATTR;
key->number = 0;
key->name = name;
}
static inline void apfs_init_snap_metadata_key(u64 xid, struct apfs_key *key)
{
key->id = xid;
key->type = APFS_TYPE_SNAP_METADATA;
key->number = 0;
key->name = NULL;
}
static inline void apfs_init_snap_name_key(const char *name, struct apfs_key *key)
{
key->id = APFS_SNAP_NAME_OBJ_ID;
key->type = APFS_TYPE_SNAP_NAME;
key->number = 0;
key->name = name;
}
static inline void apfs_init_omap_snap_key(u64 xid, struct apfs_key *key)
{
key->id = xid;
key->type = 0;
key->number = 0;
key->name = NULL;
}
/**
* apfs_key_set_hdr - Set the header for a raw catalog key
* @type: record type
* @id: record id
* @key: the key to initialize
*/
static inline void apfs_key_set_hdr(u64 type, u64 id, void *key)
{
struct apfs_key_header *hdr = key;
hdr->obj_id_and_type = cpu_to_le64(id | type << APFS_OBJ_TYPE_SHIFT);
}
/**
* apfs_cat_type - Read the record type of a catalog key
* @key: the raw catalog key
*/
static inline int apfs_cat_type(struct apfs_key_header *key)
{
return (le64_to_cpu(key->obj_id_and_type) & APFS_OBJ_TYPE_MASK) >> APFS_OBJ_TYPE_SHIFT;
}
/**
* apfs_cat_cnid - Read the cnid value on a catalog key
* @key: the raw catalog key
*
* TODO: rename this function, since it's not just for the catalog anymore
*/
static inline u64 apfs_cat_cnid(struct apfs_key_header *key)
{
return le64_to_cpu(key->obj_id_and_type) & APFS_OBJ_ID_MASK;
}
/* Flags for the query structure */
#define APFS_QUERY_TREE_MASK 000177 /* Which b-tree we query */
#define APFS_QUERY_OMAP 000001 /* This is a b-tree object map query */
#define APFS_QUERY_CAT 000002 /* This is a catalog tree query */
#define APFS_QUERY_FREE_QUEUE 000004 /* This is a free queue query */
#define APFS_QUERY_EXTENTREF 000010 /* This is an extent reference query */
#define APFS_QUERY_FEXT 000020 /* This is a fext tree query */
#define APFS_QUERY_SNAP_META 000040 /* This is a snapshot meta query */
#define APFS_QUERY_OMAP_SNAP 000100 /* This is an omap snapshots query */
#define APFS_QUERY_NEXT 000200 /* Find next of multiple matches */
#define APFS_QUERY_EXACT 000400 /* Search for an exact match */
#define APFS_QUERY_DONE 001000 /* The search at this level is over */
#define APFS_QUERY_ANY_NAME 002000 /* Multiple search for any name */
#define APFS_QUERY_ANY_NUMBER 004000 /* Multiple search for any number */
#define APFS_QUERY_MULTIPLE (APFS_QUERY_ANY_NAME | APFS_QUERY_ANY_NUMBER)
#define APFS_QUERY_PREV 010000 /* Find previous record */
/*
* Structure used to retrieve data from an APFS B-Tree.
*/
struct apfs_query {
struct apfs_node *node; /* Node being searched */
struct apfs_key key; /* What the query is looking for */
struct apfs_query *parent; /* Query for parent node */
unsigned int flags;
/* Set by the query on success */
int index; /* Index of the entry in the node */
int key_off; /* Offset of the key in the node */
int key_len; /* Length of the key */
int off; /* Offset of the value in the node */
int len; /* Length of the value */
int depth; /* Put a limit on recursion */
};
/**
* apfs_query_storage - Get the storage type for a query's btree
* @query: the query structure
*/
static inline u32 apfs_query_storage(struct apfs_query *query)
{
if (query->flags & APFS_QUERY_OMAP)
return APFS_OBJ_PHYSICAL;
if (query->flags & APFS_QUERY_CAT)
return APFS_OBJ_VIRTUAL;
if (query->flags & APFS_QUERY_FEXT)
return APFS_OBJ_PHYSICAL;
if (query->flags & APFS_QUERY_FREE_QUEUE)
return APFS_OBJ_EPHEMERAL;
if (query->flags & APFS_QUERY_EXTENTREF)
return APFS_OBJ_PHYSICAL;
if (query->flags & APFS_QUERY_SNAP_META)
return APFS_OBJ_PHYSICAL;
if (query->flags & APFS_QUERY_OMAP_SNAP)
return APFS_OBJ_PHYSICAL;
/* Absurd, but don't panic: let the callers fail and report it */
return -1;
}
/*
* Extent record data in memory
*/
struct apfs_file_extent {
u64 logical_addr;
u64 phys_block_num;
u64 len;
u64 crypto_id;
};
/*
* Physical extent record data in memory
*/
struct apfs_phys_extent {
u64 bno;
u64 blkcount;
u64 len; /* In bytes */
u32 refcnt;
u8 kind;
};
/*
* Data stream info in memory
*/
struct apfs_dstream_info {
struct super_block *ds_sb; /* Filesystem superblock */
struct inode *ds_inode; /* NULL for xattr dstreams */
u64 ds_id; /* ID of the extent records */
u64 ds_size; /* Length of the stream */
u64 ds_sparse_bytes;/* Hole byte count in stream */
struct apfs_file_extent ds_cached_ext; /* Latest extent record */
bool ds_ext_dirty; /* Is ds_cached_ext dirty? */
spinlock_t ds_ext_lock; /* Protects ds_cached_ext */
bool ds_shared; /* Has multiple references? */
};
/**
* apfs_alloced_size - Return the alloced size for a data stream
* @dstream: data stream info
*
* TODO: is this always correct? Or could the extents have an unused tail?
*/
static inline u64 apfs_alloced_size(struct apfs_dstream_info *dstream)
{
struct super_block *sb = dstream->ds_sb;
u64 blks = (dstream->ds_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
return blks << sb->s_blocksize_bits;
}
/*
* APFS inode data in memory
*/
struct apfs_inode_info {
u64 i_ino64; /* 32-bit-safe inode number */
u64 i_parent_id; /* ID of primary parent */
struct timespec64 i_crtime; /* Time of creation */
u32 i_nchildren; /* Child count for directory */
uid_t i_saved_uid; /* User ID on disk */
gid_t i_saved_gid; /* Group ID on disk */
u32 i_key_class; /* Security class for directory */
u64 i_int_flags; /* Internal flags */
u32 i_bsd_flags; /* BSD flags */
struct list_head i_list; /* List of inodes in transaction */
bool i_has_dstream; /* Is there a dstream record? */
struct apfs_dstream_info i_dstream; /* Dstream data, if any */
bool i_cleaned; /* Orphan data already deleted */
struct inode vfs_inode;
};
static inline struct apfs_inode_info *APFS_I(const struct inode *inode)
{
return container_of(inode, struct apfs_inode_info, vfs_inode);
}
/**
* apfs_ino - Get the 64-bit id of an inode
* @inode: the vfs inode
*
* Returns all 64 bits of @inode's id, even on 32-bit architectures.
*/
static inline u64 apfs_ino(const struct inode *inode)
{
return APFS_I(inode)->i_ino64;
}
/**
* apfs_set_ino - Set a 64-bit id on an inode
* @inode: the vfs inode
* @id: id to set
*
* Sets both the vfs inode number and the actual 32-bit-safe id.
*/
static inline void apfs_set_ino(struct inode *inode, u64 id)
{
inode->i_ino = id; /* Higher bits may be lost, but it doesn't matter */
APFS_I(inode)->i_ino64 = id;
}
/* Make the compiler complain if we ever access i_ino directly by mistake */
#define i_ino DONT_USE_I_INO
/*
* Directory entry record in memory
*/
struct apfs_drec {
u8 *name;
u64 ino;
u64 sibling_id; /* The sibling id; 0 if none */
int name_len;
unsigned int type;
};
/*
* Xattr record data in memory
*/
struct apfs_xattr {
u8 *name;
u8 *xdata;
int name_len;
int xdata_len;
bool has_dstream;
};
struct apfs_compressed_data {
bool has_dstream;
u64 size;
union {
struct apfs_dstream_info *dstream;
void *data;
};
};
/*
* Report function name and line number for the message types that are likely
* to signal a bug, to make things easier for reporters. Don't do this for the
* common messages, there is no point and it makes the console look too busy.
*/
#define apfs_emerg(sb, fmt, ...) apfs_msg(sb, KERN_EMERG, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define apfs_alert(sb, fmt, ...) apfs_msg(sb, KERN_ALERT, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define apfs_crit(sb, fmt, ...) apfs_msg(sb, KERN_CRIT, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define apfs_err(sb, fmt, ...) apfs_msg(sb, KERN_ERR, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define apfs_warn(sb, fmt, ...) apfs_msg(sb, KERN_WARNING, NULL, 0, fmt, ##__VA_ARGS__)
#define apfs_notice(sb, fmt, ...) apfs_msg(sb, KERN_NOTICE, NULL, 0, fmt, ##__VA_ARGS__)
#define apfs_info(sb, fmt, ...) apfs_msg(sb, KERN_INFO, NULL, 0, fmt, ##__VA_ARGS__)
#ifdef CONFIG_APFS_DEBUG
#define ASSERT(expr) WARN_ON(!(expr))
#define apfs_debug(sb, fmt, ...) apfs_msg(sb, KERN_DEBUG, __func__, __LINE__, fmt, ##__VA_ARGS__)
#else
#define ASSERT(expr) ((void)0)
#define apfs_debug(sb, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
#endif /* CONFIG_APFS_DEBUG */
/**
* apfs_assert_in_transaction - Assert that the object is in current transaction
* @sb: superblock structure
* @obj: on-disk object to check
*/
#define apfs_assert_in_transaction(sb, obj) \
do { \
(void)sb; \
(void)obj; \
ASSERT(le64_to_cpu((obj)->o_xid) == APFS_NXI(sb)->nx_xid); \
} while (0)
/* btree.c */
extern struct apfs_node *apfs_query_root(const struct apfs_query *query);
extern struct apfs_query *apfs_alloc_query(struct apfs_node *node,
struct apfs_query *parent);
extern void apfs_free_query(struct apfs_query *query);
extern int apfs_btree_query(struct super_block *sb, struct apfs_query **query);
extern int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap, u64 id, u64 *block, bool write);
extern int apfs_omap_lookup_newest_block(struct super_block *sb, struct apfs_omap *omap, u64 id, u64 *block, bool write);
extern int apfs_create_omap_rec(struct super_block *sb, u64 oid, u64 bno);
extern int apfs_delete_omap_rec(struct super_block *sb, u64 oid);
extern int apfs_query_join_transaction(struct apfs_query *query);
extern int __apfs_btree_insert(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
extern int apfs_btree_insert(struct apfs_query *query, void *key, int key_len,
void *val, int val_len);
extern int apfs_btree_remove(struct apfs_query *query);
extern void apfs_btree_change_node_count(struct apfs_query *query, int change);
extern int apfs_btree_replace(struct apfs_query *query, void *key, int key_len,
void *val, int val_len);
extern void apfs_query_direct_forward(struct apfs_query *query);
/* compress.c */
extern int apfs_compress_get_size(struct inode *inode, loff_t *size);
/* dir.c */
extern int apfs_inode_by_name(struct inode *dir, const struct qstr *child,
u64 *ino);
extern int apfs_mkany(struct inode *dir, struct dentry *dentry,
umode_t mode, dev_t rdev, const char *symname);
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
extern int apfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
dev_t rdev);
extern int apfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode);
extern int apfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags);
extern int apfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool excl);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0) && !RHEL_VERSION_GE(9, 6)
extern int apfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, dev_t rdev);
extern int apfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode);
extern int apfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags);
extern int apfs_create(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 15, 0)
extern int apfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, dev_t rdev);
extern int apfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode);
extern int apfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags);
extern int apfs_create(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl);
#else
extern int apfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, dev_t rdev);
extern struct dentry *apfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode);
extern int apfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags);
extern int apfs_create(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl);
#endif
extern int apfs_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *dentry);
extern int apfs_unlink(struct inode *dir, struct dentry *dentry);
extern int apfs_rmdir(struct inode *dir, struct dentry *dentry);
extern int apfs_delete_orphan_link(struct inode *inode);
extern u64 apfs_any_orphan_ino(struct super_block *sb, u64 *ino_p);
/* extents.c */
extern int apfs_extent_from_query(struct apfs_query *query,
struct apfs_file_extent *extent);
extern int apfs_logic_to_phys_bno(struct apfs_dstream_info *dstream, sector_t dsblock, u64 *bno);
extern int __apfs_get_block(struct apfs_dstream_info *dstream, sector_t iblock,
struct buffer_head *bh_result, int create);
extern int apfs_get_block(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create);
extern int apfs_flush_extent_cache(struct apfs_dstream_info *dstream);
extern int apfs_dstream_get_new_bno(struct apfs_dstream_info *dstream, u64 dsblock, u64 *bno);
extern int apfs_get_new_block(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create);
extern int apfs_truncate(struct apfs_dstream_info *dstream, loff_t new_size);
extern int apfs_inode_delete_front(struct inode *inode);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
extern loff_t apfs_remap_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, loff_t len, unsigned int remap_flags);
#else
extern int apfs_clone_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, u64 len);
#endif
extern int apfs_clone_extents(struct apfs_dstream_info *dstream, u64 new_id);
extern int apfs_nonsparse_dstream_read(struct apfs_dstream_info *dstream, void *buf, size_t count, u64 offset);
extern void apfs_nonsparse_dstream_preread(struct apfs_dstream_info *dstream);
/* file.c */
extern int apfs_file_mmap(struct file *file, struct vm_area_struct *vma);
extern int apfs_fsync(struct file *file, loff_t start, loff_t end, int datasync);
/* inode.c */
extern struct inode *apfs_iget(struct super_block *sb, u64 cnid);
extern int apfs_update_inode(struct inode *inode, char *new_name);
extern void apfs_schedule_orphan_cleanup(struct super_block *sb);
extern void apfs_orphan_cleanup_work(struct work_struct *work);
extern void apfs_evict_inode(struct inode *inode);
extern struct inode *apfs_new_inode(struct inode *dir, umode_t mode,
dev_t rdev);
extern int apfs_create_inode_rec(struct super_block *sb, struct inode *inode,
struct dentry *dentry);
extern int apfs_inode_create_exclusive_dstream(struct inode *inode);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0)
extern int __apfs_write_begin(const struct kiocb *iocb, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int flags, struct page **pagep, void **fsdata);
extern int __apfs_write_end(const struct kiocb *iocb, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int copied, struct page *page, void *fsdata);
#else
extern int __apfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int flags, struct page **pagep, void **fsdata);
extern int __apfs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int copied, struct page *page, void *fsdata);
#endif
extern int apfs_dstream_adj_refcnt(struct apfs_dstream_info *dstream, u32 delta);
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
extern int apfs_setattr(struct dentry *dentry, struct iattr *iattr);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0) && !RHEL_VERSION_GE(9, 6)
extern int apfs_setattr(struct user_namespace *mnt_userns,
struct dentry *dentry, struct iattr *iattr);
#else
extern int apfs_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *iattr);
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
extern int apfs_update_time(struct inode *inode, enum fs_update_time time, unsigned int flags);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
extern int apfs_update_time(struct inode *inode, struct timespec64 *time, int flags);
#else
extern int apfs_update_time(struct inode *inode, int flags);
#endif
long apfs_dir_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
long apfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) /* No statx yet... */
extern int apfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
extern int apfs_getattr(const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int query_flags);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0) && !RHEL_VERSION_GE(9, 6)
extern int apfs_getattr(struct user_namespace *mnt_userns,
const struct path *path, struct kstat *stat, u32 request_mask,
unsigned int query_flags);
#else
extern int apfs_getattr(struct mnt_idmap *idmap,
const struct path *path, struct kstat *stat, u32 request_mask,
unsigned int query_flags);
#endif
extern int apfs_crypto_adj_refcnt(struct super_block *sb, u64 crypto_id, int delta);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0)
#define fileattr file_kattr
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) || RHEL_VERSION_GE(9, 6)
extern int apfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
extern int apfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, struct fileattr *fa);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
extern int apfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
extern int apfs_fileattr_set(struct user_namespace *mnt_userns, struct dentry *dentry, struct fileattr *fa);
#endif
/* key.c */
extern int apfs_filename_cmp(struct super_block *sb, const char *name1, unsigned int len1, const char *name2, unsigned int len2);
extern int apfs_keycmp(struct apfs_key *k1, struct apfs_key *k2);
extern int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed);
extern int apfs_read_fext_key(void *raw, int size, struct apfs_key *key);
extern int apfs_read_free_queue_key(void *raw, int size, struct apfs_key *key);
extern int apfs_read_omap_key(void *raw, int size, struct apfs_key *key);
extern int apfs_read_extentref_key(void *raw, int size, struct apfs_key *key);
extern int apfs_read_snap_meta_key(void *raw, int size, struct apfs_key *key);
extern int apfs_read_omap_snap_key(void *raw, int size, struct apfs_key *key);
/* message.c */
extern __printf(5, 6) void apfs_msg(struct super_block *sb, const char *prefix, const char *func, int line, const char *fmt, ...);
/* node.c */
extern struct apfs_node *apfs_read_node(struct super_block *sb, u64 oid,
u32 storage, bool write);
extern void apfs_update_node(struct apfs_node *node);
extern int apfs_delete_node(struct apfs_node *node, int type);
extern int apfs_node_query(struct super_block *sb, struct apfs_query *query);
extern void apfs_node_query_first(struct apfs_query *query);
extern int apfs_omap_map_from_query(struct apfs_query *query, struct apfs_omap_map *map);
extern int apfs_node_split(struct apfs_query *query);
extern int apfs_node_locate_key(struct apfs_node *node, int index, int *off);
extern void apfs_node_free(struct apfs_node *node);
extern void apfs_node_free_range(struct apfs_node *node, u16 off, u16 len);
extern bool apfs_node_has_room(struct apfs_node *node, int length, bool replace);
extern int apfs_node_replace(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
extern int apfs_node_insert(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
extern int apfs_create_single_rec_node(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
extern int apfs_make_empty_btree_root(struct super_block *sb, u32 subtype, u64 *oid);
/* object.c */
extern int apfs_obj_verify_csum(struct super_block *sb, struct buffer_head *bh);
extern void apfs_obj_set_csum(struct super_block *sb, struct apfs_obj_phys *obj);
extern int apfs_multiblock_verify_csum(char *object, u32 size);
extern void apfs_multiblock_set_csum(char *object, u32 size);
extern int apfs_create_cpm_block(struct super_block *sb, u64 bno, struct buffer_head **bh_p);
extern int apfs_create_cpoint_map(struct super_block *sb, struct apfs_checkpoint_map_phys *cpm, struct apfs_obj_phys *obj, u64 bno, u32 size);
extern struct apfs_ephemeral_object_info *apfs_ephemeral_object_lookup(struct super_block *sb, u64 oid);
extern struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool write, bool preserve);
extern u32 apfs_index_in_data_area(struct super_block *sb, u64 bno);
extern u64 apfs_data_index_to_bno(struct super_block *sb, u32 index);
/* snapshot.c */
extern int apfs_ioc_take_snapshot(struct file *file, void __user *user_arg);
extern int apfs_switch_to_snapshot(struct super_block *sb);
/* spaceman.c */
extern int apfs_read_spaceman(struct super_block *sb);
extern int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count);
extern int apfs_free_queue_insert(struct super_block *sb, u64 bno, u64 count);
extern int apfs_spaceman_allocate_block(struct super_block *sb, u64 *bno, bool backwards);
extern int apfs_write_ip_bitmaps(struct super_block *sb);
extern int apfs_spaceman_get_free_blkcnt(struct super_block *sb, u64 *blkcnt);
/* super.c */
extern int apfs_map_volume_super_bno(struct super_block *sb, u64 bno, bool check);
extern int apfs_map_volume_super(struct super_block *sb, bool write);
extern void apfs_unmap_volume_super(struct super_block *sb);
extern int apfs_read_omap(struct super_block *sb, bool write);
extern int apfs_read_catalog(struct super_block *sb, bool write);
extern int apfs_sync_fs(struct super_block *sb, int wait);
/* transaction.c */
extern int apfs_cpoint_data_free(struct super_block *sb, u64 bno);
extern void apfs_transaction_init(struct apfs_nx_transaction *trans);
extern int apfs_transaction_start(struct super_block *sb, enum apfs_trans_kind kind);
extern int apfs_transaction_commit(struct super_block *sb);
extern void apfs_inode_join_transaction(struct super_block *sb, struct inode *inode);
extern int apfs_transaction_join(struct super_block *sb,
struct buffer_head *bh);
void apfs_transaction_abort(struct super_block *sb);
extern int apfs_transaction_flush_all_inodes(struct super_block *sb);
extern int apfs_read_ephemeral_objects(struct super_block *sb);
/* xattr.c */
extern int ____apfs_xattr_get(struct inode *inode, const char *name, void *buffer,
size_t size, bool only_whole);
extern int __apfs_xattr_get(struct inode *inode, const char *name, void *buffer,
size_t size);
extern int apfs_delete_all_xattrs(struct inode *inode);
extern int apfs_xattr_set(struct inode *inode, const char *name, const void *value,
size_t size, int flags);
extern ssize_t apfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
extern int apfs_xattr_get_compressed_data(struct inode *inode, const char *name, struct apfs_compressed_data *cdata);
extern void apfs_release_compressed_data(struct apfs_compressed_data *cdata);
extern int apfs_compressed_data_read(struct apfs_compressed_data *cdata, void *buf, size_t count, u64 offset);
/* xfield.c */
extern int apfs_find_xfield(u8 *xfields, int len, u8 xtype, char **xval);
extern int apfs_init_xfields(u8 *buffer, int buflen);
extern int apfs_insert_xfield(u8 *buffer, int buflen,
const struct apfs_x_field *xkey,
const void *xval);
/*
* Inode and file operations
*/
/* compress.c */
extern const struct address_space_operations apfs_compress_aops;
extern const struct file_operations apfs_compress_file_operations;
/* dir.c */
extern const struct file_operations apfs_dir_operations;
/* file.c */
extern const struct file_operations apfs_file_operations;
extern const struct inode_operations apfs_file_inode_operations;
/* namei.c */
extern const struct inode_operations apfs_dir_inode_operations;
extern const struct inode_operations apfs_special_inode_operations;
extern const struct dentry_operations apfs_dentry_operations;
/* symlink.c */
extern const struct inode_operations apfs_symlink_inode_operations;
/* xattr.c */
extern const struct xattr_handler *apfs_xattr_handlers[];
/**
* apfs_assert_query_is_valid - Assert that all of a query's ancestors are set
* @query: the query to check
*
* A query may lose some of its ancestors during a node split, but nothing
* should be done to such a query until it gets refreshed.
*/
static inline void apfs_assert_query_is_valid(const struct apfs_query *query)
{
ASSERT(apfs_node_is_root(apfs_query_root(query)));
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 15, 0)
#define page_has_buffers(page) folio_buffers(page_folio(page))
#endif
static inline int apfs_inode_state_read_once(struct inode *inode)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 19, 0)
return inode_state_read_once(inode);
#else
return inode->i_state;
#endif
}
static inline void apfs_inode_state_set_raw(struct inode *inode, int flags)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 19, 0)
inode_state_set_raw(inode, flags);
#else
inode->i_state |= flags;
#endif
}
static inline void apfs_inode_state_clear_raw(struct inode *inode, int flags)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 19, 0)
inode_state_clear_raw(inode, flags);
#else
inode->i_state &= ~flags;
#endif
}
/*
* TODO: the following are modified variants of buffer head functions that will
* work with the shared block device for the container. The correct approach
* here would be to avoid buffer heads and use bios, but for now this will do.
*/
static inline void
apfs_map_bh(struct buffer_head *bh, struct super_block *sb, sector_t block)
{
struct apfs_nxsb_info *nxi = NULL;
struct apfs_blkdev_info *info = NULL;
nxi = APFS_NXI(sb);
info = nxi->nx_blkdev_info;
/*
* Block numbers above s_tier2_bno are for the tier 2 device of a
* fusion drive. Don't bother returning an error code if a regular
* drive gets corrupted and reports a tier 2 block number: just treat
* it the same as any other out-of-range block.
*/
if (block >= nxi->nx_tier2_bno) {
if (nxi->nx_tier2_info) {
info = nxi->nx_tier2_info;
block -= nxi->nx_tier2_bno;
} else {
apfs_err(sb, "block number in tier 2 range (0x%llx)", (unsigned long long)block);
}
}
set_buffer_mapped(bh);
bh->b_bdev = info->blki_bdev;
bh->b_blocknr = block;
bh->b_size = sb->s_blocksize;
}
static inline struct buffer_head *
apfs_sb_bread(struct super_block *sb, sector_t block)
{
struct apfs_nxsb_info *nxi = NULL;
struct apfs_blkdev_info *info = NULL;
nxi = APFS_NXI(sb);
info = nxi->nx_blkdev_info;
if (block >= nxi->nx_tier2_bno) {
if (!nxi->nx_tier2_info) {
/* Not really a fusion drive, so it's corrupted */
apfs_err(sb, "block number in tier 2 range (0x%llx)", (unsigned long long)block);
return NULL;
}
info = nxi->nx_tier2_info;
block -= nxi->nx_tier2_bno;
}
return __bread_gfp(info->blki_bdev, block, sb->s_blocksize, __GFP_MOVABLE);
}
/* Like apfs_getblk(), but doesn't mark the buffer uptodate */
static inline struct buffer_head *
__apfs_getblk(struct super_block *sb, sector_t block)
{
struct apfs_nxsb_info *nxi = NULL;
struct apfs_blkdev_info *info = NULL;
nxi = APFS_NXI(sb);
info = nxi->nx_blkdev_info;
if (block >= nxi->nx_tier2_bno) {
if (!nxi->nx_tier2_info) {
/* Not really a fusion drive, so it's corrupted */
apfs_err(sb, "block number in tier 2 range (0x%llx)", (unsigned long long)block);
return NULL;
}
info = nxi->nx_tier2_info;
block -= nxi->nx_tier2_bno;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
return __getblk_gfp(info->blki_bdev, block, sb->s_blocksize, __GFP_MOVABLE);
#else
return bdev_getblk(info->blki_bdev, block, sb->s_blocksize, __GFP_MOVABLE);
#endif
}
/* Use instead of apfs_sb_bread() for blocks that will just be overwritten */
static inline struct buffer_head *
apfs_getblk(struct super_block *sb, sector_t block)
{
struct buffer_head *bh = NULL;
bh = __apfs_getblk(sb, block);
if (bh)
set_buffer_uptodate(bh);
return bh;
}
#endif /* _APFS_H */