From: Andreas Gruenbacher Subject: VFS hooks for per-filesystem permission models Patch-mainline: Not yet Add may_create and may_delete inode operations that filesystems can implement in order to override the vfs provided default behavior. This is required for implementing permission models which go beyond the traditional UNIX semantics. If a filesystem does not implement these hooks, the behavior is unchanged. Signed-off-by: Andreas Gruenbacher --- fs/namei.c | 48 +++++++++++++++++++++++++++++++++++++----------- include/linux/fs.h | 2 ++ 2 files changed, 39 insertions(+), 11 deletions(-) --- a/fs/namei.c +++ b/fs/namei.c @@ -1320,13 +1320,24 @@ static int may_delete(struct inode *dir, BUG_ON(victim->d_parent->d_inode != dir); audit_inode_child(victim, dir); - error = inode_permission(dir, MAY_WRITE | MAY_EXEC); + if (dir->i_op->may_delete) { + if (IS_RDONLY(dir)) + return -EROFS; + if (IS_IMMUTABLE(dir)) + return -EACCES; + error = dir->i_op->may_delete(dir, victim->d_inode); + if (!error) + error = security_inode_permission(dir, MAY_WRITE | MAY_EXEC); + } else { + error = inode_permission(dir, MAY_WRITE | MAY_EXEC); + if (!error && check_sticky(dir, victim->d_inode)) + error = -EPERM; + } if (error) return error; if (IS_APPEND(dir)) return -EPERM; - if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)|| - IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode)) + if (IS_APPEND(victim->d_inode) || IS_IMMUTABLE(victim->d_inode)) return -EPERM; if (isdir) { if (!S_ISDIR(victim->d_inode->i_mode)) @@ -1350,13 +1361,28 @@ static int may_delete(struct inode *dir, * 3. We should have write and exec permissions on dir * 4. We can't do it if dir is immutable (done in permission()) */ -static inline int may_create(struct inode *dir, struct dentry *child) +static inline int may_create(struct inode *dir, struct dentry *child, + int isdir) { + int error; + if (child->d_inode) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; - return inode_permission(dir, MAY_WRITE | MAY_EXEC); + + if (dir->i_op->may_create) { + if (IS_RDONLY(dir)) + return -EROFS; + if (IS_IMMUTABLE(dir)) + return -EACCES; + error = dir->i_op->may_create(dir, isdir); + if (!error) + error = security_inode_permission(dir, MAY_WRITE | MAY_EXEC); + } else + error = inode_permission(dir, MAY_WRITE | MAY_EXEC); + + return error; } /* @@ -1404,7 +1430,7 @@ void unlock_rename(struct dentry *p1, st int vfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { - int error = may_create(dir, dentry); + int error = may_create(dir, dentry, 0); if (error) return error; @@ -1963,7 +1989,7 @@ EXPORT_SYMBOL_GPL(lookup_create); int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { - int error = may_create(dir, dentry); + int error = may_create(dir, dentry, 0); if (error) return error; @@ -2067,7 +2093,7 @@ SYSCALL_DEFINE3(mknod, const char __user int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { - int error = may_create(dir, dentry); + int error = may_create(dir, dentry, 1); if (error) return error; @@ -2350,7 +2376,7 @@ SYSCALL_DEFINE1(unlink, const char __use int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) { - int error = may_create(dir, dentry); + int error = may_create(dir, dentry, 0); if (error) return error; @@ -2423,7 +2449,7 @@ int vfs_link(struct dentry *old_dentry, if (!inode) return -ENOENT; - error = may_create(dir, new_dentry); + error = may_create(dir, new_dentry, S_ISDIR(inode->i_mode)); if (error) return error; @@ -2636,7 +2662,7 @@ int vfs_rename(struct inode *old_dir, st return error; if (!new_dentry->d_inode) - error = may_create(new_dir, new_dentry); + error = may_create(new_dir, new_dentry, is_dir); else error = may_delete(new_dir, new_dentry, is_dir); if (error) --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1531,6 +1531,8 @@ struct inode_operations { void (*truncate) (struct inode *); int (*permission) (struct inode *, int); int (*check_acl)(struct inode *, int); + int (*may_create) (struct inode *, int); + int (*may_delete) (struct inode *, struct inode *); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *); int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);