146 lines
4.4 KiB
Diff
146 lines
4.4 KiB
Diff
From: Andreas Gruenbacher <agruen@suse.de>
|
|
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 <agruen@suse.de>
|
|
|
|
---
|
|
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);
|