]> git.hungrycats.org Git - linux/commitdiff
[PATCH] generic acl support for ->permission
authorChristoph Hellwig <hch@lst.de>
Tue, 19 Oct 2004 01:05:13 +0000 (18:05 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Tue, 19 Oct 2004 01:05:13 +0000 (18:05 -0700)
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 and jfs.  XFS will follow a little later as it's
  permission checking is burried under several layers of abstraction.

From: Dave Kleikamp <shaggy@austin.ibm.com>

  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.

Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
14 files changed:
fs/cifs/CHANGES
fs/cifs/cifsfs.c
fs/exec.c
fs/ext2/acl.c
fs/ext3/acl.c
fs/hfs/inode.c
fs/hfsplus/inode.c
fs/hostfs/hostfs_kern.c
fs/jfs/acl.c
fs/jfs/super.c
fs/namei.c
fs/nfs/dir.c
fs/proc/base.c
include/linux/fs.h

index 087be36211eb96ac6a880c6b0ba52e1421897623..2a1924078059cecbf87c00bc38408afcc7fc9c9a 100644 (file)
@@ -7,8 +7,8 @@ or out of order NULL pointer checks in little used error paths).
 
 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
index 729cdba71530d4f2119f4f93d6f8cc04cdf39a19..996fc298a306679d298930fdb476999bd5d341c8 100644 (file)
@@ -200,7 +200,7 @@ static int cifs_permission(struct inode * inode, int mask, struct nameidata *nd)
                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;
index 4124b39bdd27508ab0a5dcb6f750b278e955cb60..4915bffb045dd4f658d445c5d04b848afc4ec5c6 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -886,7 +886,7 @@ int prepare_binprm(struct linux_binprm *bprm)
        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;
index 89d1df91411f6e9a35655c6a8feda7578c0d41db..0d961d5e00edeba38adb345e09061e6cc306a287 100644 (file)
@@ -280,60 +280,24 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
        return error;
 }
 
