Commit c4e89cc6 authored by Eric Sandeen's avatar Eric Sandeen Committed by Jan Kara
Browse files

udf: convert to new mount API



Convert the UDF filesystem to the new mount API.

UDF is slightly unique in that it always preserves prior mount
options across a remount, so that's handled by udf_init_options().

Signed-off-by: default avatarEric Sandeen <sandeen@redhat.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Message-Id: <fcb3faf3-e2df-450c-b37a-11000c274585@redhat.com>
parent 7a8e72c1
Loading
Loading
Loading
Loading
+265 −242
Original line number Diff line number Diff line
@@ -40,20 +40,20 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/parser.h>
#include <linux/stat.h>
#include <linux/cdrom.h>
#include <linux/nls.h>
#include <linux/vfs.h>
#include <linux/vmalloc.h>
#include <linux/errno.h>
#include <linux/mount.h>
#include <linux/seq_file.h>
#include <linux/bitmap.h>
#include <linux/crc-itu-t.h>
#include <linux/log2.h>
#include <asm/byteorder.h>
#include <linux/iversion.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>

#include "udf_sb.h"
#include "udf_i.h"
@@ -91,16 +91,20 @@ enum { UDF_MAX_LINKS = 0xffff };
#define UDF_MAX_FILESIZE (1ULL << 42)

/* These are the "meat" - everything else is stuffing */
static int udf_fill_super(struct super_block *, void *, int);
static int udf_fill_super(struct super_block *sb, struct fs_context *fc);
static void udf_put_super(struct super_block *);
static int udf_sync_fs(struct super_block *, int);
static int udf_remount_fs(struct super_block *, int *, char *);
static void udf_load_logicalvolint(struct super_block *, struct kernel_extent_ad);
static void udf_open_lvid(struct super_block *);
static void udf_close_lvid(struct super_block *);
static unsigned int udf_count_free(struct super_block *);
static int udf_statfs(struct dentry *, struct kstatfs *);
static int udf_show_options(struct seq_file *, struct dentry *);
static int udf_init_fs_context(struct fs_context *fc);
static int udf_parse_param(struct fs_context *fc, struct fs_parameter *param);
static int udf_reconfigure(struct fs_context *fc);
static void udf_free_fc(struct fs_context *fc);
static const struct fs_parameter_spec udf_param_spec[];

struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb)
{
@@ -119,18 +123,25 @@ struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb)
}

/* UDF filesystem type */
static struct dentry *udf_mount(struct file_system_type *fs_type,
		      int flags, const char *dev_name, void *data)
static int udf_get_tree(struct fs_context *fc)
{
	return mount_bdev(fs_type, flags, dev_name, data, udf_fill_super);
	return get_tree_bdev(fc, udf_fill_super);
}

static const struct fs_context_operations udf_context_ops = {
	.parse_param	= udf_parse_param,
	.get_tree	= udf_get_tree,
	.reconfigure	= udf_reconfigure,
	.free		= udf_free_fc,
};

static struct file_system_type udf_fstype = {
	.owner		= THIS_MODULE,
	.name		= "udf",
	.mount		= udf_mount,
	.kill_sb	= kill_block_super,
	.fs_flags	= FS_REQUIRES_DEV,
	.init_fs_context = udf_init_fs_context,
	.parameters	= udf_param_spec,
};
MODULE_ALIAS_FS("udf");

@@ -204,7 +215,6 @@ static const struct super_operations udf_sb_ops = {
	.put_super	= udf_put_super,
	.sync_fs	= udf_sync_fs,
	.statfs		= udf_statfs,
	.remount_fs	= udf_remount_fs,
	.show_options	= udf_show_options,
};

@@ -222,6 +232,65 @@ struct udf_options {
	struct nls_table *nls_map;
};

/*
 * UDF has historically preserved prior mount options across
 * a remount, so copy those here if remounting, otherwise set
 * initial mount defaults.
 */
