162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#include <linux/buffer_head.h>
362306a36Sopenharmony_ci#include <linux/fs.h>
462306a36Sopenharmony_ci#include <linux/adfs_fs.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/* Internal data structures for ADFS */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define ADFS_FREE_FRAG		 0
962306a36Sopenharmony_ci#define ADFS_BAD_FRAG		 1
1062306a36Sopenharmony_ci#define ADFS_ROOT_FRAG		 2
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define ADFS_FILETYPE_NONE	((u16)~0)
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* RISC OS 12-bit filetype is stored in load_address[19:8] */
1562306a36Sopenharmony_cistatic inline u16 adfs_filetype(u32 loadaddr)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	return (loadaddr & 0xfff00000) == 0xfff00000 ?
1862306a36Sopenharmony_ci	       (loadaddr >> 8) & 0xfff : ADFS_FILETYPE_NONE;
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define ADFS_NDA_OWNER_READ	(1 << 0)
2262306a36Sopenharmony_ci#define ADFS_NDA_OWNER_WRITE	(1 << 1)
2362306a36Sopenharmony_ci#define ADFS_NDA_LOCKED		(1 << 2)
2462306a36Sopenharmony_ci#define ADFS_NDA_DIRECTORY	(1 << 3)
2562306a36Sopenharmony_ci#define ADFS_NDA_EXECUTE	(1 << 4)
2662306a36Sopenharmony_ci#define ADFS_NDA_PUBLIC_READ	(1 << 5)
2762306a36Sopenharmony_ci#define ADFS_NDA_PUBLIC_WRITE	(1 << 6)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * adfs file system inode data in memory
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_cistruct adfs_inode_info {
3362306a36Sopenharmony_ci	loff_t		mmu_private;
3462306a36Sopenharmony_ci	__u32		parent_id;	/* parent indirect disc address	*/
3562306a36Sopenharmony_ci	__u32		indaddr;	/* object indirect disc address	*/
3662306a36Sopenharmony_ci	__u32		loadaddr;	/* RISC OS load address		*/
3762306a36Sopenharmony_ci	__u32		execaddr;	/* RISC OS exec address		*/
3862306a36Sopenharmony_ci	unsigned int	attr;		/* RISC OS permissions		*/
3962306a36Sopenharmony_ci	struct inode vfs_inode;
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic inline struct adfs_inode_info *ADFS_I(struct inode *inode)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	return container_of(inode, struct adfs_inode_info, vfs_inode);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic inline bool adfs_inode_is_stamped(struct inode *inode)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	return (ADFS_I(inode)->loadaddr & 0xfff00000) == 0xfff00000;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/*
5362306a36Sopenharmony_ci * Forward-declare this
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_cistruct adfs_discmap;
5662306a36Sopenharmony_cistruct adfs_dir_ops;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * ADFS file system superblock data in memory
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_cistruct adfs_sb_info {
6262306a36Sopenharmony_ci	union { struct {
6362306a36Sopenharmony_ci		struct adfs_discmap *s_map;	/* bh list containing map */
6462306a36Sopenharmony_ci		const struct adfs_dir_ops *s_dir; /* directory operations */
6562306a36Sopenharmony_ci		};
6662306a36Sopenharmony_ci		struct rcu_head rcu;	/* used only at shutdown time	 */
6762306a36Sopenharmony_ci	};
6862306a36Sopenharmony_ci	kuid_t		s_uid;		/* owner uid */
6962306a36Sopenharmony_ci	kgid_t		s_gid;		/* owner gid */
7062306a36Sopenharmony_ci	umode_t		s_owner_mask;	/* ADFS owner perm -> unix perm */
7162306a36Sopenharmony_ci	umode_t		s_other_mask;	/* ADFS other perm -> unix perm	*/
7262306a36Sopenharmony_ci	int		s_ftsuffix;	/* ,xyz hex filetype suffix option */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	__u32		s_ids_per_zone;	/* max. no ids in one zone */
7562306a36Sopenharmony_ci	__u32		s_idlen;	/* length of ID in map */
7662306a36Sopenharmony_ci	__u32		s_map_size;	/* sector size of a map	*/
7762306a36Sopenharmony_ci	signed int	s_map2blk;	/* shift left by this for map->sector*/
7862306a36Sopenharmony_ci	unsigned int	s_log2sharesize;/* log2 share size */
7962306a36Sopenharmony_ci	unsigned int	s_namelen;	/* maximum number of characters in name	 */
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic inline struct adfs_sb_info *ADFS_SB(struct super_block *sb)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	return sb->s_fs_info;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * Directory handling
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistruct adfs_dir {
9162306a36Sopenharmony_ci	struct super_block	*sb;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	int			nr_buffers;
9462306a36Sopenharmony_ci	struct buffer_head	*bh[4];
9562306a36Sopenharmony_ci	struct buffer_head	**bhs;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	unsigned int		pos;
9862306a36Sopenharmony_ci	__u32			parent_id;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	union {
10162306a36Sopenharmony_ci		struct adfs_dirheader	*dirhead;
10262306a36Sopenharmony_ci		struct adfs_bigdirheader *bighead;
10362306a36Sopenharmony_ci	};
10462306a36Sopenharmony_ci	union {
10562306a36Sopenharmony_ci		struct adfs_newdirtail	*newtail;
10662306a36Sopenharmony_ci		struct adfs_bigdirtail	*bigtail;
10762306a36Sopenharmony_ci	};
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci/*
11162306a36Sopenharmony_ci * This is the overall maximum name length
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_ci#define ADFS_MAX_NAME_LEN	(256 + 4) /* +4 for ,xyz hex filetype suffix */
11462306a36Sopenharmony_cistruct object_info {
11562306a36Sopenharmony_ci	__u32		parent_id;		/* parent object id	*/
11662306a36Sopenharmony_ci	__u32		indaddr;		/* indirect disc addr	*/
11762306a36Sopenharmony_ci	__u32		loadaddr;		/* load address		*/
11862306a36Sopenharmony_ci	__u32		execaddr;		/* execution address	*/
11962306a36Sopenharmony_ci	__u32		size;			/* size			*/
12062306a36Sopenharmony_ci	__u8		attr;			/* RISC OS attributes	*/
12162306a36Sopenharmony_ci	unsigned int	name_len;		/* name length		*/
12262306a36Sopenharmony_ci	char		name[ADFS_MAX_NAME_LEN];/* file name		*/
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistruct adfs_dir_ops {
12662306a36Sopenharmony_ci	int	(*read)(struct super_block *sb, unsigned int indaddr,
12762306a36Sopenharmony_ci			unsigned int size, struct adfs_dir *dir);
12862306a36Sopenharmony_ci	int	(*iterate)(struct adfs_dir *dir, struct dir_context *ctx);
12962306a36Sopenharmony_ci	int	(*setpos)(struct adfs_dir *dir, unsigned int fpos);
13062306a36Sopenharmony_ci	int	(*getnext)(struct adfs_dir *dir, struct object_info *obj);
13162306a36Sopenharmony_ci	int	(*update)(struct adfs_dir *dir, struct object_info *obj);
13262306a36Sopenharmony_ci	int	(*create)(struct adfs_dir *dir, struct object_info *obj);
13362306a36Sopenharmony_ci	int	(*remove)(struct adfs_dir *dir, struct object_info *obj);
13462306a36Sopenharmony_ci	int	(*commit)(struct adfs_dir *dir);
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistruct adfs_discmap {
13862306a36Sopenharmony_ci	struct buffer_head	*dm_bh;
13962306a36Sopenharmony_ci	__u32			dm_startblk;
14062306a36Sopenharmony_ci	unsigned int		dm_startbit;
14162306a36Sopenharmony_ci	unsigned int		dm_endbit;
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/* Inode stuff */
14562306a36Sopenharmony_cistruct inode *adfs_iget(struct super_block *sb, struct object_info *obj);
14662306a36Sopenharmony_ciint adfs_write_inode(struct inode *inode, struct writeback_control *wbc);
14762306a36Sopenharmony_ciint adfs_notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
14862306a36Sopenharmony_ci		       struct iattr *attr);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci/* map.c */
15162306a36Sopenharmony_ciint adfs_map_lookup(struct super_block *sb, u32 frag_id, unsigned int offset);
15262306a36Sopenharmony_civoid adfs_map_statfs(struct super_block *sb, struct kstatfs *buf);
15362306a36Sopenharmony_cistruct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr);
15462306a36Sopenharmony_civoid adfs_free_map(struct super_block *sb);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/* Misc */
15762306a36Sopenharmony_ci__printf(3, 4)
15862306a36Sopenharmony_civoid __adfs_error(struct super_block *sb, const char *function,
15962306a36Sopenharmony_ci		  const char *fmt, ...);
16062306a36Sopenharmony_ci#define adfs_error(sb, fmt...) __adfs_error(sb, __func__, fmt)
16162306a36Sopenharmony_civoid adfs_msg(struct super_block *sb, const char *pfx, const char *fmt, ...);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/* super.c */
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/*
16662306a36Sopenharmony_ci * Inodes and file operations
16762306a36Sopenharmony_ci */
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/* dir_*.c */
17062306a36Sopenharmony_ciextern const struct inode_operations adfs_dir_inode_operations;
17162306a36Sopenharmony_ciextern const struct file_operations adfs_dir_operations;
17262306a36Sopenharmony_ciextern const struct dentry_operations adfs_dentry_operations;
17362306a36Sopenharmony_ciextern const struct adfs_dir_ops adfs_f_dir_ops;
17462306a36Sopenharmony_ciextern const struct adfs_dir_ops adfs_fplus_dir_ops;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ciint adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
17762306a36Sopenharmony_ci		      size_t len);
17862306a36Sopenharmony_ciint adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
17962306a36Sopenharmony_ci		    size_t len);
18062306a36Sopenharmony_civoid adfs_dir_relse(struct adfs_dir *dir);
18162306a36Sopenharmony_ciint adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
18262306a36Sopenharmony_ci			  unsigned int size, struct adfs_dir *dir);
18362306a36Sopenharmony_civoid adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj);
18462306a36Sopenharmony_ciextern int adfs_dir_update(struct super_block *sb, struct object_info *obj,
18562306a36Sopenharmony_ci			   int wait);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/* file.c */
18862306a36Sopenharmony_ciextern const struct inode_operations adfs_file_inode_operations;
18962306a36Sopenharmony_ciextern const struct file_operations adfs_file_operations;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic inline __u32 signed_asl(__u32 val, signed int shift)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	if (shift >= 0)
19462306a36Sopenharmony_ci		val <<= shift;
19562306a36Sopenharmony_ci	else
19662306a36Sopenharmony_ci		val >>= -shift;
19762306a36Sopenharmony_ci	return val;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/*
20162306a36Sopenharmony_ci * Calculate the address of a block in an object given the block offset
20262306a36Sopenharmony_ci * and the object identity.
20362306a36Sopenharmony_ci *
20462306a36Sopenharmony_ci * The root directory ID should always be looked up in the map [3.4]
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_cistatic inline int __adfs_block_map(struct super_block *sb, u32 indaddr,
20762306a36Sopenharmony_ci				   unsigned int block)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	if (indaddr & 255) {
21062306a36Sopenharmony_ci		unsigned int off;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		off = (indaddr & 255) - 1;
21362306a36Sopenharmony_ci		block += off << ADFS_SB(sb)->s_log2sharesize;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return adfs_map_lookup(sb, indaddr >> 8, block);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci/* Return the disc record from the map */
22062306a36Sopenharmony_cistatic inline
22162306a36Sopenharmony_cistruct adfs_discrecord *adfs_map_discrecord(struct adfs_discmap *dm)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	return (void *)(dm[0].dm_bh->b_data + 4);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic inline u64 adfs_disc_size(const struct adfs_discrecord *dr)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	return (u64)le32_to_cpu(dr->disc_size_high) << 32 |
22962306a36Sopenharmony_ci		    le32_to_cpu(dr->disc_size);
23062306a36Sopenharmony_ci}
231