-/*
- * Inode operation permission().
- *
- * inode->i_sem: don't care
- */
-int
-ext2_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int
+ext2_check_acl(struct inode *inode, int mask)
 {
-       int mode = inode->i_mode;
-
-       /* Nobody gets write access to a read-only fs */
-       if ((mask & MAY_WRITE) && IS_RDONLY(inode) &&
-           (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
-               return -EROFS;
-       /* Nobody gets write access to an immutable file */
-       if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
-           return -EACCES;
-       if (current->fsuid == inode->i_uid) {
-               mode >>= 6;
-       } else if (test_opt(inode->i_sb, POSIX_ACL)) {
-               struct posix_acl *acl;
-
-               /* The access ACL cannot grant access if the group class
-                  permission bits don't contain all requested permissions. */
-               if (((mode >> 3) & mask & S_IRWXO) != mask)
-                       goto check_groups;
-               acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
-               if (acl) {
-                       int error = posix_acl_permission(inode, acl, mask);
-                       posix_acl_release(acl);
-                       if (error == -EACCES)
-                               goto check_capabilities;
-                       return error;
-               } else
-                       goto check_groups;
-       } else {
-check_groups:
-               if (in_group_p(inode->i_gid))
-                       mode >>= 3;
+       struct posix_acl *acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
+
+       if (acl) {
+               int error = posix_acl_permission(inode, acl, mask);
+               posix_acl_release(acl);
+               return error;
        }
-       if ((mode & mask & S_IRWXO) == mask)
-               return 0;
 
-check_capabilities:
-       /* Allowed to override Discretionary Access Control? */
-       if (!(mask & MAY_EXEC) ||
-           (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
-               if (capable(CAP_DAC_OVERRIDE))
-                       return 0;
-       /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */
-       if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) ||
-           (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))))
-               return 0;
-       return -EACCES;
+       return -EAGAIN;
+}
+
+int
+ext2_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+       return generic_permission(inode, mask, ext2_check_acl);
 }
 
 /*
index a3cf77de0e43bb8618bee56fbb04d9a10a6da24a..a335fd1343526c90075b1a162db2b66eb9900778 100644 (file)
@@ -285,60 +285,24 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type,
        return error;
 }
 
-/*
- * Inode operation permission().
- *
- * inode->i_sem: don't care
- */
-int
-ext3_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int
+ext3_check_acl(struct inode *inode, int mask)
 {
-       int mode = inode->i_mode;
-
-       /* Nobody gets write access to a read-only fs */
-       if ((mask & MAY_WRITE) && IS_RDONLY(inode) &&
-           (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
-               return -EROFS;
-       /* Nobody gets write access to an immutable file */
-       if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
-           return -EACCES;
-       if (current->fsuid == inode->i_uid) {
-               mode >>= 6;
-       } else if (test_opt(inode->i_sb, POSIX_ACL)) {
-               struct posix_acl *acl;
-
-               /* The access ACL cannot grant access if the group class
-                  permission bits don't contain all requested permissions. */
-               if (((mode >> 3) & mask & S_IRWXO) != mask)
-                       goto check_groups;
-               acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
-               if (acl) {
-                       int error = posix_acl_permission(inode, acl, mask);
-                       posix_acl_release(acl);
-                       if (error == -EACCES)
-                               goto check_capabilities;
-                       return error;
-               } else
-                       goto check_groups;
-       } else {
-check_groups:
-               if (in_group_p(inode->i_gid))
-                       mode >>= 3;
+       struct posix_acl *acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
+
+       if (acl) {
+               int error = posix_acl_permission(inode, acl, mask);
+               posix_acl_release(acl);
+               return error;
        }
-       if ((mode & mask & S_IRWXO) == mask)
-               return 0;
 
-check_capabilities:
-       /* Allowed to override Discretionary Access Control? */
-       if (!(mask & MAY_EXEC) ||
-           (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
-               if (capable(CAP_DAC_OVERRIDE))
-                       return 0;
-       /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */
-       if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) ||
-           (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))))
-               return 0;
-       return -EACCES;
+       return -EAGAIN;
+}
+
+int
+ext3_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+       return generic_permission(inode, mask, ext3_check_acl);
 }
 
 /*
index 6c869f3779648adb008b10403e678001e2ef38c2..ae55ce1a540537941ab21aee59f3a2f4d2d80e21 100644 (file)
@@ -517,7 +517,7 @@ static int hfs_permission(struct inode *inode, int mask,
 {
        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)
index eff1c987b6fb08acec4927cc1773b6c5e0419982..f58025cb38af7cfa92cb66da67a55d8b4d8e6b3a 100644 (file)
@@ -260,7 +260,7 @@ static int hfsplus_permission(struct inode *inode, int mask, struct nameidata *n
         */
        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);
 }
 
 
index 7d42da0a304d46b6f6c7338353584ffe49f88e14..937fac26b34ae0a74868391289825d411cf562d7 100644 (file)
@@ -808,7 +808,7 @@ int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd)
        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);
 }
 
index fc584f2194f4fa65542ba500485f3ab5ddc99fd6..8d2a9ab981d4394f2fc5d4cb13ad64d7e20978be 100644 (file)
@@ -123,88 +123,25 @@ out:
        return rc;
 }
 
