From f38b564aa4619fbc8258d01f6f1f44cb85cd40fe Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Tue, 13 Oct 2009 15:00:16 +0530 Subject: [RFC PATCH 01/14] vfs: VFS hooks for per-filesystem permission models 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 Signed-off-by: Aneesh Kumar K.V --- fs/namei.c | 50 ++++++++++++++++++++++++++++++++++++++------------ include/linux/fs.h | 4 ++++ 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index d11f404..8fc1b29 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1382,14 +1382,26 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir) BUG_ON(victim->d_parent->d_inode != dir); audit_inode_child(victim->d_name.name, 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) || + IS_SWAPFILE(victim->d_inode)) return -EPERM; if (isdir) { if (!S_ISDIR(victim->d_inode->i_mode)) @@ -1413,13 +1425,27 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir) * 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; } /* @@ -1483,7 +1509,7 @@ void unlock_rename(struct dentry *p1, struct dentry *p2) 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; @@ -1982,7 +2008,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; @@ -2087,7 +2113,7 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, int, mode, unsigned, dev) 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; @@ -2372,7 +2398,7 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname) 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; @@ -2446,7 +2472,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de 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; @@ -2658,7 +2684,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, 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) diff --git a/include/linux/fs.h b/include/linux/fs.h index 2620a8c..4570090 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1536,6 +1536,10 @@ struct inode_operations { loff_t len); int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len); + int (*may_create) (struct inode *, int); + int (*may_delete) (struct inode *, struct inode *); + + }; struct seq_file; -- 1.6.5.2.74.g610f9