Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b16f7e57 authored by Andreas Gruenbacher's avatar Andreas Gruenbacher
Browse files

gfs2: Fix and clean up {GET,SET}FLAGS ioctl



Switch to a simple array for mapping between the FS_*_FL and GFS_DIF_*
flags.  Clarify how the mapping between FS_JOURNAL_DATA_FL and the
filesystem flags works.  The GFS2_DIF_SYSTEM flag cannot be set from
user space, so remove it from GFS2_FLAGS_USER_SET.  Fail with -EINVAL
when trying to set flags that are not supported instead of silently
ignoring those flags.

Partially fixes xfstest generic/424.

Signed-off-by: default avatarAndreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: default avatarAndrew Price <anprice@redhat.com>
Signed-off-by: default avatarBob Peterson <rpeterso@redhat.com>
parent 61d6899a
Loading
Loading
Loading
Loading
+47 −55
Original line number Original line Diff line number Diff line
@@ -119,45 +119,22 @@ static int gfs2_readdir(struct file *file, struct dir_context *ctx)
}
}


/**
/**
 * fsflags_cvt
 * fsflag_gfs2flag
 * @table: A table of 32 u32 flags
 * @val: a 32 bit value to convert
 *
 *
 * This function can be used to convert between fsflags values and
 * The FS_JOURNAL_DATA_FL flag maps to GFS2_DIF_INHERIT_JDATA for directories,
 * GFS2's own flags values.
 * and to GFS2_DIF_JDATA for non-directories.
 *
 * Returns: the converted flags
 */
 */
static u32 fsflags_cvt(const u32 *table, u32 val)
static struct {
{
	u32 fsflag;
	u32 res = 0;
	u32 gfsflag;
	while(val) {
} fsflag_gfs2flag[] = {
		if (val & 1)
	{FS_SYNC_FL, GFS2_DIF_SYNC},
			res |= *table;
	{FS_IMMUTABLE_FL, GFS2_DIF_IMMUTABLE},
		table++;
	{FS_APPEND_FL, GFS2_DIF_APPENDONLY},
		val >>= 1;
	{FS_NOATIME_FL, GFS2_DIF_NOATIME},
	}
	{FS_INDEX_FL, GFS2_DIF_EXHASH},
	return res;
	{FS_TOPDIR_FL, GFS2_DIF_TOPDIR},
}
	{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},

static const u32 fsflags_to_gfs2[32] = {
	[3] = GFS2_DIF_SYNC,
	[4] = GFS2_DIF_IMMUTABLE,
	[5] = GFS2_DIF_APPENDONLY,
	[7] = GFS2_DIF_NOATIME,
	[12] = GFS2_DIF_EXHASH,
	[14] = GFS2_DIF_INHERIT_JDATA,
	[17] = GFS2_DIF_TOPDIR,
};

static const u32 gfs2_to_fsflags[32] = {
	[gfs2fl_Sync] = FS_SYNC_FL,
	[gfs2fl_Immutable] = FS_IMMUTABLE_FL,
	[gfs2fl_AppendOnly] = FS_APPEND_FL,
	[gfs2fl_NoAtime] = FS_NOATIME_FL,
	[gfs2fl_ExHash] = FS_INDEX_FL,
	[gfs2fl_TopLevel] = FS_TOPDIR_FL,
	[gfs2fl_InheritJdata] = FS_JOURNAL_DATA_FL,
};
};