-/*
- *     jfs_permission()
- *
- * modified vfs_permission to check posix acl
- */
-int jfs_permission(struct inode * inode, int mask, struct nameidata *nd)
+static int jfs_check_acl(struct inode *inode, int mask)
 {
-       umode_t mode = inode->i_mode;
        struct jfs_inode_info *ji = JFS_IP(inode);
 
-       if (mask & MAY_WRITE) {
-               /*
-                * Nobody gets write access to a read-only fs.
-                */
-               if (IS_RDONLY(inode) &&
-                   (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
-                       return -EROFS;
-
-               /*
-                * Nobody gets write access to an immutable file.
-                */
-               if (IS_IMMUTABLE(inode))
-                       return -EACCES;
-       }
-
-       if (current->fsuid == inode->i_uid) {
-               mode >>= 6;
-               goto check_mode;
-       }
-       /*
-        * ACL can't contain additional permissions if the ACL_MASK entry
-        * is zero.
-        */
-       if (!(mode & S_IRWXG))
-               goto check_groups;
-
        if (ji->i_acl == JFS_ACL_NOT_CACHED) {
-               struct posix_acl *acl;
-
-               acl = jfs_get_acl(inode, ACL_TYPE_ACCESS);
-
+               struct posix_acl *acl = jfs_get_acl(inode, ACL_TYPE_ACCESS);
                if (IS_ERR(acl))
                        return PTR_ERR(acl);
                posix_acl_release(acl);
        }
 
-       if (ji->i_acl) {
-               int rc = posix_acl_permission(inode, ji->i_acl, mask);
-               if (rc == -EACCES)
-                       goto check_capabilities;
-               return rc;
-       }
-
-check_groups:
-       if (in_group_p(inode->i_gid))
-               mode >>= 3;
-
-check_mode:
-       /*
-        * If the DACs are ok we don't need any capability check.
-        */
-       if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
-               return 0;
+       if (ji->i_acl)
+               return posix_acl_permission(inode, ji->i_acl, mask);
+       return -EAGAIN;
+}
 
-check_capabilities:
-       /*
-        * Read/write DACs are always overridable.
-        * Executable DACs are overridable if at least one exec bit is set.
-        */
-       if (!(mask & MAY_EXEC) ||
-           (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
-               if (capable(CAP_DAC_OVERRIDE))
-                       return 0;
-
-       /*
-        * Searching includes executable on directories, else just read.
-        */
-       if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
-               if (capable(CAP_DAC_READ_SEARCH))
-                       return 0;
-
-       return -EACCES;
+int jfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+       return generic_permission(inode, mask, jfs_check_acl);
 }
 
 int jfs_init_acl(struct inode *inode, struct inode *dir)
index 7c91ccfe382f4068b8d793b1fcf7cf304c7af776..ecf1bca7305060026a9dade2fd546ab9e3341dc8 100644 (file)
@@ -403,6 +403,10 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
        }
        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;
index bf259a9c5b217ddc4038abaa0b5e80776c01fb73..287339bb7b7c54c3a7f0e6401408cfebc20fa8b7 100644 (file)
@@ -152,15 +152,19 @@ char * getname(const char __user * filename)
        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;
 
@@ -181,8 +185,18 @@ int vfs_permission(struct inode * inode, int mask)
 
        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.
@@ -190,6 +204,7 @@ int vfs_permission(struct inode * inode, int mask)
        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.
@@ -220,7 +235,7 @@ int permission(struct inode * inode,int mask, struct nameidata *nd)
        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;
 
@@ -315,7 +330,7 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name,
 /*
  * 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
@@ -2456,7 +2471,7 @@ EXPORT_SYMBOL(vfs_follow_link);
 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);
index 626252aec95a3c2733eb73319c8314e8ba1a2b07..eac30582ff9e9ae211d6acca199fbd7122952751 100644 (file)
@@ -1600,7 +1600,7 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
        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;
 }
index 987240398c4a8058239e10450006e55691cd6ad9..91060b9921ccac151bd27cd037db9a6c09e51a0a 100644 (file)
@@ -473,7 +473,7 @@ out:
 
 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);
 }
index dc457c3fede2f91d3e2891544df5e9ab3b321e77..13e1b4cb7c748b4eb15519ef061ed80c50741c94 100644 (file)
@@ -1339,7 +1339,9 @@ extern sector_t bmap(struct inode *, sector_t);
 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)