static void udf_init_options(struct fs_context *fc, struct udf_options *uopt)
{
	if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
		struct super_block *sb = fc->root->d_sb;
		struct udf_sb_info *sbi = UDF_SB(sb);

		uopt->flags = sbi->s_flags;
		uopt->uid   = sbi->s_uid;
		uopt->gid   = sbi->s_gid;
		uopt->umask = sbi->s_umask;
		uopt->fmode = sbi->s_fmode;
		uopt->dmode = sbi->s_dmode;
		uopt->nls_map = NULL;
	} else {
		uopt->flags = (1 << UDF_FLAG_USE_AD_IN_ICB) |
			      (1 << UDF_FLAG_STRICT);
		/*
		 * By default we'll use overflow[ug]id when UDF
		 * inode [ug]id == -1
		 */
		uopt->uid = make_kuid(current_user_ns(), overflowuid);
		uopt->gid = make_kgid(current_user_ns(), overflowgid);
		uopt->umask = 0;
		uopt->fmode = UDF_INVALID_MODE;
		uopt->dmode = UDF_INVALID_MODE;
		uopt->nls_map = NULL;
		uopt->session = 0xFFFFFFFF;
	}
}

static int udf_init_fs_context(struct fs_context *fc)
{
	struct udf_options *uopt;

	uopt = kzalloc(sizeof(*uopt), GFP_KERNEL);
	if (!uopt)
		return -ENOMEM;

	udf_init_options(fc, uopt);

	fc->fs_private = uopt;
	fc->ops = &udf_context_ops;

	return 0;
}

static void udf_free_fc(struct fs_context *fc)
{
	struct udf_options *uopt = fc->fs_private;

	unload_nls(uopt->nls_map);
	kfree(fc->fs_private);
}

static int __init init_udf_fs(void)
{
	int err;
@@ -357,7 +426,7 @@ static int udf_show_options(struct seq_file *seq, struct dentry *root)
}

