]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
nfsd: allow filecache to hold S_IFDIR files
authorJeff Layton <jlayton@kernel.org>
Tue, 11 Nov 2025 14:12:55 +0000 (09:12 -0500)
committerChristian Brauner <brauner@kernel.org>
Wed, 12 Nov 2025 08:38:36 +0000 (09:38 +0100)
The filecache infrastructure will only handle S_IFREG files at the
moment. Directory delegations will require adding support for opening
S_IFDIR inodes.

Plumb a "type" argument into nfsd_file_do_acquire() and have all of the
existing callers set it to S_IFREG. Add a new nfsd_file_acquire_dir()
wrapper that nfsd can call to request a nfsd_file that holds a directory
open.

For now, there is no need for a fsnotify_mark for directories, as
CB_NOTIFY is not yet supported. Change nfsd_file_do_acquire() to avoid
allocating one for non-S_IFREG inodes.

Reviewed-by: Chuck Lever <chuck.lever@oracle.com>
Reviewed-by: NeilBrown <neil@brown.name>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20251111-dir-deleg-ro-v6-14-52f3feebb2f2@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/nfsd/filecache.c
fs/nfsd/filecache.h
fs/nfsd/vfs.c
fs/nfsd/vfs.h

index a238b6725008a5c2988bd3da874d1f34ee778437..93798575b8075c63f95cd415b6d24df706ada0f6 100644 (file)
@@ -1086,7 +1086,7 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net,
                     struct auth_domain *client,
                     struct svc_fh *fhp,
                     unsigned int may_flags, struct file *file,
