Commit 36098557 authored by Eric Biggers's avatar Eric Biggers Committed by Jaegeuk Kim

f2fs: separate f2fs i_flags from fs_flags and ext4 i_flags

f2fs copied all the on-disk i_flags from ext4, and along with it the
assumption that the on-disk i_flags are the same as the bits used by
FS_IOC_GETFLAGS and FS_IOC_SETFLAGS.  This is problematic because
reserving an on-disk inode flag in either filesystem's i_flags or in
these ioctls effectively reserves it in all the other places too.  In
fact, most of the "f2fs i_flags" are not used by f2fs at all.

Fix this by separating f2fs's i_flags from the ioctl bits and ext4's
i_flags.

In the process, un-reserve all "f2fs i_flags" that aren't actually
supported by f2fs.  This included various flags that were not settable
at all, as well as various flags that were settable by FS_IOC_SETFLAGS
but didn't actually do anything.

There's a slight chance we'll need to add some flag(s) back to
FS_IOC_SETFLAGS in order to avoid breaking users who expect f2fs to
accept some random flag(s).  But hopefully such users don't exist.
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
Reviewed-by: default avatarChao Yu <yuchao0@huawei.com>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent 176ef3c4
...@@ -2334,57 +2334,23 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr) ...@@ -2334,57 +2334,23 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
} }
/* /*
* Inode flags * On-disk inode flags (f2fs_inode::i_flags)
*/ */
#define F2FS_SECRM_FL 0x00000001 /* Secure deletion */
#define F2FS_UNRM_FL 0x00000002 /* Undelete */
#define F2FS_COMPR_FL 0x00000004 /* Compress file */
#define F2FS_SYNC_FL 0x00000008 /* Synchronous updates */ #define F2FS_SYNC_FL 0x00000008 /* Synchronous updates */
#define F2FS_IMMUTABLE_FL 0x00000010 /* Immutable file */ #define F2FS_IMMUTABLE_FL 0x00000010 /* Immutable file */
#define F2FS_APPEND_FL 0x00000020 /* writes to file may only append */ #define F2FS_APPEND_FL 0x00000020 /* writes to file may only append */
#define F2FS_NODUMP_FL 0x00000040 /* do not dump file */ #define F2FS_NODUMP_FL 0x00000040 /* do not dump file */
#define F2FS_NOATIME_FL 0x00000080 /* do not update atime */ #define F2FS_NOATIME_FL 0x00000080 /* do not update atime */
/* Reserved for compression usage... */
#define F2FS_DIRTY_FL 0x00000100
#define F2FS_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
#define F2FS_NOCOMPR_FL 0x00000400 /* Don't compress */
#define F2FS_ENCRYPT_FL 0x00000800 /* encrypted file */
/* End compression flags --- maybe not all used */
#define F2FS_INDEX_FL 0x00001000 /* hash-indexed directory */ #define F2FS_INDEX_FL 0x00001000 /* hash-indexed directory */
#define F2FS_IMAGIC_FL 0x00002000 /* AFS directory */
#define F2FS_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */
#define F2FS_NOTAIL_FL 0x00008000 /* file tail should not be merged */
#define F2FS_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */ #define F2FS_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */
#define F2FS_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/
#define F2FS_HUGE_FILE_FL 0x00040000 /* Set to each huge file */
#define F2FS_EXTENTS_FL 0x00080000 /* Inode uses extents */
#define F2FS_EA_INODE_FL 0x00200000 /* Inode used for large EA */
#define F2FS_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */
#define F2FS_NOCOW_FL 0x00800000 /* Do not cow file */
#define F2FS_INLINE_DATA_FL 0x10000000 /* Inode has inline data. */
#define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */ #define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
#define F2FS_RESERVED_FL 0x80000000 /* reserved for ext4 lib */
#define F2FS_FL_USER_VISIBLE 0x30CBDFFF /* User visible flags */
#define F2FS_FL_USER_MODIFIABLE 0x204BC0FF /* User modifiable flags */
/* Flags we can manipulate with through F2FS_IOC_FSSETXATTR */
#define F2FS_FL_XFLAG_VISIBLE (F2FS_SYNC_FL | \
F2FS_IMMUTABLE_FL | \
F2FS_APPEND_FL | \
F2FS_NODUMP_FL | \
F2FS_NOATIME_FL | \
F2FS_PROJINHERIT_FL)
/* Flags that should be inherited by new inodes from their parent. */ /* Flags that should be inherited by new inodes from their parent. */
#define F2FS_FL_INHERITED (F2FS_SECRM_FL | F2FS_UNRM_FL | F2FS_COMPR_FL |\ #define F2FS_FL_INHERITED (F2FS_SYNC_FL | F2FS_NODUMP_FL | F2FS_NOATIME_FL | \
F2FS_SYNC_FL | F2FS_NODUMP_FL | F2FS_NOATIME_FL |\ F2FS_DIRSYNC_FL | F2FS_PROJINHERIT_FL)
F2FS_NOCOMPR_FL | F2FS_JOURNAL_DATA_FL |\
F2FS_NOTAIL_FL | F2FS_DIRSYNC_FL |\
F2FS_PROJINHERIT_FL)
/* Flags that are appropriate for regular files (all but dir-specific ones). */ /* Flags that are appropriate for regular files (all but dir-specific ones). */
#define F2FS_REG_FLMASK (~(F2FS_DIRSYNC_FL | F2FS_TOPDIR_FL)) #define F2FS_REG_FLMASK (~F2FS_DIRSYNC_FL)
/* Flags that are appropriate for non-directories/regular files. */ /* Flags that are appropriate for non-directories/regular files. */
#define F2FS_OTHER_FLMASK (F2FS_NODUMP_FL | F2FS_NOATIME_FL) #define F2FS_OTHER_FLMASK (F2FS_NODUMP_FL | F2FS_NOATIME_FL)
......
...@@ -707,11 +707,9 @@ int f2fs_getattr(const struct path *path, struct kstat *stat, ...@@ -707,11 +707,9 @@ int f2fs_getattr(const struct path *path, struct kstat *stat,
stat->btime.tv_nsec = fi->i_crtime.tv_nsec; stat->btime.tv_nsec = fi->i_crtime.tv_nsec;
} }
flags = fi->i_flags & F2FS_FL_USER_VISIBLE; flags = fi->i_flags;
if (flags & F2FS_APPEND_FL) if (flags & F2FS_APPEND_FL)
stat->attributes |= STATX_ATTR_APPEND; stat->attributes |= STATX_ATTR_APPEND;
if (flags & F2FS_COMPR_FL)
stat->attributes |= STATX_ATTR_COMPRESSED;
if (IS_ENCRYPTED(inode)) if (IS_ENCRYPTED(inode))
stat->attributes |= STATX_ATTR_ENCRYPTED; stat->attributes |= STATX_ATTR_ENCRYPTED;
if (flags & F2FS_IMMUTABLE_FL) if (flags & F2FS_IMMUTABLE_FL)
...@@ -720,7 +718,6 @@ int f2fs_getattr(const struct path *path, struct kstat *stat, ...@@ -720,7 +718,6 @@ int f2fs_getattr(const struct path *path, struct kstat *stat,
stat->attributes |= STATX_ATTR_NODUMP; stat->attributes |= STATX_ATTR_NODUMP;
stat->attributes_mask |= (STATX_ATTR_APPEND | stat->attributes_mask |= (STATX_ATTR_APPEND |
STATX_ATTR_COMPRESSED |
STATX_ATTR_ENCRYPTED | STATX_ATTR_ENCRYPTED |
STATX_ATTR_IMMUTABLE | STATX_ATTR_IMMUTABLE |
STATX_ATTR_NODUMP); STATX_ATTR_NODUMP);
...@@ -1648,44 +1645,22 @@ static int f2fs_file_flush(struct file *file, fl_owner_t id) ...@@ -1648,44 +1645,22 @@ static int f2fs_file_flush(struct file *file, fl_owner_t id)
return 0; return 0;
} }
static int f2fs_ioc_getflags(struct file *filp, unsigned long arg) static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
{
struct inode *inode = file_inode(filp);
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int flags = fi->i_flags;
if (IS_ENCRYPTED(inode))
flags |= F2FS_ENCRYPT_FL;
if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode))
flags |= F2FS_INLINE_DATA_FL;
if (is_inode_flag_set(inode, FI_PIN_FILE))
flags |= F2FS_NOCOW_FL;
flags &= F2FS_FL_USER_VISIBLE;
return put_user(flags, (int __user *)arg);
}
static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
{ {
struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int oldflags; u32 oldflags;
/* Is it quota file? Do not allow user to mess with it */ /* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode)) if (IS_NOQUOTA(inode))
return -EPERM; return -EPERM;
flags = f2fs_mask_flags(inode->i_mode, flags);
oldflags = fi->i_flags; oldflags = fi->i_flags;
if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL)) if ((iflags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
if (!capable(CAP_LINUX_IMMUTABLE)) if (!capable(CAP_LINUX_IMMUTABLE))
return -EPERM; return -EPERM;
flags = flags & F2FS_FL_USER_MODIFIABLE; fi->i_flags = iflags | (oldflags & ~mask);
flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
fi->i_flags = flags;
if (fi->i_flags & F2FS_PROJINHERIT_FL) if (fi->i_flags & F2FS_PROJINHERIT_FL)
set_inode_flag(inode, FI_PROJ_INHERIT); set_inode_flag(inode, FI_PROJ_INHERIT);
...@@ -1698,26 +1673,124 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags) ...@@ -1698,26 +1673,124 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
return 0; return 0;
} }
/* FS_IOC_GETFLAGS and FS_IOC_SETFLAGS support */
/*
* To make a new on-disk f2fs i_flag gettable via FS_IOC_GETFLAGS, add an entry
* for it to f2fs_fsflags_map[], and add its FS_*_FL equivalent to
* F2FS_GETTABLE_FS_FL. To also make it settable via FS_IOC_SETFLAGS, also add
* its FS_*_FL equivalent to F2FS_SETTABLE_FS_FL.
*/
static const struct {
u32 iflag;
u32 fsflag;
} f2fs_fsflags_map[] = {
{ F2FS_SYNC_FL, FS_SYNC_FL },
{ F2FS_IMMUTABLE_FL, FS_IMMUTABLE_FL },
{ F2FS_APPEND_FL, FS_APPEND_FL },
{ F2FS_NODUMP_FL, FS_NODUMP_FL },
{ F2FS_NOATIME_FL, FS_NOATIME_FL },
{ F2FS_INDEX_FL, FS_INDEX_FL },
{ F2FS_DIRSYNC_FL, FS_DIRSYNC_FL },
{ F2FS_PROJINHERIT_FL, FS_PROJINHERIT_FL },
};
#define F2FS_GETTABLE_FS_FL ( \
FS_SYNC_FL | \
FS_IMMUTABLE_FL | \
FS_APPEND_FL | \
FS_NODUMP_FL | \
FS_NOATIME_FL | \
FS_INDEX_FL | \
FS_DIRSYNC_FL | \
FS_PROJINHERIT_FL | \
FS_ENCRYPT_FL | \
FS_INLINE_DATA_FL | \
FS_NOCOW_FL)
#define F2FS_SETTABLE_FS_FL ( \
FS_SYNC_FL | \
FS_IMMUTABLE_FL | \
FS_APPEND_FL | \
FS_NODUMP_FL | \
FS_NOATIME_FL | \
FS_DIRSYNC_FL | \
FS_PROJINHERIT_FL)
/* Convert f2fs on-disk i_flags to FS_IOC_{GET,SET}FLAGS flags */
static inline u32 f2fs_iflags_to_fsflags(u32 iflags)
{
u32 fsflags = 0;
int i;
for (i = 0; i < ARRAY_SIZE(f2fs_fsflags_map); i++)
if (iflags & f2fs_fsflags_map[i].iflag)
fsflags |= f2fs_fsflags_map[i].fsflag;
return fsflags;
}
/* Convert FS_IOC_{GET,SET}FLAGS flags to f2fs on-disk i_flags */
static inline u32 f2fs_fsflags_to_iflags(u32 fsflags)
{
u32 iflags = 0;
int i;
for (i = 0; i < ARRAY_SIZE(f2fs_fsflags_map); i++)
if (fsflags & f2fs_fsflags_map[i].fsflag)
iflags |= f2fs_fsflags_map[i].iflag;
return iflags;
}
static int f2fs_ioc_getflags(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_inode_info *fi = F2FS_I(inode);
u32 fsflags = f2fs_iflags_to_fsflags(fi->i_flags);
if (IS_ENCRYPTED(inode))
fsflags |= FS_ENCRYPT_FL;
if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode))
fsflags |= FS_INLINE_DATA_FL;
if (is_inode_flag_set(inode, FI_PIN_FILE))
fsflags |= FS_NOCOW_FL;
fsflags &= F2FS_GETTABLE_FS_FL;
return put_user(fsflags, (int __user *)arg);
}
static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) static int f2fs_ioc_setflags(struct file *filp, unsigned long arg)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
unsigned int flags; u32 fsflags;
u32 iflags;
int ret; int ret;
if (!inode_owner_or_capable(inode)) if (!inode_owner_or_capable(inode))
return -EACCES; return -EACCES;
if (get_user(flags, (int __user *)arg)) if (get_user(fsflags, (int __user *)arg))
return -EFAULT; return -EFAULT;
if (fsflags & ~F2FS_GETTABLE_FS_FL)
return -EOPNOTSUPP;
fsflags &= F2FS_SETTABLE_FS_FL;
iflags = f2fs_fsflags_to_iflags(fsflags);
if (f2fs_mask_flags(inode->i_mode, iflags) != iflags)
return -EOPNOTSUPP;
ret = mnt_want_write_file(filp); ret = mnt_want_write_file(filp);
if (ret) if (ret)
return ret; return ret;
inode_lock(inode); inode_lock(inode);
ret = __f2fs_ioc_setflags(inode, flags); ret = f2fs_setflags_common(inode, iflags,
f2fs_fsflags_to_iflags(F2FS_SETTABLE_FS_FL));
inode_unlock(inode); inode_unlock(inode);
mnt_drop_write_file(filp); mnt_drop_write_file(filp);
return ret; return ret;
...@@ -2727,47 +2800,56 @@ static int f2fs_ioc_setproject(struct file *filp, __u32 projid) ...@@ -2727,47 +2800,56 @@ static int f2fs_ioc_setproject(struct file *filp, __u32 projid)
} }
#endif #endif
/* Transfer internal flags to xflags */ /* FS_IOC_FSGETXATTR and FS_IOC_FSSETXATTR support */
static inline __u32 f2fs_iflags_to_xflags(unsigned long iflags)
{ /*
__u32 xflags = 0; * To make a new on-disk f2fs i_flag gettable via FS_IOC_FSGETXATTR and settable
* via FS_IOC_FSSETXATTR, add an entry for it to f2fs_xflags_map[], and add its
if (iflags & F2FS_SYNC_FL) * FS_XFLAG_* equivalent to F2FS_SUPPORTED_XFLAGS.
xflags |= FS_XFLAG_SYNC; */
if (iflags & F2FS_IMMUTABLE_FL)
xflags |= FS_XFLAG_IMMUTABLE; static const struct {
if (iflags & F2FS_APPEND_FL) u32 iflag;
xflags |= FS_XFLAG_APPEND; u32 xflag;
if (iflags & F2FS_NODUMP_FL) } f2fs_xflags_map[] = {
xflags |= FS_XFLAG_NODUMP; { F2FS_SYNC_FL, FS_XFLAG_SYNC },
if (iflags & F2FS_NOATIME_FL) { F2FS_IMMUTABLE_FL, FS_XFLAG_IMMUTABLE },
xflags |= FS_XFLAG_NOATIME; { F2FS_APPEND_FL, FS_XFLAG_APPEND },
if (iflags & F2FS_PROJINHERIT_FL) { F2FS_NODUMP_FL, FS_XFLAG_NODUMP },
xflags |= FS_XFLAG_PROJINHERIT; { F2FS_NOATIME_FL, FS_XFLAG_NOATIME },
{ F2FS_PROJINHERIT_FL, FS_XFLAG_PROJINHERIT },
};
#define F2FS_SUPPORTED_XFLAGS ( \
FS_XFLAG_SYNC | \
FS_XFLAG_IMMUTABLE | \
FS_XFLAG_APPEND | \
FS_XFLAG_NODUMP | \
FS_XFLAG_NOATIME | \
FS_XFLAG_PROJINHERIT)
/* Convert f2fs on-disk i_flags to FS_IOC_FS{GET,SET}XATTR flags */
static inline u32 f2fs_iflags_to_xflags(u32 iflags)
{
u32 xflags = 0;
int i;
for (i = 0; i < ARRAY_SIZE(f2fs_xflags_map); i++)
if (iflags & f2fs_xflags_map[i].iflag)
xflags |= f2fs_xflags_map[i].xflag;
return xflags; return xflags;
} }
#define F2FS_SUPPORTED_FS_XFLAGS (FS_XFLAG_SYNC | FS_XFLAG_IMMUTABLE | \ /* Convert FS_IOC_FS{GET,SET}XATTR flags to f2fs on-disk i_flags */
FS_XFLAG_APPEND | FS_XFLAG_NODUMP | \ static inline u32 f2fs_xflags_to_iflags(u32 xflags)
FS_XFLAG_NOATIME | FS_XFLAG_PROJINHERIT)
/* Transfer xflags flags to internal */
static inline unsigned long f2fs_xflags_to_iflags(__u32 xflags)
{ {
unsigned long iflags = 0; u32 iflags = 0;
int i;
if (xflags & FS_XFLAG_SYNC) for (i = 0; i < ARRAY_SIZE(f2fs_xflags_map); i++)
iflags |= F2FS_SYNC_FL; if (xflags & f2fs_xflags_map[i].xflag)
if (xflags & FS_XFLAG_IMMUTABLE) iflags |= f2fs_xflags_map[i].iflag;
iflags |= F2FS_IMMUTABLE_FL;
if (xflags & FS_XFLAG_APPEND)
iflags |= F2FS_APPEND_FL;
if (xflags & FS_XFLAG_NODUMP)
iflags |= F2FS_NODUMP_FL;
if (xflags & FS_XFLAG_NOATIME)
iflags |= F2FS_NOATIME_FL;
if (xflags & FS_XFLAG_PROJINHERIT)
iflags |= F2FS_PROJINHERIT_FL;
return iflags; return iflags;
} }
...@@ -2779,8 +2861,7 @@ static int f2fs_ioc_fsgetxattr(struct file *filp, unsigned long arg) ...@@ -2779,8 +2861,7 @@ static int f2fs_ioc_fsgetxattr(struct file *filp, unsigned long arg)
struct fsxattr fa; struct fsxattr fa;
memset(&fa, 0, sizeof(struct fsxattr)); memset(&fa, 0, sizeof(struct fsxattr));
fa.fsx_xflags = f2fs_iflags_to_xflags(fi->i_flags & fa.fsx_xflags = f2fs_iflags_to_xflags(fi->i_flags);
F2FS_FL_USER_VISIBLE);
if (f2fs_sb_has_project_quota(F2FS_I_SB(inode))) if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)))
fa.fsx_projid = (__u32)from_kprojid(&init_user_ns, fa.fsx_projid = (__u32)from_kprojid(&init_user_ns,
...@@ -2818,9 +2899,8 @@ static int f2fs_ioctl_check_project(struct inode *inode, struct fsxattr *fa) ...@@ -2818,9 +2899,8 @@ static int f2fs_ioctl_check_project(struct inode *inode, struct fsxattr *fa)
static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg) static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct f2fs_inode_info *fi = F2FS_I(inode);
struct fsxattr fa; struct fsxattr fa;
unsigned int flags; u32 iflags;
int err; int err;
if (copy_from_user(&fa, (struct fsxattr __user *)arg, sizeof(fa))) if (copy_from_user(&fa, (struct fsxattr __user *)arg, sizeof(fa)))
...@@ -2830,11 +2910,11 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg) ...@@ -2830,11 +2910,11 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
if (!inode_owner_or_capable(inode)) if (!inode_owner_or_capable(inode))
return -EACCES; return -EACCES;
if (fa.fsx_xflags & ~F2FS_SUPPORTED_FS_XFLAGS) if (fa.fsx_xflags & ~F2FS_SUPPORTED_XFLAGS)
return -EOPNOTSUPP; return -EOPNOTSUPP;
flags = f2fs_xflags_to_iflags(fa.fsx_xflags); iflags = f2fs_xflags_to_iflags(fa.fsx_xflags);
if (f2fs_mask_flags(inode->i_mode, flags) != flags) if (f2fs_mask_flags(inode->i_mode, iflags) != iflags)
return -EOPNOTSUPP; return -EOPNOTSUPP;
err = mnt_want_write_file(filp); err = mnt_want_write_file(filp);
...@@ -2845,9 +2925,8 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg) ...@@ -2845,9 +2925,8 @@ static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
err = f2fs_ioctl_check_project(inode, &fa); err = f2fs_ioctl_check_project(inode, &fa);
if (err) if (err)
goto out; goto out;
flags = (fi->i_flags & ~F2FS_FL_XFLAG_VISIBLE) | err = f2fs_setflags_common(inode, iflags,
(flags & F2FS_FL_XFLAG_VISIBLE); f2fs_xflags_to_iflags(F2FS_SUPPORTED_XFLAGS));
err = __f2fs_ioc_setflags(inode, flags);
if (err) if (err)
goto out; goto out;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment