18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci#include <linux/buffer_head.h> 38c2ecf20Sopenharmony_ci#include <linux/fs.h> 48c2ecf20Sopenharmony_ci#include <linux/adfs_fs.h> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/* Internal data structures for ADFS */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define ADFS_FREE_FRAG 0 98c2ecf20Sopenharmony_ci#define ADFS_BAD_FRAG 1 108c2ecf20Sopenharmony_ci#define ADFS_ROOT_FRAG 2 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define ADFS_FILETYPE_NONE ((u16)~0) 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* RISC OS 12-bit filetype is stored in load_address[19:8] */ 158c2ecf20Sopenharmony_cistatic inline u16 adfs_filetype(u32 loadaddr) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci return (loadaddr & 0xfff00000) == 0xfff00000 ? 188c2ecf20Sopenharmony_ci (loadaddr >> 8) & 0xfff : ADFS_FILETYPE_NONE; 198c2ecf20Sopenharmony_ci} 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define ADFS_NDA_OWNER_READ (1 << 0) 228c2ecf20Sopenharmony_ci#define ADFS_NDA_OWNER_WRITE (1 << 1) 238c2ecf20Sopenharmony_ci#define ADFS_NDA_LOCKED (1 << 2) 248c2ecf20Sopenharmony_ci#define ADFS_NDA_DIRECTORY (1 << 3) 258c2ecf20Sopenharmony_ci#define ADFS_NDA_EXECUTE (1 << 4) 268c2ecf20Sopenharmony_ci#define ADFS_NDA_PUBLIC_READ (1 << 5) 278c2ecf20Sopenharmony_ci#define ADFS_NDA_PUBLIC_WRITE (1 << 6) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * adfs file system inode data in memory 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_cistruct adfs_inode_info { 338c2ecf20Sopenharmony_ci loff_t mmu_private; 348c2ecf20Sopenharmony_ci __u32 parent_id; /* parent indirect disc address */ 358c2ecf20Sopenharmony_ci __u32 indaddr; /* object indirect disc address */ 368c2ecf20Sopenharmony_ci __u32 loadaddr; /* RISC OS load address */ 378c2ecf20Sopenharmony_ci __u32 execaddr; /* RISC OS exec address */ 388c2ecf20Sopenharmony_ci unsigned int attr; /* RISC OS permissions */ 398c2ecf20Sopenharmony_ci struct inode vfs_inode; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic inline struct adfs_inode_info *ADFS_I(struct inode *inode) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci return container_of(inode, struct adfs_inode_info, vfs_inode); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic inline bool adfs_inode_is_stamped(struct inode *inode) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci return (ADFS_I(inode)->loadaddr & 0xfff00000) == 0xfff00000; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * Forward-declare this 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_cistruct adfs_discmap; 568c2ecf20Sopenharmony_cistruct adfs_dir_ops; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * ADFS file system superblock data in memory 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistruct adfs_sb_info { 628c2ecf20Sopenharmony_ci union { struct { 638c2ecf20Sopenharmony_ci struct adfs_discmap *s_map; /* bh list containing map */ 648c2ecf20Sopenharmony_ci const struct adfs_dir_ops *s_dir; /* directory operations */ 658c2ecf20Sopenharmony_ci }; 668c2ecf20Sopenharmony_ci struct rcu_head rcu; /* used only at shutdown time */ 678c2ecf20Sopenharmony_ci }; 688c2ecf20Sopenharmony_ci kuid_t s_uid; /* owner uid */ 698c2ecf20Sopenharmony_ci kgid_t s_gid; /* owner gid */ 708c2ecf20Sopenharmony_ci umode_t s_owner_mask; /* ADFS owner perm -> unix perm */ 718c2ecf20Sopenharmony_ci umode_t s_other_mask; /* ADFS other perm -> unix perm */ 728c2ecf20Sopenharmony_ci int s_ftsuffix; /* ,xyz hex filetype suffix option */ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci __u32 s_ids_per_zone; /* max. no ids in one zone */ 758c2ecf20Sopenharmony_ci __u32 s_idlen; /* length of ID in map */ 768c2ecf20Sopenharmony_ci __u32 s_map_size; /* sector size of a map */ 778c2ecf20Sopenharmony_ci signed int s_map2blk; /* shift left by this for map->sector*/ 788c2ecf20Sopenharmony_ci unsigned int s_log2sharesize;/* log2 share size */ 798c2ecf20Sopenharmony_ci unsigned int s_namelen; /* maximum number of characters in name */ 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic inline struct adfs_sb_info *ADFS_SB(struct super_block *sb) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci return sb->s_fs_info; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* 888c2ecf20Sopenharmony_ci * Directory handling 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_cistruct adfs_dir { 918c2ecf20Sopenharmony_ci struct super_block *sb; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci int nr_buffers; 948c2ecf20Sopenharmony_ci struct buffer_head *bh[4]; 958c2ecf20Sopenharmony_ci struct buffer_head **bhs; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci unsigned int pos; 988c2ecf20Sopenharmony_ci __u32 parent_id; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci union { 1018c2ecf20Sopenharmony_ci struct adfs_dirheader *dirhead; 1028c2ecf20Sopenharmony_ci struct adfs_bigdirheader *bighead; 1038c2ecf20Sopenharmony_ci }; 1048c2ecf20Sopenharmony_ci union { 1058c2ecf20Sopenharmony_ci struct adfs_newdirtail *newtail; 1068c2ecf20Sopenharmony_ci struct adfs_bigdirtail *bigtail; 1078c2ecf20Sopenharmony_ci }; 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* 1118c2ecf20Sopenharmony_ci * This is the overall maximum name length 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci#define ADFS_MAX_NAME_LEN (256 + 4) /* +4 for ,xyz hex filetype suffix */ 1148c2ecf20Sopenharmony_cistruct object_info { 1158c2ecf20Sopenharmony_ci __u32 parent_id; /* parent object id */ 1168c2ecf20Sopenharmony_ci __u32 indaddr; /* indirect disc addr */ 1178c2ecf20Sopenharmony_ci __u32 loadaddr; /* load address */ 1188c2ecf20Sopenharmony_ci __u32 execaddr; /* execution address */ 1198c2ecf20Sopenharmony_ci __u32 size; /* size */ 1208c2ecf20Sopenharmony_ci __u8 attr; /* RISC OS attributes */ 1218c2ecf20Sopenharmony_ci unsigned int name_len; /* name length */ 1228c2ecf20Sopenharmony_ci char name[ADFS_MAX_NAME_LEN];/* file name */ 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistruct adfs_dir_ops { 1268c2ecf20Sopenharmony_ci int (*read)(struct super_block *sb, unsigned int indaddr, 1278c2ecf20Sopenharmony_ci unsigned int size, struct adfs_dir *dir); 1288c2ecf20Sopenharmony_ci int (*iterate)(struct adfs_dir *dir, struct dir_context *ctx); 1298c2ecf20Sopenharmony_ci int (*setpos)(struct adfs_dir *dir, unsigned int fpos); 1308c2ecf20Sopenharmony_ci int (*getnext)(struct adfs_dir *dir, struct object_info *obj); 1318c2ecf20Sopenharmony_ci int (*update)(struct adfs_dir *dir, struct object_info *obj); 1328c2ecf20Sopenharmony_ci int (*create)(struct adfs_dir *dir, struct object_info *obj); 1338c2ecf20Sopenharmony_ci int (*remove)(struct adfs_dir *dir, struct object_info *obj); 1348c2ecf20Sopenharmony_ci int (*commit)(struct adfs_dir *dir); 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistruct adfs_discmap { 1388c2ecf20Sopenharmony_ci struct buffer_head *dm_bh; 1398c2ecf20Sopenharmony_ci __u32 dm_startblk; 1408c2ecf20Sopenharmony_ci unsigned int dm_startbit; 1418c2ecf20Sopenharmony_ci unsigned int dm_endbit; 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* Inode stuff */ 1458c2ecf20Sopenharmony_cistruct inode *adfs_iget(struct super_block *sb, struct object_info *obj); 1468c2ecf20Sopenharmony_ciint adfs_write_inode(struct inode *inode, struct writeback_control *wbc); 1478c2ecf20Sopenharmony_ciint adfs_notify_change(struct dentry *dentry, struct iattr *attr); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* map.c */ 1508c2ecf20Sopenharmony_ciint adfs_map_lookup(struct super_block *sb, u32 frag_id, unsigned int offset); 1518c2ecf20Sopenharmony_civoid adfs_map_statfs(struct super_block *sb, struct kstatfs *buf); 1528c2ecf20Sopenharmony_cistruct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr); 1538c2ecf20Sopenharmony_civoid adfs_free_map(struct super_block *sb); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* Misc */ 1568c2ecf20Sopenharmony_ci__printf(3, 4) 1578c2ecf20Sopenharmony_civoid __adfs_error(struct super_block *sb, const char *function, 1588c2ecf20Sopenharmony_ci const char *fmt, ...); 1598c2ecf20Sopenharmony_ci#define adfs_error(sb, fmt...) __adfs_error(sb, __func__, fmt) 1608c2ecf20Sopenharmony_civoid adfs_msg(struct super_block *sb, const char *pfx, const char *fmt, ...); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* super.c */ 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* 1658c2ecf20Sopenharmony_ci * Inodes and file operations 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* dir_*.c */ 1698c2ecf20Sopenharmony_ciextern const struct inode_operations adfs_dir_inode_operations; 1708c2ecf20Sopenharmony_ciextern const struct file_operations adfs_dir_operations; 1718c2ecf20Sopenharmony_ciextern const struct dentry_operations adfs_dentry_operations; 1728c2ecf20Sopenharmony_ciextern const struct adfs_dir_ops adfs_f_dir_ops; 1738c2ecf20Sopenharmony_ciextern const struct adfs_dir_ops adfs_fplus_dir_ops; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ciint adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset, 1768c2ecf20Sopenharmony_ci size_t len); 1778c2ecf20Sopenharmony_ciint adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src, 1788c2ecf20Sopenharmony_ci size_t len); 1798c2ecf20Sopenharmony_civoid adfs_dir_relse(struct adfs_dir *dir); 1808c2ecf20Sopenharmony_ciint adfs_dir_read_buffers(struct super_block *sb, u32 indaddr, 1818c2ecf20Sopenharmony_ci unsigned int size, struct adfs_dir *dir); 1828c2ecf20Sopenharmony_civoid adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj); 1838c2ecf20Sopenharmony_ciextern int adfs_dir_update(struct super_block *sb, struct object_info *obj, 1848c2ecf20Sopenharmony_ci int wait); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* file.c */ 1878c2ecf20Sopenharmony_ciextern const struct inode_operations adfs_file_inode_operations; 1888c2ecf20Sopenharmony_ciextern const struct file_operations adfs_file_operations; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic inline __u32 signed_asl(__u32 val, signed int shift) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci if (shift >= 0) 1938c2ecf20Sopenharmony_ci val <<= shift; 1948c2ecf20Sopenharmony_ci else 1958c2ecf20Sopenharmony_ci val >>= -shift; 1968c2ecf20Sopenharmony_ci return val; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* 2008c2ecf20Sopenharmony_ci * Calculate the address of a block in an object given the block offset 2018c2ecf20Sopenharmony_ci * and the object identity. 2028c2ecf20Sopenharmony_ci * 2038c2ecf20Sopenharmony_ci * The root directory ID should always be looked up in the map [3.4] 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_cistatic inline int __adfs_block_map(struct super_block *sb, u32 indaddr, 2068c2ecf20Sopenharmony_ci unsigned int block) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci if (indaddr & 255) { 2098c2ecf20Sopenharmony_ci unsigned int off; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci off = (indaddr & 255) - 1; 2128c2ecf20Sopenharmony_ci block += off << ADFS_SB(sb)->s_log2sharesize; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return adfs_map_lookup(sb, indaddr >> 8, block); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* Return the disc record from the map */ 2198c2ecf20Sopenharmony_cistatic inline 2208c2ecf20Sopenharmony_cistruct adfs_discrecord *adfs_map_discrecord(struct adfs_discmap *dm) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci return (void *)(dm[0].dm_bh->b_data + 4); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic inline u64 adfs_disc_size(const struct adfs_discrecord *dr) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci return (u64)le32_to_cpu(dr->disc_size_high) << 32 | 2288c2ecf20Sopenharmony_ci le32_to_cpu(dr->disc_size); 2298c2ecf20Sopenharmony_ci} 230