Add temporary mount options

Add the required kernel side infrastructure to parse arbitrary
mount options.  This enables us to support temporary mount
options in largely the same way it is handled on other platforms.

See the 'Temporary Mount Point Properties' section of zfs(8)
for complete details.

Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #985
Closes #3351
This commit is contained in:
Brian Behlendorf
2015-08-31 16:46:01 -07:00
parent 69de34219a
commit 0282c4137e
13 changed files with 332 additions and 62 deletions
+1 -1
View File
@@ -1449,7 +1449,7 @@ zfs_sb_hold(const char *name, void *tag, zfs_sb_t **zsbp, boolean_t writer)
int error = 0;
if (get_zfs_sb(name, zsbp) != 0)
error = zfs_sb_create(name, zsbp);
error = zfs_sb_create(name, NULL, zsbp);
if (error == 0) {
rrm_enter(&(*zsbp)->z_teardown_lock, (writer) ? RW_WRITER :
RW_READER, tag);
+69 -16
View File
@@ -262,11 +262,22 @@ zfs_register_callbacks(zfs_sb_t *zsb)
{
struct dsl_dataset *ds = NULL;
objset_t *os = zsb->z_os;
boolean_t do_readonly = B_FALSE;
zfs_mntopts_t *zmo = zsb->z_mntopts;
int error = 0;
if (zfs_is_readonly(zsb) || !spa_writeable(dmu_objset_spa(os)))
do_readonly = B_TRUE;
ASSERT(zsb);
ASSERT(zmo);
/*
* The act of registering our callbacks will destroy any mount
* options we may have. In order to enable temporary overrides
* of mount options, we stash away the current values and
* restore them after we register the callbacks.
*/
if (zfs_is_readonly(zsb) || !spa_writeable(dmu_objset_spa(os))) {
zmo->z_do_readonly = B_TRUE;
zmo->z_readonly = B_TRUE;
}
/*
* Register property callbacks.
@@ -307,8 +318,25 @@ zfs_register_callbacks(zfs_sb_t *zsb)
if (error)
goto unregister;
if (do_readonly)
readonly_changed_cb(zsb, B_TRUE);
/*
* Invoke our callbacks to restore temporary mount options.
*/
if (zmo->z_do_readonly)
readonly_changed_cb(zsb, zmo->z_readonly);
if (zmo->z_do_setuid)
setuid_changed_cb(zsb, zmo->z_setuid);
if (zmo->z_do_exec)
exec_changed_cb(zsb, zmo->z_exec);
if (zmo->z_do_devices)
devices_changed_cb(zsb, zmo->z_devices);
if (zmo->z_do_xattr)
xattr_changed_cb(zsb, zmo->z_xattr);
if (zmo->z_do_atime)
atime_changed_cb(zsb, zmo->z_atime);
if (zmo->z_do_relatime)
relatime_changed_cb(zsb, zmo->z_relatime);
if (zmo->z_do_nbmand)
nbmand_changed_cb(zsb, zmo->z_nbmand);
return (0);
@@ -642,8 +670,26 @@ zfs_owner_overquota(zfs_sb_t *zsb, znode_t *zp, boolean_t isgroup)
}
EXPORT_SYMBOL(zfs_owner_overquota);
zfs_mntopts_t *
zfs_mntopts_alloc(void)
{
return (kmem_zalloc(sizeof (zfs_mntopts_t), KM_SLEEP));
}
void
zfs_mntopts_free(zfs_mntopts_t *zmo)
{
if (zmo->z_osname)
strfree(zmo->z_osname);
if (zmo->z_mntpoint)
strfree(zmo->z_mntpoint);
kmem_free(zmo, sizeof (zfs_mntopts_t));
}
int
zfs_sb_create(const char *osname, zfs_sb_t **zsbp)
zfs_sb_create(const char *osname, zfs_mntopts_t *zmo, zfs_sb_t **zsbp)
{
objset_t *os;
zfs_sb_t *zsb;
@@ -663,6 +709,11 @@ zfs_sb_create(const char *osname, zfs_sb_t **zsbp)
return (error);
}
/*
* Optional temporary mount options, free'd in zfs_sb_free().
*/
zsb->z_mntopts = (zmo ? zmo : zfs_mntopts_alloc());
/*
* Initialize the zfs-specific filesystem structure.
* Should probably make this a kmem cache, shuffle fields,
@@ -892,6 +943,7 @@ zfs_sb_free(zfs_sb_t *zsb)
for (i = 0; i != ZFS_OBJ_MTX_SZ; i++)
mutex_destroy(&zsb->z_hold_mtx[i]);
vmem_free(zsb->z_hold_mtx, sizeof (kmutex_t) * ZFS_OBJ_MTX_SZ);
zfs_mntopts_free(zsb->z_mntopts);
kmem_free(zsb, sizeof (zfs_sb_t));
}
EXPORT_SYMBOL(zfs_sb_free);
@@ -1310,16 +1362,15 @@ atomic_long_t zfs_bdi_seq = ATOMIC_LONG_INIT(0);
#endif
int
zfs_domount(struct super_block *sb, void *data, int silent)
zfs_domount(struct super_block *sb, zfs_mntopts_t *zmo, int silent)
{
zpl_mount_data_t *zmd = data;
const char *osname = zmd->z_osname;
const char *osname = zmo->z_osname;
zfs_sb_t *zsb;
struct inode *root_inode;
uint64_t recordsize;
int error;
error = zfs_sb_create(osname, &zsb);
error = zfs_sb_create(osname, zmo, &zsb);
if (error)
return (error);
@@ -1462,13 +1513,15 @@ zfs_umount(struct super_block *sb)
EXPORT_SYMBOL(zfs_umount);
int
zfs_remount(struct super_block *sb, int *flags, char *data)
zfs_remount(struct super_block *sb, int *flags, zfs_mntopts_t *zmo)
{
/*
* All namespace flags (MNT_*) and super block flags (MS_*) will
* be handled by the Linux VFS. Only handle custom options here.
*/
return (0);
zfs_sb_t *zsb = sb->s_fs_info;
int error;
zfs_unregister_callbacks(zsb);
error = zfs_register_callbacks(zsb);
return (error);
}
EXPORT_SYMBOL(zfs_remount);
+205 -21
View File
@@ -184,35 +184,204 @@ zpl_statfs(struct dentry *dentry, struct kstatfs *statp)
return (error);
}
enum {
TOKEN_RO,
TOKEN_RW,
TOKEN_SETUID,
TOKEN_NOSETUID,
TOKEN_EXEC,
TOKEN_NOEXEC,
TOKEN_DEVICES,
TOKEN_NODEVICES,
TOKEN_XATTR,
TOKEN_NOXATTR,
TOKEN_ATIME,
TOKEN_NOATIME,
TOKEN_RELATIME,
TOKEN_NORELATIME,
TOKEN_NBMAND,
TOKEN_NONBMAND,
TOKEN_MNTPOINT,
TOKEN_LAST,
};
static const match_table_t zpl_tokens = {
{ TOKEN_RO, MNTOPT_RO },
{ TOKEN_RW, MNTOPT_RW },
{ TOKEN_SETUID, MNTOPT_SETUID },
{ TOKEN_NOSETUID, MNTOPT_NOSETUID },
{ TOKEN_EXEC, MNTOPT_EXEC },
{ TOKEN_NOEXEC, MNTOPT_NOEXEC },
{ TOKEN_DEVICES, MNTOPT_DEVICES },
{ TOKEN_NODEVICES, MNTOPT_NODEVICES },
{ TOKEN_XATTR, MNTOPT_XATTR },
{ TOKEN_NOXATTR, MNTOPT_NOXATTR },
{ TOKEN_ATIME, MNTOPT_ATIME },
{ TOKEN_NOATIME, MNTOPT_NOATIME },
{ TOKEN_RELATIME, MNTOPT_RELATIME },
{ TOKEN_NORELATIME, MNTOPT_NORELATIME },
{ TOKEN_NBMAND, MNTOPT_NBMAND },
{ TOKEN_NONBMAND, MNTOPT_NONBMAND },
{ TOKEN_MNTPOINT, MNTOPT_MNTPOINT "=%s" },
{ TOKEN_LAST, NULL },
};
static int
zpl_parse_option(char *option, int token, substring_t *args,
zfs_mntopts_t *zmo, boolean_t isremount)
{
switch (token) {
case TOKEN_RO:
zmo->z_readonly = B_TRUE;
zmo->z_do_readonly = B_TRUE;
break;
case TOKEN_RW:
zmo->z_readonly = B_FALSE;
zmo->z_do_readonly = B_TRUE;
break;
case TOKEN_SETUID:
zmo->z_setuid = B_TRUE;
zmo->z_do_setuid = B_TRUE;
break;
case TOKEN_NOSETUID:
zmo->z_setuid = B_FALSE;
zmo->z_do_setuid = B_TRUE;
break;
case TOKEN_EXEC:
zmo->z_exec = B_TRUE;
zmo->z_do_exec = B_TRUE;
break;
case TOKEN_NOEXEC:
zmo->z_exec = B_FALSE;
zmo->z_do_exec = B_TRUE;
break;
case TOKEN_DEVICES:
zmo->z_devices = B_TRUE;
zmo->z_do_devices = B_TRUE;
break;
case TOKEN_NODEVICES:
zmo->z_devices = B_FALSE;
zmo->z_do_devices = B_TRUE;
break;
case TOKEN_XATTR:
zmo->z_xattr = B_TRUE;
zmo->z_do_xattr = B_TRUE;
break;
case TOKEN_NOXATTR:
zmo->z_xattr = B_FALSE;
zmo->z_do_xattr = B_TRUE;
break;
case TOKEN_ATIME:
zmo->z_atime = B_TRUE;
zmo->z_do_atime = B_TRUE;
break;
case TOKEN_NOATIME:
zmo->z_atime = B_FALSE;
zmo->z_do_atime = B_TRUE;
break;
case TOKEN_RELATIME:
zmo->z_relatime = B_TRUE;
zmo->z_do_relatime = B_TRUE;
break;
case TOKEN_NORELATIME:
zmo->z_relatime = B_FALSE;
zmo->z_do_relatime = B_TRUE;
break;
case TOKEN_NBMAND:
zmo->z_nbmand = B_TRUE;
zmo->z_do_nbmand = B_TRUE;
break;
case TOKEN_NONBMAND:
zmo->z_nbmand = B_FALSE;
zmo->z_do_nbmand = B_TRUE;
break;
case TOKEN_MNTPOINT:
zmo->z_mntpoint = match_strdup(&args[0]);
if (zmo->z_mntpoint == NULL)
return (-ENOMEM);
break;
default:
break;
}
return (0);
}
/*
* Parse the mntopts string storing the results in provided zmo argument.
* If an error occurs the zmo argument will not be modified. The caller
* needs to set isremount when recycling an existing zfs_mntopts_t.
*/
static int
zpl_parse_options(char *osname, char *mntopts, zfs_mntopts_t *zmo,
boolean_t isremount)
{
zfs_mntopts_t *tmp_zmo;
substring_t args[MAX_OPT_ARGS];
char *tmp_mntopts, *p;
int error, token;
if (mntopts == NULL)
return (-EINVAL);
tmp_zmo = zfs_mntopts_alloc();
tmp_zmo->z_osname = strdup(osname);
tmp_mntopts = strdup(mntopts);
while ((p = strsep(&tmp_mntopts, ",")) != NULL) {
if (!*p)
continue;
args[0].to = args[0].from = NULL;
token = match_token(p, zpl_tokens, args);
error = zpl_parse_option(p, token, args, tmp_zmo, isremount);
if (error) {
zfs_mntopts_free(tmp_zmo);
strfree(tmp_mntopts);
return (error);
}
}
strfree(tmp_mntopts);
if (isremount == B_TRUE) {
if (zmo->z_osname)
strfree(zmo->z_osname);
if (zmo->z_mntpoint)
strfree(zmo->z_mntpoint);
} else {
ASSERT3P(zmo->z_osname, ==, NULL);
ASSERT3P(zmo->z_mntpoint, ==, NULL);
}
memcpy(zmo, tmp_zmo, sizeof (zfs_mntopts_t));
kmem_free(tmp_zmo, sizeof (zfs_mntopts_t));
return (0);
}
static int
zpl_remount_fs(struct super_block *sb, int *flags, char *data)
{
zfs_sb_t *zsb = sb->s_fs_info;
fstrans_cookie_t cookie;
int error;
error = zpl_parse_options(zsb->z_mntopts->z_osname, data,
zsb->z_mntopts, B_TRUE);
if (error)
return (error);
cookie = spl_fstrans_mark();
error = -zfs_remount(sb, flags, data);
error = -zfs_remount(sb, flags, zsb->z_mntopts);
spl_fstrans_unmark(cookie);
ASSERT3S(error, <=, 0);
return (error);
}
/*
* ZFS specific features must be explicitly handled here, the VFS will
* automatically handled the following generic functionality.
*
* MNT_NOSUID,
* MNT_NODEV,
* MNT_NOEXEC,
* MNT_NOATIME,
* MNT_NODIRATIME,
* MNT_READONLY,
* MNT_STRICTATIME,
* MS_SYNCHRONOUS,
* MS_DIRSYNC,
* MS_MANDLOCK.
*/
static int
__zpl_show_options(struct seq_file *seq, zfs_sb_t *zsb)
{
@@ -249,11 +418,12 @@ zpl_show_options(struct seq_file *seq, struct vfsmount *vfsp)
static int
zpl_fill_super(struct super_block *sb, void *data, int silent)
{
zfs_mntopts_t *zmo = (zfs_mntopts_t *)data;
fstrans_cookie_t cookie;
int error;
cookie = spl_fstrans_mark();
error = -zfs_domount(sb, data, silent);
error = -zfs_domount(sb, zmo, silent);
spl_fstrans_unmark(cookie);
ASSERT3S(error, <=, 0);
@@ -265,18 +435,32 @@ static struct dentry *
zpl_mount(struct file_system_type *fs_type, int flags,
const char *osname, void *data)
{
zpl_mount_data_t zmd = { osname, data };
zfs_mntopts_t *zmo = zfs_mntopts_alloc();
int error;
return (mount_nodev(fs_type, flags, &zmd, zpl_fill_super));
error = zpl_parse_options((char *)osname, (char *)data, zmo, B_FALSE);
if (error) {
zfs_mntopts_free(zmo);
return (ERR_PTR(error));
}
return (mount_nodev(fs_type, flags, zmo, zpl_fill_super));
}
#else
static int
zpl_get_sb(struct file_system_type *fs_type, int flags,
const char *osname, void *data, struct vfsmount *mnt)
{
zpl_mount_data_t zmd = { osname, data };
zfs_mntopts_t *zmo = zfs_mntopts_alloc();
int error;
return (get_sb_nodev(fs_type, flags, &zmd, zpl_fill_super, mnt));
error = zpl_parse_options((char *)osname, (char *)data, zmo, B_FALSE);
if (error) {
zfs_mntopts_free(zmo);
return (error);
}
return (get_sb_nodev(fs_type, flags, zmo, zpl_fill_super, mnt));
}
#endif /* HAVE_MOUNT_NODEV */