nfsd: use get_acl and ->set_acl
[cascardo/linux.git] / fs / nfsd / vfs.c
index 94b5f5d..1426eb6 100644 (file)
@@ -298,41 +298,12 @@ commit_metadata(struct svc_fh *fhp)
 }
 
 /*
- * Set various file attributes.
- * N.B. After this call fhp needs an fh_put
+ * Go over the attributes and take care of the small differences between
+ * NFS semantics and what Linux expects.
  */
-__be32
-nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
-            int check_guard, time_t guardtime)
+static void
+nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
 {
-       struct dentry   *dentry;
-       struct inode    *inode;
-       int             accmode = NFSD_MAY_SATTR;
-       umode_t         ftype = 0;
-       __be32          err;
-       int             host_err;
-       int             size_change = 0;
-
-       if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
-               accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
-       if (iap->ia_valid & ATTR_SIZE)
-               ftype = S_IFREG;
-
-       /* Get inode */
-       err = fh_verify(rqstp, fhp, ftype, accmode);
-       if (err)
-               goto out;
-
-       dentry = fhp->fh_dentry;
-       inode = dentry->d_inode;
-
-       /* Ignore any mode updates on symlinks */
-       if (S_ISLNK(inode->i_mode))
-               iap->ia_valid &= ~ATTR_MODE;
-
-       if (!iap->ia_valid)
-               goto out;
-
        /*
         * NFSv2 does not differentiate between "set-[ac]time-to-now"
         * which only requires access, and "set-[ac]time-to-X" which
@@ -342,8 +313,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
         * convert to "set to now" instead of "set to explicit time"
         *
         * We only call inode_change_ok as the last test as technically
-        * it is not an interface that we should be using.  It is only
-        * valid if the filesystem does not define it's own i_op->setattr.
+        * it is not an interface that we should be using.
         */
 #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
 #define        MAX_TOUCH_TIME_ERROR (30*60)
@@ -369,30 +339,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
                        iap->ia_valid &= ~BOTH_TIME_SET;
                }
        }
-           
-       /*
-        * The size case is special.
-        * It changes the file as well as the attributes.
-        */
-       if (iap->ia_valid & ATTR_SIZE) {
-               if (iap->ia_size < inode->i_size) {
-                       err = nfsd_permission(rqstp, fhp->fh_export, dentry,
-                                       NFSD_MAY_TRUNC|NFSD_MAY_OWNER_OVERRIDE);
-                       if (err)
-                               goto out;
-               }
-
-               host_err = get_write_access(inode);
-               if (host_err)
-                       goto out_nfserr;
-
-               size_change = 1;
-               host_err = locks_verify_truncate(inode, NULL, iap->ia_size);
-               if (host_err) {
-                       put_write_access(inode);
-                       goto out_nfserr;
-               }
-       }
 
        /* sanitize the mode change */
        if (iap->ia_valid & ATTR_MODE) {
@@ -415,186 +361,114 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
                        iap->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID);
                }
        }
-
-       /* Change the attributes. */
-
-       iap->ia_valid |= ATTR_CTIME;
-
-       err = nfserr_notsync;
-       if (!check_guard || guardtime == inode->i_ctime.tv_sec) {
-               host_err = nfsd_break_lease(inode);
-               if (host_err)
-                       goto out_nfserr;
-               fh_lock(fhp);
-
-               host_err = notify_change(dentry, iap, NULL);
-               err = nfserrno(host_err);
-               fh_unlock(fhp);
-       }
-       if (size_change)
-               put_write_access(inode);
-       if (!err)
-               commit_metadata(fhp);
-out:
-       return err;
-
-out_nfserr:
-       err = nfserrno(host_err);
-       goto out;
 }
 
-#if defined(CONFIG_NFSD_V2_ACL) || \
-    defined(CONFIG_NFSD_V3_ACL) || \
-    defined(CONFIG_NFSD_V4)
-static ssize_t nfsd_getxattr(struct dentry *dentry, char *key, void **buf)
+static __be32
+nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp,
+               struct iattr *iap)
 {
-       ssize_t buflen;
-       ssize_t ret;
-
-       buflen = vfs_getxattr(dentry, key, NULL, 0);
-       if (buflen <= 0)
-               return buflen;
+       struct inode *inode = fhp->fh_dentry->d_inode;
+       int host_err;
 
-       *buf = kmalloc(buflen, GFP_KERNEL);
-       if (!*buf)
-               return -ENOMEM;
+       if (iap->ia_size < inode->i_size) {
+               __be32 err;
 
-       ret = vfs_getxattr(dentry, key, *buf, buflen);
-       if (ret < 0)
-               kfree(*buf);
-       return ret;
-}
-#endif
+               err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
+                               NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
+               if (err)
+                       return err;
+       }
 
