cifs: return ENAMETOOLONG for overlong names in cifs_open()/cifs_lookup()
authorRonnie Sahlberg <lsahlber@redhat.com>
Wed, 23 Aug 2017 04:48:14 +0000 (14:48 +1000)
committerSteve French <smfrench@gmail.com>
Wed, 23 Aug 2017 18:34:52 +0000 (13:34 -0500)
Add checking for the path component length and verify it is <= the maximum
that the server advertizes via FileFsAttributeInformation.

With this patch cifs.ko will now return ENAMETOOLONG instead of ENOENT
when users to access an overlong path.

To test this, try to cd into a (non-existing) directory on a CIFS share
that has a too long name:
cd /mnt/aaaaaaaaaaaaaaa...

and it now should show a good error message from the shell:
bash: cd: /mnt/aaaaaaaaaaaaaaaa...aaaaaa: File name too long

rh bz 1153996

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>
Cc: <stable@vger.kernel.org>
fs/cifs/dir.c

index 56366e9840769dd8a45250ec3a7b65097c979ff9..569d3fb736be070d2693f600f867bd836aeb1dc4 100644 (file)
@@ -194,15 +194,20 @@ cifs_bp_rename_retry:
 }
 
 /*
+ * Don't allow path components longer than the server max.
  * Don't allow the separator character in a path component.
  * The VFS will not allow "/", but "\" is allowed by posix.
  */
 static int
-check_name(struct dentry *direntry)
+check_name(struct dentry *direntry, struct cifs_tcon *tcon)
 {
        struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
        int i;
 
+       if (unlikely(direntry->d_name.len >
+                    tcon->fsAttrInfo.MaxPathNameComponentLength))
+               return -ENAMETOOLONG;
+
        if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
                for (i = 0; i < direntry->d_name.len; i++) {
                        if (direntry->d_name.name[i] == '\\') {
@@ -500,10 +505,6 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
                return finish_no_open(file, res);
        }
 
-       rc = check_name(direntry);
-       if (rc)
-               return rc;
-
        xid = get_xid();
 
        cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
@@ -516,6 +517,11 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
        }
 
        tcon = tlink_tcon(tlink);
+
+       rc = check_name(direntry, tcon);
+       if (rc)
+               goto out_free_xid;
+
        server = tcon->ses->server;
 
        if (server->ops->new_lease_key)
@@ -776,7 +782,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
        }
        pTcon = tlink_tcon(tlink);
 
-       rc = check_name(direntry);
+       rc = check_name(direntry, pTcon);
        if (rc)
                goto lookup_out;