]>
Commit | Line | Data |
---|---|---|
8f69975d BS |
1 | From: Andreas Gruenbacher <agruen@suse.de> |
2 | Subject: VFS hooks for per-filesystem permission models | |
3 | ||
4 | Add may_create and may_delete inode operations that filesystems can | |
5 | implement in order to override the vfs provided default behavior. | |
6 | This is required for implementing permission models which go beyond | |
7 | the traditional UNIX semantics. | |
8 | ||
9 | If a filesystem does not implement these hooks, the behavior is | |
10 | unchanged. | |
11 | ||
12 | Signed-off-by: Andreas Gruenbacher <agruen@suse.de> | |
13 | ||
14 | --- | |
15 | fs/namei.c | 48 +++++++++++++++++++++++++++++++++++++----------- | |
16 | include/linux/fs.h | 2 ++ | |
17 | 2 files changed, 39 insertions(+), 11 deletions(-) | |
18 | ||
19 | --- a/fs/namei.c | |
20 | +++ b/fs/namei.c | |
21 | @@ -1402,13 +1402,24 @@ static int may_delete(struct inode *dir, | |
22 | BUG_ON(victim->d_parent->d_inode != dir); | |
23 | audit_inode_child(victim->d_name.name, victim, dir); | |
24 | ||
25 | - error = inode_permission(dir, MAY_WRITE | MAY_EXEC); | |
26 | + if (dir->i_op->may_delete) { | |
27 | + if (IS_RDONLY(dir)) | |
28 | + return -EROFS; | |
29 | + if (IS_IMMUTABLE(dir)) | |
30 | + return -EACCES; | |
31 | + error = dir->i_op->may_delete(dir, victim->d_inode); | |
32 | + if (!error) | |
33 | + error = security_inode_permission(dir, MAY_WRITE | MAY_EXEC); | |
34 | + } else { | |
35 | + error = inode_permission(dir, MAY_WRITE | MAY_EXEC); | |
36 | + if (!error && check_sticky(dir, victim->d_inode)) | |
37 | + error = -EPERM; | |
38 | + } | |
39 | if (error) | |
40 | return error; | |
41 | if (IS_APPEND(dir)) | |
42 | return -EPERM; | |
43 | - if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)|| | |
44 | - IS_IMMUTABLE(victim->d_inode)) | |
45 | + if (IS_APPEND(victim->d_inode) || IS_IMMUTABLE(victim->d_inode)) | |
46 | return -EPERM; | |
47 | if (isdir) { | |
48 | if (!S_ISDIR(victim->d_inode->i_mode)) | |
49 | @@ -1432,13 +1443,28 @@ static int may_delete(struct inode *dir, | |
50 | * 3. We should have write and exec permissions on dir | |
51 | * 4. We can't do it if dir is immutable (done in permission()) | |
52 | */ | |
53 | -static inline int may_create(struct inode *dir, struct dentry *child) | |
54 | +static inline int may_create(struct inode *dir, struct dentry *child, | |
55 | + int isdir) | |
56 | { | |
57 | + int error; | |
58 | + | |
59 | if (child->d_inode) | |
60 | return -EEXIST; | |
61 | if (IS_DEADDIR(dir)) | |
62 | return -ENOENT; | |
63 | - return inode_permission(dir, MAY_WRITE | MAY_EXEC); | |
64 | + | |
65 | + if (dir->i_op->may_create) { | |
66 | + if (IS_RDONLY(dir)) | |
67 | + return -EROFS; | |
68 | + if (IS_IMMUTABLE(dir)) | |
69 | + return -EACCES; | |
70 | + error = dir->i_op->may_create(dir, isdir); | |
71 | + if (!error) | |
72 | + error = security_inode_permission(dir, MAY_WRITE | MAY_EXEC); | |
73 | + } else | |
74 | + error = inode_permission(dir, MAY_WRITE | MAY_EXEC); | |
75 | + | |
76 | + return error; | |
77 | } | |
78 | ||
79 | /* | |
80 | @@ -1504,7 +1530,7 @@ void unlock_rename(struct dentry *p1, st | |
81 | int vfs_create(struct inode *dir, struct dentry *dentry, int mode, | |
82 | struct nameidata *nd) | |
83 | { | |
84 | - int error = may_create(dir, dentry); | |
85 | + int error = may_create(dir, dentry, 0); | |
86 | ||
87 | if (error) | |
88 | return error; | |
89 | @@ -1948,7 +1974,7 @@ EXPORT_SYMBOL_GPL(lookup_create); | |
90 | ||
91 | int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) | |
92 | { | |
93 | - int error = may_create(dir, dentry); | |
94 | + int error = may_create(dir, dentry, 0); | |
95 | ||
96 | if (error) | |
97 | return error; | |
98 | @@ -2049,7 +2075,7 @@ SYSCALL_DEFINE3(mknod, const char __user | |
99 | ||
100 | int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |
101 | { | |
102 | - int error = may_create(dir, dentry); | |
103 | + int error = may_create(dir, dentry, 1); | |
104 | ||
105 | if (error) | |
106 | return error; | |
107 | @@ -2316,7 +2342,7 @@ SYSCALL_DEFINE1(unlink, const char __use | |
108 | ||
109 | int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) | |
110 | { | |
111 | - int error = may_create(dir, dentry); | |
112 | + int error = may_create(dir, dentry, 0); | |
113 | ||
114 | if (error) | |
115 | return error; | |
116 | @@ -2386,7 +2412,7 @@ int vfs_link(struct dentry *old_dentry, | |
117 | if (!inode) | |
118 | return -ENOENT; | |
119 | ||
120 | - error = may_create(dir, new_dentry); | |
121 | + error = may_create(dir, new_dentry, S_ISDIR(inode->i_mode)); | |
122 | if (error) | |
123 | return error; | |
124 | ||
125 | @@ -2594,7 +2620,7 @@ int vfs_rename(struct inode *old_dir, st | |
126 | return error; | |
127 | ||
128 | if (!new_dentry->d_inode) | |
129 | - error = may_create(new_dir, new_dentry); | |
130 | + error = may_create(new_dir, new_dentry, is_dir); | |
131 | else | |
132 | error = may_delete(new_dir, new_dentry, is_dir); | |
133 | if (error) | |
134 | --- a/include/linux/fs.h | |
135 | +++ b/include/linux/fs.h | |
136 | @@ -1293,6 +1293,8 @@ struct inode_operations { | |
137 | void (*put_link) (struct dentry *, struct nameidata *, void *); | |
138 | void (*truncate) (struct inode *); | |
139 | int (*permission) (struct inode *, int); | |
140 | + int (*may_create) (struct inode *, int); | |
141 | + int (*may_delete) (struct inode *, struct inode *); | |
142 | int (*setattr) (struct dentry *, struct iattr *); | |
143 | int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *); | |
144 | int (*setxattr) (struct dentry *, const char *,const void *,size_t,int); |