-#if defined(CONFIG_NFSD_V4)
-static int
-set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key)
-{
-       int len;
-       size_t buflen;
-       char *buf = NULL;
-       int error = 0;
-
-       buflen = posix_acl_xattr_size(pacl->a_count);
-       buf = kmalloc(buflen, GFP_KERNEL);
-       error = -ENOMEM;
-       if (buf == NULL)
-               goto out;
+       host_err = get_write_access(inode);
+       if (host_err)
+               goto out_nfserrno;
 
-       len = posix_acl_to_xattr(&init_user_ns, pacl, buf, buflen);
-       if (len < 0) {
-               error = len;
-               goto out;
-       }
+       host_err = locks_verify_truncate(inode, NULL, iap->ia_size);
+       if (host_err)
+               goto out_put_write_access;
+       return 0;
 
-       error = vfs_setxattr(dentry, key, buf, len, 0);
-out:
-       kfree(buf);
-       return error;
+out_put_write_access:
+       put_write_access(inode);
+out_nfserrno:
+       return nfserrno(host_err);
 }
 
+/*
+ * Set various file attributes.  After this call fhp needs an fh_put.
+ */
 __be32
-nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
-    struct nfs4_acl *acl)
+nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
+            int check_guard, time_t guardtime)
 {
-       __be32 error;
-       int host_error;
-       struct dentry *dentry;
-       struct inode *inode;
-       struct posix_acl *pacl = NULL, *dpacl = NULL;
-       unsigned int flags = 0;
+       struct dentry   *dentry;
+       struct inode    *inode;
+       int             accmode = NFSD_MAY_SATTR;
+       umode_t         ftype = 0;
+       __be32          err;
+       int             host_err;
+       int             size_change = 0;
+
+       if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
+               accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
+       if (iap->ia_valid & ATTR_SIZE)
+               ftype = S_IFREG;
 
        /* Get inode */
-       error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
-       if (error)
-               return error;
+       err = fh_verify(rqstp, fhp, ftype, accmode);
+       if (err)
+               goto out;
 
        dentry = fhp->fh_dentry;
        inode = dentry->d_inode;
-       if (S_ISDIR(inode->i_mode))
-               flags = NFS4_ACL_DIR;
 
-       host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
-       if (host_error == -EINVAL) {
-               return nfserr_attrnotsupp;
-       } else if (host_error < 0)
-               goto out_nfserr;
+       /* Ignore any mode updates on symlinks */
+       if (S_ISLNK(inode->i_mode))
+               iap->ia_valid &= ~ATTR_MODE;
 
-       host_error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS);
-       if (host_error < 0)
-               goto out_release;
+       if (!iap->ia_valid)
+               goto out;
 
-       if (S_ISDIR(inode->i_mode))
-               host_error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT);
+       nfsd_sanitize_attrs(inode, iap);
 
-out_release:
-       posix_acl_release(pacl);
-       posix_acl_release(dpacl);
-out_nfserr:
-       if (host_error == -EOPNOTSUPP)
-               return nfserr_attrnotsupp;
-       else
-               return nfserrno(host_error);
-}
+       /*
+        * The size case is special, it changes the file in addition to the
+        * attributes.
+        */
+       if (iap->ia_valid & ATTR_SIZE) {
+               err = nfsd_get_write_access(rqstp, fhp, iap);
+               if (err)
+                       goto out;
+               size_change = 1;
+       }
 
-static struct posix_acl *
-_get_posix_acl(struct dentry *dentry, char *key)
-{
-       void *buf = NULL;
-       struct posix_acl *pacl = NULL;
-       int buflen;
-
-       buflen = nfsd_getxattr(dentry, key, &buf);
-       if (!buflen)
-               buflen = -ENODATA;
-       if (buflen <= 0)
-               return ERR_PTR(buflen);
-
-       pacl = posix_acl_from_xattr(&init_user_ns, buf, buflen);
-       kfree(buf);
-       return pacl;
-}
+       iap->ia_valid |= ATTR_CTIME;
 
