brintos

brintos / linux-shallow public Read only

0
0
Text · 11.4 KiB · f0b999a Raw
496 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  linux/fs/adfs/super.c4 *5 *  Copyright (C) 1997-1999 Russell King6 */7#include <linux/module.h>8#include <linux/init.h>9#include <linux/parser.h>10#include <linux/mount.h>11#include <linux/seq_file.h>12#include <linux/slab.h>13#include <linux/statfs.h>14#include <linux/user_namespace.h>15#include <linux/blkdev.h>16#include "adfs.h"17#include "dir_f.h"18#include "dir_fplus.h"19 20#define ADFS_SB_FLAGS SB_NOATIME21 22#define ADFS_DEFAULT_OWNER_MASK S_IRWXU23#define ADFS_DEFAULT_OTHER_MASK (S_IRWXG | S_IRWXO)24 25void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...)26{27	struct va_format vaf;28	va_list args;29 30	va_start(args, fmt);31	vaf.fmt = fmt;32	vaf.va = &args;33 34	printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %pV\n",35		sb->s_id, function ? ": " : "",36		function ? function : "", &vaf);37 38	va_end(args);39}40 41void adfs_msg(struct super_block *sb, const char *pfx, const char *fmt, ...)42{43	struct va_format vaf;44	va_list args;45 46	va_start(args, fmt);47	vaf.fmt = fmt;48	vaf.va = &args;49	printk("%sADFS-fs (%s): %pV\n", pfx, sb->s_id, &vaf);50	va_end(args);51}52 53static int adfs_checkdiscrecord(struct adfs_discrecord *dr)54{55	unsigned int max_idlen;56	int i;57 58	/* sector size must be 256, 512 or 1024 bytes */59	if (dr->log2secsize != 8 &&60	    dr->log2secsize != 9 &&61	    dr->log2secsize != 10)62		return 1;63 64	/* idlen must be at least log2secsize + 3 */65	if (dr->idlen < dr->log2secsize + 3)66		return 1;67 68	/* we cannot have such a large disc that we69	 * are unable to represent sector offsets in70	 * 32 bits.  This works out at 2.0 TB.71	 */72	if (le32_to_cpu(dr->disc_size_high) >> dr->log2secsize)73		return 1;74 75	/*76	 * Maximum idlen is limited to 16 bits for new directories by77	 * the three-byte storage of an indirect disc address.  For78	 * big directories, idlen must be no greater than 19 v2 [1.0]79	 */80	max_idlen = dr->format_version ? 19 : 16;81	if (dr->idlen > max_idlen)82		return 1;83 84	/* reserved bytes should be zero */85	for (i = 0; i < sizeof(dr->unused52); i++)86		if (dr->unused52[i] != 0)87			return 1;88 89	return 0;90}91 92static void adfs_put_super(struct super_block *sb)93{94	struct adfs_sb_info *asb = ADFS_SB(sb);95 96	adfs_free_map(sb);97	kfree_rcu(asb, rcu);98}99 100static int adfs_show_options(struct seq_file *seq, struct dentry *root)101{102	struct adfs_sb_info *asb = ADFS_SB(root->d_sb);103 104	if (!uid_eq(asb->s_uid, GLOBAL_ROOT_UID))105		seq_printf(seq, ",uid=%u", from_kuid_munged(&init_user_ns, asb->s_uid));106	if (!gid_eq(asb->s_gid, GLOBAL_ROOT_GID))107		seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, asb->s_gid));108	if (asb->s_owner_mask != ADFS_DEFAULT_OWNER_MASK)109		seq_printf(seq, ",ownmask=%o", asb->s_owner_mask);110	if (asb->s_other_mask != ADFS_DEFAULT_OTHER_MASK)111		seq_printf(seq, ",othmask=%o", asb->s_other_mask);112	if (asb->s_ftsuffix != 0)113		seq_printf(seq, ",ftsuffix=%u", asb->s_ftsuffix);114 115	return 0;116}117 118enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_ftsuffix, Opt_err};119 120static const match_table_t tokens = {121	{Opt_uid, "uid=%u"},122	{Opt_gid, "gid=%u"},123	{Opt_ownmask, "ownmask=%o"},124	{Opt_othmask, "othmask=%o"},125	{Opt_ftsuffix, "ftsuffix=%u"},126	{Opt_err, NULL}127};128 129static int parse_options(struct super_block *sb, struct adfs_sb_info *asb,130			 char *options)131{132	char *p;133	int option;134 135	if (!options)136		return 0;137 138	while ((p = strsep(&options, ",")) != NULL) {139		substring_t args[MAX_OPT_ARGS];140		int token;141		if (!*p)142			continue;143 144		token = match_token(p, tokens, args);145		switch (token) {146		case Opt_uid:147			if (match_int(args, &option))148				return -EINVAL;149			asb->s_uid = make_kuid(current_user_ns(), option);150			if (!uid_valid(asb->s_uid))151				return -EINVAL;152			break;153		case Opt_gid:154			if (match_int(args, &option))155				return -EINVAL;156			asb->s_gid = make_kgid(current_user_ns(), option);157			if (!gid_valid(asb->s_gid))158				return -EINVAL;159			break;160		case Opt_ownmask:161			if (match_octal(args, &option))162				return -EINVAL;163			asb->s_owner_mask = option;164			break;165		case Opt_othmask:166			if (match_octal(args, &option))167				return -EINVAL;168			asb->s_other_mask = option;169			break;170		case Opt_ftsuffix:171			if (match_int(args, &option))172				return -EINVAL;173			asb->s_ftsuffix = option;174			break;175		default:176			adfs_msg(sb, KERN_ERR,177				 "unrecognised mount option \"%s\" or missing value",178				 p);179			return -EINVAL;180		}181	}182	return 0;183}184 185static int adfs_remount(struct super_block *sb, int *flags, char *data)186{187	struct adfs_sb_info temp_asb;188	int ret;189 190	sync_filesystem(sb);191	*flags |= ADFS_SB_FLAGS;192 193	temp_asb = *ADFS_SB(sb);194	ret = parse_options(sb, &temp_asb, data);195	if (ret == 0)196		*ADFS_SB(sb) = temp_asb;197 198	return ret;199}200 201static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf)202{203	struct super_block *sb = dentry->d_sb;204	struct adfs_sb_info *sbi = ADFS_SB(sb);205	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);206 207	adfs_map_statfs(sb, buf);208 209	buf->f_type    = ADFS_SUPER_MAGIC;210	buf->f_namelen = sbi->s_namelen;211	buf->f_bsize   = sb->s_blocksize;212	buf->f_ffree   = (long)(buf->f_bfree * buf->f_files) / (long)buf->f_blocks;213	buf->f_fsid    = u64_to_fsid(id);214 215	return 0;216}217 218static struct kmem_cache *adfs_inode_cachep;219 220static struct inode *adfs_alloc_inode(struct super_block *sb)221{222	struct adfs_inode_info *ei;223	ei = alloc_inode_sb(sb, adfs_inode_cachep, GFP_KERNEL);224	if (!ei)225		return NULL;226	return &ei->vfs_inode;227}228 229static void adfs_free_inode(struct inode *inode)230{231	kmem_cache_free(adfs_inode_cachep, ADFS_I(inode));232}233 234static int adfs_drop_inode(struct inode *inode)235{236	/* always drop inodes if we are read-only */237	return !IS_ENABLED(CONFIG_ADFS_FS_RW) || IS_RDONLY(inode);238}239 240static void init_once(void *foo)241{242	struct adfs_inode_info *ei = (struct adfs_inode_info *) foo;243 244	inode_init_once(&ei->vfs_inode);245}246 247static int __init init_inodecache(void)248{249	adfs_inode_cachep = kmem_cache_create("adfs_inode_cache",250					     sizeof(struct adfs_inode_info),251					     0, (SLAB_RECLAIM_ACCOUNT|252						SLAB_ACCOUNT),253					     init_once);254	if (adfs_inode_cachep == NULL)255		return -ENOMEM;256	return 0;257}258 259static void destroy_inodecache(void)260{261	/*262	 * Make sure all delayed rcu free inodes are flushed before we263	 * destroy cache.264	 */265	rcu_barrier();266	kmem_cache_destroy(adfs_inode_cachep);267}268 269static const struct super_operations adfs_sops = {270	.alloc_inode	= adfs_alloc_inode,271	.free_inode	= adfs_free_inode,272	.drop_inode	= adfs_drop_inode,273	.write_inode	= adfs_write_inode,274	.put_super	= adfs_put_super,275	.statfs		= adfs_statfs,276	.remount_fs	= adfs_remount,277	.show_options	= adfs_show_options,278};279 280static int adfs_probe(struct super_block *sb, unsigned int offset, int silent,281		      int (*validate)(struct super_block *sb,282				      struct buffer_head *bh,283				      struct adfs_discrecord **bhp))284{285	struct adfs_sb_info *asb = ADFS_SB(sb);286	struct adfs_discrecord *dr;287	struct buffer_head *bh;288	unsigned int blocksize = BLOCK_SIZE;289	int ret, try;290 291	for (try = 0; try < 2; try++) {292		/* try to set the requested block size */293		if (sb->s_blocksize != blocksize &&294		    !sb_set_blocksize(sb, blocksize)) {295			if (!silent)296				adfs_msg(sb, KERN_ERR,297					 "error: unsupported blocksize");298			return -EINVAL;299		}300 301		/* read the buffer */302		bh = sb_bread(sb, offset >> sb->s_blocksize_bits);303		if (!bh) {304			adfs_msg(sb, KERN_ERR,305				 "error: unable to read block %u, try %d",306				 offset >> sb->s_blocksize_bits, try);307			return -EIO;308		}309 310		/* validate it */311		ret = validate(sb, bh, &dr);312		if (ret) {313			brelse(bh);314			return ret;315		}316 317		/* does the block size match the filesystem block size? */318		blocksize = 1 << dr->log2secsize;319		if (sb->s_blocksize == blocksize) {320			asb->s_map = adfs_read_map(sb, dr);321			brelse(bh);322			return PTR_ERR_OR_ZERO(asb->s_map);323		}324 325		brelse(bh);326	}327 328	return -EIO;329}330 331static int adfs_validate_bblk(struct super_block *sb, struct buffer_head *bh,332			      struct adfs_discrecord **drp)333{334	struct adfs_discrecord *dr;335	unsigned char *b_data;336 337	b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);338	if (adfs_checkbblk(b_data))339		return -EILSEQ;340 341	/* Do some sanity checks on the ADFS disc record */342	dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);343	if (adfs_checkdiscrecord(dr))344		return -EILSEQ;345 346	*drp = dr;347	return 0;348}349 350static int adfs_validate_dr0(struct super_block *sb, struct buffer_head *bh,351			      struct adfs_discrecord **drp)352{353	struct adfs_discrecord *dr;354 355	/* Do some sanity checks on the ADFS disc record */356	dr = (struct adfs_discrecord *)(bh->b_data + 4);357	if (adfs_checkdiscrecord(dr) || dr->nzones_high || dr->nzones != 1)358		return -EILSEQ;359 360	*drp = dr;361	return 0;362}363 364static int adfs_fill_super(struct super_block *sb, void *data, int silent)365{366	struct adfs_discrecord *dr;367	struct object_info root_obj;368	struct adfs_sb_info *asb;369	struct inode *root;370	int ret = -EINVAL;371 372	sb->s_flags |= ADFS_SB_FLAGS;373 374	asb = kzalloc(sizeof(*asb), GFP_KERNEL);375	if (!asb)376		return -ENOMEM;377 378	sb->s_fs_info = asb;379	sb->s_magic = ADFS_SUPER_MAGIC;380	sb->s_time_gran = 10000000;381 382	/* set default options */383	asb->s_uid = GLOBAL_ROOT_UID;384	asb->s_gid = GLOBAL_ROOT_GID;385	asb->s_owner_mask = ADFS_DEFAULT_OWNER_MASK;386	asb->s_other_mask = ADFS_DEFAULT_OTHER_MASK;387	asb->s_ftsuffix = 0;388 389	if (parse_options(sb, asb, data))390		goto error;391 392	/* Try to probe the filesystem boot block */393	ret = adfs_probe(sb, ADFS_DISCRECORD, 1, adfs_validate_bblk);394	if (ret == -EILSEQ)395		ret = adfs_probe(sb, 0, silent, adfs_validate_dr0);396	if (ret == -EILSEQ) {397		if (!silent)398			adfs_msg(sb, KERN_ERR,399				 "error: can't find an ADFS filesystem on dev %s.",400				 sb->s_id);401		ret = -EINVAL;402	}403	if (ret)404		goto error;405 406	/* set up enough so that we can read an inode */407	sb->s_op = &adfs_sops;408 409	dr = adfs_map_discrecord(asb->s_map);410 411	root_obj.parent_id = root_obj.indaddr = le32_to_cpu(dr->root);412	root_obj.name_len  = 0;413	/* Set root object date as 01 Jan 1987 00:00:00 */414	root_obj.loadaddr  = 0xfff0003f;415	root_obj.execaddr  = 0xec22c000;416	root_obj.size	   = ADFS_NEWDIR_SIZE;417	root_obj.attr	   = ADFS_NDA_DIRECTORY   | ADFS_NDA_OWNER_READ |418			     ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ;419 420	/*421	 * If this is a F+ disk with variable length directories,422	 * get the root_size from the disc record.423	 */424	if (dr->format_version) {425		root_obj.size = le32_to_cpu(dr->root_size);426		asb->s_dir     = &adfs_fplus_dir_ops;427		asb->s_namelen = ADFS_FPLUS_NAME_LEN;428	} else {429		asb->s_dir     = &adfs_f_dir_ops;430		asb->s_namelen = ADFS_F_NAME_LEN;431	}432	/*433	 * ,xyz hex filetype suffix may be added by driver434	 * to files that have valid RISC OS filetype435	 */436	if (asb->s_ftsuffix)437		asb->s_namelen += 4;438 439	sb->s_d_op = &adfs_dentry_operations;440	root = adfs_iget(sb, &root_obj);441	sb->s_root = d_make_root(root);442	if (!sb->s_root) {443		adfs_free_map(sb);444		adfs_error(sb, "get root inode failed\n");445		ret = -EIO;446		goto error;447	}448	return 0;449 450error:451	sb->s_fs_info = NULL;452	kfree(asb);453	return ret;454}455 456static struct dentry *adfs_mount(struct file_system_type *fs_type,457	int flags, const char *dev_name, void *data)458{459	return mount_bdev(fs_type, flags, dev_name, data, adfs_fill_super);460}461 462static struct file_system_type adfs_fs_type = {463	.owner		= THIS_MODULE,464	.name		= "adfs",465	.mount		= adfs_mount,466	.kill_sb	= kill_block_super,467	.fs_flags	= FS_REQUIRES_DEV,468};469MODULE_ALIAS_FS("adfs");470 471static int __init init_adfs_fs(void)472{473	int err = init_inodecache();474	if (err)475		goto out1;476	err = register_filesystem(&adfs_fs_type);477	if (err)478		goto out;479	return 0;480out:481	destroy_inodecache();482out1:483	return err;484}485 486static void __exit exit_adfs_fs(void)487{488	unregister_filesystem(&adfs_fs_type);489	destroy_inodecache();490}491 492module_init(init_adfs_fs)493module_exit(exit_adfs_fs)494MODULE_DESCRIPTION("Acorn Disc Filing System");495MODULE_LICENSE("GPL");496