diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2019-07-01 11:25:34 -0400 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2019-07-01 11:25:34 -0400 |
commit | 5aca284210ce827f780ea2f4f9c6ab8d6e2d6648 (patch) | |
tree | 485c43c3dd95105dccf7fcc32934112a76ea2129 | |
parent | d1fdb6d8f6a4109a4263176c84b899076a5f8008 (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.c | 13 | ||||
-rw-r--r-- | fs/efivarfs/file.c | 26 | ||||
-rw-r--r-- | fs/ext2/ioctl.c | 16 | ||||
-rw-r--r-- | fs/ext4/ioctl.c | 13 | ||||
-rw-r--r-- | fs/gfs2/file.c | 42 | ||||
-rw-r--r-- | fs/hfsplus/ioctl.c | 21 | ||||
-rw-r--r-- | fs/inode.c | 24 | ||||
-rw-r--r-- | fs/jfs/ioctl.c | 22 | ||||
-rw-r--r-- | fs/nilfs2/ioctl.c | 9 | ||||
-rw-r--r-- | fs/ocfs2/ioctl.c | 13 | ||||
-rw-r--r-- | fs/orangefs/file.c | 37 | ||||
-rw-r--r-- | fs/reiserfs/ioctl.c | 10 | ||||
-rw-r--r-- | fs/ubifs/ioctl.c | 13 | ||||
-rw-r--r-- | include/linux/fs.h | 3 |
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 | ||
113 | static int | 113 | static inline unsigned int efivarfs_getflags(struct inode *inode) |
114 | efivarfs_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 | |||
124 | static int | ||
125 | efivarfs_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); |
168 | out: | ||
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 | ||
166 | static long | 174 | static 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 | ||
139 | static 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 | |||
139 | static int gfs2_get_flags(struct file *filp, u32 __user *ptr) | 155 | static 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 | */ |
205 | static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask) | 215 | static 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 | ||
314 | static int gfs2_getlabel(struct file *filp, char __user *label) | 330 | static 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 | ||
60 | static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags) | 60 | static 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 | |||
74 | static 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 | } |
2172 | EXPORT_SYMBOL(current_time); | 2172 | EXPORT_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 | */ | ||
2181 | int 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 | } | ||
2196 | EXPORT_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 | ||
360 | static 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 | */ |
363 | static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 379 | static 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 | ||
3549 | int 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 */ |