static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
@@ -165,17 +142,23 @@ static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
	struct inode *inode = file_inode(filp);
	struct inode *inode = file_inode(filp);
	struct gfs2_inode *ip = GFS2_I(inode);
	struct gfs2_inode *ip = GFS2_I(inode);
	struct gfs2_holder gh;
	struct gfs2_holder gh;
	int error;
	int i, error;
	u32 fsflags;
	u32 gfsflags, fsflags = 0;


	gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
	gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
	error = gfs2_glock_nq(&gh);
	error = gfs2_glock_nq(&gh);
	if (error)
	if (error)
		goto out_uninit;
		goto out_uninit;


	fsflags = fsflags_cvt(gfs2_to_fsflags, ip->i_diskflags);
	gfsflags = ip->i_diskflags;
	if (!S_ISDIR(inode->i_mode) && ip->i_diskflags & GFS2_DIF_JDATA)
	if (S_ISDIR(inode->i_mode))
		fsflags |= FS_JOURNAL_DATA_FL;
		gfsflags &= ~GFS2_DIF_JDATA;
	else
		gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
	for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
		if (gfsflags & fsflag_gfs2flag[i].gfsflag)
			fsflags |= fsflag_gfs2flag[i].fsflag;

	if (put_user(fsflags, ptr))
	if (put_user(fsflags, ptr))
		error = -EFAULT;
		error = -EFAULT;


@@ -210,7 +193,6 @@ void gfs2_set_inode_flags(struct inode *inode)
			     GFS2_DIF_APPENDONLY|		\
			     GFS2_DIF_APPENDONLY|		\
			     GFS2_DIF_NOATIME|			\
			     GFS2_DIF_NOATIME|			\
			     GFS2_DIF_SYNC|			\
			     GFS2_DIF_SYNC|			\
			     GFS2_DIF_SYSTEM|			\
			     GFS2_DIF_TOPDIR|			\
			     GFS2_DIF_TOPDIR|			\
			     GFS2_DIF_INHERIT_JDATA)
			     GFS2_DIF_INHERIT_JDATA)


@@ -249,10 +231,6 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
	if ((new_flags ^ flags) == 0)
	if ((new_flags ^ flags) == 0)
		goto out;
		goto out;


	error = -EINVAL;
	if ((new_flags ^ flags) & ~GFS2_FLAGS_USER_SET)
		goto out;

	error = -EPERM;
	error = -EPERM;
	if (IS_IMMUTABLE(inode) && (new_flags & GFS2_DIF_IMMUTABLE))
	if (IS_IMMUTABLE(inode) && (new_flags & GFS2_DIF_IMMUTABLE))
		goto out;
		goto out;
@@ -303,19 +281,33 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
{
{
	struct inode *inode = file_inode(filp);
	struct inode *inode = file_inode(filp);
	u32 fsflags, gfsflags;
	u32 fsflags, gfsflags = 0;
	u32 mask;
	int i;


	if (get_user(fsflags, ptr))
	if (get_user(fsflags, ptr))
		return -EFAULT;
		return -EFAULT;


	gfsflags = fsflags_cvt(fsflags_to_gfs2, fsflags);
	for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++) {
	if (!S_ISDIR(inode->i_mode)) {
		if (fsflags & fsflag_gfs2flag[i].fsflag) {
		gfsflags &= ~GFS2_DIF_TOPDIR;
			fsflags &= ~fsflag_gfs2flag[i].fsflag;
		if (gfsflags & GFS2_DIF_INHERIT_JDATA)
			gfsflags |= fsflag_gfs2flag[i].gfsflag;
			gfsflags ^= (GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA);
		}
		return do_gfs2_set_flags(filp, gfsflags, ~GFS2_DIF_SYSTEM);
	}
	}
	return do_gfs2_set_flags(filp, gfsflags, ~(GFS2_DIF_SYSTEM | GFS2_DIF_JDATA));
	if (fsflags || gfsflags & ~GFS2_FLAGS_USER_SET)
		return -EINVAL;

	mask = GFS2_FLAGS_USER_SET;
	if (S_ISDIR(inode->i_mode)) {
		mask &= ~GFS2_DIF_JDATA;
	} else {
		/* The GFS2_DIF_TOPDIR flag is only valid for directories. */
		if (gfsflags & GFS2_DIF_TOPDIR)
			return -EINVAL;
		mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
	}

	return do_gfs2_set_flags(filp, gfsflags, mask);
}
}


static long gfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
static long gfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)