]> err.no Git - linux-2.6/blobdiff - fs/nfsd/vfs.c
Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus
[linux-2.6] / fs / nfsd / vfs.c
index 4f2cd3d2756665a1f600f8bcac96b4f2756b1ded..eef0576a77857577805e053d428ebe13bd6ce9a6 100644 (file)
@@ -48,8 +48,8 @@
 #include <linux/fsnotify.h>
 #include <linux/posix_acl.h>
 #include <linux/posix_acl_xattr.h>
-#ifdef CONFIG_NFSD_V4
 #include <linux/xattr.h>
+#ifdef CONFIG_NFSD_V4
 #include <linux/nfs4.h>
 #include <linux/nfs4_acl.h>
 #include <linux/nfsd_idmap.h>
@@ -254,12 +254,19 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
 
        /* Get inode */
        err = fh_verify(rqstp, fhp, ftype, accmode);
-       if (err || !iap->ia_valid)
+       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
         * requires ownership.
@@ -358,8 +365,30 @@ out_nfserr:
        goto out;
 }
 
-#if defined(CONFIG_NFSD_V4)
+#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)
+{
+       ssize_t buflen;
+       int error;
 
+       buflen = vfs_getxattr(dentry, key, NULL, 0);
+       if (buflen <= 0)
+               return buflen;
+
+       *buf = kmalloc(buflen, GFP_KERNEL);
+       if (!*buf)
+               return -ENOMEM;
+
+       error = vfs_getxattr(dentry, key, *buf, buflen);
+       if (error < 0)
+               return error;
+       return buflen;
+}
+#endif
+
+#if defined(CONFIG_NFSD_V4)
 static int
 set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key)
 {
@@ -367,7 +396,6 @@ set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key)
        size_t buflen;
        char *buf = NULL;
        int error = 0;
-       struct inode *inode = dentry->d_inode;
 
        buflen = posix_acl_xattr_size(pacl->a_count);
        buf = kmalloc(buflen, GFP_KERNEL);
@@ -381,15 +409,7 @@ set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key)
                goto out;
        }
 
-       error = -EOPNOTSUPP;
-       if (inode->i_op && inode->i_op->setxattr) {
-               down(&inode->i_sem);
-               security_inode_setxattr(dentry, key, buf, len, 0);
-               error = inode->i_op->setxattr(dentry, key, buf, len, 0);
-               if (!error)
-                       security_inode_post_setxattr(dentry, key, buf, len, 0);
-               up(&inode->i_sem);
-       }
+       error = vfs_setxattr(dentry, key, buf, len, 0);
 out:
        kfree(buf);
        return error;
@@ -448,44 +468,19 @@ out_nfserr:
 static struct posix_acl *
 _get_posix_acl(struct dentry *dentry, char *key)
 {
-       struct inode *inode = dentry->d_inode;
-       char *buf = NULL;
-       int buflen, error = 0;
+       void *buf = NULL;
        struct posix_acl *pacl = NULL;
+       int buflen;
 
-       error = -EOPNOTSUPP;
-       if (inode->i_op == NULL)
-               goto out_err;
-       if (inode->i_op->getxattr == NULL)
-               goto out_err;
-
-       error = security_inode_getxattr(dentry, key);
-       if (error)
-               goto out_err;
-
-       buflen = inode->i_op->getxattr(dentry, key, NULL, 0);
-       if (buflen <= 0) {
-               error = buflen < 0 ? buflen : -ENODATA;
-               goto out_err;
-       }
-
-       buf = kmalloc(buflen, GFP_KERNEL);
-       if (buf == NULL) {
-               error = -ENOMEM;
-               goto out_err;
-       }
-
-       error = inode->i_op->getxattr(dentry, key, buf, buflen);
-       if (error < 0)
-               goto out_err;
+       buflen = nfsd_getxattr(dentry, key, &buf);
+       if (!buflen)
+               buflen = -ENODATA;
+       if (buflen <= 0)
+               return ERR_PTR(buflen);
 
        pacl = posix_acl_from_xattr(buf, buflen);
- out:
        kfree(buf);
        return pacl;
- out_err:
-       pacl = ERR_PTR(error);
-       goto out;
 }
 
 int
@@ -710,27 +705,33 @@ nfsd_close(struct file *filp)
  * As this calls fsync (not fdatasync) there is no need for a write_inode
  * after it.
  */
