summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2019-07-01 11:25:34 -0400
committerDarrick J. Wong <darrick.wong@oracle.com>2019-07-01 11:25:34 -0400
commit5aca284210ce827f780ea2f4f9c6ab8d6e2d6648 (patch)
tree485c43c3dd95105dccf7fcc32934112a76ea2129
parentd1fdb6d8f6a4109a4263176c84b899076a5f8008 (diff)
vfs: create a generic checking and prep function for FS_IOC_SETFLAGS
Create a generic function to check incoming FS_IOC_SETFLAGS flag values and later prepare the inode for updates so that we can standardize the implementations that follow ext4's flag values. Note that the efivarfs implementation no longer fails a no-op SETFLAGS without CAP_LINUX_IMMUTABLE since that's the behavior in ext*. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Christoph Hellwig <hch@lst.de> Acked-by: David Sterba <dsterba@suse.com> Reviewed-by: Bob Peterson <rpeterso@redhat.com>
-rw-r--r--fs/btrfs/ioctl.c13
-rw-r--r--fs/efivarfs/file.c26
-rw-r--r--fs/ext2/ioctl.c16
-rw-r--r--fs/ext4/ioctl.c13
-rw-r--r--fs/gfs2/file.c42
-rw-r--r--fs/hfsplus/ioctl.c21
-rw-r--r--fs/inode.c24
-rw-r--r--fs/jfs/ioctl.c22
-rw-r--r--fs/nilfs2/ioctl.c9
-rw-r--r--fs/ocfs2/ioctl.c13
-rw-r--r--fs/orangefs/file.c37
-rw-r--r--fs/reiserfs/ioctl.c10
-rw-r--r--fs/ubifs/ioctl.c13
-rw-r--r--include/linux/fs.h3
14 files changed, 144 insertions, 118 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6dafa857bbb9..d3d9b4abb09b 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
187 struct btrfs_inode *binode = BTRFS_I(inode); 187 struct btrfs_inode *binode = BTRFS_I(inode);
188 struct btrfs_root *root = binode->root; 188 struct btrfs_root *root = binode->root;
189 struct btrfs_trans_handle *trans; 189 struct btrfs_trans_handle *trans;
190 unsigned int fsflags; 190 unsigned int fsflags, old_fsflags;
191 int ret; 191 int ret;
192 const char *comp = NULL; 192 const char *comp = NULL;
193 u32 binode_flags = binode->flags; 193 u32 binode_flags = binode->flags;
@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
212 inode_lock(inode); 212 inode_lock(inode);
213 213
214 fsflags = btrfs_mask_fsflags_for_type(inode, fsflags); 214 fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
215 if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) & 215 old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
216 (FS_APPEND_FL | FS_IMMUTABLE_FL)) { 216 ret = vfs_ioc_setflags_prepare(inode, old_fsflags, fsflags);
217 if (!capable(CAP_LINUX_IMMUTABLE)) { 217 if (ret)
218 ret = -EPERM; 218 goto out_unlock;
219 goto out_unlock;
220 }
221 }
222 219
223 if (fsflags & FS_SYNC_FL) 220 if (fsflags & FS_SYNC_FL)
224 binode_flags |= BTRFS_INODE_SYNC; 221 binode_flags |= BTRFS_INODE_SYNC;
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 8e568428c88b..a3cc10b1bfe1 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -110,16 +110,22 @@ out_free:
110 return size; 110 return size;
111} 111}
112 112
113static int 113static inline unsigned int efivarfs_getflags(struct inode *inode)
114efivarfs_ioc_getxflags(struct file *file, void __user *arg)
115{ 114{
116 struct inode *inode = file->f_mapping->host;
117 unsigned int i_flags; 115 unsigned int i_flags;
118 unsigned int flags = 0; 116 unsigned int flags = 0;
119 117
120 i_flags = inode->i_flags; 118 i_flags = inode->i_flags;
121 if (i_flags & S_IMMUTABLE) 119 if (i_flags & S_IMMUTABLE)
122 flags |= FS_IMMUTABLE_FL; 120 flags |= FS_IMMUTABLE_FL;
121 return flags;
122}
123
124static int
125efivarfs_ioc_getxflags(struct file *file, void __user *arg)
126{
127 struct inode *inode = file->f_mapping->host;
128 unsigned int flags = efivarfs_getflags(inode);
123 129
124 if (copy_to_user(arg, &flags, sizeof(flags))) 130 if (copy_to_user(arg, &flags, sizeof(flags)))
125 return -EFAULT; 131 return -EFAULT;
@@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
132 struct inode *inode = file->f_mapping->host; 138 struct inode *inode = file->f_mapping->host;
133 unsigned int flags; 139 unsigned int flags;
134 unsigned int i_flags = 0; 140 unsigned int i_flags = 0;
141 unsigned int oldflags = efivarfs_getflags(inode);
135 int error; 142 int error;
136 143
137 if (!inode_owner_or_capable(inode)) 144 if (!inode_owner_or_capable(inode))
@@ -143,9 +150,6 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
143 if (flags & ~FS_IMMUTABLE_FL) 150 if (flags & ~FS_IMMUTABLE_FL)
144 return -EOPNOTSUPP; 151 return -EOPNOTSUPP;
145 152
146 if (!capable(CAP_LINUX_IMMUTABLE))
147 return -EPERM;
148
149 if (flags & FS_IMMUTABLE_FL) 153 if (flags & FS_IMMUTABLE_FL)
150 i_flags |= S_IMMUTABLE; 154 i_flags |= S_IMMUTABLE;
151 155
@@ -155,12 +159,16 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
155 return error; 159 return error;
156 160
157 inode_lock(inode); 161 inode_lock(inode);
162
163 error = vfs_ioc_setflags_prepare(inode, oldflags, flags);
164 if (error)
165 goto out;
166
158 inode_set_flags(inode, i_flags, S_IMMUTABLE); 167 inode_set_flags(inode, i_flags, S_IMMUTABLE);
168out:
159 inode_unlock(inode); 169 inode_unlock(inode);
160
161 mnt_drop_write_file(file); 170 mnt_drop_write_file(file);
162 171 return error;
163 return 0;
164} 172}
165 173
166static long 174static long
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 0367c0039e68..1b853fb0b163 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
60 } 60 }
61 oldflags = ei->i_flags; 61 oldflags = ei->i_flags;
62 62
63 /* 63 ret = vfs_ioc_setflags_prepare(inode, oldflags, flags);
64 * The IMMUTABLE and APPEND_ONLY flags can only be changed by 64 if (ret) {
65 * the relevant capability. 65 inode_unlock(inode);
66 * 66 goto setflags_out;
67 * This test looks nicer. Thanks to Pauline Middelink
68 */
69 if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
70 if (!capable(CAP_LINUX_IMMUTABLE)) {
71 inode_unlock(inode);
72 ret = -EPERM;
73 goto setflags_out;
74 }
75 } 67 }
76 68
77 flags = flags & EXT2_FL_USER_MODIFIABLE; 69 flags = flags & EXT2_FL_USER_MODIFIABLE;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..272b6e44191b 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
289 /* The JOURNAL_DATA flag is modifiable only by root */ 289 /* The JOURNAL_DATA flag is modifiable only by root */
290 jflag = flags & EXT4_JOURNAL_DATA_FL; 290 jflag = flags & EXT4_JOURNAL_DATA_FL;
291 291
292 /* 292 err = vfs_ioc_setflags_prepare(inode, oldflags, flags);
293 * The IMMUTABLE and APPEND_ONLY flags can only be changed by 293 if (err)
294 * the relevant capability. 294 goto flags_out;
295 *
296 * This test looks nicer. Thanks to Pauline Middelink
297 */
298 if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
299 if (!capable(CAP_LINUX_IMMUTABLE))
300 goto flags_out;
301 }
302 295
303 /* 296 /*
304 * The JOURNAL_DATA flag can only be changed by 297 * The JOURNAL_DATA flag can only be changed by
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d174b1f8fd08..1cb0c3afd3dc 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -136,27 +136,36 @@ static struct {
136 {FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA}, 136 {FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
137}; 137};
138 138
139static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
140{
141 int i;
142 u32 fsflags = 0;
143
144 if (S_ISDIR(inode->i_mode))
145 gfsflags &= ~GFS2_DIF_JDATA;
146 else
147 gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
148
149 for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
150 if (gfsflags & fsflag_gfs2flag[i].gfsflag)
151 fsflags |= fsflag_gfs2flag[i].fsflag;
152 return fsflags;
153}
154
139static int gfs2_get_flags(struct file *filp, u32 __user *ptr) 155static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
140{ 156{
141 struct inode *inode = file_inode(filp); 157 struct inode *inode = file_inode(filp);
142 struct gfs2_inode *ip = GFS2_I(inode); 158 struct gfs2_inode *ip = GFS2_I(inode);
143 struct gfs2_holder gh; 159 struct gfs2_holder gh;
144 int i, error; 160 int error;
145 u32 gfsflags, fsflags = 0; 161 u32 fsflags;
146 162
147 gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh); 163 gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
148 error = gfs2_glock_nq(&gh); 164 error = gfs2_glock_nq(&gh);
149 if (error) 165 if (error)
150 goto out_uninit; 166 goto out_uninit;
151 167
152 gfsflags = ip->i_diskflags; 168 fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
153 if (S_ISDIR(inode->i_mode))
154 gfsflags &= ~GFS2_DIF_JDATA;
155 else
156 gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
157 for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
158 if (gfsflags & fsflag_gfs2flag[i].gfsflag)
159 fsflags |= fsflag_gfs2flag[i].fsflag;
160 169
161 if (put_user(fsflags, ptr)) 170 if (put_user(fsflags, ptr))
162 error = -EFAULT; 171 error = -EFAULT;
@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
200 * @filp: file pointer 209 * @filp: file pointer
201 * @reqflags: The flags to set 210 * @reqflags: The flags to set
202 * @mask: Indicates which flags are valid 211 * @mask: Indicates which flags are valid
212 * @fsflags: The FS_* inode flags passed in
203 * 213 *
204 */ 214 */
205static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask) 215static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
216 const u32 fsflags)
206{ 217{
207 struct inode *inode = file_inode(filp); 218 struct inode *inode = file_inode(filp);
208 struct gfs2_inode *ip = GFS2_I(inode); 219 struct gfs2_inode *ip = GFS2_I(inode);
@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
210 struct buffer_head *bh; 221 struct buffer_head *bh;
211 struct gfs2_holder gh; 222 struct gfs2_holder gh;
212 int error; 223 int error;
213 u32 new_flags, flags; 224 u32 new_flags, flags, oldflags;
214 225
215 error = mnt_want_write_file(filp); 226 error = mnt_want_write_file(filp);
216 if (error) 227 if (error)
@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
220 if (error) 231 if (error)
221 goto out_drop_write; 232 goto out_drop_write;
222 233
234 oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
235 error = vfs_ioc_setflags_prepare(inode, oldflags, fsflags);
236 if (error)
237 goto out;
238
223 error = -EACCES; 239 error = -EACCES;
224 if (!inode_owner_or_capable(inode)) 240 if (!inode_owner_or_capable(inode))
225 goto out; 241 goto out;
@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
308 mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA); 324 mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
309 } 325 }
310 326
311 return do_gfs2_set_flags(filp, gfsflags, mask); 327 return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
312} 328}
313 329
314static int gfs2_getlabel(struct file *filp, char __user *label) 330static int gfs2_getlabel(struct file *filp, char __user *label)
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 5e6502ef7415..ce15b9496b77 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
57 return 0; 57 return 0;
58} 58}
59 59
60static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags) 60static inline unsigned int hfsplus_getflags(struct inode *inode)
61{ 61{
62 struct inode *inode = file_inode(file);
63 struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 62 struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
64 unsigned int flags = 0; 63 unsigned int flags = 0;
65 64
@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
69 flags |= FS_APPEND_FL; 68 flags |= FS_APPEND_FL;
70 if (hip->userflags & HFSPLUS_FLG_NODUMP) 69 if (hip->userflags & HFSPLUS_FLG_NODUMP)
71 flags |= FS_NODUMP_FL; 70 flags |= FS_NODUMP_FL;
71 return flags;
72}
73
74static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
75{
76 struct inode *inode = file_inode(file);
77 unsigned int flags = hfsplus_getflags(inode);
72 78
73 return put_user(flags, user_flags); 79 return put_user(flags, user_flags);
74} 80}
@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
78 struct inode *inode = file_inode(file); 84 struct inode *inode = file_inode(file);
79 struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 85 struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
80 unsigned int flags, new_fl = 0; 86 unsigned int flags, new_fl = 0;
87 unsigned int oldflags = hfsplus_getflags(inode);
81 int err = 0; 88 int err = 0;
82 89
83 err = mnt_want_write_file(file); 90 err = mnt_want_write_file(file);
@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
96 103
97 inode_lock(inode); 104 inode_lock(inode);
98 105
99 if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) || 106 err = vfs_ioc_setflags_prepare(inode, oldflags, flags);
100 inode->i_flags & (S_IMMUTABLE|S_APPEND)) { 107 if (err)
101 if (!capable(CAP_LINUX_IMMUTABLE)) { 108 goto out_unlock_inode;
102 err = -EPERM;
103 goto out_unlock_inode;
104 }
105 }
106 109
107 /* don't silently ignore unsupported ext2 flags */ 110 /* don't silently ignore unsupported ext2 flags */
108 if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) { 111 if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
diff --git a/fs/inode.c b/fs/inode.c
index df6542ec3b88..8072a09fd0b9 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2170,3 +2170,27 @@ struct timespec64 current_time(struct inode *inode)
2170 return timespec64_trunc(now, inode->i_sb->s_time_gran); 2170 return timespec64_trunc(now, inode->i_sb->s_time_gran);
2171} 2171}
2172EXPORT_SYMBOL(current_time); 2172EXPORT_SYMBOL(current_time);
2173
2174/*
2175 * Generic function to check FS_IOC_SETFLAGS values and reject any invalid
2176 * configurations.
2177 *
2178 * Note: the caller should be holding i_mutex, or else be sure that they have
2179 * exclusive access to the inode structure.
2180 */
2181int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags,
2182 unsigned int flags)
2183{
2184 /*
2185 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
2186 * the relevant capability.
2187 *
2188 * This test looks nicer. Thanks to Pauline Middelink
2189 */
2190 if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
2191 !capable(CAP_LINUX_IMMUTABLE))
2192 return -EPERM;
2193
2194 return 0;
2195}
2196EXPORT_SYMBOL(vfs_ioc_setflags_prepare);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index ba34dae8bd9f..10ee0ecca1a8 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
98 /* Lock against other parallel changes of flags */ 98 /* Lock against other parallel changes of flags */
99 inode_lock(inode); 99 inode_lock(inode);
100 100
101 oldflags = jfs_inode->mode2; 101 oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
102 102 0);
103 /* 103 err = vfs_ioc_setflags_prepare(inode, oldflags, flags);
104 * The IMMUTABLE and APPEND_ONLY flags can only be changed by 104 if (err) {
105 * the relevant capability. 105 inode_unlock(inode);
106 */ 106 goto setflags_out;
107 if ((oldflags & JFS_IMMUTABLE_FL) ||
108 ((flags ^ oldflags) &
109 (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
110 if (!capable(CAP_LINUX_IMMUTABLE)) {
111 inode_unlock(inode);
112 err = -EPERM;
113 goto setflags_out;
114 }
115 } 107 }
116 108
117 flags = flags & JFS_FL_USER_MODIFIABLE; 109 flags = flags & JFS_FL_USER_MODIFIABLE;
118 flags |= oldflags & ~JFS_FL_USER_MODIFIABLE; 110 flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
119 jfs_inode->mode2 = flags; 111 jfs_inode->mode2 = flags;
120 112
121 jfs_set_inode_flags(inode); 113 jfs_set_inode_flags(inode);
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 9b96d79eea6c..91b9dac6b2cc 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
148 148
149 oldflags = NILFS_I(inode)->i_flags; 149 oldflags = NILFS_I(inode)->i_flags;
150 150
151 /* 151 ret = vfs_ioc_setflags_prepare(inode, oldflags, flags);
152 * The IMMUTABLE and APPEND_ONLY flags can only be changed by the 152 if (ret)
153 * relevant capability.
154 */
155 ret = -EPERM;
156 if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
157 !capable(CAP_LINUX_IMMUTABLE))
158 goto out; 153 goto out;
159 154
160 ret = nilfs_transaction_begin(inode->i_sb, &ti, 0); 155 ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726ada857..d6f7b299eb23 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
106 flags = flags & mask; 106 flags = flags & mask;
107 flags |= oldflags & ~mask; 107 flags |= oldflags & ~mask;
108 108
109 /* 109 status = vfs_ioc_setflags_prepare(inode, oldflags, flags);
110 * The IMMUTABLE and APPEND_ONLY flags can only be changed by 110 if (status)
111 * the relevant capability. 111 goto bail_unlock;
112 */
113 status = -EPERM;
114 if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
115 (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
116 if (!capable(CAP_LINUX_IMMUTABLE))
117 goto bail_unlock;
118 }
119 112
120 handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); 113 handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
121 if (IS_ERR(handle)) { 114 if (IS_ERR(handle)) {
diff --git a/fs/orangefs/file.c b/fs/orangefs/file.c
index a35c17017210..679a3c8e4fb3 100644
--- a/fs/orangefs/file.c
+++ b/fs/orangefs/file.c
@@ -357,11 +357,28 @@ static ssize_t orangefs_file_write_iter(struct kiocb *iocb,
357 return ret; 357 return ret;
358} 358}
359 359
360static int orangefs_getflags(struct inode *inode, unsigned long *uval)
361{
362 __u64 val = 0;
363 int ret;
364
365 ret = orangefs_inode_getxattr(inode,
366 "user.pvfs2.meta_hint",
367 &val, sizeof(val));
368 if (ret < 0 && ret != -ENODATA)
369 return ret;
370 else if (ret == -ENODATA)
371 val = 0;
372 *uval = val;
373 return 0;
374}
375
360/* 376/*
361 * Perform a miscellaneous operation on a file. 377 * Perform a miscellaneous operation on a file.
362 */ 378 */
363static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 379static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
364{ 380{
381 struct inode *inode = file_inode(file);
365 int ret = -ENOTTY; 382 int ret = -ENOTTY;
366 __u64 val = 0; 383 __u64 val = 0;
367 unsigned long uval; 384 unsigned long uval;
@@ -375,20 +392,16 @@ static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long ar
375 * and append flags 392 * and append flags
376 */ 393 */
377 if (cmd == FS_IOC_GETFLAGS) { 394 if (cmd == FS_IOC_GETFLAGS) {
378 val = 0; 395 ret = orangefs_getflags(inode, &uval);
379 ret = orangefs_inode_getxattr(file_inode(file), 396 if (ret)
380 "user.pvfs2.meta_hint",
381 &val, sizeof(val));
382 if (ret < 0 && ret != -ENODATA)
383 return ret; 397 return ret;
384 else if (ret == -ENODATA)
385 val = 0;
386 uval = val;
387 gossip_debug(GOSSIP_FILE_DEBUG, 398 gossip_debug(GOSSIP_FILE_DEBUG,
388 "orangefs_ioctl: FS_IOC_GETFLAGS: %llu\n", 399 "orangefs_ioctl: FS_IOC_GETFLAGS: %llu\n",
389 (unsigned long long)uval); 400 (unsigned long long)uval);
390 return put_user(uval, (int __user *)arg); 401 return put_user(uval, (int __user *)arg);
391 } else if (cmd == FS_IOC_SETFLAGS) { 402 } else if (cmd == FS_IOC_SETFLAGS) {
403 unsigned long old_uval;
404
392 ret = 0; 405 ret = 0;
393 if (get_user(uval, (int __user *)arg)) 406 if (get_user(uval, (int __user *)arg))
394 return -EFAULT; 407 return -EFAULT;
@@ -404,11 +417,17 @@ static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long ar
404 gossip_err("orangefs_ioctl: the FS_IOC_SETFLAGS only supports setting one of FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NOATIME_FL\n"); 417 gossip_err("orangefs_ioctl: the FS_IOC_SETFLAGS only supports setting one of FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NOATIME_FL\n");
405 return -EINVAL; 418 return -EINVAL;
406 } 419 }
420 ret = orangefs_getflags(inode, &old_uval);
421 if (ret)
422 return ret;
423 ret = vfs_ioc_setflags_prepare(inode, old_uval, uval);
424 if (ret)
425 return ret;
407 val = uval; 426 val = uval;
408 gossip_debug(GOSSIP_FILE_DEBUG, 427 gossip_debug(GOSSIP_FILE_DEBUG,
409 "orangefs_ioctl: FS_IOC_SETFLAGS: %llu\n", 428 "orangefs_ioctl: FS_IOC_SETFLAGS: %llu\n",
410 (unsigned long long)val); 429 (unsigned long long)val);
411 ret = orangefs_inode_setxattr(file_inode(file), 430 ret = orangefs_inode_setxattr(inode,
412 "user.pvfs2.meta_hint", 431 "user.pvfs2.meta_hint",
413 &val, sizeof(val), 0); 432 &val, sizeof(val), 0);
414 } 433 }
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index acbbaf7a0bb2..45e1a5d11af3 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
74 err = -EPERM; 74 err = -EPERM;
75 goto setflags_out; 75 goto setflags_out;
76 } 76 }
77 if (((flags ^ REISERFS_I(inode)-> 77 err = vfs_ioc_setflags_prepare(inode,
78 i_attrs) & (REISERFS_IMMUTABLE_FL | 78 REISERFS_I(inode)->i_attrs,
79 REISERFS_APPEND_FL)) 79 flags);
80 && !capable(CAP_LINUX_IMMUTABLE)) { 80 if (err)
81 err = -EPERM;
82 goto setflags_out; 81 goto setflags_out;
83 }
84 if ((flags & REISERFS_NOTAIL_FL) && 82 if ((flags & REISERFS_NOTAIL_FL) &&
85 S_ISREG(inode->i_mode)) { 83 S_ISREG(inode->i_mode)) {
86 int result; 84 int result;
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 4f1a397fda69..034ad14710d1 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
107 if (err) 107 if (err)
108 return err; 108 return err;
109 109
110 /*
111 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
112 * the relevant capability.
113 */
114 mutex_lock(&ui->ui_mutex); 110 mutex_lock(&ui->ui_mutex);
115 oldflags = ubifs2ioctl(ui->flags); 111 oldflags = ubifs2ioctl(ui->flags);
116 if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { 112 err = vfs_ioc_setflags_prepare(inode, oldflags, flags);
117 if (!capable(CAP_LINUX_IMMUTABLE)) { 113 if (err)
118 err = -EPERM; 114 goto out_unlock;
119 goto out_unlock;
120 }
121 }
122 115
123 ui->flags = ioctl2ubifs(flags); 116 ui->flags = ioctl2ubifs(flags);
124 ubifs_set_inode_flags(inode); 117 ubifs_set_inode_flags(inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..41d5175ffdd7 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3546,4 +3546,7 @@ static inline struct sock *io_uring_get_socket(struct file *file)
3546} 3546}
3547#endif 3547#endif
3548 3548
3549int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags,
3550 unsigned int flags);
3551
3549#endif /* _LINUX_FS_H */ 3552#endif /* _LINUX_FS_H */