Skip to content

Commit 7bb698f

Browse files
Andreas GruenbacherAl Viro
authored andcommitted
fs: Move notify_change permission checks into may_setattr
Move the permission checks in notify_change into a separate function to make them available to filesystems. When notify_change is called, the vfs performs those checks before calling into iop->setattr. However, a filesystem like gfs2 can only lock and revalidate the inode inside ->setattr, and it must then repeat those checks to err on the safe side. It would be nice to get rid of the double checking, but moving the permission check into iop->setattr altogether isn't really an option. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Signed-off-by: Bob Peterson <rpeterso@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent e73f0f0 commit 7bb698f

File tree

2 files changed

+33
-19
lines changed

2 files changed

+33
-19
lines changed

fs/attr.c

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,34 @@ void setattr_copy(struct user_namespace *mnt_userns, struct inode *inode,
249249
}
250250
EXPORT_SYMBOL(setattr_copy);
251251

252+
int may_setattr(struct user_namespace *mnt_userns, struct inode *inode,
253+
unsigned int ia_valid)
254+
{
255+
int error;
256+
257+
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_TIMES_SET)) {
258+
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
259+
return -EPERM;
260+
}
261+
262+
/*
263+
* If utimes(2) and friends are called with times == NULL (or both
264+
* times are UTIME_NOW), then we need to check for write permission
265+
*/
266+
if (ia_valid & ATTR_TOUCH) {
267+
if (IS_IMMUTABLE(inode))
268+
return -EPERM;
269+
270+
if (!inode_owner_or_capable(mnt_userns, inode)) {
271+
error = inode_permission(mnt_userns, inode, MAY_WRITE);
272+
if (error)
273+
return error;
274+
}
275+
}
276+
return 0;
277+
}
278+
EXPORT_SYMBOL(may_setattr);
279+
252280
/**
253281
* notify_change - modify attributes of a filesytem object
254282
* @mnt_userns: user namespace of the mount the inode was found from
@@ -290,25 +318,9 @@ int notify_change(struct user_namespace *mnt_userns, struct dentry *dentry,
290318

291319
WARN_ON_ONCE(!inode_is_locked(inode));
292320

293-
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_TIMES_SET)) {
294-
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
295-
return -EPERM;
296-
}
297-
298-
/*
299-
* If utimes(2) and friends are called with times == NULL (or both
300-
* times are UTIME_NOW), then we need to check for write permission
301-
*/
302-
if (ia_valid & ATTR_TOUCH) {
303-
if (IS_IMMUTABLE(inode))
304-
return -EPERM;
305-
306-
if (!inode_owner_or_capable(mnt_userns, inode)) {
307-
error = inode_permission(mnt_userns, inode, MAY_WRITE);
308-
if (error)
309-
return error;
310-
}
311-
}
321+
error = may_setattr(mnt_userns, inode, ia_valid);
322+
if (error)
323+
return error;
312324

313325
if ((ia_valid & ATTR_MODE)) {
314326
umode_t amode = attr->ia_mode;

include/linux/fs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3469,6 +3469,8 @@ extern int buffer_migrate_page_norefs(struct address_space *,
34693469
#define buffer_migrate_page_norefs NULL
34703470
#endif
34713471

3472+
int may_setattr(struct user_namespace *mnt_userns, struct inode *inode,
3473+
unsigned int ia_valid);
34723474
int setattr_prepare(struct user_namespace *, struct dentry *, struct iattr *);
34733475
extern int inode_newsize_ok(const struct inode *, loff_t offset);
34743476
void setattr_copy(struct user_namespace *, struct inode *inode,

0 commit comments

Comments
 (0)