-                    struct nfsd_file **pnf, bool want_gc)
+                    umode_t type, bool want_gc, struct nfsd_file **pnf)
 {
        unsigned char need = may_flags & NFSD_FILE_MAY_MASK;
        struct nfsd_file *new, *nf;
@@ -1097,13 +1097,13 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net,
        int ret;
 
 retry:
-       if (rqstp) {
-               status = fh_verify(rqstp, fhp, S_IFREG,
+       if (rqstp)
+               status = fh_verify(rqstp, fhp, type,
                                   may_flags|NFSD_MAY_OWNER_OVERRIDE);
-       } else {
-               status = fh_verify_local(net, cred, client, fhp, S_IFREG,
+       else
+               status = fh_verify_local(net, cred, client, fhp, type,
                                         may_flags|NFSD_MAY_OWNER_OVERRIDE);
-       }
+
        if (status != nfs_ok)
                return status;
        inode = d_inode(fhp->fh_dentry);
@@ -1176,15 +1176,18 @@ out:
 
 open_file:
        trace_nfsd_file_alloc(nf);
-       nf->nf_mark = nfsd_file_mark_find_or_create(inode);
-       if (nf->nf_mark) {
+
+       if (type == S_IFREG)
+               nf->nf_mark = nfsd_file_mark_find_or_create(inode);
+
+       if (type != S_IFREG || nf->nf_mark) {
                if (file) {
                        get_file(file);
                        nf->nf_file = file;
                        status = nfs_ok;
                        trace_nfsd_file_opened(nf, status);
                } else {
-                       ret = nfsd_open_verified(fhp, may_flags, &nf->nf_file);
+                       ret = nfsd_open_verified(fhp, type, may_flags, &nf->nf_file);
                        if (ret == -EOPENSTALE && stale_retry) {
                                stale_retry = false;
                                nfsd_file_unhash(nf);
@@ -1246,7 +1249,7 @@ nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
                     unsigned int may_flags, struct nfsd_file **pnf)
 {
        return nfsd_file_do_acquire(rqstp, SVC_NET(rqstp), NULL, NULL,
-                                   fhp, may_flags, NULL, pnf, true);
+                                   fhp, may_flags, NULL, S_IFREG, true, pnf);
 }
 
 /**
@@ -1271,7 +1274,7 @@ nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
                  unsigned int may_flags, struct nfsd_file **pnf)
 {
        return nfsd_file_do_acquire(rqstp, SVC_NET(rqstp), NULL, NULL,
-                                   fhp, may_flags, NULL, pnf, false);
+                                   fhp, may_flags, NULL, S_IFREG, false, pnf);
 }
 
 /**
@@ -1314,8 +1317,8 @@ nfsd_file_acquire_local(struct net *net, struct svc_cred *cred,
        const struct cred *save_cred = get_current_cred();
        __be32 beres;
 
-       beres = nfsd_file_do_acquire(NULL, net, cred, client,
-                                    fhp, may_flags, NULL, pnf, false);
+       beres = nfsd_file_do_acquire(NULL, net, cred, client, fhp, may_flags,
+                                    NULL, S_IFREG, false, pnf);
        put_cred(revert_creds(save_cred));
        return beres;
 }
@@ -1344,7 +1347,33 @@ nfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp,
                         struct nfsd_file **pnf)
 {
        return nfsd_file_do_acquire(rqstp, SVC_NET(rqstp), NULL, NULL,
-                                   fhp, may_flags, file, pnf, false);
+                                   fhp, may_flags, file, S_IFREG, false, pnf);
+}
+
+/**
+ * nfsd_file_acquire_dir - Get a struct nfsd_file with an open directory
+ * @rqstp: the RPC transaction being executed
+ * @fhp: the NFS filehandle of the file to be opened
+ * @pnf: OUT: new or found "struct nfsd_file" object
+ *
+ * The nfsd_file_object returned by this API is reference-counted
+ * but not garbage-collected. The object is unhashed after the
+ * final nfsd_file_put(). This opens directories only, and only
+ * in O_RDONLY mode.
+ *
+ * Return values:
+ *   %nfs_ok - @pnf points to an nfsd_file with its reference
+ *   count boosted.
+ *
+ * On error, an nfsstat value in network byte order is returned.
+ */
+__be32
+nfsd_file_acquire_dir(struct svc_rqst *rqstp, struct svc_fh *fhp,
+                     struct nfsd_file **pnf)
+{
+       return nfsd_file_do_acquire(rqstp, SVC_NET(rqstp), NULL, NULL, fhp,
+                                   NFSD_MAY_READ|NFSD_MAY_64BIT_COOKIE,
+                                   NULL, S_IFDIR, false, pnf);
 }
 
 /*
index e3d6ca2b60308e5e91ba4bb32d935f54527d8bda..b383dbc5b9218d21a29b852572f80fab08de9fa9 100644 (file)
@@ -82,5 +82,7 @@ __be32 nfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp,
 __be32 nfsd_file_acquire_local(struct net *net, struct svc_cred *cred,
                               struct auth_domain *client, struct svc_fh *fhp,
                               unsigned int may_flags, struct nfsd_file **pnf);
+__be32 nfsd_file_acquire_dir(struct svc_rqst *rqstp, struct svc_fh *fhp,
+                 struct nfsd_file **pnf);
 int nfsd_file_cache_stats_show(struct seq_file *m, void *v);
 #endif /* _FS_NFSD_FILECACHE_H */
index 28710da4cce7cc7fc1e14d29420239dc357316f6..8c3ffacc533e9de0d506fb2ec222387446ba8e9f 100644 (file)
@@ -959,15 +959,16 @@ retry:
 /**
  * nfsd_open_verified - Open a regular file for the filecache
  * @fhp: NFS filehandle of the file to open
+ * @type: S_IFMT inode type allowed (0 means any type is allowed)
  * @may_flags: internal permission flags
  * @filp: OUT: open "struct file *"
  *
  * Returns zero on success, or a negative errno value.
  */
 int
-nfsd_open_verified(struct svc_fh *fhp, int may_flags, struct file **filp)
+nfsd_open_verified(struct svc_fh *fhp, umode_t type, int may_flags, struct file **filp)
 {
-       return __nfsd_open(fhp, S_IFREG, may_flags, filp);
+       return __nfsd_open(fhp, type, may_flags, filp);
 }
 
 /*
index 0c0292611c6de3daf6f3ed51e2c61c0ad2751de4..09de48c50cbef8e7c4828b38dcb663b529514a30 100644 (file)
@@ -114,7 +114,7 @@ __be32              nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
 int            nfsd_open_break_lease(struct inode *, int);
 __be32         nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
                                int, struct file **);
-int            nfsd_open_verified(struct svc_fh *fhp, int may_flags,
+int            nfsd_open_verified(struct svc_fh *fhp, umode_t type, int may_flags,
                                struct file **filp);
 __be32         nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
                                struct file *file, loff_t offset,