If the client requested dir attribute change notifications, send those
alongside any set of add/remove/rename events. Note that the server will
still recall the delegation on a SETATTR, so these are only sent for
changes to child entries.

Signed-off-by: Jeff Layton <[email protected]>
---
 fs/nfsd/nfs4state.c | 25 ++++++++++++++++++++--
 fs/nfsd/nfs4xdr.c   | 60 +++++++++++++++++++++++++++++++++++++++++++++--------
 fs/nfsd/xdr4.h      |  1 +
 3 files changed, 75 insertions(+), 11 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 
2381dbb2e48290debf28bbd35d0b9a4bb677ac07..a411ffb5f208a2e4d9b55dd77226b4e1a24eaee2
 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3342,9 +3342,14 @@ nfsd4_cb_notify_prepare(struct nfsd4_callback *cb)
        struct nfsd_notify_event *events[NOTIFY4_EVENT_QUEUE_SIZE];
        struct xdr_buf xdr = { .buflen = PAGE_SIZE * NOTIFY4_PAGE_ARRAY_SIZE,
                               .pages  = ncn->ncn_pages };
+       int limit = NOTIFY4_EVENT_QUEUE_SIZE;
        struct xdr_stream stream;
-       int count, i;
        bool error = false;
+       int count, i;
+
+       /* Save a slot for dir attr update if requested */
+       if (dp->dl_notify_mask & BIT(NOTIFY4_CHANGE_DIR_ATTRS))
+               --limit;
 
        xdr_init_encode_pages(&stream, &xdr);
 
@@ -3358,7 +3363,7 @@ nfsd4_cb_notify_prepare(struct nfsd4_callback *cb)
        }
 
        /* we can't keep up! */
