| /* SPDX-License-Identifier: GPL-2.0 */ | |
| /* | |
| * linux/include/linux/hfsplus_fs.h | |
| * | |
| * Copyright (C) 1999 | |
| * Brad Boyer (flar@pants.nu) | |
| * (C) 2003 Ardis Technologies <roman@ardistech.com> | |
| * | |
| */ | |
| #ifndef _LINUX_HFSPLUS_FS_H | |
| #define _LINUX_HFSPLUS_FS_H | |
| #include <linux/fs.h> | |
| #include <linux/mutex.h> | |
| #include <linux/buffer_head.h> | |
| #include <linux/blkdev.h> | |
| #include <linux/fs_context.h> | |
| #include "hfsplus_raw.h" | |
| /* Runtime config options */ | |
| #define HFSPLUS_DEF_CR_TYPE 0x3F3F3F3F /* '????' */ | |
| #define HFSPLUS_TYPE_DATA 0x00 | |
| #define HFSPLUS_TYPE_RSRC 0xFF | |
| typedef int (*btree_keycmp)(const hfsplus_btree_key *, | |
| const hfsplus_btree_key *); | |
| #define NODE_HASH_SIZE 256 | |
| /* B-tree mutex nested subclasses */ | |
| enum hfsplus_btree_mutex_classes { | |
| CATALOG_BTREE_MUTEX, | |
| EXTENTS_BTREE_MUTEX, | |
| ATTR_BTREE_MUTEX, | |
| }; | |
| /* An HFS+ BTree held in memory */ | |
| struct hfs_btree { | |
| struct super_block *sb; | |
| struct inode *inode; | |
| btree_keycmp keycmp; | |
| u32 cnid; | |
| u32 root; | |
| u32 leaf_count; | |
| u32 leaf_head; | |
| u32 leaf_tail; | |
| u32 node_count; | |
| u32 free_nodes; | |
| u32 attributes; | |
| unsigned int node_size; | |
| unsigned int node_size_shift; | |
| unsigned int max_key_len; | |
| unsigned int depth; | |
| struct mutex tree_lock; | |
| unsigned int pages_per_bnode; | |
| spinlock_t hash_lock; | |
| struct hfs_bnode *node_hash[NODE_HASH_SIZE]; | |
| int node_hash_cnt; | |
| }; | |
| struct page; | |
| /* An HFS+ BTree node in memory */ | |
| struct hfs_bnode { | |
| struct hfs_btree *tree; | |
| u32 prev; | |
| u32 this; | |
| u32 next; | |
| u32 parent; | |
| u16 num_recs; | |
| u8 type; | |
| u8 height; | |
| struct hfs_bnode *next_hash; | |
| unsigned long flags; | |
| wait_queue_head_t lock_wq; | |
| atomic_t refcnt; | |
| unsigned int page_offset; | |
| struct page *page[]; | |
| }; | |
| #define HFS_BNODE_LOCK 0 | |
| #define HFS_BNODE_ERROR 1 | |
| #define HFS_BNODE_NEW 2 | |
| #define HFS_BNODE_DIRTY 3 | |
| #define HFS_BNODE_DELETED 4 | |
| /* | |
| * Attributes file states | |
| */ | |
| #define HFSPLUS_EMPTY_ATTR_TREE 0 | |
| #define HFSPLUS_CREATING_ATTR_TREE 1 | |
| #define HFSPLUS_VALID_ATTR_TREE 2 | |
| #define HFSPLUS_FAILED_ATTR_TREE 3 | |
| /* | |
| * HFS+ superblock info (built from Volume Header on disk) | |
| */ | |
| struct hfsplus_vh; | |
| struct hfs_btree; | |
| struct hfsplus_sb_info { | |
| void *s_vhdr_buf; | |
| struct hfsplus_vh *s_vhdr; | |
| void *s_backup_vhdr_buf; | |
| struct hfsplus_vh *s_backup_vhdr; | |
| struct hfs_btree *ext_tree; | |
| struct hfs_btree *cat_tree; | |
| struct hfs_btree *attr_tree; | |
| atomic_t attr_tree_state; | |
| struct inode *alloc_file; | |
| struct inode *hidden_dir; | |
| struct nls_table *nls; | |
| /* Runtime variables */ | |
| u32 blockoffset; | |
| u32 min_io_size; | |
| sector_t part_start; | |
| sector_t sect_count; | |
| int fs_shift; | |
| /* immutable data from the volume header */ | |
| u32 alloc_blksz; | |
| int alloc_blksz_shift; | |
| u32 total_blocks; | |
| u32 data_clump_blocks, rsrc_clump_blocks; | |
| /* mutable data from the volume header, protected by alloc_mutex */ | |
| u32 free_blocks; | |
| struct mutex alloc_mutex; | |
| /* mutable data from the volume header, protected by vh_mutex */ | |
| u32 next_cnid; | |
| u32 file_count; | |
| u32 folder_count; | |
| struct mutex vh_mutex; | |
| /* Config options */ | |
| u32 creator; | |
| u32 type; | |
| umode_t umask; | |
| kuid_t uid; | |
| kgid_t gid; | |
| int part, session; | |
| unsigned long flags; | |
| int work_queued; /* non-zero delayed work is queued */ | |
| struct delayed_work sync_work; /* FS sync delayed work */ | |
| spinlock_t work_lock; /* protects sync_work and work_queued */ | |
| struct rcu_head rcu; | |
| }; | |
| #define HFSPLUS_SB_WRITEBACKUP 0 | |
| #define HFSPLUS_SB_NODECOMPOSE 1 | |
| #define HFSPLUS_SB_FORCE 2 | |
| #define HFSPLUS_SB_HFSX 3 | |
| #define HFSPLUS_SB_CASEFOLD 4 | |
| #define HFSPLUS_SB_NOBARRIER 5 | |
| #define HFSPLUS_SB_UID 6 | |
| #define HFSPLUS_SB_GID 7 | |
| static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb) | |
| { | |
| return sb->s_fs_info; | |
| } | |
| struct hfsplus_inode_info { | |
| atomic_t opencnt; | |
| /* | |
| * Extent allocation information, protected by extents_lock. | |
| */ | |
| u32 first_blocks; | |
| u32 clump_blocks; | |
| u32 alloc_blocks; | |
| u32 cached_start; | |
| u32 cached_blocks; | |
| hfsplus_extent_rec first_extents; | |
| hfsplus_extent_rec cached_extents; | |
| unsigned int extent_state; | |
| struct mutex extents_lock; | |
| /* | |
| * Immutable data. | |
| */ | |
| struct inode *rsrc_inode; | |
| __be32 create_date; | |
| /* | |
| * Protected by sbi->vh_mutex. | |
| */ | |
| u32 linkid; | |
| /* | |
| * Accessed using atomic bitops. | |
| */ | |
| unsigned long flags; | |
| /* | |
| * Protected by i_mutex. | |
| */ | |
| sector_t fs_blocks; | |
| u8 userflags; /* BSD user file flags */ | |
| u32 subfolders; /* Subfolder count (HFSX only) */ | |
| loff_t phys_size; | |
| struct inode vfs_inode; | |
| }; | |
| #define HFSPLUS_EXT_DIRTY 0x0001 | |
| #define HFSPLUS_EXT_NEW 0x0002 | |
| #define HFSPLUS_I_RSRC 0 /* represents a resource fork */ | |
| #define HFSPLUS_I_CAT_DIRTY 1 /* has changes in the catalog tree */ | |
| #define HFSPLUS_I_EXT_DIRTY 2 /* has changes in the extent tree */ | |
| #define HFSPLUS_I_ALLOC_DIRTY 3 /* has changes in the allocation file */ | |
| #define HFSPLUS_I_ATTR_DIRTY 4 /* has changes in the attributes tree */ | |
| #define HFSPLUS_IS_RSRC(inode) \ | |
| test_bit(HFSPLUS_I_RSRC, &HFSPLUS_I(inode)->flags) | |
| static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode) | |
| { | |
| return container_of(inode, struct hfsplus_inode_info, vfs_inode); | |
| } | |
| #define HFSPLUS_CAT_TREE_I(sb) \ | |
| HFSPLUS_SB(sb)->cat_tree->inode | |
| #define HFSPLUS_EXT_TREE_I(sb) \ | |
| HFSPLUS_SB(sb)->ext_tree->inode | |
| #define HFSPLUS_ATTR_TREE_I(sb) \ | |
| HFSPLUS_SB(sb)->attr_tree->inode | |
| /* | |
| * Mark an inode dirty, and also mark the btree in which the | |
| * specific type of metadata is stored. | |
| * For data or metadata that gets written back by into the catalog btree | |
| * by hfsplus_write_inode a plain mark_inode_dirty call is enough. | |
| */ | |
| static inline void hfsplus_mark_inode_dirty(struct inode *inode, | |
| unsigned int flag) | |
| { | |
| set_bit(flag, &HFSPLUS_I(inode)->flags); | |
| mark_inode_dirty(inode); | |
| } | |
| struct hfs_find_data { | |
| /* filled by caller */ | |
| hfsplus_btree_key *search_key; | |
| hfsplus_btree_key *key; | |
| /* filled by find */ | |
| struct hfs_btree *tree; | |
| struct hfs_bnode *bnode; | |
| /* filled by findrec */ | |
| int record; | |
| int keyoffset, keylength; | |
| int entryoffset, entrylength; | |
| }; | |
| struct hfsplus_readdir_data { | |
| loff_t pos; | |
| struct hfsplus_cat_key key; | |
| }; | |
| /* | |
| * Find minimum acceptible I/O size for an hfsplus sb. | |
| */ | |
| static inline unsigned short hfsplus_min_io_size(struct super_block *sb) | |
| { | |
| return max_t(unsigned short, HFSPLUS_SB(sb)->min_io_size, | |
| HFSPLUS_SECTOR_SIZE); | |
| } | |
| #define hfs_btree_open hfsplus_btree_open | |
| #define hfs_btree_close hfsplus_btree_close | |
| #define hfs_btree_write hfsplus_btree_write | |
| #define hfs_bmap_reserve hfsplus_bmap_reserve | |
| #define hfs_bmap_alloc hfsplus_bmap_alloc | |
| #define hfs_bmap_free hfsplus_bmap_free | |
| #define hfs_bnode_read hfsplus_bnode_read | |
| #define hfs_bnode_read_u16 hfsplus_bnode_read_u16 | |
| #define hfs_bnode_read_u8 hfsplus_bnode_read_u8 | |
| #define hfs_bnode_read_key hfsplus_bnode_read_key | |
| #define hfs_bnode_write hfsplus_bnode_write | |
| #define hfs_bnode_write_u16 hfsplus_bnode_write_u16 | |
| #define hfs_bnode_clear hfsplus_bnode_clear | |
| #define hfs_bnode_copy hfsplus_bnode_copy | |
| #define hfs_bnode_move hfsplus_bnode_move | |
| #define hfs_bnode_dump hfsplus_bnode_dump | |
| #define hfs_bnode_unlink hfsplus_bnode_unlink | |
| #define hfs_bnode_findhash hfsplus_bnode_findhash | |
| #define hfs_bnode_find hfsplus_bnode_find | |
| #define hfs_bnode_unhash hfsplus_bnode_unhash | |
| #define hfs_bnode_free hfsplus_bnode_free | |
| #define hfs_bnode_create hfsplus_bnode_create | |
| #define hfs_bnode_get hfsplus_bnode_get | |
| #define hfs_bnode_put hfsplus_bnode_put | |
| #define hfs_brec_lenoff hfsplus_brec_lenoff | |
| #define hfs_brec_keylen hfsplus_brec_keylen | |
| #define hfs_brec_insert hfsplus_brec_insert | |
| #define hfs_brec_remove hfsplus_brec_remove | |
| #define hfs_find_init hfsplus_find_init | |
| #define hfs_find_exit hfsplus_find_exit | |
| #define __hfs_brec_find __hfsplus_brec_find | |
| #define hfs_brec_find hfsplus_brec_find | |
| #define hfs_brec_read hfsplus_brec_read | |
| #define hfs_brec_goto hfsplus_brec_goto | |
| #define hfs_part_find hfsplus_part_find | |
| /* | |
| * hfs+-specific ioctl for making the filesystem bootable | |
| */ | |
| #define HFSPLUS_IOC_BLESS _IO('h', 0x80) | |
| typedef int (*search_strategy_t)(struct hfs_bnode *, | |
| struct hfs_find_data *, | |
| int *, int *, int *); | |
| /* | |
| * Functions in any *.c used in other files | |
| */ | |
| /* attributes.c */ | |
| int __init hfsplus_create_attr_tree_cache(void); | |
| void hfsplus_destroy_attr_tree_cache(void); | |
| int hfsplus_attr_bin_cmp_key(const hfsplus_btree_key *k1, | |
| const hfsplus_btree_key *k2); | |
| int hfsplus_attr_build_key(struct super_block *sb, hfsplus_btree_key *key, | |
| u32 cnid, const char *name); | |
| hfsplus_attr_entry *hfsplus_alloc_attr_entry(void); | |
| void hfsplus_destroy_attr_entry(hfsplus_attr_entry *entry); | |
| int hfsplus_find_attr(struct super_block *sb, u32 cnid, const char *name, | |
| struct hfs_find_data *fd); | |
| int hfsplus_attr_exists(struct inode *inode, const char *name); | |
| int hfsplus_create_attr(struct inode *inode, const char *name, | |
| const void *value, size_t size); | |
| int hfsplus_delete_attr(struct inode *inode, const char *name); | |
| int hfsplus_delete_all_attrs(struct inode *dir, u32 cnid); | |
| int hfsplus_replace_attr(struct inode *inode, | |
| const char *name, | |
| const void *value, size_t size); | |
| /* bitmap.c */ | |
| int hfsplus_block_allocate(struct super_block *sb, u32 size, u32 offset, | |
| u32 *max); | |
| int hfsplus_block_free(struct super_block *sb, u32 offset, u32 count); | |
| /* btree.c */ | |
| u32 hfsplus_calc_btree_clump_size(u32 block_size, u32 node_size, u64 sectors, | |
| int file_id); | |
| struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id); | |
| void hfs_btree_close(struct hfs_btree *tree); | |
| int hfs_btree_write(struct hfs_btree *tree); | |
| int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes); | |
| struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree); | |
| void hfs_bmap_free(struct hfs_bnode *node); | |
| /* bnode.c */ | |
| void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len); | |
| u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off); | |
| u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off); | |
| void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off); | |
| void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len); | |
| void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data); | |
| void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len); | |
| void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst, | |
| struct hfs_bnode *src_node, u32 src, u32 len); | |
| void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len); | |
| void hfs_bnode_dump(struct hfs_bnode *node); | |
| void hfs_bnode_unlink(struct hfs_bnode *node); | |
| struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid); | |
| void hfs_bnode_unhash(struct hfs_bnode *node); | |
| struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num); | |
| void hfs_bnode_free(struct hfs_bnode *node); | |
| struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num); | |
| void hfs_bnode_get(struct hfs_bnode *node); | |
| void hfs_bnode_put(struct hfs_bnode *node); | |
| bool hfs_bnode_need_zeroout(struct hfs_btree *tree); | |
| /* brec.c */ | |
| u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off); | |
| u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec); | |
| int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len); | |
| int hfs_brec_remove(struct hfs_find_data *fd); | |
| /* bfind.c */ | |
| int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd); | |
| void hfs_find_exit(struct hfs_find_data *fd); | |
| int hfs_find_1st_rec_by_cnid(struct hfs_bnode *bnode, struct hfs_find_data *fd, | |
| int *begin, int *end, int *cur_rec); | |
| int hfs_find_rec_by_key(struct hfs_bnode *bnode, struct hfs_find_data *fd, | |
| int *begin, int *end, int *cur_rec); | |
| int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd, | |
| search_strategy_t rec_found); | |
| int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare); | |
| int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len); | |
| int hfs_brec_goto(struct hfs_find_data *fd, int cnt); | |
| /* catalog.c */ | |
| int hfsplus_cat_case_cmp_key(const hfsplus_btree_key *k1, | |
| const hfsplus_btree_key *k2); | |
| int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1, | |
| const hfsplus_btree_key *k2); | |
| int hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key, | |
| u32 parent, const struct qstr *str); | |
| void hfsplus_cat_build_key_with_cnid(struct super_block *sb, | |
| hfsplus_btree_key *key, u32 parent); | |
| void hfsplus_cat_set_perms(struct inode *inode, struct hfsplus_perm *perms); | |
| int hfsplus_find_cat(struct super_block *sb, u32 cnid, | |
| struct hfs_find_data *fd); | |
| int hfsplus_create_cat(u32 cnid, struct inode *dir, const struct qstr *str, | |
| struct inode *inode); | |
| int hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str); | |
| int hfsplus_rename_cat(u32 cnid, struct inode *src_dir, const struct qstr *src_name, | |
| struct inode *dst_dir, const struct qstr *dst_name); | |
| /* dir.c */ | |
| extern const struct inode_operations hfsplus_dir_inode_operations; | |
| extern const struct file_operations hfsplus_dir_operations; | |
| /* extents.c */ | |
| int hfsplus_ext_cmp_key(const hfsplus_btree_key *k1, | |
| const hfsplus_btree_key *k2); | |
| int hfsplus_ext_write_extent(struct inode *inode); | |
| int hfsplus_get_block(struct inode *inode, sector_t iblock, | |
| struct buffer_head *bh_result, int create); | |
| int hfsplus_free_fork(struct super_block *sb, u32 cnid, | |
| struct hfsplus_fork_raw *fork, int type); | |
| int hfsplus_file_extend(struct inode *inode, bool zeroout); | |
| void hfsplus_file_truncate(struct inode *inode); | |
| /* inode.c */ | |
| extern const struct address_space_operations hfsplus_aops; | |
| extern const struct address_space_operations hfsplus_btree_aops; | |
| extern const struct dentry_operations hfsplus_dentry_operations; | |
| int hfsplus_write_begin(const struct kiocb *iocb, | |
| struct address_space *mapping, | |
| loff_t pos, unsigned len, struct folio **foliop, | |
| void **fsdata); | |
| struct inode *hfsplus_new_inode(struct super_block *sb, struct inode *dir, | |
| umode_t mode); | |
| void hfsplus_delete_inode(struct inode *inode); | |
| void hfsplus_inode_read_fork(struct inode *inode, | |
| struct hfsplus_fork_raw *fork); | |
| void hfsplus_inode_write_fork(struct inode *inode, | |
| struct hfsplus_fork_raw *fork); | |
| int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd); | |
| int hfsplus_cat_write_inode(struct inode *inode); | |
| int hfsplus_getattr(struct mnt_idmap *idmap, const struct path *path, | |
| struct kstat *stat, u32 request_mask, | |
| unsigned int query_flags); | |
| int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end, | |
| int datasync); | |
| int hfsplus_fileattr_get(struct dentry *dentry, struct file_kattr *fa); | |
| int hfsplus_fileattr_set(struct mnt_idmap *idmap, | |
| struct dentry *dentry, struct file_kattr *fa); | |
| /* ioctl.c */ | |
| long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); | |
| /* options.c */ | |
| void hfsplus_fill_defaults(struct hfsplus_sb_info *opts); | |
| int hfsplus_parse_param(struct fs_context *fc, struct fs_parameter *param); | |
| int hfsplus_show_options(struct seq_file *seq, struct dentry *root); | |
| /* part_tbl.c */ | |
| int hfs_part_find(struct super_block *sb, sector_t *part_start, | |
| sector_t *part_size); | |
| /* super.c */ | |
| struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino); | |
| void hfsplus_mark_mdb_dirty(struct super_block *sb); | |
| void hfsplus_prepare_volume_header_for_commit(struct hfsplus_vh *vhdr); | |
| int hfsplus_commit_superblock(struct super_block *sb); | |
| /* tables.c */ | |
| extern u16 hfsplus_case_fold_table[]; | |
| extern u16 hfsplus_decompose_table[]; | |
| extern u16 hfsplus_compose_table[]; | |
| /* unicode.c */ | |
| int hfsplus_strcasecmp(const struct hfsplus_unistr *s1, | |
| const struct hfsplus_unistr *s2); | |
| int hfsplus_strcmp(const struct hfsplus_unistr *s1, | |
| const struct hfsplus_unistr *s2); | |
| int hfsplus_uni2asc_str(struct super_block *sb, | |
| const struct hfsplus_unistr *ustr, char *astr, | |
| int *len_p); | |
| int hfsplus_uni2asc_xattr_str(struct super_block *sb, | |
| const struct hfsplus_attr_unistr *ustr, | |
| char *astr, int *len_p); | |
| int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, | |
| int max_unistr_len, const char *astr, int len, | |
| int name_type); | |
| int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str); | |
| int hfsplus_compare_dentry(const struct dentry *dentry, unsigned int len, | |
| const char *str, const struct qstr *name); | |
| /* wrapper.c */ | |
| int hfsplus_submit_bio(struct super_block *sb, sector_t sector, void *buf, | |
| void **data, blk_opf_t opf); | |
| int hfsplus_read_wrapper(struct super_block *sb); | |
| static inline u32 hfsplus_cat_thread_size(const struct hfsplus_cat_thread *thread) | |
| { | |
| return offsetof(struct hfsplus_cat_thread, nodeName) + | |
| offsetof(struct hfsplus_unistr, unicode) + | |
| be16_to_cpu(thread->nodeName.length) * sizeof(hfsplus_unichr); | |
| } | |
| static inline | |
| bool is_hfs_thread_record_type(u16 type) | |
| { | |
| return type == HFSPLUS_FOLDER_THREAD || type == HFSPLUS_FILE_THREAD; | |
| } | |
| int hfsplus_brec_read_cat(struct hfs_find_data *fd, hfsplus_cat_entry *entry); | |
| /* | |
| * time helpers: convert between 1904-base and 1970-base timestamps | |
| * | |
| * HFS+ implementations are highly inconsistent, this one matches the | |
| * traditional behavior of 64-bit Linux, giving the most useful | |
| * time range between 1970 and 2106, by treating any on-disk timestamp | |
| * under HFSPLUS_UTC_OFFSET (Jan 1 1970) as a time between 2040 and 2106. | |
| */ | |
| #define HFSPLUS_UTC_OFFSET 2082844800U | |
| static inline time64_t __hfsp_mt2ut(__be32 mt) | |
| { | |
| time64_t ut = (u32)(be32_to_cpu(mt) - HFSPLUS_UTC_OFFSET); | |
| return ut; | |
| } | |
| static inline __be32 __hfsp_ut2mt(time64_t ut) | |
| { | |
| return cpu_to_be32(lower_32_bits(ut) + HFSPLUS_UTC_OFFSET); | |
| } | |
| static inline enum hfsplus_btree_mutex_classes | |
| hfsplus_btree_lock_class(struct hfs_btree *tree) | |
| { | |
| enum hfsplus_btree_mutex_classes class; | |
| switch (tree->cnid) { | |
| case HFSPLUS_CAT_CNID: | |
| class = CATALOG_BTREE_MUTEX; | |
| break; | |
| case HFSPLUS_EXT_CNID: | |
| class = EXTENTS_BTREE_MUTEX; | |
| break; | |
| case HFSPLUS_ATTR_CNID: | |
| class = ATTR_BTREE_MUTEX; | |
| break; | |
| default: | |
| BUG(); | |
| } | |
| return class; | |
| } | |
| static inline | |
| bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off) | |
| { | |
| bool is_valid; | |
| if (!node || !node->tree) | |
| return false; | |
| is_valid = off < node->tree->node_size; | |
| if (!is_valid) { | |
| pr_err("requested invalid offset: " | |
| "NODE: id %u, type %#x, height %u, " | |
| "node_size %u, offset %u\n", | |
| node->this, node->type, node->height, | |
| node->tree->node_size, off); | |
| } | |
| return is_valid; | |
| } | |
| static inline | |
| bool hfs_bnode_num_recs_invalid(struct hfs_bnode *node) | |
| { | |
| u32 node_size; | |
| u32 table_size; | |
| u32 area_size; | |
| u32 rec_size = sizeof(__be16); | |
| u32 desc_size = sizeof(struct hfs_bnode_desc); | |
| if (!node || !node->tree) | |
| return true; | |
| node_size = node->tree->node_size; | |
| if (node_size < desc_size) | |
| return true; | |
| area_size = node_size - desc_size; | |
| table_size = ((u32)node->num_recs + 1) * rec_size; | |
| return table_size > area_size; | |
| } | |
| static inline | |
| bool hfs_brec_record_invalid(struct hfs_bnode *node, int record) | |
| { | |
| if (hfs_bnode_num_recs_invalid(node)) | |
| return true; | |
| if (record < 0) | |
| return true; | |
| return record >= node->num_recs; | |
| } | |
| static inline | |
| bool hfs_brec_offsets_invalid(struct hfs_bnode *node, u16 off, u16 next_off) | |
| { | |
| u32 table_size; | |
| u32 table_start; | |
| u32 rec_size = sizeof(__be16); | |
| u32 desc_size = sizeof(struct hfs_bnode_desc); | |
| if (!node || !node->tree) | |
| return true; | |
| if (off < desc_size || (off & 1)) | |
| return true; | |
| if (next_off <= off || | |
| next_off > node->tree->node_size || | |
| (next_off & 1)) | |
| return true; | |
| table_size = ((u32)node->num_recs + 1) * rec_size; | |
| table_start = node->tree->node_size - table_size; | |
| if (next_off > table_start) | |
| return true; | |
| return false; | |
| } | |
| static inline | |
| bool hfs_brec_len_invalid(struct hfs_bnode *node, u16 len) | |
| { | |
| if (!node || !node->tree) | |
| return true; | |
| return len == 0 || len > node->tree->node_size; | |
| } | |
| static inline | |
| void hfs_find_result_init(struct hfs_find_data *fd) | |
| { | |
| fd->record = -1; | |
| fd->keyoffset = -1; | |
| fd->keylength = -1; | |
| fd->entryoffset = -1; | |
| fd->entrylength = -1; | |
| } | |
| static inline | |
| u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len) | |
| { | |
| unsigned int node_size; | |
| if (!is_bnode_offset_valid(node, off)) | |
| return 0; | |
| node_size = node->tree->node_size; | |
| if ((u64)off + len > node_size) { | |
| u32 new_len = node_size - off; | |
| pr_err("requested length has been corrected: " | |
| "NODE: id %u, type %#x, height %u, " | |
| "node_size %u, offset %u, " | |
| "requested_len %u, corrected_len %u\n", | |
| node->this, node->type, node->height, | |
| node->tree->node_size, off, len, new_len); | |
| return new_len; | |
| } | |
| return len; | |
| } | |
| /* compatibility */ | |
| #define hfsp_mt2ut(t) (struct timespec64){ .tv_sec = __hfsp_mt2ut(t) } | |
| #define hfsp_ut2mt(t) __hfsp_ut2mt((t).tv_sec) | |
| #define hfsp_now2mt() __hfsp_ut2mt(ktime_get_real_seconds()) | |
| #endif |