-int
-nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl)
-{
-       struct inode *inode = dentry->d_inode;
-       int error = 0;
-       struct posix_acl *pacl = NULL, *dpacl = NULL;
-       unsigned int flags = 0;
-
-       pacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_ACCESS);
-       if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA)
-               pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
-       if (IS_ERR(pacl)) {
-               error = PTR_ERR(pacl);
-               pacl = NULL;
-               goto out;
+       if (check_guard && guardtime != inode->i_ctime.tv_sec) {
+               err = nfserr_notsync;
+               goto out_put_write_access;
        }
 
-       if (S_ISDIR(inode->i_mode)) {
-               dpacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_DEFAULT);
-               if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA)
-                       dpacl = NULL;
-               else if (IS_ERR(dpacl)) {
-                       error = PTR_ERR(dpacl);
-                       dpacl = NULL;
-                       goto out;
-               }
-               flags = NFS4_ACL_DIR;
-       }
+       host_err = nfsd_break_lease(inode);
+       if (host_err)
+               goto out_put_write_access_nfserror;
 
-       *acl = nfs4_acl_posix_to_nfsv4(pacl, dpacl, flags);
-       if (IS_ERR(*acl)) {
-               error = PTR_ERR(*acl);
-               *acl = NULL;
-       }
- out:
-       posix_acl_release(pacl);
-       posix_acl_release(dpacl);
-       return error;
+       fh_lock(fhp);
+       host_err = notify_change(dentry, iap, NULL);
+       fh_unlock(fhp);
+
+out_put_write_access_nfserror:
+       err = nfserrno(host_err);
+out_put_write_access:
+       if (size_change)
+               put_write_access(inode);
+       if (!err)
+               commit_metadata(fhp);
+out:
+       return err;
 }
 
+#if defined(CONFIG_NFSD_V4)
 /*
  * NFS junction information is stored in an extended attribute.
  */
@@ -2259,93 +2133,3 @@ out_nomem:
        nfsd_racache_shutdown();
        return -ENOMEM;
 }
-
-#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
-struct posix_acl *
-nfsd_get_posix_acl(struct svc_fh *fhp, int type)
-{
-       struct inode *inode = fhp->fh_dentry->d_inode;
-       char *name;
-       void *value = NULL;
-       ssize_t size;
-       struct posix_acl *acl;
-
-       if (!IS_POSIXACL(inode))
-               return ERR_PTR(-EOPNOTSUPP);
-
-       switch (type) {
-       case ACL_TYPE_ACCESS:
-               name = POSIX_ACL_XATTR_ACCESS;
-               break;
-       case ACL_TYPE_DEFAULT:
-               name = POSIX_ACL_XATTR_DEFAULT;
-               break;
-       default:
-               return ERR_PTR(-EOPNOTSUPP);
-       }
-
-       size = nfsd_getxattr(fhp->fh_dentry, name, &value);
-       if (size < 0)
-               return ERR_PTR(size);
-
-       acl = posix_acl_from_xattr(&init_user_ns, value, size);
-       kfree(value);
-       return acl;
-}
-
-int
-nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl)
-{
-       struct inode *inode = fhp->fh_dentry->d_inode;
-       char *name;
-       void *value = NULL;
-       size_t size;
-       int error;
-
-       if (!IS_POSIXACL(inode) ||
-           !inode->i_op->setxattr || !inode->i_op->removexattr)
-               return -EOPNOTSUPP;
-       switch(type) {
-               case ACL_TYPE_ACCESS:
-                       name = POSIX_ACL_XATTR_ACCESS;
-                       break;
-               case ACL_TYPE_DEFAULT:
-                       name = POSIX_ACL_XATTR_DEFAULT;
-                       break;
-               default:
-                       return -EOPNOTSUPP;
-       }
-
-       if (acl && acl->a_count) {
-               size = posix_acl_xattr_size(acl->a_count);
-               value = kmalloc(size, GFP_KERNEL);
-               if (!value)
-                       return -ENOMEM;
-               error = posix_acl_to_xattr(&init_user_ns, acl, value, size);
-               if (error < 0)
-                       goto getout;
-               size = error;
-       } else
-               size = 0;
-
-       error = fh_want_write(fhp);
-       if (error)
-               goto getout;
-       if (size)
-               error = vfs_setxattr(fhp->fh_dentry, name, value, size, 0);
-       else {
-               if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT)
-                       error = 0;
-               else {
-                       error = vfs_removexattr(fhp->fh_dentry, name);
-                       if (error == -ENODATA)
-                               error = 0;
-               }
-       }
-       fh_drop_write(fhp);
-
-getout:
-       kfree(value);
-       return error;
-}
-#endif  /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */