When nfsd starts requesting directory delegations, setlease handlers may
see requests for leases on directories. Push the !S_ISREG check down
into the non-trivial setlease handlers, so we can selectively enable
them where they're supported.

FUSE is special: It's the only filesystem that supports atomic_open and
allows kernel-internal leases. Ensure that we don't allow directory
leases by default going forward by explicitly disabling them there.

Signed-off-by: Jeff Layton <[email protected]>
---
 fs/fuse/dir.c          | 1 +
 fs/locks.c             | 5 +++--
 fs/nfs/nfs4file.c      | 2 ++
 fs/smb/client/cifsfs.c | 3 +++
 4 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 
5c569c3cb53f3d20a9f284124dee657fca5ffc9a..f5288eef5711dca46e78ef6b784ae78737e92201
 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -2237,6 +2237,7 @@ static const struct file_operations fuse_dir_operations = 
{
        .fsync          = fuse_dir_fsync,
        .unlocked_ioctl = fuse_dir_ioctl,
        .compat_ioctl   = fuse_dir_compat_ioctl,
+       .setlease       = simple_nosetlease,
 };
 
 static const struct inode_operations fuse_common_inode_operations = {
diff --git a/fs/locks.c b/fs/locks.c
index 
559f02aa4172214a14d907b7bc090c1a9235967c..edf34b9859a16c34dd75ce4d1a6a412dd426c875
 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1929,6 +1929,9 @@ static int generic_delete_lease(struct file *filp, void 
*owner)
 int generic_setlease(struct file *filp, int arg, struct file_lease **flp,
                        void **priv)
 {
+       if (!S_ISREG(file_inode(filp)->i_mode))
+               return -EINVAL;
+
        switch (arg) {
        case F_UNLCK:
                return generic_delete_lease(filp, *priv);
@@ -2018,8 +2021,6 @@ vfs_setlease(struct file *filp, int arg, struct 
file_lease **lease, void **priv)
 
        if ((!vfsuid_eq_kuid(vfsuid, current_fsuid())) && !capable(CAP_LEASE))
                return -EACCES;
-       if (!S_ISREG(inode->i_mode))
-               return -EINVAL;
        error = security_file_lock(filp, arg);
        if (error)
                return error;
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index 
c9a0d1e420c6cb17b209e3f7f48a12dd479dbc24..730e04b8a768debd34ec0c97a0cf0c44f1c18de5
 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -431,6 +431,8 @@ void nfs42_ssc_unregister_ops(void)
 static int nfs4_setlease(struct file *file, int arg, struct file_lease **lease,
                         void **priv)
 {
+       if (!S_ISREG(file_inode(file)->i_mode))
+               return -EINVAL;
        return nfs4_proc_setlease(file, arg, lease, priv);
 }
 
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 
e1848276bab41364f894f5f92d7ec08f343aeb1c..8364eed8a246bef43a10bc4078906d8a9aaf26fc
 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -1093,6 +1093,9 @@ cifs_setlease(struct file *file, int arg, struct 
file_lease **lease, void **priv
        struct inode *inode = file_inode(file);
        struct cifsFileInfo *cfile = file->private_data;
 
+       if (!S_ISREG(inode->i_mode))
+               return -EINVAL;
+
        /* Check if file is oplocked if this is request for new lease */
        if (arg == F_UNLCK ||
            ((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) ||

-- 
2.51.0


Reply via email to