Fix cifs symlink handling of "NFS" reparse point symlinks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



>From 8d615dc23963a48f8177c143f05e859aa6b549a0 Mon Sep 17 00:00:00 2001
From: Steve French <smfrench@xxxxxxxxxx>
Date: Sat, 28 Sep 2013 18:24:12 -0500
Subject: [PATCH] [CIFS] do not treat non-symlink reparse points as valid
 symlinks

Windows 8 and later can create NFS symlinks (within reparse points)
which we were assuming were normal NTFS symlinks and thus reporting
corrupt paths for.  Add check for reparse points to make sure that
they really are normal symlinks before we try to parse the pathname.

We also should not be parsing other types of reparse points (DFS
junctions etc) as if they were a  symlink so return EOPNOTSUPP
on those.  Also fix endian errors (we were not parsing symlink
lengths as little endian).  With this patch we also return EOPNOTSUPP on
the other non-symlink types of "NFS" reparse points ie those
for fifos, char and block special devices.   We can add support
for these in a followon patch.

In debugging the "NFS symlink" problem, I noticed that
although we support NTFS style file symlinks and now properly
handle "NFS" style symlinks - we don't support NTFS "directory"
symlinks (ie those created by "mklink /D" in Windows).

This patch fixes commit d244bf2dfbebfded05f494ffd53659fa7b1e32c1
which implemented follow link for non-Unix CIFS mounts

CC: Stable <stable@xxxxxxxxxx>
Reviewed-by: Andrew Bartlett <abartlet@xxxxxxxxx>
Signed-off-by: Steve French <smfrench@xxxxxxxxx>
---
 fs/cifs/cifspdu.h  | 31 +++++++++++++++++++++++--------
 fs/cifs/cifssmb.c  | 40 ++++++++++++++++++++++++++++++++++------
 fs/cifs/smbfsctl.h | 14 ++++++++++++++
 3 files changed, 71 insertions(+), 14 deletions(-)

diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index a630475..08f9dfb 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -1491,15 +1491,30 @@ struct file_notify_information {
     __u8  FileName[0];
 } __attribute__((packed));

