Merge tag 'nfsd-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
[sfrench/cifs-2.6.git] / fs / nfsd / vfs.c
index b7c7a9273ea01d9d84bcc2ea487bd6d886bc060d..6a4c506038e00d3903908adb22737a3d97a8467f 100644 (file)
@@ -476,7 +476,6 @@ static int __nfsd_setattr(struct dentry *dentry, struct iattr *iap)
  * @rqstp: controlling RPC transaction
  * @fhp: filehandle of target
  * @attr: attributes to set
- * @check_guard: set to 1 if guardtime is a valid timestamp
  * @guardtime: do not act if ctime.tv_sec does not match this timestamp
  *
  * This call may adjust the contents of @attr (in particular, this
@@ -488,8 +487,7 @@ static int __nfsd_setattr(struct dentry *dentry, struct iattr *iap)
  */
 __be32
 nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
-            struct nfsd_attrs *attr,
-            int check_guard, time64_t guardtime)
+            struct nfsd_attrs *attr, const struct timespec64 *guardtime)
 {
        struct dentry   *dentry;
        struct inode    *inode;
@@ -497,7 +495,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
        int             accmode = NFSD_MAY_SATTR;
        umode_t         ftype = 0;
        __be32          err;
-       int             host_err;
+       int             host_err = 0;
        bool            get_write_count;
        bool            size_change = (iap->ia_valid & ATTR_SIZE);
        int             retries;
@@ -538,9 +536,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
 
        nfsd_sanitize_attrs(inode, iap);
 
-       if (check_guard && guardtime != inode_get_ctime_sec(inode))
-               return nfserr_notsync;
-
        /*
         * The size case is special, it changes the file in addition to the
         * attributes, and file systems don't expect it to be mixed with
@@ -555,6 +550,19 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
        }
 
        inode_lock(inode);
+       err = fh_fill_pre_attrs(fhp);
+       if (err)
+               goto out_unlock;
+
+       if (guardtime) {
+               struct timespec64 ctime = inode_get_ctime(inode);
+               if ((u32)guardtime->tv_sec != (u32)ctime.tv_sec ||
+                   guardtime->tv_nsec != ctime.tv_nsec) {
+                       err = nfserr_notsync;
+                       goto out_fill_attrs;
+               }
+       }
+
        for (retries = 1;;) {
                struct iattr attrs;
 
@@ -582,13 +590,23 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
                attr->na_aclerr = set_posix_acl(&nop_mnt_idmap,
                                                dentry, ACL_TYPE_DEFAULT,
                                                attr->na_dpacl);
+out_fill_attrs:
+       /*
+        * RFC 1813 Section 3.3.2 does not mandate that an NFS server
+        * returns wcc_data for SETATTR. Some client implementations
+        * depend on receiving wcc_data, however, to sort out partial
+        * updates (eg., the client requested that size and mode be
+        * modified, but the server changed only the file mode).
+        */
+       fh_fill_post_attrs(fhp);
+out_unlock:
        inode_unlock(inode);
        if (size_change)
                put_write_access(inode);
 out:
        if (!host_err)
                host_err = commit_metadata(fhp);
-       return nfserrno(host_err);
+       return err != 0 ? err : nfserrno(host_err);
 }
 
 #if defined(CONFIG_NFSD_V4)
@@ -1002,7 +1020,9 @@ static __be32 nfsd_finish_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
                               unsigned long *count, u32 *eof, ssize_t host_err)
 {
        if (host_err >= 0) {
-               nfsd_stats_io_read_add(fhp->fh_export, host_err);
+               struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+
+               nfsd_stats_io_read_add(nn, fhp->fh_export, host_err);
                *eof = nfsd_eof_on_read(file, offset, host_err, *count);
                *count = host_err;
                fsnotify_access(file);
@@ -1185,7 +1205,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
                goto out_nfserr;
        }
        *cnt = host_err;
-       nfsd_stats_io_write_add(exp, *cnt);
+       nfsd_stats_io_write_add(nn, exp, *cnt);
        fsnotify_modify(file);
        host_err = filemap_check_wb_err(file->f_mapping, since);
        if (host_err < 0)
@@ -1404,7 +1424,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
         * if the attributes have not changed.
         */
        if (iap->ia_valid)
-               status = nfsd_setattr(rqstp, resfhp, attrs, 0, (time64_t)0);
+               status = nfsd_setattr(rqstp, resfhp, attrs, NULL);
        else
                status = nfserrno(commit_metadata(resfhp));
 
@@ -1906,10 +1926,10 @@ out_unlock:
        fh_drop_write(ffhp);
 
        /*
-        * If the target dentry has cached open files, then we need to try to
-        * close them prior to doing the rename. Flushing delayed fput
-        * shouldn't be done with locks held however, so we delay it until this
-        * point and then reattempt the whole shebang.
+        * If the target dentry has cached open files, then we need to
+        * try to close them prior to doing the rename.  Final fput
+        * shouldn't be done with locks held however, so we delay it
+        * until this point and then reattempt the whole shebang.
         */
        if (close_cached) {
                close_cached = false;
@@ -2177,11 +2197,43 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
        if (err == nfserr_eof || err == nfserr_toosmall)
                err = nfs_ok; /* can still be found in ->err */
 out_close:
-       fput(file);
+       nfsd_filp_close(file);
 out:
        return err;
 }
 
+/**
+ * nfsd_filp_close: close a file synchronously
+ * @fp: the file to close
+ *
+ * nfsd_filp_close() is similar in behaviour to filp_close().
+ * The difference is that if this is the final close on the
+ * file, the that finalisation happens immediately, rather then
+ * being handed over to a work_queue, as it the case for
+ * filp_close().
+ * When a user-space process closes a file (even when using
+ * filp_close() the finalisation happens before returning to
+ * userspace, so it is effectively synchronous.  When a kernel thread
+ * uses file_close(), on the other hand, the handling is completely
+ * asynchronous.  This means that any cost imposed by that finalisation
+ * is not imposed on the nfsd thread, and nfsd could potentually
+ * close files more quickly than the work queue finalises the close,
+ * which would lead to unbounded growth in the queue.
+ *
+ * In some contexts is it not safe to synchronously wait for
+ * close finalisation (see comment for __fput_sync()), but nfsd
+ * does not match those contexts.  In partcilarly it does not, at the
+ * time that this function is called, hold and locks and no finalisation
+ * of any file, socket, or device driver would have any cause to wait
+ * for nfsd to make progress.
+ */
+void nfsd_filp_close(struct file *fp)
+{
+       get_file(fp);
+       filp_close(fp, NULL);
+       __fput_sync(fp);
+}
+
 /*
  * Get file system stats
  * N.B. After this call fhp needs an fh_put