-       if (count > NOTIFY4_EVENT_QUEUE_SIZE) {
+       if (count > limit) {
                spin_unlock(&ncn->ncn_lock);
                goto out_recall;
        }
@@ -3396,6 +3401,22 @@ nfsd4_cb_notify_prepare(struct nfsd4_callback *cb)
                nfsd_notify_event_put(nne);
        }
        if (!error) {
+               if (dp->dl_notify_mask & BIT(NOTIFY4_CHANGE_DIR_ATTRS)) {
+                       u32 *maskp = (u32 *)xdr_reserve_space(&stream, 
sizeof(*maskp));
+
+                       if (maskp) {
+                               u8 *p = nfsd4_encode_dir_attr_change(&stream, 
dp);
+
+                               if (p) {
+                                       *maskp = BIT(NOTIFY4_CHANGE_DIR_ATTRS);
+                                       ncn->ncn_nf[count].notify_mask.count = 
1;
+                                       ncn->ncn_nf[count].notify_mask.element 
= maskp;
+                                       ncn->ncn_nf[count].notify_vals.data = p;
+                                       ncn->ncn_nf[count].notify_vals.len = 
(u8 *)stream.p - p;
+                                       ++count;
+                               }
+                       }
+               }
                ncn->ncn_nf_cnt = count;
                return true;
        }
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 
0c411c758279177837c078d393048aaebf31d46f..eca2b483e28397166d24756fcc97b5a1e0fdb9aa
 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3806,11 +3806,11 @@ nfsd4_setup_notify_entry4(struct notify_entry4 *ne, 
struct xdr_stream *xdr,
                          char *name, u32 namelen)
 {
        struct nfs4_file *fi = dp->dl_stid.sc_file;
-       struct path path =  { .mnt = fi->fi_deleg_file->nf_file->f_path.mnt,
-                             .dentry = dentry };
+       struct path path = fi->fi_deleg_file->nf_file->f_path;
        struct nfsd4_fattr_args args = { };
        uint32_t *attrmask;
        __be32 status;
+       bool parent;
        int ret;
 
        /* Reserve space for attrmask */
@@ -3822,6 +3822,9 @@ nfsd4_setup_notify_entry4(struct notify_entry4 *ne, 
struct xdr_stream *xdr,
        ne->ne_file.len = namelen;
        ne->ne_attrs.attrmask.element = attrmask;
 
+       parent = (dentry == path.dentry);
+       path.dentry = dentry;
+
        /* FIXME: d_find_alias for inode ? */
        if (!path.dentry || !d_inode(path.dentry))
                goto noattrs;
@@ -3837,15 +3840,20 @@ nfsd4_setup_notify_entry4(struct notify_entry4 *ne, 
struct xdr_stream *xdr,
 
        args.change_attr = nfsd4_change_attribute(&args.stat);
 
-       attrmask[0] = dp->dl_child_attrs[0];
-       attrmask[1] = dp->dl_child_attrs[1];
-       attrmask[2] = 0;
+       if (parent) {
+               attrmask[0] = dp->dl_dir_attrs[0];
+               attrmask[1] = dp->dl_dir_attrs[1];
+       } else {
+               attrmask[0] = dp->dl_child_attrs[0];
+               attrmask[1] = dp->dl_child_attrs[1];
 
-       if (!setup_notify_fhandle(dentry, fi, &args))
-               attrmask[0] &= ~FATTR4_WORD0_FILEHANDLE;
+               if (!setup_notify_fhandle(dentry, fi, &args))
+                       attrmask[0] &= ~FATTR4_WORD0_FILEHANDLE;
 
-       if (!(args.stat.result_mask & STATX_BTIME))
-               attrmask[1] &= ~FATTR4_WORD1_TIME_CREATE;
+               if (!(args.stat.result_mask & STATX_BTIME))
+                       attrmask[1] &= ~FATTR4_WORD1_TIME_CREATE;
+       }
+       attrmask[2] = 0;
 
        ne->ne_attrs.attrmask.count = 2;
        ne->ne_attrs.attr_vals.data = (u8 *)xdr->p;
@@ -3936,6 +3944,40 @@ u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, 
struct nfsd_notify_event *
        return NULL;
 }
 
+/**
+ * nfsd4_encode_dir_attr_change
+ * @xdr: stream to which to encode the fattr4
+ * @dp: delegation where the event occurred
+ *
+ * Encode a dir attr change event.
+ */
+u8 *nfsd4_encode_dir_attr_change(struct xdr_stream *xdr, struct 
nfs4_delegation *dp)
+{
+       struct nfs4_file *fi = dp->dl_stid.sc_file;
+       struct dentry *dentry = fi->fi_deleg_file->nf_file->f_path.dentry;
+       struct notify_attr4 na = { };
+       struct name_snapshot n;
+       bool ret;
+       u8 *p = NULL;
+
+       if (!(dp->dl_notify_mask & BIT(NOTIFY4_CHANGE_DIR_ATTRS)))
+               return NULL;
+
+       take_dentry_name_snapshot(&n, dentry);
+       ret = nfsd4_setup_notify_entry4(&na.na_changed_entry, xdr,
+                                       dentry, dp, (char *)n.name.name,
+                                       n.name.len);
+
+       /* Don't bother with the event if we're not encoding attrs */
+       if (ret && na.na_changed_entry.ne_attrs.attr_vals.len) {
+               p = (u8 *)xdr->p;
+               if (!xdrgen_encode_notify_attr4(xdr, &na))
+                       p = NULL;
+       }
+       release_dentry_name_snapshot(&n);
+       return p;
+}
+
 static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
                                struct xdr_buf *buf, __be32 *p, int bytes)
 {
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 
19f468b5bc54343dca928f0b8286c868f2133241..93a38b104fb425413f3d7a59fa029c760618559c
 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -969,6 +969,7 @@ __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
                u32 *bmval, struct svc_rqst *, int ignore_crossmnt);
 u8 *nfsd4_encode_notify_event(struct xdr_stream *xdr, struct nfsd_notify_event 
*nne,
                              struct nfs4_delegation *dd, u32 *notify_mask);
+u8 *nfsd4_encode_dir_attr_change(struct xdr_stream *xdr, struct 
nfs4_delegation *dp);
 extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp,
                struct nfsd4_compound_state *, union nfsd4_op_u *u);
 extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp,

-- 
2.51.0


Reply via email to