/*
 * udf_parse_options
 * udf_parse_param
 *
 * PURPOSE
 *	Parse mount options.
@@ -400,12 +469,12 @@ static int udf_show_options(struct seq_file *seq, struct dentry *root)
 *		yield highly unpredictable results.
 *
 * PRE-CONDITIONS
 *	options		Pointer to mount options string.
 *	uopts		Pointer to mount options variable.
 *	fc		fs_context with pointer to mount options variable.
 *	param		Pointer to fs_parameter being parsed.
 *
 * POST-CONDITIONS
 *	<return>	1	Mount options parsed okay.
 *	<return>	0	Error parsing mount options.
 *	<return>	0	Mount options parsed okay.
 *	<return>	errno	Error parsing mount options.
 *
 * HISTORY
 *	July 1, 1997 - Andrew E. Mileski
@@ -417,74 +486,56 @@ enum {
	Opt_noadinicb, Opt_adinicb, Opt_shortad, Opt_longad,
	Opt_gid, Opt_uid, Opt_umask, Opt_session, Opt_lastblock,
	Opt_anchor, Opt_volume, Opt_partition, Opt_fileset,
	Opt_rootdir, Opt_utf8, Opt_iocharset,
	Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore,
	Opt_fmode, Opt_dmode
	Opt_rootdir, Opt_utf8, Opt_iocharset, Opt_err, Opt_fmode, Opt_dmode
};

static const match_table_t tokens = {
	{Opt_novrs,	"novrs"},
	{Opt_nostrict,	"nostrict"},
	{Opt_bs,	"bs=%u"},
	{Opt_unhide,	"unhide"},
	{Opt_undelete,	"undelete"},
	{Opt_noadinicb,	"noadinicb"},
	{Opt_adinicb,	"adinicb"},
	{Opt_shortad,	"shortad"},
	{Opt_longad,	"longad"},
	{Opt_uforget,	"uid=forget"},
	{Opt_uignore,	"uid=ignore"},
	{Opt_gforget,	"gid=forget"},
	{Opt_gignore,	"gid=ignore"},
	{Opt_gid,	"gid=%u"},
	{Opt_uid,	"uid=%u"},
	{Opt_umask,	"umask=%o"},
	{Opt_session,	"session=%u"},
	{Opt_lastblock,	"lastblock=%u"},
	{Opt_anchor,	"anchor=%u"},
	{Opt_volume,	"volume=%u"},
	{Opt_partition,	"partition=%u"},
	{Opt_fileset,	"fileset=%u"},
	{Opt_rootdir,	"rootdir=%u"},
	{Opt_utf8,	"utf8"},
	{Opt_iocharset,	"iocharset=%s"},
	{Opt_fmode,     "mode=%o"},
	{Opt_dmode,     "dmode=%o"},
	{Opt_err,	NULL}
static const struct fs_parameter_spec udf_param_spec[] = {
	fsparam_flag	("novrs",		Opt_novrs),
	fsparam_flag	("nostrict",		Opt_nostrict),
	fsparam_u32	("bs",			Opt_bs),
	fsparam_flag	("unhide",		Opt_unhide),
	fsparam_flag	("undelete",		Opt_undelete),
	fsparam_flag_no	("adinicb",		Opt_adinicb),
	fsparam_flag	("shortad",		Opt_shortad),
	fsparam_flag	("longad",		Opt_longad),
	fsparam_string	("gid",			Opt_gid),
	fsparam_string	("uid",			Opt_uid),
	fsparam_u32	("umask",		Opt_umask),
	fsparam_u32	("session",		Opt_session),
	fsparam_u32	("lastblock",		Opt_lastblock),
	fsparam_u32	("anchor",		Opt_anchor),
	fsparam_u32	("volume",		Opt_volume),
	fsparam_u32	("partition",		Opt_partition),
	fsparam_u32	("fileset",		Opt_fileset),
	fsparam_u32	("rootdir",		Opt_rootdir),
	fsparam_flag	("utf8",		Opt_utf8),
	fsparam_string	("iocharset",		Opt_iocharset),
	fsparam_u32	("mode",		Opt_fmode),
	fsparam_u32	("dmode",		Opt_dmode),
	{}
 };

static int udf_parse_options(char *options, struct udf_options *uopt,
			     bool remount)
static int udf_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
	char *p;
	int option;
	unsigned int uv;

	uopt->session = 0xFFFFFFFF;
	uopt->lastblock = 0;
	uopt->anchor = 0;

	if (!options)
		return 1;

	while ((p = strsep(&options, ",")) != NULL) {
		substring_t args[MAX_OPT_ARGS];
	unsigned int n;
	struct udf_options *uopt = fc->fs_private;
	struct fs_parse_result result;
	int token;
		unsigned n;
		if (!*p)
			continue;
	bool remount = (fc->purpose & FS_CONTEXT_FOR_RECONFIGURE);

	token = fs_parse(fc, udf_param_spec, param, &result);
	if (token < 0)
		return token;

		token = match_token(p, tokens, args);
	switch (token) {
	case Opt_novrs:
		uopt->flags |= (1 << UDF_FLAG_NOVRS);
		break;
	case Opt_bs:
			if (match_int(&args[0], &option))
				return 0;
			n = option;
		n = result.uint_32;
		if (n != 512 && n != 1024 && n != 2048 && n != 4096)
				return 0;
			return -EINVAL;
		uopt->blocksize = n;
		uopt->flags |= (1 << UDF_FLAG_BLOCKSIZE_SET);
		break;
@@ -494,10 +545,10 @@ static int udf_parse_options(char *options, struct udf_options *uopt,
	case Opt_undelete:
		uopt->flags |= (1 << UDF_FLAG_UNDELETE);
		break;
		case Opt_noadinicb:
			uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB);
			break;
	case Opt_adinicb:
		if (result.negated)
			uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB);
		else
			uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB);
		break;
	case Opt_shortad:
@@ -507,47 +558,55 @@ static int udf_parse_options(char *options, struct udf_options *uopt,
		uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD);
		break;
	case Opt_gid:
			if (match_uint(args, &uv))
				return 0;
			uopt->gid = make_kgid(current_user_ns(), uv);
			if (!gid_valid(uopt->gid))
				return 0;
		if (kstrtoint(param->string, 10, &uv) == 0) {
			kgid_t gid = make_kgid(current_user_ns(), uv);
			if (!gid_valid(gid))
				return -EINVAL;
			uopt->gid = gid;
			uopt->flags |= (1 << UDF_FLAG_GID_SET);
		} else if (!strcmp(param->string, "forget")) {
			uopt->flags |= (1 << UDF_FLAG_GID_FORGET);
		} else if (!strcmp(param->string, "ignore")) {
			/* this option is superseded by gid=<number> */
			;
		} else {
			return -EINVAL;
		}
		break;
	case Opt_uid:
			if (match_uint(args, &uv))
				return 0;
			uopt->uid = make_kuid(current_user_ns(), uv);
			if (!uid_valid(uopt->uid))
				return 0;
		if (kstrtoint(param->string, 10, &uv) == 0) {
			kuid_t uid = make_kuid(current_user_ns(), uv);
			if (!uid_valid(uid))
				return -EINVAL;
			uopt->uid = uid;
			uopt->flags |= (1 << UDF_FLAG_UID_SET);
		} else if (!strcmp(param->string, "forget")) {
			uopt->flags |= (1 << UDF_FLAG_UID_FORGET);
		} else if (!strcmp(param->string, "ignore")) {
			/* this option is superseded by uid=<number> */
			;
		} else {
			return -EINVAL;
		}
		break;
	case Opt_umask:
			if (match_octal(args, &option))
				return 0;
			uopt->umask = option;
		uopt->umask = result.uint_32;
		break;
	case Opt_nostrict:
		uopt->flags &= ~(1 << UDF_FLAG_STRICT);
		break;
	case Opt_session:
			if (match_int(args, &option))
				return 0;
			uopt->session = option;
		uopt->session = result.uint_32;
		if (!remount)
			uopt->flags |= (1 << UDF_FLAG_SESSION_SET);
		break;
	case Opt_lastblock:
			if (match_int(args, &option))
				return 0;
			uopt->lastblock = option;
		uopt->lastblock = result.uint_32;
		if (!remount)
			uopt->flags |= (1 << UDF_FLAG_LASTBLOCK_SET);
		break;
	case Opt_anchor:
			if (match_int(args, &option))
				return 0;
			uopt->anchor = option;
		uopt->anchor = result.uint_32;
		break;
	case Opt_volume:
	case Opt_partition:
@@ -567,78 +626,53 @@ static int udf_parse_options(char *options, struct udf_options *uopt,
			uopt->nls_map = NULL;
		}
		/* When nls_map is not loaded then UTF-8 is used */
			if (!remount && strcmp(args[0].from, "utf8") != 0) {
				uopt->nls_map = load_nls(args[0].from);
		if (!remount && strcmp(param->string, "utf8") != 0) {
			uopt->nls_map = load_nls(param->string);
			if (!uopt->nls_map) {
					pr_err("iocharset %s not found\n",
						args[0].from);
					return 0;
				errorf(fc, "iocharset %s not found",
					param->string);
				return -EINVAL;;
			}
		}
		break;
		case Opt_uforget:
			uopt->flags |= (1 << UDF_FLAG_UID_FORGET);
			break;
		case Opt_uignore:
		case Opt_gignore:
			/* These options are superseeded by uid=<number> */
			break;
		case Opt_gforget:
			uopt->flags |= (1 << UDF_FLAG_GID_FORGET);
			break;
	case Opt_fmode:
			if (match_octal(args, &option))
				return 0;
			uopt->fmode = option & 0777;
		uopt->fmode = result.uint_32 & 0777;
		break;
	case Opt_dmode:
			if (match_octal(args, &option))
				return 0;
			uopt->dmode = option & 0777;
		uopt->dmode = result.uint_32 & 0777;
		break;
	default:
			pr_err("bad mount option \"%s\" or missing value\n", p);
			return 0;
		}
		return -EINVAL;
	}
	return 1;
	return 0;
}

static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
static int udf_reconfigure(struct fs_context *fc)
{
	struct udf_options uopt;
	struct udf_options *uopt = fc->fs_private;
	struct super_block *sb = fc->root->d_sb;
	struct udf_sb_info *sbi = UDF_SB(sb);
	int readonly = fc->sb_flags & SB_RDONLY;
	int error = 0;

	if (!(*flags & SB_RDONLY) && UDF_QUERY_FLAG(sb, UDF_FLAG_RW_INCOMPAT))
	if (!readonly && UDF_QUERY_FLAG(sb, UDF_FLAG_RW_INCOMPAT))
		return -EACCES;

	sync_filesystem(sb);

	uopt.flags = sbi->s_flags;
	uopt.uid   = sbi->s_uid;
	uopt.gid   = sbi->s_gid;
	uopt.umask = sbi->s_umask;
	uopt.fmode = sbi->s_fmode;
	uopt.dmode = sbi->s_dmode;
	uopt.nls_map = NULL;

	if (!udf_parse_options(options, &uopt, true))
		return -EINVAL;

	write_lock(&sbi->s_cred_lock);
	sbi->s_flags = uopt.flags;
	sbi->s_uid   = uopt.uid;
	sbi->s_gid   = uopt.gid;
	sbi->s_umask = uopt.umask;
	sbi->s_fmode = uopt.fmode;
	sbi->s_dmode = uopt.dmode;
	sbi->s_flags = uopt->flags;
	sbi->s_uid   = uopt->uid;
	sbi->s_gid   = uopt->gid;
	sbi->s_umask = uopt->umask;
	sbi->s_fmode = uopt->fmode;
	sbi->s_dmode = uopt->dmode;
	write_unlock(&sbi->s_cred_lock);

	if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
	if (readonly == sb_rdonly(sb))
		goto out_unlock;

	if (*flags & SB_RDONLY)
	if (readonly)
		udf_close_lvid(sb);
	else
		udf_open_lvid(sb);
@@ -2090,23 +2124,15 @@ u64 lvid_get_unique_id(struct super_block *sb)
	return ret;
}

