diff options
author | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:49:58 -0500 |
---|---|---|
committer | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:50:29 -0500 |
commit | b74c79e99389cd79b31fcc08f82c24e492e63c7e (patch) | |
tree | 763c6b412517306670bc625e90035f2d16bb739f /fs/namei.c | |
parent | 34286d6662308d82aed891852d04c7c3a2649b16 (diff) |
fs: provide rcu-walk aware permission i_ops
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 75 |
1 files changed, 29 insertions, 46 deletions
diff --git a/fs/namei.c b/fs/namei.c index 6e275363e89d..4e957bf744ae 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -169,8 +169,8 @@ EXPORT_SYMBOL(putname); | |||
169 | /* | 169 | /* |
170 | * This does basic POSIX ACL permission checking | 170 | * This does basic POSIX ACL permission checking |
171 | */ | 171 | */ |
172 | static inline int __acl_permission_check(struct inode *inode, int mask, | 172 | static int acl_permission_check(struct inode *inode, int mask, unsigned int flags, |
173 | int (*check_acl)(struct inode *inode, int mask), int rcu) | 173 | int (*check_acl)(struct inode *inode, int mask, unsigned int flags)) |
174 | { | 174 | { |
175 | umode_t mode = inode->i_mode; | 175 | umode_t mode = inode->i_mode; |
176 | 176 | ||
@@ -180,13 +180,9 @@ static inline int __acl_permission_check(struct inode *inode, int mask, | |||
180 | mode >>= 6; | 180 | mode >>= 6; |
181 | else { | 181 | else { |
182 | if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) { | 182 | if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) { |
183 | if (rcu) { | 183 | int error = check_acl(inode, mask, flags); |
184 | return -ECHILD; | 184 | if (error != -EAGAIN) |
185 | } else { | 185 | return error; |
186 | int error = check_acl(inode, mask); | ||
187 | if (error != -EAGAIN) | ||
188 | return error; | ||
189 | } | ||
190 | } | 186 | } |
191 | 187 | ||
192 | if (in_group_p(inode->i_gid)) | 188 | if (in_group_p(inode->i_gid)) |
@@ -201,32 +197,31 @@ static inline int __acl_permission_check(struct inode *inode, int mask, | |||
201 | return -EACCES; | 197 | return -EACCES; |
202 | } | 198 | } |
203 | 199 | ||
204 | static inline int acl_permission_check(struct inode *inode, int mask, | ||
205 | int (*check_acl)(struct inode *inode, int mask)) | ||
206 | { | ||
207 | return __acl_permission_check(inode, mask, check_acl, 0); | ||
208 | } | ||
209 | |||
210 | /** | 200 | /** |
211 | * generic_permission - check for access rights on a Posix-like filesystem | 201 | * generic_permission - check for access rights on a Posix-like filesystem |
212 | * @inode: inode to check access rights for | 202 | * @inode: inode to check access rights for |
213 | * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) | 203 | * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) |
214 | * @check_acl: optional callback to check for Posix ACLs | 204 | * @check_acl: optional callback to check for Posix ACLs |
205 | * @flags IPERM_FLAG_ flags. | ||
215 | * | 206 | * |
216 | * Used to check for read/write/execute permissions on a file. | 207 | * Used to check for read/write/execute permissions on a file. |
217 | * We use "fsuid" for this, letting us set arbitrary permissions | 208 | * We use "fsuid" for this, letting us set arbitrary permissions |
218 | * for filesystem access without changing the "normal" uids which | 209 | * for filesystem access without changing the "normal" uids which |
219 | * are used for other things.. | 210 | * are used for other things. |
211 | * | ||
212 | * generic_permission is rcu-walk aware. It returns -ECHILD in case an rcu-walk | ||
213 | * request cannot be satisfied (eg. requires blocking or too much complexity). | ||
214 | * It would then be called again in ref-walk mode. | ||
220 | */ | 215 | */ |
221 | int generic_permission(struct inode *inode, int mask, | 216 | int generic_permission(struct inode *inode, int mask, unsigned int flags, |
222 | int (*check_acl)(struct inode *inode, int mask)) | 217 | int (*check_acl)(struct inode *inode, int mask, unsigned int flags)) |
223 | { | 218 | { |
224 | int ret; | 219 | int ret; |
225 | 220 | ||
226 | /* | 221 | /* |
227 | * Do the basic POSIX ACL permission checks. | 222 | * Do the basic POSIX ACL permission checks. |
228 | */ | 223 | */ |
229 | ret = acl_permission_check(inode, mask, check_acl); | 224 | ret = acl_permission_check(inode, mask, flags, check_acl); |
230 | if (ret != -EACCES) | 225 | if (ret != -EACCES) |
231 | return ret; | 226 | return ret; |
232 | 227 | ||
@@ -281,9 +276,10 @@ int inode_permission(struct inode *inode, int mask) | |||
281 | } | 276 | } |
282 | 277 | ||
283 | if (inode->i_op->permission) | 278 | if (inode->i_op->permission) |
284 | retval = inode->i_op->permission(inode, mask); | 279 | retval = inode->i_op->permission(inode, mask, 0); |
285 | else | 280 | else |
286 | retval = generic_permission(inode, mask, inode->i_op->check_acl); | 281 | retval = generic_permission(inode, mask, 0, |
282 | inode->i_op->check_acl); | ||
287 | 283 | ||
288 | if (retval) | 284 | if (retval) |
289 | return retval; | 285 | return retval; |
@@ -668,22 +664,19 @@ force_reval_path(struct path *path, struct nameidata *nd) | |||
668 | * short-cut DAC fails, then call ->permission() to do more | 664 | * short-cut DAC fails, then call ->permission() to do more |
669 | * complete permission check. | 665 | * complete permission check. |
670 | */ | 666 | */ |
671 | static inline int __exec_permission(struct inode *inode, int rcu) | 667 | static inline int exec_permission(struct inode *inode, unsigned int flags) |
672 | { | 668 | { |
673 | int ret; | 669 | int ret; |
674 | 670 | ||
675 | if (inode->i_op->permission) { | 671 | if (inode->i_op->permission) { |
676 | if (rcu) | 672 | ret = inode->i_op->permission(inode, MAY_EXEC, flags); |
677 | return -ECHILD; | 673 | } else { |
678 | ret = inode->i_op->permission(inode, MAY_EXEC); | 674 | ret = acl_permission_check(inode, MAY_EXEC, flags, |
679 | if (!ret) | 675 | inode->i_op->check_acl); |
680 | goto ok; | ||
681 | return ret; | ||
682 | } | 676 | } |
683 | ret = __acl_permission_check(inode, MAY_EXEC, inode->i_op->check_acl, rcu); | 677 | if (likely(!ret)) |
684 | if (!ret) | ||
685 | goto ok; | 678 | goto ok; |
686 | if (rcu && ret == -ECHILD) | 679 | if (ret == -ECHILD) |
687 | return ret; | 680 | return ret; |
688 | 681 | ||
689 | if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH)) | 682 | if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH)) |
@@ -691,17 +684,7 @@ static inline int __exec_permission(struct inode *inode, int rcu) | |||
691 | 684 | ||
692 | return ret; | 685 | return ret; |
693 | ok: | 686 | ok: |
694 | return security_inode_exec_permission(inode, rcu); | 687 | return security_inode_exec_permission(inode, flags); |
695 | } | ||
696 | |||
697 | static int exec_permission(struct inode *inode) | ||
698 | { | ||
699 | return __exec_permission(inode, 0); | ||
700 | } | ||
701 | |||
702 | static int exec_permission_rcu(struct inode *inode) | ||
703 | { | ||
704 | return __exec_permission(inode, 1); | ||
705 | } | 688 | } |
706 | 689 | ||
707 | static __always_inline void set_root(struct nameidata *nd) | 690 | static __always_inline void set_root(struct nameidata *nd) |
@@ -1165,7 +1148,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) | |||
1165 | 1148 | ||
1166 | nd->flags |= LOOKUP_CONTINUE; | 1149 | nd->flags |= LOOKUP_CONTINUE; |
1167 | if (nd->flags & LOOKUP_RCU) { | 1150 | if (nd->flags & LOOKUP_RCU) { |
1168 | err = exec_permission_rcu(nd->inode); | 1151 | err = exec_permission(nd->inode, IPERM_FLAG_RCU); |
1169 | if (err == -ECHILD) { | 1152 | if (err == -ECHILD) { |
1170 | if (nameidata_drop_rcu(nd)) | 1153 | if (nameidata_drop_rcu(nd)) |
1171 | return -ECHILD; | 1154 | return -ECHILD; |
@@ -1173,7 +1156,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) | |||
1173 | } | 1156 | } |
1174 | } else { | 1157 | } else { |
1175 | exec_again: | 1158 | exec_again: |
1176 | err = exec_permission(nd->inode); | 1159 | err = exec_permission(nd->inode, 0); |
1177 | } | 1160 | } |
1178 | if (err) | 1161 | if (err) |
1179 | break; | 1162 | break; |
@@ -1620,7 +1603,7 @@ static struct dentry *__lookup_hash(struct qstr *name, | |||
1620 | struct dentry *dentry; | 1603 | struct dentry *dentry; |
1621 | int err; | 1604 | int err; |
1622 | 1605 | ||
1623 | err = exec_permission(inode); | 1606 | err = exec_permission(inode, 0); |
1624 | if (err) | 1607 | if (err) |
1625 | return ERR_PTR(err); | 1608 | return ERR_PTR(err); |
1626 | 1609 | ||