-static inline void nfsd_dosync(struct file *filp, struct dentry *dp,
-                              struct file_operations *fop)
+static inline int nfsd_dosync(struct file *filp, struct dentry *dp,
+                             struct file_operations *fop)
 {
        struct inode *inode = dp->d_inode;
        int (*fsync) (struct file *, struct dentry *, int);
+       int err = nfs_ok;
 
        filemap_fdatawrite(inode->i_mapping);
        if (fop && (fsync = fop->fsync))
-               fsync(filp, dp, 0);
+               err=fsync(filp, dp, 0);
        filemap_fdatawait(inode->i_mapping);
+
+       return nfserrno(err);
 }
        
 
-static void
+static int
 nfsd_sync(struct file *filp)
 {
+        int err;
        struct inode *inode = filp->f_dentry->d_inode;
        dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name);
-       down(&inode->i_sem);
-       nfsd_dosync(filp, filp->f_dentry, filp->f_op);
-       up(&inode->i_sem);
+       mutex_lock(&inode->i_mutex);
+       err=nfsd_dosync(filp, filp->f_dentry, filp->f_op);
+       mutex_unlock(&inode->i_mutex);
+
+       return err;
 }
 
 void
@@ -867,6 +868,16 @@ out:
        return err;
 }
 
+static void kill_suid(struct dentry *dentry)
+{
+       struct iattr    ia;
+       ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID;
+
+       mutex_lock(&dentry->d_inode->i_mutex);
+       notify_change(dentry, &ia);
+       mutex_unlock(&dentry->d_inode->i_mutex);
+}
+
 static inline int
 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
                                loff_t offset, struct kvec *vec, int vlen,
@@ -920,14 +931,8 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
        }
 
        /* clear setuid/setgid flag after write */
-       if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) {
-               struct iattr    ia;
-               ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID;
-
-               down(&inode->i_sem);
-               notify_change(dentry, &ia);
-               up(&inode->i_sem);
-       }
+       if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID)))
+               kill_suid(dentry);
 
        if (err >= 0 && stable) {
                static ino_t    last_ino;
@@ -955,7 +960,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
 
                        if (inode->i_state & I_DIRTY) {
                                dprintk("nfsd: write sync %d\n", current->pid);
-                               nfsd_sync(file);
+                               err=nfsd_sync(file);
                        }
 #if 0
                        wake_up(&inode->i_wait);
@@ -1059,7 +1064,7 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
                return err;
        if (EX_ISSYNC(fhp->fh_export)) {
                if (file->f_op && file->f_op->fsync) {
-                       nfsd_sync(file);
+                       err = nfsd_sync(file);
                } else {
                        err = nfserr_notsupp;
                }
@@ -1867,39 +1872,25 @@ nfsd_get_posix_acl(struct svc_fh *fhp, int type)
        ssize_t size;
        struct posix_acl *acl;
 
-       if (!IS_POSIXACL(inode) || !inode->i_op || !inode->i_op->getxattr)
+       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);
-       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 = inode->i_op->getxattr(fhp->fh_dentry, name, NULL, 0);
+       size = nfsd_getxattr(fhp->fh_dentry, name, &value);
+       if (size < 0)
+               return ERR_PTR(size);
 
-       if (size < 0) {
-               acl = ERR_PTR(size);
-               goto getout;
-       } else if (size > 0) {
-               value = kmalloc(size, GFP_KERNEL);
-               if (!value) {
-                       acl = ERR_PTR(-ENOMEM);
-                       goto getout;
-               }
-               size = inode->i_op->getxattr(fhp->fh_dentry, name, value, size);
-               if (size < 0) {
-                       acl = ERR_PTR(size);
-                       goto getout;
-               }
-       }
        acl = posix_acl_from_xattr(value, size);
-
-getout:
        kfree(value);
        return acl;
 }
@@ -1940,16 +1931,13 @@ nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl)
        } else
                size = 0;
 
-       if (!fhp->fh_locked)
-               fh_lock(fhp);  /* unlocking is done automatically */
        if (size)
-               error = inode->i_op->setxattr(fhp->fh_dentry, name,
-                                             value, size, 0);
+               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 = inode->i_op->removexattr(fhp->fh_dentry, name);
+                       error = vfs_removexattr(fhp->fh_dentry, name);
                        if (error == -ENODATA)
                                error = 0;
                }