static int udf_fill_super(struct super_block *sb, void *options, int silent)
static int udf_fill_super(struct super_block *sb, struct fs_context *fc)
{
	int ret = -EINVAL;
	struct inode *inode = NULL;
	struct udf_options uopt;
	struct udf_options *uopt = fc->fs_private;
	struct kernel_lb_addr rootdir, fileset;
	struct udf_sb_info *sbi;
	bool lvid_open = false;

	uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT);
	/* By default we'll use overflow[ug]id when UDF inode [ug]id == -1 */
	uopt.uid = make_kuid(current_user_ns(), overflowuid);
	uopt.gid = make_kgid(current_user_ns(), overflowgid);
	uopt.umask = 0;
	uopt.fmode = UDF_INVALID_MODE;
	uopt.dmode = UDF_INVALID_MODE;
	uopt.nls_map = NULL;
	int silent = fc->sb_flags & SB_SILENT;

	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
	if (!sbi)
@@ -2116,25 +2142,23 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)

	mutex_init(&sbi->s_alloc_mutex);

	if (!udf_parse_options((char *)options, &uopt, false))
		goto parse_options_failure;

	fileset.logicalBlockNum = 0xFFFFFFFF;
	fileset.partitionReferenceNum = 0xFFFF;

	sbi->s_flags = uopt.flags;
	sbi->s_uid = uopt.uid;
	sbi->s_gid = uopt.gid;
	sbi->s_umask = uopt.umask;
	sbi->s_fmode = uopt.fmode;
	sbi->s_dmode = uopt.dmode;
	sbi->s_nls_map = uopt.nls_map;
	sbi->s_flags = uopt->flags;
	sbi->s_uid = uopt->uid;
	sbi->s_gid = uopt->gid;
	sbi->s_umask = uopt->umask;
	sbi->s_fmode = uopt->fmode;
	sbi->s_dmode = uopt->dmode;
	sbi->s_nls_map = uopt->nls_map;
	uopt->nls_map = NULL;
	rwlock_init(&sbi->s_cred_lock);

	if (uopt.session == 0xFFFFFFFF)
	if (uopt->session == 0xFFFFFFFF)
		sbi->s_session = udf_get_last_session(sb);
	else
		sbi->s_session = uopt.session;
		sbi->s_session = uopt->session;

	udf_debug("Multi-session=%d\n", sbi->s_session);

@@ -2145,16 +2169,16 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
	sb->s_magic = UDF_SUPER_MAGIC;
	sb->s_time_gran = 1000;

	if (uopt.flags & (1 << UDF_FLAG_BLOCKSIZE_SET)) {
		ret = udf_load_vrs(sb, &uopt, silent, &fileset);
	if (uopt->flags & (1 << UDF_FLAG_BLOCKSIZE_SET)) {
		ret = udf_load_vrs(sb, uopt, silent, &fileset);
	} else {
		uopt.blocksize = bdev_logical_block_size(sb->s_bdev);
		while (uopt.blocksize <= 4096) {
			ret = udf_load_vrs(sb, &uopt, silent, &fileset);
		uopt->blocksize = bdev_logical_block_size(sb->s_bdev);
		while (uopt->blocksize <= 4096) {
			ret = udf_load_vrs(sb, uopt, silent, &fileset);
			if (ret < 0) {
				if (!silent && ret != -EACCES) {
					pr_notice("Scanning with blocksize %u failed\n",
						  uopt.blocksize);
						  uopt->blocksize);
				}
				brelse(sbi->s_lvid_bh);
				sbi->s_lvid_bh = NULL;
@@ -2167,7 +2191,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
			} else
				break;

			uopt.blocksize <<= 1;
			uopt->blocksize <<= 1;
		}
	}
	if (ret < 0) {
@@ -2272,8 +2296,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)

error_out:
	iput(sbi->s_vat_inode);
parse_options_failure:
	unload_nls(uopt.nls_map);
	unload_nls(uopt->nls_map);
	if (lvid_open)
		udf_close_lvid(sb);
	brelse(sbi->s_lvid_bh);