162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/affs/inode.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (c) 1996 Hans-Joachim Widmaier - Rewritten 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * (C) 1991 Linus Torvalds - minix filesystem 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/statfs.h> 1762306a36Sopenharmony_ci#include <linux/parser.h> 1862306a36Sopenharmony_ci#include <linux/magic.h> 1962306a36Sopenharmony_ci#include <linux/sched.h> 2062306a36Sopenharmony_ci#include <linux/cred.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/writeback.h> 2362306a36Sopenharmony_ci#include <linux/blkdev.h> 2462306a36Sopenharmony_ci#include <linux/seq_file.h> 2562306a36Sopenharmony_ci#include <linux/iversion.h> 2662306a36Sopenharmony_ci#include "affs.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int affs_statfs(struct dentry *dentry, struct kstatfs *buf); 2962306a36Sopenharmony_cistatic int affs_show_options(struct seq_file *m, struct dentry *root); 3062306a36Sopenharmony_cistatic int affs_remount (struct super_block *sb, int *flags, char *data); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void 3362306a36Sopenharmony_ciaffs_commit_super(struct super_block *sb, int wait) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct affs_sb_info *sbi = AFFS_SB(sb); 3662306a36Sopenharmony_ci struct buffer_head *bh = sbi->s_root_bh; 3762306a36Sopenharmony_ci struct affs_root_tail *tail = AFFS_ROOT_TAIL(sb, bh); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci lock_buffer(bh); 4062306a36Sopenharmony_ci affs_secs_to_datestamp(ktime_get_real_seconds(), &tail->disk_change); 4162306a36Sopenharmony_ci affs_fix_checksum(sb, bh); 4262306a36Sopenharmony_ci unlock_buffer(bh); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci mark_buffer_dirty(bh); 4562306a36Sopenharmony_ci if (wait) 4662306a36Sopenharmony_ci sync_dirty_buffer(bh); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void 5062306a36Sopenharmony_ciaffs_put_super(struct super_block *sb) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct affs_sb_info *sbi = AFFS_SB(sb); 5362306a36Sopenharmony_ci pr_debug("%s()\n", __func__); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci cancel_delayed_work_sync(&sbi->sb_work); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int 5962306a36Sopenharmony_ciaffs_sync_fs(struct super_block *sb, int wait) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci affs_commit_super(sb, wait); 6262306a36Sopenharmony_ci return 0; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void flush_superblock(struct work_struct *work) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct affs_sb_info *sbi; 6862306a36Sopenharmony_ci struct super_block *sb; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci sbi = container_of(work, struct affs_sb_info, sb_work.work); 7162306a36Sopenharmony_ci sb = sbi->sb; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci spin_lock(&sbi->work_lock); 7462306a36Sopenharmony_ci sbi->work_queued = 0; 7562306a36Sopenharmony_ci spin_unlock(&sbi->work_lock); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci affs_commit_super(sb, 1); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_civoid affs_mark_sb_dirty(struct super_block *sb) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct affs_sb_info *sbi = AFFS_SB(sb); 8362306a36Sopenharmony_ci unsigned long delay; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (sb_rdonly(sb)) 8662306a36Sopenharmony_ci return; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci spin_lock(&sbi->work_lock); 8962306a36Sopenharmony_ci if (!sbi->work_queued) { 9062306a36Sopenharmony_ci delay = msecs_to_jiffies(dirty_writeback_interval * 10); 9162306a36Sopenharmony_ci queue_delayed_work(system_long_wq, &sbi->sb_work, delay); 9262306a36Sopenharmony_ci sbi->work_queued = 1; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci spin_unlock(&sbi->work_lock); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic struct kmem_cache * affs_inode_cachep; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic struct inode *affs_alloc_inode(struct super_block *sb) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct affs_inode_info *i; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci i = alloc_inode_sb(sb, affs_inode_cachep, GFP_KERNEL); 10462306a36Sopenharmony_ci if (!i) 10562306a36Sopenharmony_ci return NULL; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci inode_set_iversion(&i->vfs_inode, 1); 10862306a36Sopenharmony_ci i->i_lc = NULL; 10962306a36Sopenharmony_ci i->i_ext_bh = NULL; 11062306a36Sopenharmony_ci i->i_pa_cnt = 0; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return &i->vfs_inode; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void affs_free_inode(struct inode *inode) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci kmem_cache_free(affs_inode_cachep, AFFS_I(inode)); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void init_once(void *foo) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct affs_inode_info *ei = (struct affs_inode_info *) foo; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci mutex_init(&ei->i_link_lock); 12562306a36Sopenharmony_ci mutex_init(&ei->i_ext_lock); 12662306a36Sopenharmony_ci inode_init_once(&ei->vfs_inode); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int __init init_inodecache(void) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci affs_inode_cachep = kmem_cache_create("affs_inode_cache", 13262306a36Sopenharmony_ci sizeof(struct affs_inode_info), 13362306a36Sopenharmony_ci 0, (SLAB_RECLAIM_ACCOUNT| 13462306a36Sopenharmony_ci SLAB_MEM_SPREAD|SLAB_ACCOUNT), 13562306a36Sopenharmony_ci init_once); 13662306a36Sopenharmony_ci if (affs_inode_cachep == NULL) 13762306a36Sopenharmony_ci return -ENOMEM; 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void destroy_inodecache(void) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * Make sure all delayed rcu free inodes are flushed before we 14562306a36Sopenharmony_ci * destroy cache. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci rcu_barrier(); 14862306a36Sopenharmony_ci kmem_cache_destroy(affs_inode_cachep); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic const struct super_operations affs_sops = { 15262306a36Sopenharmony_ci .alloc_inode = affs_alloc_inode, 15362306a36Sopenharmony_ci .free_inode = affs_free_inode, 15462306a36Sopenharmony_ci .write_inode = affs_write_inode, 15562306a36Sopenharmony_ci .evict_inode = affs_evict_inode, 15662306a36Sopenharmony_ci .put_super = affs_put_super, 15762306a36Sopenharmony_ci .sync_fs = affs_sync_fs, 15862306a36Sopenharmony_ci .statfs = affs_statfs, 15962306a36Sopenharmony_ci .remount_fs = affs_remount, 16062306a36Sopenharmony_ci .show_options = affs_show_options, 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cienum { 16462306a36Sopenharmony_ci Opt_bs, Opt_mode, Opt_mufs, Opt_notruncate, Opt_prefix, Opt_protect, 16562306a36Sopenharmony_ci Opt_reserved, Opt_root, Opt_setgid, Opt_setuid, 16662306a36Sopenharmony_ci Opt_verbose, Opt_volume, Opt_ignore, Opt_err, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic const match_table_t tokens = { 17062306a36Sopenharmony_ci {Opt_bs, "bs=%u"}, 17162306a36Sopenharmony_ci {Opt_mode, "mode=%o"}, 17262306a36Sopenharmony_ci {Opt_mufs, "mufs"}, 17362306a36Sopenharmony_ci {Opt_notruncate, "nofilenametruncate"}, 17462306a36Sopenharmony_ci {Opt_prefix, "prefix=%s"}, 17562306a36Sopenharmony_ci {Opt_protect, "protect"}, 17662306a36Sopenharmony_ci {Opt_reserved, "reserved=%u"}, 17762306a36Sopenharmony_ci {Opt_root, "root=%u"}, 17862306a36Sopenharmony_ci {Opt_setgid, "setgid=%u"}, 17962306a36Sopenharmony_ci {Opt_setuid, "setuid=%u"}, 18062306a36Sopenharmony_ci {Opt_verbose, "verbose"}, 18162306a36Sopenharmony_ci {Opt_volume, "volume=%s"}, 18262306a36Sopenharmony_ci {Opt_ignore, "grpquota"}, 18362306a36Sopenharmony_ci {Opt_ignore, "noquota"}, 18462306a36Sopenharmony_ci {Opt_ignore, "quota"}, 18562306a36Sopenharmony_ci {Opt_ignore, "usrquota"}, 18662306a36Sopenharmony_ci {Opt_err, NULL}, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int 19062306a36Sopenharmony_ciparse_options(char *options, kuid_t *uid, kgid_t *gid, int *mode, int *reserved, s32 *root, 19162306a36Sopenharmony_ci int *blocksize, char **prefix, char *volume, unsigned long *mount_opts) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci char *p; 19462306a36Sopenharmony_ci substring_t args[MAX_OPT_ARGS]; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Fill in defaults */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci *uid = current_uid(); 19962306a36Sopenharmony_ci *gid = current_gid(); 20062306a36Sopenharmony_ci *reserved = 2; 20162306a36Sopenharmony_ci *root = -1; 20262306a36Sopenharmony_ci *blocksize = -1; 20362306a36Sopenharmony_ci volume[0] = ':'; 20462306a36Sopenharmony_ci volume[1] = 0; 20562306a36Sopenharmony_ci *mount_opts = 0; 20662306a36Sopenharmony_ci if (!options) 20762306a36Sopenharmony_ci return 1; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci while ((p = strsep(&options, ",")) != NULL) { 21062306a36Sopenharmony_ci int token, n, option; 21162306a36Sopenharmony_ci if (!*p) 21262306a36Sopenharmony_ci continue; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci token = match_token(p, tokens, args); 21562306a36Sopenharmony_ci switch (token) { 21662306a36Sopenharmony_ci case Opt_bs: 21762306a36Sopenharmony_ci if (match_int(&args[0], &n)) 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci if (n != 512 && n != 1024 && n != 2048 22062306a36Sopenharmony_ci && n != 4096) { 22162306a36Sopenharmony_ci pr_warn("Invalid blocksize (512, 1024, 2048, 4096 allowed)\n"); 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci *blocksize = n; 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci case Opt_mode: 22762306a36Sopenharmony_ci if (match_octal(&args[0], &option)) 22862306a36Sopenharmony_ci return 0; 22962306a36Sopenharmony_ci *mode = option & 0777; 23062306a36Sopenharmony_ci affs_set_opt(*mount_opts, SF_SETMODE); 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci case Opt_mufs: 23362306a36Sopenharmony_ci affs_set_opt(*mount_opts, SF_MUFS); 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case Opt_notruncate: 23662306a36Sopenharmony_ci affs_set_opt(*mount_opts, SF_NO_TRUNCATE); 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case Opt_prefix: 23962306a36Sopenharmony_ci kfree(*prefix); 24062306a36Sopenharmony_ci *prefix = match_strdup(&args[0]); 24162306a36Sopenharmony_ci if (!*prefix) 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci affs_set_opt(*mount_opts, SF_PREFIX); 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci case Opt_protect: 24662306a36Sopenharmony_ci affs_set_opt(*mount_opts, SF_IMMUTABLE); 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci case Opt_reserved: 24962306a36Sopenharmony_ci if (match_int(&args[0], reserved)) 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci case Opt_root: 25362306a36Sopenharmony_ci if (match_int(&args[0], root)) 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci case Opt_setgid: 25762306a36Sopenharmony_ci if (match_int(&args[0], &option)) 25862306a36Sopenharmony_ci return 0; 25962306a36Sopenharmony_ci *gid = make_kgid(current_user_ns(), option); 26062306a36Sopenharmony_ci if (!gid_valid(*gid)) 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci affs_set_opt(*mount_opts, SF_SETGID); 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci case Opt_setuid: 26562306a36Sopenharmony_ci if (match_int(&args[0], &option)) 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci *uid = make_kuid(current_user_ns(), option); 26862306a36Sopenharmony_ci if (!uid_valid(*uid)) 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci affs_set_opt(*mount_opts, SF_SETUID); 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci case Opt_verbose: 27362306a36Sopenharmony_ci affs_set_opt(*mount_opts, SF_VERBOSE); 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci case Opt_volume: { 27662306a36Sopenharmony_ci char *vol = match_strdup(&args[0]); 27762306a36Sopenharmony_ci if (!vol) 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci strscpy(volume, vol, 32); 28062306a36Sopenharmony_ci kfree(vol); 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci case Opt_ignore: 28462306a36Sopenharmony_ci /* Silently ignore the quota options */ 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci default: 28762306a36Sopenharmony_ci pr_warn("Unrecognized mount option \"%s\" or missing value\n", 28862306a36Sopenharmony_ci p); 28962306a36Sopenharmony_ci return 0; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci return 1; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int affs_show_options(struct seq_file *m, struct dentry *root) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct super_block *sb = root->d_sb; 29862306a36Sopenharmony_ci struct affs_sb_info *sbi = AFFS_SB(sb); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (sb->s_blocksize) 30162306a36Sopenharmony_ci seq_printf(m, ",bs=%lu", sb->s_blocksize); 30262306a36Sopenharmony_ci if (affs_test_opt(sbi->s_flags, SF_SETMODE)) 30362306a36Sopenharmony_ci seq_printf(m, ",mode=%o", sbi->s_mode); 30462306a36Sopenharmony_ci if (affs_test_opt(sbi->s_flags, SF_MUFS)) 30562306a36Sopenharmony_ci seq_puts(m, ",mufs"); 30662306a36Sopenharmony_ci if (affs_test_opt(sbi->s_flags, SF_NO_TRUNCATE)) 30762306a36Sopenharmony_ci seq_puts(m, ",nofilenametruncate"); 30862306a36Sopenharmony_ci if (affs_test_opt(sbi->s_flags, SF_PREFIX)) 30962306a36Sopenharmony_ci seq_printf(m, ",prefix=%s", sbi->s_prefix); 31062306a36Sopenharmony_ci if (affs_test_opt(sbi->s_flags, SF_IMMUTABLE)) 31162306a36Sopenharmony_ci seq_puts(m, ",protect"); 31262306a36Sopenharmony_ci if (sbi->s_reserved != 2) 31362306a36Sopenharmony_ci seq_printf(m, ",reserved=%u", sbi->s_reserved); 31462306a36Sopenharmony_ci if (sbi->s_root_block != (sbi->s_reserved + sbi->s_partition_size - 1) / 2) 31562306a36Sopenharmony_ci seq_printf(m, ",root=%u", sbi->s_root_block); 31662306a36Sopenharmony_ci if (affs_test_opt(sbi->s_flags, SF_SETGID)) 31762306a36Sopenharmony_ci seq_printf(m, ",setgid=%u", 31862306a36Sopenharmony_ci from_kgid_munged(&init_user_ns, sbi->s_gid)); 31962306a36Sopenharmony_ci if (affs_test_opt(sbi->s_flags, SF_SETUID)) 32062306a36Sopenharmony_ci seq_printf(m, ",setuid=%u", 32162306a36Sopenharmony_ci from_kuid_munged(&init_user_ns, sbi->s_uid)); 32262306a36Sopenharmony_ci if (affs_test_opt(sbi->s_flags, SF_VERBOSE)) 32362306a36Sopenharmony_ci seq_puts(m, ",verbose"); 32462306a36Sopenharmony_ci if (sbi->s_volume[0]) 32562306a36Sopenharmony_ci seq_printf(m, ",volume=%s", sbi->s_volume); 32662306a36Sopenharmony_ci return 0; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci/* This function definitely needs to be split up. Some fine day I'll 33062306a36Sopenharmony_ci * hopefully have the guts to do so. Until then: sorry for the mess. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic int affs_fill_super(struct super_block *sb, void *data, int silent) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct affs_sb_info *sbi; 33662306a36Sopenharmony_ci struct buffer_head *root_bh = NULL; 33762306a36Sopenharmony_ci struct buffer_head *boot_bh; 33862306a36Sopenharmony_ci struct inode *root_inode = NULL; 33962306a36Sopenharmony_ci s32 root_block; 34062306a36Sopenharmony_ci int size, blocksize; 34162306a36Sopenharmony_ci u32 chksum; 34262306a36Sopenharmony_ci int num_bm; 34362306a36Sopenharmony_ci int i, j; 34462306a36Sopenharmony_ci kuid_t uid; 34562306a36Sopenharmony_ci kgid_t gid; 34662306a36Sopenharmony_ci int reserved; 34762306a36Sopenharmony_ci unsigned long mount_flags; 34862306a36Sopenharmony_ci int tmp_flags; /* fix remount prototype... */ 34962306a36Sopenharmony_ci u8 sig[4]; 35062306a36Sopenharmony_ci int ret; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci pr_debug("read_super(%s)\n", data ? (const char *)data : "no options"); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci sb->s_magic = AFFS_SUPER_MAGIC; 35562306a36Sopenharmony_ci sb->s_op = &affs_sops; 35662306a36Sopenharmony_ci sb->s_flags |= SB_NODIRATIME; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci sb->s_time_gran = NSEC_PER_SEC; 35962306a36Sopenharmony_ci sb->s_time_min = sys_tz.tz_minuteswest * 60 + AFFS_EPOCH_DELTA; 36062306a36Sopenharmony_ci sb->s_time_max = 86400LL * U32_MAX + 86400 + sb->s_time_min; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci sbi = kzalloc(sizeof(struct affs_sb_info), GFP_KERNEL); 36362306a36Sopenharmony_ci if (!sbi) 36462306a36Sopenharmony_ci return -ENOMEM; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci sb->s_fs_info = sbi; 36762306a36Sopenharmony_ci sbi->sb = sb; 36862306a36Sopenharmony_ci mutex_init(&sbi->s_bmlock); 36962306a36Sopenharmony_ci spin_lock_init(&sbi->symlink_lock); 37062306a36Sopenharmony_ci spin_lock_init(&sbi->work_lock); 37162306a36Sopenharmony_ci INIT_DELAYED_WORK(&sbi->sb_work, flush_superblock); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block, 37462306a36Sopenharmony_ci &blocksize,&sbi->s_prefix, 37562306a36Sopenharmony_ci sbi->s_volume, &mount_flags)) { 37662306a36Sopenharmony_ci pr_err("Error parsing options\n"); 37762306a36Sopenharmony_ci return -EINVAL; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci /* N.B. after this point s_prefix must be released */ 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci sbi->s_flags = mount_flags; 38262306a36Sopenharmony_ci sbi->s_mode = i; 38362306a36Sopenharmony_ci sbi->s_uid = uid; 38462306a36Sopenharmony_ci sbi->s_gid = gid; 38562306a36Sopenharmony_ci sbi->s_reserved= reserved; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* Get the size of the device in 512-byte blocks. 38862306a36Sopenharmony_ci * If we later see that the partition uses bigger 38962306a36Sopenharmony_ci * blocks, we will have to change it. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci size = bdev_nr_sectors(sb->s_bdev); 39362306a36Sopenharmony_ci pr_debug("initial blocksize=%d, #blocks=%d\n", 512, size); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci affs_set_blocksize(sb, PAGE_SIZE); 39662306a36Sopenharmony_ci /* Try to find root block. Its location depends on the block size. */ 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci i = bdev_logical_block_size(sb->s_bdev); 39962306a36Sopenharmony_ci j = PAGE_SIZE; 40062306a36Sopenharmony_ci if (blocksize > 0) { 40162306a36Sopenharmony_ci i = j = blocksize; 40262306a36Sopenharmony_ci size = size / (blocksize / 512); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci for (blocksize = i; blocksize <= j; blocksize <<= 1, size >>= 1) { 40662306a36Sopenharmony_ci sbi->s_root_block = root_block; 40762306a36Sopenharmony_ci if (root_block < 0) 40862306a36Sopenharmony_ci sbi->s_root_block = (reserved + size - 1) / 2; 40962306a36Sopenharmony_ci pr_debug("setting blocksize to %d\n", blocksize); 41062306a36Sopenharmony_ci affs_set_blocksize(sb, blocksize); 41162306a36Sopenharmony_ci sbi->s_partition_size = size; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* The root block location that was calculated above is not 41462306a36Sopenharmony_ci * correct if the partition size is an odd number of 512- 41562306a36Sopenharmony_ci * byte blocks, which will be rounded down to a number of 41662306a36Sopenharmony_ci * 1024-byte blocks, and if there were an even number of 41762306a36Sopenharmony_ci * reserved blocks. Ideally, all partition checkers should 41862306a36Sopenharmony_ci * report the real number of blocks of the real blocksize, 41962306a36Sopenharmony_ci * but since this just cannot be done, we have to try to 42062306a36Sopenharmony_ci * find the root block anyways. In the above case, it is one 42162306a36Sopenharmony_ci * block behind the calculated one. So we check this one, too. 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci for (num_bm = 0; num_bm < 2; num_bm++) { 42462306a36Sopenharmony_ci pr_debug("Dev %s, trying root=%u, bs=%d, " 42562306a36Sopenharmony_ci "size=%d, reserved=%d\n", 42662306a36Sopenharmony_ci sb->s_id, 42762306a36Sopenharmony_ci sbi->s_root_block + num_bm, 42862306a36Sopenharmony_ci blocksize, size, reserved); 42962306a36Sopenharmony_ci root_bh = affs_bread(sb, sbi->s_root_block + num_bm); 43062306a36Sopenharmony_ci if (!root_bh) 43162306a36Sopenharmony_ci continue; 43262306a36Sopenharmony_ci if (!affs_checksum_block(sb, root_bh) && 43362306a36Sopenharmony_ci be32_to_cpu(AFFS_ROOT_HEAD(root_bh)->ptype) == T_SHORT && 43462306a36Sopenharmony_ci be32_to_cpu(AFFS_ROOT_TAIL(sb, root_bh)->stype) == ST_ROOT) { 43562306a36Sopenharmony_ci sbi->s_hashsize = blocksize / 4 - 56; 43662306a36Sopenharmony_ci sbi->s_root_block += num_bm; 43762306a36Sopenharmony_ci goto got_root; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci affs_brelse(root_bh); 44062306a36Sopenharmony_ci root_bh = NULL; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci if (!silent) 44462306a36Sopenharmony_ci pr_err("No valid root block on device %s\n", sb->s_id); 44562306a36Sopenharmony_ci return -EINVAL; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* N.B. after this point bh must be released */ 44862306a36Sopenharmony_cigot_root: 44962306a36Sopenharmony_ci /* Keep super block in cache */ 45062306a36Sopenharmony_ci sbi->s_root_bh = root_bh; 45162306a36Sopenharmony_ci root_block = sbi->s_root_block; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* Find out which kind of FS we have */ 45462306a36Sopenharmony_ci boot_bh = sb_bread(sb, 0); 45562306a36Sopenharmony_ci if (!boot_bh) { 45662306a36Sopenharmony_ci pr_err("Cannot read boot block\n"); 45762306a36Sopenharmony_ci return -EINVAL; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci memcpy(sig, boot_bh->b_data, 4); 46062306a36Sopenharmony_ci brelse(boot_bh); 46162306a36Sopenharmony_ci chksum = be32_to_cpu(*(__be32 *)sig); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* Dircache filesystems are compatible with non-dircache ones 46462306a36Sopenharmony_ci * when reading. As long as they aren't supported, writing is 46562306a36Sopenharmony_ci * not recommended. 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_ci if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS 46862306a36Sopenharmony_ci || chksum == MUFS_DCOFS) && !sb_rdonly(sb)) { 46962306a36Sopenharmony_ci pr_notice("Dircache FS - mounting %s read only\n", sb->s_id); 47062306a36Sopenharmony_ci sb->s_flags |= SB_RDONLY; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci switch (chksum) { 47362306a36Sopenharmony_ci case MUFS_FS: 47462306a36Sopenharmony_ci case MUFS_INTLFFS: 47562306a36Sopenharmony_ci case MUFS_DCFFS: 47662306a36Sopenharmony_ci affs_set_opt(sbi->s_flags, SF_MUFS); 47762306a36Sopenharmony_ci fallthrough; 47862306a36Sopenharmony_ci case FS_INTLFFS: 47962306a36Sopenharmony_ci case FS_DCFFS: 48062306a36Sopenharmony_ci affs_set_opt(sbi->s_flags, SF_INTL); 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci case MUFS_FFS: 48362306a36Sopenharmony_ci affs_set_opt(sbi->s_flags, SF_MUFS); 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci case FS_FFS: 48662306a36Sopenharmony_ci break; 48762306a36Sopenharmony_ci case MUFS_OFS: 48862306a36Sopenharmony_ci affs_set_opt(sbi->s_flags, SF_MUFS); 48962306a36Sopenharmony_ci fallthrough; 49062306a36Sopenharmony_ci case FS_OFS: 49162306a36Sopenharmony_ci affs_set_opt(sbi->s_flags, SF_OFS); 49262306a36Sopenharmony_ci sb->s_flags |= SB_NOEXEC; 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci case MUFS_DCOFS: 49562306a36Sopenharmony_ci case MUFS_INTLOFS: 49662306a36Sopenharmony_ci affs_set_opt(sbi->s_flags, SF_MUFS); 49762306a36Sopenharmony_ci fallthrough; 49862306a36Sopenharmony_ci case FS_DCOFS: 49962306a36Sopenharmony_ci case FS_INTLOFS: 50062306a36Sopenharmony_ci affs_set_opt(sbi->s_flags, SF_INTL); 50162306a36Sopenharmony_ci affs_set_opt(sbi->s_flags, SF_OFS); 50262306a36Sopenharmony_ci sb->s_flags |= SB_NOEXEC; 50362306a36Sopenharmony_ci break; 50462306a36Sopenharmony_ci default: 50562306a36Sopenharmony_ci pr_err("Unknown filesystem on device %s: %08X\n", 50662306a36Sopenharmony_ci sb->s_id, chksum); 50762306a36Sopenharmony_ci return -EINVAL; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (affs_test_opt(mount_flags, SF_VERBOSE)) { 51162306a36Sopenharmony_ci u8 len = AFFS_ROOT_TAIL(sb, root_bh)->disk_name[0]; 51262306a36Sopenharmony_ci pr_notice("Mounting volume \"%.*s\": Type=%.3s\\%c, Blocksize=%d\n", 51362306a36Sopenharmony_ci len > 31 ? 31 : len, 51462306a36Sopenharmony_ci AFFS_ROOT_TAIL(sb, root_bh)->disk_name + 1, 51562306a36Sopenharmony_ci sig, sig[3] + '0', blocksize); 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci sb->s_flags |= SB_NODEV | SB_NOSUID; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci sbi->s_data_blksize = sb->s_blocksize; 52162306a36Sopenharmony_ci if (affs_test_opt(sbi->s_flags, SF_OFS)) 52262306a36Sopenharmony_ci sbi->s_data_blksize -= 24; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci tmp_flags = sb->s_flags; 52562306a36Sopenharmony_ci ret = affs_init_bitmap(sb, &tmp_flags); 52662306a36Sopenharmony_ci if (ret) 52762306a36Sopenharmony_ci return ret; 52862306a36Sopenharmony_ci sb->s_flags = tmp_flags; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* set up enough so that it can read an inode */ 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci root_inode = affs_iget(sb, root_block); 53362306a36Sopenharmony_ci if (IS_ERR(root_inode)) 53462306a36Sopenharmony_ci return PTR_ERR(root_inode); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (affs_test_opt(AFFS_SB(sb)->s_flags, SF_INTL)) 53762306a36Sopenharmony_ci sb->s_d_op = &affs_intl_dentry_operations; 53862306a36Sopenharmony_ci else 53962306a36Sopenharmony_ci sb->s_d_op = &affs_dentry_operations; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci sb->s_root = d_make_root(root_inode); 54262306a36Sopenharmony_ci if (!sb->s_root) { 54362306a36Sopenharmony_ci pr_err("AFFS: Get root inode failed\n"); 54462306a36Sopenharmony_ci return -ENOMEM; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci sb->s_export_op = &affs_export_ops; 54862306a36Sopenharmony_ci pr_debug("s_flags=%lX\n", sb->s_flags); 54962306a36Sopenharmony_ci return 0; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int 55362306a36Sopenharmony_ciaffs_remount(struct super_block *sb, int *flags, char *data) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct affs_sb_info *sbi = AFFS_SB(sb); 55662306a36Sopenharmony_ci int blocksize; 55762306a36Sopenharmony_ci kuid_t uid; 55862306a36Sopenharmony_ci kgid_t gid; 55962306a36Sopenharmony_ci int mode; 56062306a36Sopenharmony_ci int reserved; 56162306a36Sopenharmony_ci int root_block; 56262306a36Sopenharmony_ci unsigned long mount_flags; 56362306a36Sopenharmony_ci int res = 0; 56462306a36Sopenharmony_ci char volume[32]; 56562306a36Sopenharmony_ci char *prefix = NULL; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci pr_debug("%s(flags=0x%x,opts=\"%s\")\n", __func__, *flags, data); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci sync_filesystem(sb); 57062306a36Sopenharmony_ci *flags |= SB_NODIRATIME; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci memcpy(volume, sbi->s_volume, 32); 57362306a36Sopenharmony_ci if (!parse_options(data, &uid, &gid, &mode, &reserved, &root_block, 57462306a36Sopenharmony_ci &blocksize, &prefix, volume, 57562306a36Sopenharmony_ci &mount_flags)) { 57662306a36Sopenharmony_ci kfree(prefix); 57762306a36Sopenharmony_ci return -EINVAL; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci flush_delayed_work(&sbi->sb_work); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci sbi->s_flags = mount_flags; 58362306a36Sopenharmony_ci sbi->s_mode = mode; 58462306a36Sopenharmony_ci sbi->s_uid = uid; 58562306a36Sopenharmony_ci sbi->s_gid = gid; 58662306a36Sopenharmony_ci /* protect against readers */ 58762306a36Sopenharmony_ci spin_lock(&sbi->symlink_lock); 58862306a36Sopenharmony_ci if (prefix) { 58962306a36Sopenharmony_ci kfree(sbi->s_prefix); 59062306a36Sopenharmony_ci sbi->s_prefix = prefix; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci memcpy(sbi->s_volume, volume, 32); 59362306a36Sopenharmony_ci spin_unlock(&sbi->symlink_lock); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb)) 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (*flags & SB_RDONLY) 59962306a36Sopenharmony_ci affs_free_bitmap(sb); 60062306a36Sopenharmony_ci else 60162306a36Sopenharmony_ci res = affs_init_bitmap(sb, flags); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return res; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic int 60762306a36Sopenharmony_ciaffs_statfs(struct dentry *dentry, struct kstatfs *buf) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct super_block *sb = dentry->d_sb; 61062306a36Sopenharmony_ci int free; 61162306a36Sopenharmony_ci u64 id = huge_encode_dev(sb->s_bdev->bd_dev); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci pr_debug("%s() partsize=%d, reserved=%d\n", 61462306a36Sopenharmony_ci __func__, AFFS_SB(sb)->s_partition_size, 61562306a36Sopenharmony_ci AFFS_SB(sb)->s_reserved); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci free = affs_count_free_blocks(sb); 61862306a36Sopenharmony_ci buf->f_type = AFFS_SUPER_MAGIC; 61962306a36Sopenharmony_ci buf->f_bsize = sb->s_blocksize; 62062306a36Sopenharmony_ci buf->f_blocks = AFFS_SB(sb)->s_partition_size - AFFS_SB(sb)->s_reserved; 62162306a36Sopenharmony_ci buf->f_bfree = free; 62262306a36Sopenharmony_ci buf->f_bavail = free; 62362306a36Sopenharmony_ci buf->f_fsid = u64_to_fsid(id); 62462306a36Sopenharmony_ci buf->f_namelen = AFFSNAMEMAX; 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic struct dentry *affs_mount(struct file_system_type *fs_type, 62962306a36Sopenharmony_ci int flags, const char *dev_name, void *data) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci return mount_bdev(fs_type, flags, dev_name, data, affs_fill_super); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic void affs_kill_sb(struct super_block *sb) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci struct affs_sb_info *sbi = AFFS_SB(sb); 63762306a36Sopenharmony_ci kill_block_super(sb); 63862306a36Sopenharmony_ci if (sbi) { 63962306a36Sopenharmony_ci affs_free_bitmap(sb); 64062306a36Sopenharmony_ci affs_brelse(sbi->s_root_bh); 64162306a36Sopenharmony_ci kfree(sbi->s_prefix); 64262306a36Sopenharmony_ci mutex_destroy(&sbi->s_bmlock); 64362306a36Sopenharmony_ci kfree(sbi); 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic struct file_system_type affs_fs_type = { 64862306a36Sopenharmony_ci .owner = THIS_MODULE, 64962306a36Sopenharmony_ci .name = "affs", 65062306a36Sopenharmony_ci .mount = affs_mount, 65162306a36Sopenharmony_ci .kill_sb = affs_kill_sb, 65262306a36Sopenharmony_ci .fs_flags = FS_REQUIRES_DEV, 65362306a36Sopenharmony_ci}; 65462306a36Sopenharmony_ciMODULE_ALIAS_FS("affs"); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic int __init init_affs_fs(void) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci int err = init_inodecache(); 65962306a36Sopenharmony_ci if (err) 66062306a36Sopenharmony_ci goto out1; 66162306a36Sopenharmony_ci err = register_filesystem(&affs_fs_type); 66262306a36Sopenharmony_ci if (err) 66362306a36Sopenharmony_ci goto out; 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ciout: 66662306a36Sopenharmony_ci destroy_inodecache(); 66762306a36Sopenharmony_ciout1: 66862306a36Sopenharmony_ci return err; 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic void __exit exit_affs_fs(void) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci unregister_filesystem(&affs_fs_type); 67462306a36Sopenharmony_ci destroy_inodecache(); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ciMODULE_DESCRIPTION("Amiga filesystem support for Linux"); 67862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cimodule_init(init_affs_fs) 68162306a36Sopenharmony_cimodule_exit(exit_affs_fs) 682