-struct reparse_data {
-    __u32    ReparseTag;
-    __u16    ReparseDataLength;
+/* For IO_REPARSE_TAG_SYMLINK */
+struct reparse_symlink_data {
+    __le32    ReparseTag;
+    __le16    ReparseDataLength;
     __u16    Reserved;
-    __u16    SubstituteNameOffset;
-    __u16    SubstituteNameLength;
-    __u16    PrintNameOffset;
-    __u16    PrintNameLength;
-    __u32    Flags;
+    __le16    SubstituteNameOffset;
+    __le16    SubstituteNameLength;
+    __le16    PrintNameOffset;
+    __le16    PrintNameLength;
+    __le32    Flags;
+    char    PathBuffer[0];
+} __attribute__((packed));
+
+/* For IO_REPARSE_TAG_NFS */
+#define NFS_SPECFILE_LNK    0x00000000014B4E4C
+#define NFS_SPECFILE_CHR    0x0000000000524843
+#define NFS_SPECFILE_BLK    0x00000000004B4C42
+#define NFS_SPECFILE_FIFO    0x000000004F464946
+#define NFS_SPECFILE_SOCK    0x000000004B434F53
+struct reparse_posix_data {
+    __le32    ReparseTag;
+    __le16    ReparseDataLength;
+    __u16    Reserved;
+    __le64    InodeType; /* LNK, FIFO, CHR etc. */
     char    PathBuffer[0];
 } __attribute__((packed));

diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 4baf359..ccd31ab 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -3088,7 +3088,8 @@ CIFSSMBQuerySymLink(const unsigned int xid,
struct cifs_tcon *tcon,
     bool is_unicode;
     unsigned int sub_len;
     char *sub_start;
-    struct reparse_data *reparse_buf;
+    struct reparse_symlink_data *reparse_buf;
+    struct reparse_posix_data *posix_buf;
     __u32 data_offset, data_count;
     char *end_of_smb;

@@ -3137,20 +3138,47 @@ CIFSSMBQuerySymLink(const unsigned int xid,
struct cifs_tcon *tcon,
         goto qreparse_out;
     }
     end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount;
-    reparse_buf = (struct reparse_data *)
+    reparse_buf = (struct reparse_symlink_data *)
                 ((char *)&pSMBr->hdr.Protocol + data_offset);
     if ((char *)reparse_buf >= end_of_smb) {
         rc = -EIO;
         goto qreparse_out;
     }
-    if ((reparse_buf->PathBuffer + reparse_buf->PrintNameOffset +
-                reparse_buf->PrintNameLength) > end_of_smb) {
+    if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) {
+        cifs_dbg(FYI, "NFS style reparse tag\n");
+        posix_buf =  (struct reparse_posix_data *)reparse_buf;
+
+        if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) {
+            cifs_dbg(FYI, "unsupported file type 0x%llx\n",
+                 le64_to_cpu(posix_buf->InodeType));
+            rc = -EOPNOTSUPP;
+            goto qreparse_out;
+        }
+        is_unicode = true;
+        sub_len = le16_to_cpu(reparse_buf->ReparseDataLength);
+        if (posix_buf->PathBuffer + sub_len > end_of_smb) {
+            cifs_dbg(FYI, "reparse buf beyond SMB\n");
+            rc = -EIO;
+            goto qreparse_out;
+        }
+        *symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer,
+                sub_len, is_unicode, nls_codepage);
+        goto qreparse_out;
+    } else if (reparse_buf->ReparseTag !=
+            cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) {
+        rc = -EOPNOTSUPP;
+        goto qreparse_out;
+    }
+
+    /* Reparse tag is NTFS symlink */
+    sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) +
+                reparse_buf->PathBuffer;
+    sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength);
+    if (sub_start + sub_len > end_of_smb) {
         cifs_dbg(FYI, "reparse buf beyond SMB\n");
         rc = -EIO;
         goto qreparse_out;
     }
-    sub_start = reparse_buf->SubstituteNameOffset + reparse_buf->PathBuffer;
-    sub_len = reparse_buf->SubstituteNameLength;
     if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
         is_unicode = true;
     else
diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h
index d952ee4..a4b2391f 100644
--- a/fs/cifs/smbfsctl.h
+++ b/fs/cifs/smbfsctl.h
@@ -97,9 +97,23 @@
 #define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC /* BB add struct */
 #define FSCTL_SRV_READ_HASH          0x001441BB /* BB add struct */

+/* See FSCC 2.1.2.5 */
 #define IO_REPARSE_TAG_MOUNT_POINT   0xA0000003
 #define IO_REPARSE_TAG_HSM           0xC0000004
 #define IO_REPARSE_TAG_SIS           0x80000007
+#define IO_REPARSE_TAG_HSM2          0x80000006
+#define IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005
+/* Used by the DFS filter. See MS-DFSC */
+#define IO_REPARSE_TAG_DFS           0x8000000A
+/* Used by the DFS filter See MS-DFSC */
+#define IO_REPARSE_TAG_DFSR          0x80000012
+#define IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B
+/* See section MS-FSCC 2.1.2.4 */
+#define IO_REPARSE_TAG_SYMLINK       0xA000000C
+#define IO_REPARSE_TAG_DEDUP         0x80000013
+#define IO_REPARSE_APPXSTREAM         0xC0000014
+/* NFS symlinks, Win 8/SMB3 and later */
+#define IO_REPARSE_TAG_NFS           0x80000014

 /* fsctl flags */
 /* If Flags is set to this value, the request is an FSCTL not ioctl request */
-- 
1.8.1.2


-- 
Thanks,

Steve

Attachment: 0001-CIFS-do-not-treat-non-symlink-reparse-points-as-vali.patch
Description: Binary data


[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux