generic acl support for ->permission
Subsystems (10)
PATCH 1/1 generic acl support for ->permission
Currently we every filesystem with Posix ACLs has it's own reimplemtation of the generic permission checking code with additonal ACL support. This patch
- adds an optional callback to vfs_permission that filesystems can use for ACL support (and renames it to generic_permission because the old name was wrong - it wasn't like the other vfs_* functions at all)
- uses it in ext2, ext3, jfs and reiserfs. XFS will follow a little later as it's permission checking is burried under several layers of abtractions.
fs/exec.c +1 -1
@@ -893,7 +893,7 @@ mode = inode->i_mode;/* * Check execute perms again - if the caller has CAP_DAC_OVERRIDE, -* vfs_permission lets a non-executable through +* generic_permission lets a non-executable through */ if (!(mode &0111 ))/* with at least _one_ execute bit set */ return -EACCES ;
fs/namei.c +24 -9
@@ -151,15 +151,19 @@return result; } -/* -* vfs_permission() +/** +* generic_permission - check for access rights on a Posix-like filesystem +* @inode: inode to check access rights for +* @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) +* @check_acl: optional callback to check for Posix ACLs * -* is used to check for read/write/execute permissions on a file. +* Used to check for read/write/execute permissions on a file. * We use "fsuid" for this, letting us set arbitrary permissions * for filesystem access without changing the "normal" uids which * are used for other things.. */ -int vfs_permission (struct inode * inode,int mask) +int generic_permission (struct inode *inode,int mask, +int (*check_acl)(struct inode *inode,int mask)) {umode_t mode = inode->i_mode;
@@ -180,8 +184,18 @@if (current->fsuid == inode->i_uid) mode >>=6 ; -else if (in_group_p (inode->i_gid)) - mode >>=3 ; +else { +if (IS_POSIXACL (inode) && (mode &S_IRWXG ) && check_acl) { +int error =check_acl (inode, mask); +if (error == -EACCES ) + goto check_capabilities; +else if (error != -EAGAIN ) +return error; + } + +if (in_group_p (inode->i_gid)) + mode >>=3 ; + }/* * If the DACs are ok we don't need any capability check.
@@ -189,6 +203,7 @@if (((mode & mask & (MAY_READ |MAY_WRITE |MAY_EXEC )) == mask))return 0 ; + check_capabilities:/* * Read/write DACs are always overridable. * Executable DACs are overridable if at least one exec bit is set.
@@ -219,7 +234,7 @@if (inode->i_op && inode->i_op->permission) retval = inode->i_op->permission (inode, submask, nd);else - retval =vfs_permission (inode, submask); + retval =generic_permission (inode, submask,NULL );if (retval)return retval;
@@ -314,7 +329,7 @@/* * Short-cut version of permission(), for calling by * path_walk(), when dcache lock is held. Combines parts -* of permission() and vfs_permission(), and tests ONLY for +* of permission() and generic_permission(), and tests ONLY for * MAY_EXEC permission. * * If appropriate, check DAC only. If not appropriate, or
@@ -2405,7 +2420,7 @@EXPORT_SYMBOL (vfs_link);EXPORT_SYMBOL (vfs_mkdir);EXPORT_SYMBOL (vfs_mknod); -EXPORT_SYMBOL (vfs_permission); +EXPORT_SYMBOL (generic_permission);EXPORT_SYMBOL (vfs_readlink);EXPORT_SYMBOL (vfs_rename);EXPORT_SYMBOL (vfs_rmdir);
fs/cifs/CHANGES +2 -2
@@ -7,8 +7,8 @@ Version 1.21 ------------ -Add new mount parm to control whether mode check (vfs_permission) is done on -the client. If Unix extensions are enabled and the uids on the client +Add new mount parm to control whether mode check (generic_permission) is done +on the client. If Unix extensions are enabled and the uids on the client and server do not match, client permission checks are meaningless on server uids that do not exist on the client (this does not affect the normal ACL check which occurs on the server). Fix default uid
fs/cifs/cifsfs.c +1 -1
@@ -200,7 +200,7 @@on the client (above and beyond ACL on servers) for servers which do not support setting and viewing mode bits, so allowing client to check permissions is useful */ -return vfs_permission (inode, mask); +return generic_permission (inode, mask,NULL ); }static kmem_cache_t *cifs_inode_cachep;
fs/ext2/acl.c +14 -50
74 lines not on this page · raw message
fs/ext3/acl.c +14 -50
74 lines not on this page · raw message
fs/fat/dir.c +2 -21
@@ -93,14 +93,6 @@ }#endif -static inline unsigned char -fat_tolower (struct nls_table *t,unsigned char c) -{ -unsigned char nc = t->charset2lower[c]; - -return nc ? nc : c; -} -static inline int fat_short2uni (struct nls_table *t,unsigned char *c,int clen,wchar_t *uni) {
@@ -140,17 +132,6 @@return charlen; } -static int -fat_strnicmp (struct nls_table *t,const unsigned char *s1, -const unsigned char *s2,int len) -{ -while (len--) -if (fat_tolower (t, *s1++) !=fat_tolower (t, *s2++)) -return 1 ; - -return 0 ; -} -static inline int fat_shortname2uni (struct nls_table *nls,unsigned char *buf,int buf_size,wchar_t *uni_buf,unsigned short opt,int lower)
@@ -311,7 +292,7 @@ :uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);if (xlate_len == name_len)if ((!anycase && !memcmp (name, bufname, xlate_len)) || - (anycase && !fat_strnicmp (nls_io, name, bufname, + (anycase && !nls_strnicmp (nls_io, name, bufname, xlate_len))) goto Found;
@@ -322,7 +303,7 @@if (xlate_len != name_len)continue ;if ((!anycase && !memcmp (name, bufname, xlate_len)) || - (anycase && !fat_strnicmp (nls_io, name, bufname, + (anycase && !nls_strnicmp (nls_io, name, bufname, xlate_len))) goto Found; }
fs/hfs/inode.c +1 -1
@@ -516,7 +516,7 @@ {if (S_ISREG (inode->i_mode) && mask &MAY_EXEC )return 0 ; -return vfs_permission (inode, mask); +return generic_permission (inode, mask,NULL ); }static int hfs_file_open (struct inode *inode,struct file *file)
fs/hfsplus/inode.c +1 -1
@@ -260,7 +260,7 @@*/ if (S_ISREG (inode->i_mode) && mask &MAY_EXEC && !(inode->i_mode &0111 ))return 0 ; -return vfs_permission (inode, mask); +return generic_permission (inode, mask,NULL ); }
fs/hostfs/hostfs_kern.c +1 -1
@@ -802,7 +802,7 @@if (name ==NULL )return (-ENOMEM ); err =access_file (name, r, w, x);kfree (name); -if (!err) err =vfs_permission (ino, desired); +if (!err) err =generic_permission (ino, desired,NULL );return (err); }
fs/jfs/acl.c +10 -73
99 lines not on this page · raw message
fs/nfs/dir.c +1 -1
@@ -1589,7 +1589,7 @@return res; out_notsup:nfs_revalidate_inode (NFS_SERVER (inode), inode); - res =vfs_permission (inode, mask); + res =generic_permission (inode, mask,NULL );unlock_kernel ();return res; }
fs/proc/base.c +1 -1
@@ -471,7 +471,7 @@static int proc_permission (struct inode *inode,int mask,struct nameidata *nd) { -if (vfs_permission (inode, mask) !=0 ) +if (generic_permission (inode, mask,NULL ) !=0 )return -EACCES ;return proc_check_root (inode); }
===== fs/reiserfs/xattr.c 1.9 vs edited =====
fs/reiserfs/xattr.c +43 -90
153 lines not on this page · raw message
include/linux/fs.h +3 -1
@@ -1336,7 +1336,9 @@extern int setattr_mask (unsigned int );extern int notify_change (struct dentry *,struct iattr *);extern int permission (struct inode *,int ,struct nameidata *); -extern int vfs_permission (struct inode *,int ); +extern int generic_permission (struct inode *,int , +int (*check_acl)(struct inode *,int )); +extern int get_write_access (struct inode *);extern int deny_write_access (struct file *);static inline void put_write_access (struct inode * inode)
Quoted 10 lines · Christoph Hellwig
Currently we every filesystem with Posix ACLs has it's own reimplemtation of the generic permission checking code with additonal ACL support. This patch
- adds an optional callback to
vfs_permissionthat filesystems can use for ACL support (and renames it togeneric_permissionbecause the old name was wrong - it wasn't like the other vfs_* functions at all)
- uses it in ext2, ext3, jfs and reiserfs. XFS will follow a little later as it's permission checking is burried under several layers of abtractions.
willy notices this patch had a stale hunk of the nls bits in. Clean patch below:
fs/exec.c +1 -1
@@ -893,7 +893,7 @@ mode = inode->i_mode;/* * Check execute perms again - if the caller has CAP_DAC_OVERRIDE, -* vfs_permission lets a non-executable through +* generic_permission lets a non-executable through */ if (!(mode &0111 ))/* with at least _one_ execute bit set */ return -EACCES ;
fs/namei.c +24 -9
@@ -151,15 +151,19 @@return result; } -/* -* vfs_permission() +/** +* generic_permission - check for access rights on a Posix-like filesystem +* @inode: inode to check access rights for +* @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) +* @check_acl: optional callback to check for Posix ACLs * -* is used to check for read/write/execute permissions on a file. +* Used to check for read/write/execute permissions on a file. * We use "fsuid" for this, letting us set arbitrary permissions * for filesystem access without changing the "normal" uids which * are used for other things.. */ -int vfs_permission (struct inode * inode,int mask) +int generic_permission (struct inode *inode,int mask, +int (*check_acl)(struct inode *inode,int mask)) {umode_t mode = inode->i_mode;
@@ -180,8 +184,18 @@if (current->fsuid == inode->i_uid) mode >>=6 ; -else if (in_group_p (inode->i_gid)) - mode >>=3 ; +else { +if (IS_POSIXACL (inode) && (mode &S_IRWXG ) && check_acl) { +int error =check_acl (inode, mask); +if (error == -EACCES ) + goto check_capabilities; +else if (error != -EAGAIN ) +return error; + } + +if (in_group_p (inode->i_gid)) + mode >>=3 ; + }/* * If the DACs are ok we don't need any capability check.
@@ -189,6 +203,7 @@if (((mode & mask & (MAY_READ |MAY_WRITE |MAY_EXEC )) == mask))return 0 ; + check_capabilities:/* * Read/write DACs are always overridable. * Executable DACs are overridable if at least one exec bit is set.
@@ -219,7 +234,7 @@if (inode->i_op && inode->i_op->permission) retval = inode->i_op->permission (inode, submask, nd);else - retval =vfs_permission (inode, submask); + retval =generic_permission (inode, submask,NULL );if (retval)return retval;
@@ -314,7 +329,7 @@/* * Short-cut version of permission(), for calling by * path_walk(), when dcache lock is held. Combines parts -* of permission() and vfs_permission(), and tests ONLY for +* of permission() and generic_permission(), and tests ONLY for * MAY_EXEC permission. * * If appropriate, check DAC only. If not appropriate, or
@@ -2405,7 +2420,7 @@EXPORT_SYMBOL (vfs_link);EXPORT_SYMBOL (vfs_mkdir);EXPORT_SYMBOL (vfs_mknod); -EXPORT_SYMBOL (vfs_permission); +EXPORT_SYMBOL (generic_permission);EXPORT_SYMBOL (vfs_readlink);EXPORT_SYMBOL (vfs_rename);EXPORT_SYMBOL (vfs_rmdir);
fs/cifs/CHANGES +2 -2
@@ -7,8 +7,8 @@ Version 1.21 ------------ -Add new mount parm to control whether mode check (vfs_permission) is done on -the client. If Unix extensions are enabled and the uids on the client +Add new mount parm to control whether mode check (generic_permission) is done +on the client. If Unix extensions are enabled and the uids on the client and server do not match, client permission checks are meaningless on server uids that do not exist on the client (this does not affect the normal ACL check which occurs on the server). Fix default uid
fs/cifs/cifsfs.c +1 -1
@@ -200,7 +200,7 @@on the client (above and beyond ACL on servers) for servers which do not support setting and viewing mode bits, so allowing client to check permissions is useful */ -return vfs_permission (inode, mask); +return generic_permission (inode, mask,NULL ); }static kmem_cache_t *cifs_inode_cachep;
fs/ext2/acl.c +14 -50
74 lines not on this page · raw message
fs/ext3/acl.c +14 -50
74 lines not on this page · raw message
fs/hfs/inode.c +1 -1
@@ -516,7 +516,7 @@ {if (S_ISREG (inode->i_mode) && mask &MAY_EXEC )return 0 ; -return vfs_permission (inode, mask); +return generic_permission (inode, mask,NULL ); }static int hfs_file_open (struct inode *inode,struct file *file)
fs/hfsplus/inode.c +1 -1
@@ -260,7 +260,7 @@*/ if (S_ISREG (inode->i_mode) && mask &MAY_EXEC && !(inode->i_mode &0111 ))return 0 ; -return vfs_permission (inode, mask); +return generic_permission (inode, mask,NULL ); }
fs/hostfs/hostfs_kern.c +1 -1
@@ -802,7 +802,7 @@if (name ==NULL )return (-ENOMEM ); err =access_file (name, r, w, x);kfree (name); -if (!err) err =vfs_permission (ino, desired); +if (!err) err =generic_permission (ino, desired,NULL );return (err); }
fs/jfs/acl.c +10 -73
99 lines not on this page · raw message
fs/nfs/dir.c +1 -1
@@ -1589,7 +1589,7 @@return res; out_notsup:nfs_revalidate_inode (NFS_SERVER (inode), inode); - res =vfs_permission (inode, mask); + res =generic_permission (inode, mask,NULL );unlock_kernel ();return res; }
fs/proc/base.c +1 -1
@@ -471,7 +471,7 @@static int proc_permission (struct inode *inode,int mask,struct nameidata *nd) { -if (vfs_permission (inode, mask) !=0 ) +if (generic_permission (inode, mask,NULL ) !=0 )return -EACCES ;return proc_check_root (inode); }
===== fs/reiserfs/xattr.c 1.9 vs edited =====
fs/reiserfs/xattr.c +43 -90
153 lines not on this page · raw message
include/linux/fs.h +3 -1
@@ -1336,7 +1336,9 @@extern int setattr_mask (unsigned int );extern int notify_change (struct dentry *,struct iattr *);extern int permission (struct inode *,int ,struct nameidata *); -extern int vfs_permission (struct inode *,int ); +extern int generic_permission (struct inode *,int , +int (*check_acl)(struct inode *,int )); +extern int get_write_access (struct inode *);extern int deny_write_access (struct file *);static inline void put_write_access (struct inode * inode)
Review line 188
Quoted 8 lines · Christoph Hellwig
(quoted diff) +2
-2@@ -180,8 +184,18 @@ if (current->fsuid == inode->i_uid) mode >>= 6; - else if (in_group_p(inode->i_gid)) - mode >>= 3; + else { + if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
jfs doesn't currently set MS_POSIXACL (it doesn't require the acl mount option), so this test would fail here. The patch below will set it.
Review line 201
Quoted 13 lines
(quoted diff) +10
-0+ int error = check_acl(inode, mask); + if (error == -EACCES) + goto check_capabilities; + else if (error != -EAGAIN) + return error; + } + + if (in_group_p(inode->i_gid)) + mode >>= 3; + } /* * If the DACs are ok we don't need any capability check.
===== super.c 1.55 vs edited =====
super.c renamed from fs/jfs/super.c +4 -0
@@ -403,6 +403,10 @@ } sbi->flag = flag; +#ifdef CONFIG_JFS_POSIX_ACL + sb->s_flags |=MS_POSIXACL ; +#endif +if (newLVSize) {printk (KERN_ERR "resize option for remount only\n" );return -EINVAL ;
Hello Christoph,
as far as I can see what you propose is very similar to the approach taken in the attached patch, with one more indirection. This looks nicer to me, at the cost of "polluting" iops instead of vfs_permission. JFS will still need Dave's patch, obviously.
The attached patch only covers ext3; not sure if it even compiles.
Cheers,
Signature · 2 lines
Andreas Gruenbacher <[email protected]> SUSE Labs, SUSE LINUX AG
fs/ext3/file.c +1 -1
@@ -136,6 +136,6 @@ struct inode_operations ext3_file_inode_ .getxattr = ext3_getxattr, .listxattr = ext3_listxattr, .removexattr = ext3_removexattr, - .permission = ext3_permission, + .check_posix_acl = ext3_check_posix_acl, };
fs/ext3/acl.h +2 -2
@@ -59,7 +59,7 @@ static inline int ext3_acl_count(size_t#define EXT3_ACL_NOT_CACHED ((void *)-1)/* acl.c */ -extern int ext3_permission (struct inode *,int ,struct nameidata *); +extern int ext3_check_posix_acl (struct inode *,int );extern int ext3_acl_chmod (struct inode *);extern int ext3_init_acl (handle_t *,struct inode *,struct inode *);
@@ -68,7 +68,7 @@ extern void exit_ext3_acl(void);#else /* CONFIG_EXT3_FS_POSIX_ACL */ #include <linux/sched.h> -#define ext3_permission NULL +#define ext3_check_posix_acl NULLstatic inline int ext3_acl_chmod (struct inode *inode)
fs/namei.c +15 -3
@@ -180,8 +180,19 @@ int vfs_permission(struct inode * inode,if (current->fsuid == inode->i_uid) mode >>=6 ; -else if (in_group_p (inode->i_gid)) - mode >>=3 ; +else { +if (IS_POSIXACL (inode) && (mode &S_IRWXG ) && + inode->i_op->check_posix_acl) { +int error = inode->i_op->check_posix_acl (inode, mask); +if (error == -EACCES ) + goto check_capabilities; +else if (error != -EAGAIN ) +return error; + } + +if (in_group_p (inode->i_gid)) + mode >>=3 ; + }/* * If the DACs are ok we don't need any capability check.
@@ -189,6 +200,7 @@ int vfs_permission(struct inode * inode,if (((mode & mask & (MAY_READ |MAY_WRITE |MAY_EXEC )) == mask))return 0 ; +check_capabilities:/* * Read/write DACs are always overridable. * Executable DACs are overridable if at least one exec bit is set.
@@ -208,7 +220,7 @@ int vfs_permission(struct inode * inode,return -EACCES ; } -int permission (struct inode * inode,int mask,struct nameidata *nd) +int permission (struct inode * inode,int mask,struct nameidata *nd) {int retval;int submask;
include/linux/fs.h +1 -0
@@ -930,6 +930,7 @@ struct inode_operations {void (*put_link) (struct dentry *,struct nameidata *);void (*truncate) (struct inode *);int (*permission) (struct inode *,int ,struct nameidata *); +int (*check_posix_acl) (struct inode *,int );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 );
fs/ext3/acl.c +7 -50
67 lines not on this page · raw message
fs/ext3/namei.c +2 -2
@@ -2359,7 +2359,7 @@ struct inode_operations ext3_dir_inode_o .getxattr = ext3_getxattr, .listxattr = ext3_listxattr, .removexattr = ext3_removexattr, - .permission = ext3_permission, + .check_posix_acl = ext3_check_posix_acl, };struct inode_operations ext3_special_inode_operations = {
@@ -2368,5 +2368,5 @@ struct inode_operations ext3_special_ino .getxattr = ext3_getxattr, .listxattr = ext3_listxattr, .removexattr = ext3_removexattr, - .permission = ext3_permission, + .check_posix_acl = ext3_check_posix_acl, };
Quoted 8 lines · Andreas Gruenbacher
Hello Christoph,
as far as I can see what you propose is very similar to the approach taken in the attached patch, with one more indirection. This looks nicer to me, at the cost of "polluting" iops instead of
vfs_permission. JFS will still need Dave's patch, obviously.The attached patch only covers ext3; not sure if it even compiles.
I don't like this one conceptually. checking a posix ACL isn't a fundamental method for the inode object. Checking permission is, and checking the ACL as a subaspect of that should really be a callback and not a method.