Merge branch 'for-linus' of git://git.samba.org/sfrench/cifs-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 18 Oct 2014 20:39:19 +0000 (13:39 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 18 Oct 2014 20:39:19 +0000 (13:39 -0700)
Pull cifs/smb3 updates from Steve French:
 "Improved SMB3 support (symlink and device emulation, and remapping by
  default the 7 reserved posix characters) and a workaround for cifs
  mounts to Mac (working around a commonly encountered Mac server bug)"

* 'for-linus' of git://git.samba.org/sfrench/cifs-2.6:
  [CIFS] Remove obsolete comment
  Check minimum response length on query_network_interface
  Workaround Mac server problem
  Remap reserved posix characters by default (part 3/3)
  Allow conversion of characters in Mac remap range (part 2)
  Allow conversion of characters in Mac remap range. Part 1
  mfsymlinks support for SMB2.1/SMB3. Part 2 query symlink
  Add mfsymlinks support for SMB2.1/SMB3. Part 1 create symlink
  Allow mknod and mkfifo on SMB2/SMB3 mounts
  add defines for two new file attributes

20 files changed:
fs/cifs/cifs_fs_sb.h
fs/cifs/cifs_unicode.c
fs/cifs/cifs_unicode.h
fs/cifs/cifsencrypt.c
fs/cifs/cifsglob.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/dir.c
fs/cifs/file.c
fs/cifs/inode.c
fs/cifs/link.c
fs/cifs/readdir.c
fs/cifs/smb1ops.c
fs/cifs/smb2misc.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/cifs/smb2pdu.h
fs/cifs/smb2proto.h
fs/cifs/smbencrypt.c
fs/cifs/xattr.c

index 9409fa10bd5cd9063159c1d3d49ca1cb8f422817..3182273a34079e2e7768ec819b39a41006c2e9c9 100644 (file)
@@ -45,6 +45,7 @@
 #define CIFS_MOUNT_POSIXACL    0x100000 /* mirror of MS_POSIXACL in mnt_cifs_flags */
 #define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */
 #define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */
+#define CIFS_MOUNT_MAP_SFM_CHR 0x800000 /* SFM/MAC mapping for illegal chars */
 
 struct cifs_sb_info {
        struct rb_root tlink_tree;
index 15e9505aa35f22a7f46e2dbd4f509000f9f1b472..0303c6793d903ab07cb7d1829bc372f0be3fc15f 100644 (file)
@@ -20,6 +20,7 @@
  */
 #include <linux/fs.h>
 #include <linux/slab.h>
+#include "cifs_fs_sb.h"
 #include "cifs_unicode.h"
 #include "cifs_uniupr.h"
 #include "cifspdu.h"
@@ -61,26 +62,24 @@ cifs_utf16_bytes(const __le16 *from, int maxbytes,
        return outlen;
 }
 
-/*
- * cifs_mapchar - convert a host-endian char to proper char in codepage
- * @target - where converted character should be copied
- * @src_char - 2 byte host-endian source character
- * @cp - codepage to which character should be converted
- * @mapchar - should character be mapped according to mapchars mount option?
- *
- * This function handles the conversion of a single character. It is the
- * responsibility of the caller to ensure that the target buffer is large
- * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE).
- */
-static int
-cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
-            bool mapchar)
+int cifs_remap(struct cifs_sb_info *cifs_sb)
 {
-       int len = 1;
+       int map_type;
+
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR)
+               map_type = SFM_MAP_UNI_RSVD;
+       else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
+               map_type = SFU_MAP_UNI_RSVD;
+       else
+               map_type = NO_MAP_UNI_RSVD;
 
-       if (!mapchar)
-               goto cp_convert;
+       return map_type;
+}
 
+/* Convert character using the SFU - "Services for Unix" remapping range */
+static bool
+convert_sfu_char(const __u16 src_char, char *target)
+{
        /*
         * BB: Cannot handle remapping UNI_SLASH until all the calls to
         *     build_path_from_dentry are modified, as they use slash as
@@ -106,19 +105,74 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
                *target = '<';
                break;
        default:
-               goto cp_convert;
+               return false;
        }
+       return true;
+}
+
+/* Convert character using the SFM - "Services for Mac" remapping range */
+static bool
+convert_sfm_char(const __u16 src_char, char *target)
+{
+       switch (src_char) {
+       case SFM_COLON:
+               *target = ':';
+               break;
+       case SFM_ASTERISK:
+               *target = '*';
+               break;
+       case SFM_QUESTION:
+               *target = '?';
+               break;
+       case SFM_PIPE:
+               *target = '|';
+               break;
+       case SFM_GRTRTHAN:
+               *target = '>';
+               break;
+       case SFM_LESSTHAN:
+               *target = '<';
+               break;
+       case SFM_SLASH:
+               *target = '\\';
+               break;
+       default:
+               return false;
+       }
+       return true;
+}
 
-out:
-       return len;
 
-cp_convert:
+/*
+ * cifs_mapchar - convert a host-endian char to proper char in codepage
+ * @target - where converted character should be copied
+ * @src_char - 2 byte host-endian source character
+ * @cp - codepage to which character should be converted
+ * @map_type - How should the 7 NTFS/SMB reserved characters be mapped to UCS2?
+ *
+ * This function handles the conversion of a single character. It is the
+ * responsibility of the caller to ensure that the target buffer is large
+ * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE).
+ */
+static int
+cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
+            int maptype)
+{
+       int len = 1;
+
+       if ((maptype == SFM_MAP_UNI_RSVD) && convert_sfm_char(src_char, target))
+               return len;
+       else if ((maptype == SFU_MAP_UNI_RSVD) &&
+                 convert_sfu_char(src_char, target))
+               return len;
+
+       /* if character not one of seven in special remap set */
        len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);
        if (len <= 0) {
                *target = '?';
                len = 1;
        }
-       goto out;
+       return len;
 }
 
 /*
@@ -145,7 +199,7 @@ cp_convert:
  */
 int
 cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
-                const struct nls_table *codepage, bool mapchar)
+               const struct nls_table *codepage, int map_type)
 {
        int i, charlen, safelen;
        int outlen = 0;
@@ -172,13 +226,13 @@ cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
                 * conversion bleed into the null terminator
                 */
                if (outlen >= safelen) {
-                       charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar);
+                       charlen = cifs_mapchar(tmp, ftmp, codepage, map_type);
                        if ((outlen + charlen) > (tolen - nullsize))
                                break;
                }
 
                /* put converted char into 'to' buffer */
-               charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar);
+               charlen = cifs_mapchar(&to[outlen], ftmp, codepage, map_type);
                outlen += charlen;
        }
 
@@ -267,7 +321,7 @@ cifs_strndup_from_utf16(const char *src, const int maxlen,
                if (!dst)
                        return NULL;
                cifs_from_utf16(dst, (__le16 *) src, len, maxlen, codepage,
-                              false);
+                              NO_MAP_UNI_RSVD);
        } else {
                len = strnlen(src, maxlen);
                len++;
@@ -280,6 +334,66 @@ cifs_strndup_from_utf16(const char *src, const int maxlen,
        return dst;
 }
 
+static __le16 convert_to_sfu_char(char src_char)
+{
+       __le16 dest_char;
+
+       switch (src_char) {
+       case ':':
+               dest_char = cpu_to_le16(UNI_COLON);
+               break;
+       case '*':
+               dest_char = cpu_to_le16(UNI_ASTERISK);
+               break;
+       case '?':
+               dest_char = cpu_to_le16(UNI_QUESTION);
+               break;
+       case '<':
+               dest_char = cpu_to_le16(UNI_LESSTHAN);
+               break;
+       case '>':
+               dest_char = cpu_to_le16(UNI_GRTRTHAN);
+               break;
+       case '|':
+               dest_char = cpu_to_le16(UNI_PIPE);
+               break;
+       default:
+               dest_char = 0;
+       }
+
+       return dest_char;
+}
+
+static __le16 convert_to_sfm_char(char src_char)
+{
+       __le16 dest_char;
+
+       switch (src_char) {
+       case ':':
+               dest_char = cpu_to_le16(SFM_COLON);
+               break;
+       case '*':
+               dest_char = cpu_to_le16(SFM_ASTERISK);
+               break;
+       case '?':
+               dest_char = cpu_to_le16(SFM_QUESTION);
+               break;
+       case '<':
+               dest_char = cpu_to_le16(SFM_LESSTHAN);
+               break;
+       case '>':
+               dest_char = cpu_to_le16(SFM_GRTRTHAN);
+               break;
+       case '|':
+               dest_char = cpu_to_le16(SFM_PIPE);
+               break;
+       default:
+               dest_char = 0;
+       }
+
+       return dest_char;
+}
+
 /*
  * Convert 16 bit Unicode pathname to wire format from string in current code
  * page. Conversion may involve remapping up the six characters that are
@@ -288,7 +402,7 @@ cifs_strndup_from_utf16(const char *src, const int maxlen,
  */
 int
 cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
-                const struct nls_table *cp, int mapChars)
+                const struct nls_table *cp, int map_chars)
 {
        int i, charlen;
        int j = 0;
@@ -296,39 +410,30 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
        __le16 dst_char;
        wchar_t tmp;
 
-       if (!mapChars)
+       if (map_chars == NO_MAP_UNI_RSVD)
                return cifs_strtoUTF16(target, source, PATH_MAX, cp);
 
        for (i = 0; i < srclen; j++) {
                src_char = source[i];
                charlen = 1;
-               switch (src_char) {
-               case 0:
+
+               /* check if end of string */
+               if (src_char == 0)
                        goto ctoUTF16_out;
-               case ':':
-                       dst_char = cpu_to_le16(UNI_COLON);
-                       break;
-               case '*':
-                       dst_char = cpu_to_le16(UNI_ASTERISK);
-                       break;
-               case '?':
-                       dst_char = cpu_to_le16(UNI_QUESTION);
-                       break;
-               case '<':
-                       dst_char = cpu_to_le16(UNI_LESSTHAN);
-                       break;
-               case '>':
-                       dst_char = cpu_to_le16(UNI_GRTRTHAN);
-                       break;
-               case '|':
-                       dst_char = cpu_to_le16(UNI_PIPE);
-                       break;
+
+               /* see if we must remap this char */
+               if (map_chars == SFU_MAP_UNI_RSVD)
+                       dst_char = convert_to_sfu_char(src_char);
+               else if (map_chars == SFM_MAP_UNI_RSVD)
+                       dst_char = convert_to_sfm_char(src_char);
+               else
+                       dst_char = 0;
                /*
                 * FIXME: We can not handle remapping backslash (UNI_SLASH)
                 * until all the calls to build_path_from_dentry are modified,
                 * as they use backslash as separator.
                 */
-               default:
+               if (dst_char == 0) {
                        charlen = cp->char2uni(source + i, srclen - i, &tmp);
                        dst_char = cpu_to_le16(tmp);
 
index d8eac3b6cefb35639000ff74367129fbc72f37dc..bdc52cb9a676db0a9041d7e198810ce8df2aca9a 100644 (file)
 #define UNI_PIPE        (__u16) ('|' + 0xF000)
 #define UNI_SLASH       (__u16) ('\\' + 0xF000)
 
+/*
+ * Macs use an older "SFM" mapping of the symbols above. Fortunately it does
+ * not conflict (although almost does) with the mapping above.
+ */
+
+#define SFM_ASTERISK    ((__u16) 0xF021)
+#define SFM_QUESTION    ((__u16) 0xF025)
+#define SFM_COLON       ((__u16) 0xF022)
+#define SFM_GRTRTHAN    ((__u16) 0xF024)
+#define SFM_LESSTHAN    ((__u16) 0xF023)
+#define SFM_PIPE        ((__u16) 0xF027)
+#define SFM_SLASH       ((__u16) 0xF026)
+
+/*
+ * Mapping mechanism to use when one of the seven reserved characters is
+ * encountered.  We can only map using one of the mechanisms at a time
+ * since otherwise readdir could return directory entries which we would
+ * not be able to open
+ *
+ * NO_MAP_UNI_RSVD  = do not perform any remapping of the character
+ * SFM_MAP_UNI_RSVD = map reserved characters using SFM scheme (MAC compatible)
+ * SFU_MAP_UNI_RSVD = map reserved characters ala SFU ("mapchars" option)
+ *
+ */
+#define NO_MAP_UNI_RSVD                0
+#define SFM_MAP_UNI_RSVD       1
+#define SFU_MAP_UNI_RSVD       2
+
 /* Just define what we want from uniupr.h.  We don't want to define the tables
  * in each source file.
  */
@@ -75,7 +103,7 @@ extern const struct UniCaseRange CifsUniLowerRange[];
 
 #ifdef __KERNEL__
 int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
-                   const struct nls_table *codepage, bool mapchar);
+                   const struct nls_table *cp, int map_type);
 int cifs_utf16_bytes(const __le16 *from, int maxbytes,
                     const struct nls_table *codepage);
 int cifs_strtoUTF16(__le16 *, const char *, int, const struct nls_table *);
@@ -84,6 +112,7 @@ char *cifs_strndup_from_utf16(const char *src, const int maxlen,
                              const struct nls_table *codepage);
 extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen,
                              const struct nls_table *cp, int mapChars);
+extern int cifs_remap(struct cifs_sb_info *cifs_sb);
 #ifdef CONFIG_CIFS_SMB2
 extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen,
                                     int *utf16_len, const struct nls_table *cp,
index 4934347321d379b61aa170d0f93dd4468c383000..4ac7445e6ec70516848e942f54a6846a8541113b 100644 (file)
@@ -431,7 +431,7 @@ find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp)
                                                return -ENOMEM;
                                cifs_from_utf16(ses->domainName,
                                        (__le16 *)blobptr, attrsize, attrsize,
-                                       nls_cp, false);
+                                       nls_cp, NO_MAP_UNI_RSVD);
                                break;
                        }
                }
index 25b8392bfdd2a7fd9864af5008e809dc41e3a7df..02a33e5299042079a6121a638d034c1139d88677 100644 (file)
@@ -323,11 +323,11 @@ struct smb_version_operations {
        int (*async_writev)(struct cifs_writedata *,
                            void (*release)(struct kref *));
        /* sync read from the server */
-       int (*sync_read)(const unsigned int, struct cifsFileInfo *,
+       int (*sync_read)(const unsigned int, struct cifs_fid *,
                         struct cifs_io_parms *, unsigned int *, char **,
                         int *);
        /* sync write to the server */
-       int (*sync_write)(const unsigned int, struct cifsFileInfo *,
+       int (*sync_write)(const unsigned int, struct cifs_fid *,
                          struct cifs_io_parms *, unsigned int *, struct kvec *,
                          unsigned long);
        /* open dir, start readdir */
@@ -466,6 +466,7 @@ struct smb_vol {
        bool direct_io:1;
        bool strict_io:1; /* strict cache behavior */
        bool remap:1;      /* set to remap seven reserved chars in filenames */
+       bool sfu_remap:1;  /* remap seven reserved chars ala SFU */
        bool posix_paths:1; /* unset to not ask for posix pathnames. */
        bool no_linux_ext:1;
        bool sfu_emul:1;
@@ -499,6 +500,7 @@ struct smb_vol {
 #define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \
                         CIFS_MOUNT_SERVER_INUM | CIFS_MOUNT_DIRECT_IO | \
                         CIFS_MOUNT_NO_XATTR | CIFS_MOUNT_MAP_SPECIAL_CHR | \
+                        CIFS_MOUNT_MAP_SFM_CHR | \
                         CIFS_MOUNT_UNX_EMUL | CIFS_MOUNT_NO_BRL | \
                         CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_OVERR_UID | \
                         CIFS_MOUNT_OVERR_GID | CIFS_MOUNT_DYNPERM | \
index 66f65001a6d836513b5872ba24b273b82464a67e..61d00a6e398fe5ed30160e2eba02df49876281ed 100644 (file)
@@ -867,7 +867,7 @@ CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
        int rc = 0;
        int bytes_returned;
        int name_len;
-       int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
+       int remap = cifs_remap(cifs_sb);
 
 DelFileRetry:
        rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
@@ -913,7 +913,7 @@ CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
        int rc = 0;
        int bytes_returned;
        int name_len;
-       int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
+       int remap = cifs_remap(cifs_sb);
 
        cifs_dbg(FYI, "In CIFSSMBRmDir\n");
 RmDirRetry:
@@ -958,7 +958,7 @@ CIFSSMBMkDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
        CREATE_DIRECTORY_RSP *pSMBr = NULL;
        int bytes_returned;
        int name_len;
-       int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
+       int remap = cifs_remap(cifs_sb);
 
        cifs_dbg(FYI, "In CIFSSMBMkDir\n");
 MkDirRetry:
@@ -1280,7 +1280,7 @@ CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock,
        __u16 count;
        struct cifs_sb_info *cifs_sb = oparms->cifs_sb;
        struct cifs_tcon *tcon = oparms->tcon;
-       int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
+       int remap = cifs_remap(cifs_sb);
        const struct nls_table *nls = cifs_sb->local_nls;
        int create_options = oparms->create_options;
        int desired_access = oparms->desired_access;
@@ -2572,7 +2572,7 @@ CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
        int bytes_returned;
        int name_len, name_len2;
        __u16 count;
-       int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
+       int remap = cifs_remap(cifs_sb);
 
        cifs_dbg(FYI, "In CIFSSMBRename\n");
 renameRetry:
@@ -2968,7 +2968,7 @@ CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
        int bytes_returned;
        int name_len, name_len2;
        __u16 count;
-       int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
+       int remap = cifs_remap(cifs_sb);
 
        cifs_dbg(FYI, "In CIFSCreateHardLink\n");
 winCreateHardLinkRetry:
@@ -4367,7 +4367,7 @@ findFirstRetry:
                return rc;
 
        nls_codepage = cifs_sb->local_nls;
-       remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
+       remap = cifs_remap(cifs_sb);
 
        if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
                name_len =
@@ -5527,7 +5527,7 @@ CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
        int name_len;
        int rc = 0;
        int bytes_returned = 0;
-       int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
+       int remap = cifs_remap(cifs_sb);
 
        __u16 params, byte_count, data_count, param_offset, offset;
 
index 239e1fb330002f5153cc2bbb1047506254aa5276..24fa08d261fbc352cd89239cf0184de2ba1ce3d2 100644 (file)
@@ -70,6 +70,7 @@ enum {
        Opt_forcegid, Opt_noforcegid,
        Opt_noblocksend, Opt_noautotune,
        Opt_hard, Opt_soft, Opt_perm, Opt_noperm,
+       Opt_mapposix, Opt_nomapposix,
        Opt_mapchars, Opt_nomapchars, Opt_sfu,
        Opt_nosfu, Opt_nodfs, Opt_posixpaths,
        Opt_noposixpaths, Opt_nounix,
@@ -124,8 +125,10 @@ static const match_table_t cifs_mount_option_tokens = {
        { Opt_soft, "soft" },
        { Opt_perm, "perm" },
        { Opt_noperm, "noperm" },
-       { Opt_mapchars, "mapchars" },
+       { Opt_mapchars, "mapchars" }, /* SFU style */
        { Opt_nomapchars, "nomapchars" },
+       { Opt_mapposix, "mapposix" }, /* SFM style */
+       { Opt_nomapposix, "nomapposix" },
        { Opt_sfu, "sfu" },
        { Opt_nosfu, "nosfu" },
        { Opt_nodfs, "nodfs" },
@@ -1231,6 +1234,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
        vol->linux_uid = current_uid();
        vol->linux_gid = current_gid();
 
+       /*
+        * default to SFM style remapping of seven reserved characters
+        * unless user overrides it or we negotiate CIFS POSIX where
+        * it is unnecessary.  Can not simultaneously use more than one mapping
+        * since then readdir could list files that open could not open
+        */
+       vol->remap = true;
+
        /* default to only allowing write access to owner of the mount */
        vol->dir_mode = vol->file_mode = S_IRUGO | S_IXUGO | S_IWUSR;
 
@@ -1338,10 +1349,18 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        vol->noperm = 1;
                        break;
                case Opt_mapchars:
-                       vol->remap = 1;
+                       vol->sfu_remap = true;
+                       vol->remap = false; /* disable SFM mapping */
                        break;
                case Opt_nomapchars:
-                       vol->remap = 0;
+                       vol->sfu_remap = false;
+                       break;
+               case Opt_mapposix:
+                       vol->remap = true;
+                       vol->sfu_remap = false; /* disable SFU mapping */
+                       break;
+               case Opt_nomapposix:
+                       vol->remap = false;
                        break;
                case Opt_sfu:
                        vol->sfu_emul = 1;
@@ -3197,6 +3216,8 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
        if (pvolume_info->server_ino)
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
        if (pvolume_info->remap)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SFM_CHR;
+       if (pvolume_info->sfu_remap)
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
        if (pvolume_info->no_xattr)
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
@@ -3239,10 +3260,20 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
        }
        if (pvolume_info->mfsymlinks) {
                if (pvolume_info->sfu_emul) {
-                       cifs_dbg(VFS, "mount option mfsymlinks ignored if sfu mount option is used\n");
-               } else {
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS;
+                       /*
+                        * Our SFU ("Services for Unix" emulation does not allow
+                        * creating symlinks but does allow reading existing SFU
+                        * symlinks (it does allow both creating and reading SFU
+                        * style mknod and FIFOs though). When "mfsymlinks" and
+                        * "sfu" are both enabled at the same time, it allows
+                        * reading both types of symlinks, but will only create
+                        * them with mfsymlinks format. This allows better
+                        * Apple compatibility (probably better for Samba too)
+                        * while still recognizing old Windows style symlinks.
+                        */
+                       cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n");
                }
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS;
        }
 
        if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm))
@@ -3330,8 +3361,7 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
        ref_path = check_prefix ? full_path + 1 : volume_info->UNC + 1;
 
        rc = get_dfs_path(xid, ses, ref_path, cifs_sb->local_nls,
-                         &num_referrals, &referrals,
-                         cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+                         &num_referrals, &referrals, cifs_remap(cifs_sb));
 
        if (!rc && num_referrals > 0) {
                char *fake_devname = NULL;
index 073640675a39684cb698ffc46e445bd7710c22c4..b72bc29cba23e7f29b4acfc5f1309150f0711aaf 100644 (file)
@@ -577,12 +577,13 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
        struct cifs_io_parms io_parms;
        char *full_path = NULL;
        struct inode *newinode = NULL;
-       int oplock = 0;
+       __u32 oplock = 0;
        struct cifs_fid fid;
        struct cifs_open_parms oparms;
        FILE_ALL_INFO *buf = NULL;
        unsigned int bytes_written;
        struct win_dev *pdev;
+       struct kvec iov[2];
 
        if (!old_valid_dev(device_number))
                return -EINVAL;
@@ -658,7 +659,11 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
        oparms.fid = &fid;
        oparms.reconnect = false;
 
-       rc = CIFS_open(xid, &oparms, &oplock, buf);
+       if (tcon->ses->server->oplocks)
+               oplock = REQ_OPLOCK;
+       else
+               oplock = 0;
+       rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
        if (rc)
                goto mknod_out;
 
@@ -668,25 +673,26 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
         */
 
        pdev = (struct win_dev *)buf;
-       io_parms.netfid = fid.netfid;
        io_parms.pid = current->tgid;
        io_parms.tcon = tcon;
        io_parms.offset = 0;
        io_parms.length = sizeof(struct win_dev);
+       iov[1].iov_base = buf;
+       iov[1].iov_len = sizeof(struct win_dev);
        if (S_ISCHR(mode)) {
                memcpy(pdev->type, "IntxCHR", 8);
                pdev->major = cpu_to_le64(MAJOR(device_number));
                pdev->minor = cpu_to_le64(MINOR(device_number));
-               rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, (char *)pdev,
-                                 NULL, 0);
+               rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+                                                       &bytes_written, iov, 1);
        } else if (S_ISBLK(mode)) {
                memcpy(pdev->type, "IntxBLK", 8);
                pdev->major = cpu_to_le64(MAJOR(device_number));
                pdev->minor = cpu_to_le64(MINOR(device_number));
-               rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, (char *)pdev,
-                                 NULL, 0);
+               rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+                                                       &bytes_written, iov, 1);
        } /* else if (S_ISFIFO) */
-       CIFSSMBClose(xid, tcon, fid.netfid);
+       tcon->ses->server->ops->close(xid, tcon, &fid);
        d_drop(direntry);
 
        /* FIXME: add code here to set EAs */
index 8f7b40fd8f3ba2947b1fad3821e18ac02c0349ae..3e4d00a06c446b997750888bd080b9868a8771f9 100644 (file)
@@ -1687,8 +1687,8 @@ cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data,
                        io_parms.tcon = tcon;
                        io_parms.offset = *offset;
                        io_parms.length = len;
-                       rc = server->ops->sync_write(xid, open_file, &io_parms,
-                                                    &bytes_written, iov, 1);
+                       rc = server->ops->sync_write(xid, &open_file->fid,
+                                       &io_parms, &bytes_written, iov, 1);
                }
                if (rc || (bytes_written == 0)) {
                        if (total_written)
@@ -3206,7 +3206,7 @@ cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
                        io_parms.tcon = tcon;
                        io_parms.offset = *offset;
                        io_parms.length = current_read_size;
-                       rc = server->ops->sync_read(xid, open_file, &io_parms,
+                       rc = server->ops->sync_read(xid, &open_file->fid, &io_parms,
                                                    &bytes_read, &cur_offset,
                                                    &buf_type);
                } while (rc == -EAGAIN);
index 8fd4ee8e07ff9ea0db8c6e83e26201f4d8134ce2..197cb503d5280b4a3f4425d531f444ef9b7f1baf 100644 (file)
@@ -30,6 +30,7 @@
 #include "cifsproto.h"
 #include "cifs_debug.h"
 #include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
 #include "fscache.h"
 
 
@@ -412,7 +413,7 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
              struct cifs_sb_info *cifs_sb, unsigned int xid)
 {
        int rc;
-       int oplock = 0;
+       __u32 oplock;
        struct tcon_link *tlink;
        struct cifs_tcon *tcon;
        struct cifs_fid fid;
@@ -451,8 +452,13 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
        oparms.fid = &fid;
        oparms.reconnect = false;
 
-       rc = CIFS_open(xid, &oparms, &oplock, NULL);
+       if (tcon->ses->server->oplocks)
+               oplock = REQ_OPLOCK;
+       else
+               oplock = 0;
+       rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
        if (rc) {
+               cifs_dbg(FYI, "check sfu type of %s, open rc = %d\n", path, rc);
                cifs_put_tlink(tlink);
                return rc;
        }
@@ -464,7 +470,8 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
        io_parms.offset = 0;
        io_parms.length = 24;
 
-       rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type);
+       rc = tcon->ses->server->ops->sync_read(xid, &fid, &io_parms,
+                                       &bytes_read, &pbuf, &buf_type);
        if ((rc == 0) && (bytes_read >= 8)) {
                if (memcmp("IntxBLK", pbuf, 8) == 0) {
                        cifs_dbg(FYI, "Block device\n");
@@ -504,7 +511,8 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
                fattr->cf_dtype = DT_REG;
                rc = -EOPNOTSUPP; /* or some unknown SFU type */
        }
-       CIFSSMBClose(xid, tcon, fid.netfid);
+
+       tcon->ses->server->ops->close(xid, tcon, &fid);
        cifs_put_tlink(tlink);
        return rc;
 }
@@ -539,7 +547,7 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
        rc = tcon->ses->server->ops->query_all_EAs(xid, tcon, path,
                        "SETFILEBITS", ea_value, 4 /* size of buf */,
                        cifs_sb->local_nls,
-                       cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+                       cifs_remap(cifs_sb));
        cifs_put_tlink(tlink);
        if (rc < 0)
                return (int)rc;
@@ -952,11 +960,18 @@ struct inode *cifs_root_iget(struct super_block *sb)
        struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
 
        xid = get_xid();
-       if (tcon->unix_ext)
+       if (tcon->unix_ext) {
                rc = cifs_get_inode_info_unix(&inode, "", sb, xid);
-       else
-               rc = cifs_get_inode_info(&inode, "", NULL, sb, xid, NULL);
+               /* some servers mistakenly claim POSIX support */
+               if (rc != -EOPNOTSUPP)
+                       goto iget_no_retry;
+               cifs_dbg(VFS, "server does not support POSIX extensions");
+               tcon->unix_ext = false;
+       }
+
+       rc = cifs_get_inode_info(&inode, "", NULL, sb, xid, NULL);
 
+iget_no_retry:
        if (!inode) {
                inode = ERR_PTR(rc);
                goto out;
@@ -1117,8 +1132,7 @@ cifs_rename_pending_delete(const char *full_path, struct dentry *dentry,
        /* rename the file */
        rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, NULL,
                                   cifs_sb->local_nls,
-                                  cifs_sb->mnt_cifs_flags &
-                                           CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                  cifs_remap(cifs_sb));
        if (rc != 0) {
                rc = -EBUSY;
                goto undo_setattr;
@@ -1159,8 +1173,7 @@ out:
         */
 undo_rename:
        CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, dentry->d_name.name,
-                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
-                                           CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_sb->local_nls, cifs_remap(cifs_sb));
 undo_setattr:
        if (dosattr != origattr) {
                info_buf->Attributes = cpu_to_le32(origattr);
@@ -1226,7 +1239,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
                                le64_to_cpu(tcon->fsUnixInfo.Capability))) {
                rc = CIFSPOSIXDelFile(xid, tcon, full_path,
                        SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls,
-                       cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+                       cifs_remap(cifs_sb));
                cifs_dbg(FYI, "posix del rc %d\n", rc);
                if ((rc == 0) || (rc == -ENOENT))
                        goto psx_del_no_retry;
@@ -1349,8 +1362,7 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
                }
                CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
                                       cifs_sb->local_nls,
-                                      cifs_sb->mnt_cifs_flags &
-                                      CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                      cifs_remap(cifs_sb));
        } else {
                struct TCP_Server_Info *server = tcon->ses->server;
                if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
@@ -1392,8 +1404,7 @@ cifs_posix_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode,
        mode &= ~current_umask();
        rc = CIFSPOSIXCreate(xid, tcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode,
                             NULL /* netfid */, info, &oplock, full_path,
-                            cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
-                            CIFS_MOUNT_MAP_SPECIAL_CHR);
+                            cifs_sb->local_nls, cifs_remap(cifs_sb));
        if (rc == -EOPNOTSUPP)
                goto posix_mkdir_out;
        else if (rc) {
@@ -1617,8 +1628,7 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
        if (rc == 0) {
                rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid,
                                (const char *) to_dentry->d_name.name,
-                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
-                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_sb->local_nls, cifs_remap(cifs_sb));
                CIFSSMBClose(xid, tcon, fid.netfid);
        }
 do_rename_exit:
@@ -1694,16 +1704,14 @@ cifs_rename2(struct inode *source_dir, struct dentry *source_dentry,
                tmprc = CIFSSMBUnixQPathInfo(xid, tcon, from_name,
                                             info_buf_source,
                                             cifs_sb->local_nls,
-                                            cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                            cifs_remap(cifs_sb));
                if (tmprc != 0)
                        goto unlink_target;
 
                tmprc = CIFSSMBUnixQPathInfo(xid, tcon, to_name,
                                             info_buf_target,
                                             cifs_sb->local_nls,
-                                            cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                            cifs_remap(cifs_sb));
 
                if (tmprc == 0 && (info_buf_source->UniqueId ==
                                   info_buf_target->UniqueId)) {
@@ -2068,8 +2076,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
                rc = SMBLegacyOpen(xid, tcon, full_path, FILE_OPEN,
                                   GENERIC_WRITE, CREATE_NOT_DIR, &netfid,
                                   &oplock, NULL, cifs_sb->local_nls,
-                                  cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                  cifs_remap(cifs_sb));
                if (rc == 0) {
                        unsigned int bytes_written;
 
index 5657416d3483fd9dea39601e61e5c64e08ec3894..2ec6037f61c758e11f7e1238e0e3c17c9a65af12 100644 (file)
 #include "cifsproto.h"
 #include "cifs_debug.h"
 #include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
+#ifdef CONFIG_CIFS_SMB2
+#include "smb2proto.h"
+#endif
 
 /*
  * M-F Symlink Functions - Begin
@@ -400,6 +404,134 @@ cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
        return rc;
 }
 
+/*
+ * SMB 2.1/SMB3 Protocol specific functions
+ */
+#ifdef CONFIG_CIFS_SMB2
+int
+smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+                     struct cifs_sb_info *cifs_sb, const unsigned char *path,
+                     char *pbuf, unsigned int *pbytes_read)
+{
+       int rc;
+       struct cifs_fid fid;
+       struct cifs_open_parms oparms;
+       struct cifs_io_parms io_parms;
+       int buf_type = CIFS_NO_BUFFER;
+       __le16 *utf16_path;
+       __u8 oplock = SMB2_OPLOCK_LEVEL_II;
+       struct smb2_file_all_info *pfile_info = NULL;
+
+       oparms.tcon = tcon;
+       oparms.cifs_sb = cifs_sb;
+       oparms.desired_access = GENERIC_READ;
+       oparms.create_options = CREATE_NOT_DIR;
+       if (backup_cred(cifs_sb))
+               oparms.create_options |= CREATE_OPEN_BACKUP_INTENT;
+       oparms.disposition = FILE_OPEN;
+       oparms.fid = &fid;
+       oparms.reconnect = false;
+
+       utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+       if (utf16_path == NULL)
+               return -ENOMEM;
+
+       pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
+                            GFP_KERNEL);
+
+       if (pfile_info == NULL) {
+               kfree(utf16_path);
+               return  -ENOMEM;
+       }
+
+       rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL);
+       if (rc)
+               goto qmf_out_open_fail;
+
+       if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
+               /* it's not a symlink */
+               rc = -ENOENT; /* Is there a better rc to return? */
+               goto qmf_out;
+       }
+
+       io_parms.netfid = fid.netfid;
+       io_parms.pid = current->tgid;
+       io_parms.tcon = tcon;
+       io_parms.offset = 0;
+       io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
+       io_parms.persistent_fid = fid.persistent_fid;
+       io_parms.volatile_fid = fid.volatile_fid;
+       rc = SMB2_read(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
+qmf_out:
+       SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+qmf_out_open_fail:
+       kfree(utf16_path);
+       kfree(pfile_info);
+       return rc;
+}
+
+int
+smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+                      struct cifs_sb_info *cifs_sb, const unsigned char *path,
+                      char *pbuf, unsigned int *pbytes_written)
+{
+       int rc;
+       struct cifs_fid fid;
+       struct cifs_open_parms oparms;
+       struct cifs_io_parms io_parms;
+       int create_options = CREATE_NOT_DIR;
+       __le16 *utf16_path;
+       __u8 oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+       struct kvec iov[2];
+
+       if (backup_cred(cifs_sb))
+               create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+       cifs_dbg(FYI, "%s: path: %s\n", __func__, path);
+
+       utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+       if (!utf16_path)
+               return -ENOMEM;
+
+       oparms.tcon = tcon;
+       oparms.cifs_sb = cifs_sb;
+       oparms.desired_access = GENERIC_WRITE;
+       oparms.create_options = create_options;
+       oparms.disposition = FILE_CREATE;
+       oparms.fid = &fid;
+       oparms.reconnect = false;
+
+       rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
+       if (rc) {
+               kfree(utf16_path);
+               return rc;
+       }
+
+       io_parms.netfid = fid.netfid;
+       io_parms.pid = current->tgid;
+       io_parms.tcon = tcon;
+       io_parms.offset = 0;
+       io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
+       io_parms.persistent_fid = fid.persistent_fid;
+       io_parms.volatile_fid = fid.volatile_fid;
+
+       /* iov[0] is reserved for smb header */
+       iov[1].iov_base = pbuf;
+       iov[1].iov_len = CIFS_MF_SYMLINK_FILE_SIZE;
+
+       rc = SMB2_write(xid, &io_parms, pbytes_written, iov, 1);
+
+       /* Make sure we wrote all of the symlink data */
+       if ((rc == 0) && (*pbytes_written != CIFS_MF_SYMLINK_FILE_SIZE))
+               rc = -EIO;
+
+       SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+
+       kfree(utf16_path);
+       return rc;
+}
+#endif /* CONFIG_CIFS_SMB2 */
+
 /*
  * M-F Symlink Functions - End
  */
@@ -435,8 +567,7 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
        if (tcon->unix_ext)
                rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name,
                                            cifs_sb->local_nls,
-                                           cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                           cifs_remap(cifs_sb));
        else {
                server = tcon->ses->server;
                if (!server->ops->create_hardlink) {
@@ -461,11 +592,7 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
                        spin_lock(&old_file->d_inode->i_lock);
                        inc_nlink(old_file->d_inode);
                        spin_unlock(&old_file->d_inode->i_lock);
-                       /*
-                        * BB should we make this contingent on superblock flag
-                        * NOATIME?
-                        */
-                       /* old_file->d_inode->i_ctime = CURRENT_TIME; */
+
                        /*
                         * parent dir timestamps will update from srv within a
                         * second, would it really be worth it to set the parent
@@ -475,7 +602,9 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
                }
                /*
                 * if not oplocked will force revalidate to get info on source
-                * file from srv
+                * file from srv.  Note Samba server prior to 4.2 has bug -
+                * not updating src file ctime on hardlinks but Windows servers
+                * handle it properly
                 */
                cifsInode->time = 0;
 
index d2141f1013829d028ac1b9df15f2a05b254923d4..8fd2a95860ba9874f1bc2d008f9272d3f5825b2a 100644 (file)
@@ -239,7 +239,7 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
        rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ,
                        OPEN_REPARSE_POINT, &fid, &oplock, NULL,
                        cifs_sb->local_nls,
-                       cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+                       cifs_remap(cifs_sb);
        if (!rc) {
                tmpbuffer = kmalloc(maxpath);
                rc = CIFSSMBQueryReparseLinkInfo(xid, ptcon, full_path,
@@ -704,15 +704,15 @@ static int cifs_filldir(char *find_entry, struct file *file,
 
        if (file_info->srch_inf.unicode) {
                struct nls_table *nlt = cifs_sb->local_nls;
+               int map_type;
 
+               map_type = cifs_remap(cifs_sb);
                name.name = scratch_buf;
                name.len =
                        cifs_from_utf16((char *)name.name, (__le16 *)de.name,
                                        UNICODE_NAME_MAX,
                                        min_t(size_t, de.namelen,
-                                             (size_t)max_len), nlt,
-                                       cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                             (size_t)max_len), nlt, map_type);
                name.len -= nls_nullsize(nlt);
        } else {
                name.name = de.name;
index 52131d8cb4d5397c6c75f0290c0d226d76e6d67c..d2979036a4c72756e91f9fa6c454337585c06800 100644 (file)
@@ -23,6 +23,7 @@
 #include "cifsproto.h"
 #include "cifs_debug.h"
 #include "cifspdu.h"
+#include "cifs_unicode.h"
 
 /*
  * An NT cancel request header looks just like the original request except:
@@ -530,13 +531,11 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 
        rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info,
                              0 /* not legacy */, cifs_sb->local_nls,
-                             cifs_sb->mnt_cifs_flags &
-                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                             cifs_remap(cifs_sb));
 
        if (rc == -EOPNOTSUPP || rc == -EINVAL)
                rc = SMBQueryInformation(xid, tcon, full_path, file_info,
-                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
-                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_sb->local_nls, cifs_remap(cifs_sb));
        kfree(file_info);
        return rc;
 }
@@ -552,8 +551,7 @@ cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 
        /* could do find first instead but this returns more info */
        rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */,
-                             cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                             cifs_sb->local_nls, cifs_remap(cifs_sb));
        /*
         * BB optimize code so we do not make the above call when server claims
         * no NT SMB support and the above call failed at least once - set flag
@@ -562,8 +560,7 @@ cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
        if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
                rc = SMBQueryInformation(xid, tcon, full_path, data,
                                         cifs_sb->local_nls,
-                                        cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                        cifs_remap(cifs_sb));
                *adjustTZ = true;
        }
 
@@ -611,8 +608,7 @@ cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
         */
        return CIFSGetSrvInodeNumber(xid, tcon, full_path, uniqueid,
                                     cifs_sb->local_nls,
-                                    cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                    cifs_remap(cifs_sb));
 }
 
 static int
@@ -703,8 +699,7 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
        dosattrs = cifsInode->cifsAttrs|ATTR_READONLY;
        info.Attributes = cpu_to_le32(dosattrs);
        rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls,
-                               cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_remap(cifs_sb));
        if (rc == 0)
                cifsInode->cifsAttrs = dosattrs;
 }
@@ -720,8 +715,7 @@ cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
                                     oparms->create_options,
                                     &oparms->fid->netfid, oplock, buf,
                                     oparms->cifs_sb->local_nls,
-                                    oparms->cifs_sb->mnt_cifs_flags
-                                               & CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                    cifs_remap(oparms->cifs_sb));
        return CIFS_open(xid, oparms, oplock, buf);
 }
 
@@ -749,21 +743,21 @@ cifs_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 static int
-cifs_sync_read(const unsigned int xid, struct cifsFileInfo *cfile,
+cifs_sync_read(const unsigned int xid, struct cifs_fid *pfid,
               struct cifs_io_parms *parms, unsigned int *bytes_read,
               char **buf, int *buf_type)
 {
-       parms->netfid = cfile->fid.netfid;
+       parms->netfid = pfid->netfid;
        return CIFSSMBRead(xid, parms, bytes_read, buf, buf_type);
 }
 
 static int
-cifs_sync_write(const unsigned int xid, struct cifsFileInfo *cfile,
+cifs_sync_write(const unsigned int xid, struct cifs_fid *pfid,
                struct cifs_io_parms *parms, unsigned int *written,
                struct kvec *iov, unsigned long nr_segs)
 {
 
-       parms->netfid = cfile->fid.netfid;
+       parms->netfid = pfid->netfid;
        return CIFSSMBWrite2(xid, parms, written, iov, nr_segs);
 }
 
@@ -800,8 +794,7 @@ smb_set_file_info(struct inode *inode, const char *full_path,
        tcon = tlink_tcon(tlink);
 
        rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls,
-                                       cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_remap(cifs_sb));
        if (rc == 0) {
                cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
                goto out;
index 4aa7a0f07d6eace61da47500bb6e83a5bd39e294..1a08a34838fc9772e2a62ff5dd69b9431d38f453 100644 (file)
@@ -379,6 +379,14 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
        int len;
        const char *start_of_path;
        __le16 *to;
+       int map_type;
+
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR)
+               map_type = SFM_MAP_UNI_RSVD;
+       else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
+               map_type = SFU_MAP_UNI_RSVD;
+       else
+               map_type = NO_MAP_UNI_RSVD;
 
        /* Windows doesn't allow paths beginning with \ */
        if (from[0] == '\\')
@@ -386,9 +394,7 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
        else
                start_of_path = from;
        to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len,
-                                  cifs_sb->local_nls,
-                                  cifs_sb->mnt_cifs_flags &
-                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                  cifs_sb->local_nls, map_type);
        return to;
 }
 
index f522193b7184facd4926cfa3447b7fcbf5268cce..c5f521bcdee247cf0ffc2b2f5af936467fd778aa 100644 (file)
@@ -265,15 +265,18 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
                        FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
                        NULL /* no data input */, 0 /* no data input */,
                        (char **)&out_buf, &ret_data_len);
-
-       if ((rc == 0)  && (ret_data_len > 0)) {
+       if (rc != 0)
+               cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc);
+       else if (ret_data_len < sizeof(struct network_interface_info_ioctl_rsp)) {
+               cifs_dbg(VFS, "server returned bad net interface info buf\n");
+               rc = -EINVAL;
+       } else {
                /* Dump info on first interface */
                cifs_dbg(FYI, "Adapter Capability 0x%x\t",
                        le32_to_cpu(out_buf->Capability));
                cifs_dbg(FYI, "Link Speed %lld\n",
                        le64_to_cpu(out_buf->LinkSpeed));
-       } else
-               cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc);
+       }
 
        return rc;
 }
@@ -711,23 +714,23 @@ smb2_read_data_length(char *buf)
 
 
 static int
-smb2_sync_read(const unsigned int xid, struct cifsFileInfo *cfile,
+smb2_sync_read(const unsigned int xid, struct cifs_fid *pfid,
               struct cifs_io_parms *parms, unsigned int *bytes_read,
               char **buf, int *buf_type)
 {
-       parms->persistent_fid = cfile->fid.persistent_fid;
-       parms->volatile_fid = cfile->fid.volatile_fid;
+       parms->persistent_fid = pfid->persistent_fid;
+       parms->volatile_fid = pfid->volatile_fid;
        return SMB2_read(xid, parms, bytes_read, buf, buf_type);
 }
 
 static int
-smb2_sync_write(const unsigned int xid, struct cifsFileInfo *cfile,
+smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid,
                struct cifs_io_parms *parms, unsigned int *written,
                struct kvec *iov, unsigned long nr_segs)
 {
 
-       parms->persistent_fid = cfile->fid.persistent_fid;
-       parms->volatile_fid = cfile->fid.volatile_fid;
+       parms->persistent_fid = pfid->persistent_fid;
+       parms->volatile_fid = pfid->volatile_fid;
        return SMB2_write(xid, parms, written, iov, nr_segs);
 }
 
@@ -1452,6 +1455,8 @@ struct smb_version_operations smb21_operations = {
        .rename = smb2_rename_path,
        .create_hardlink = smb2_create_hardlink,
        .query_symlink = smb2_query_symlink,
+       .query_mf_symlink = smb3_query_mf_symlink,
+       .create_mf_symlink = smb3_create_mf_symlink,
        .open = smb2_open_file,
        .set_fid = smb2_set_fid,
        .close = smb2_close_file,
@@ -1531,6 +1536,8 @@ struct smb_version_operations smb30_operations = {
        .rename = smb2_rename_path,
        .create_hardlink = smb2_create_hardlink,
        .query_symlink = smb2_query_symlink,
+       .query_mf_symlink = smb3_query_mf_symlink,
+       .create_mf_symlink = smb3_create_mf_symlink,
        .open = smb2_open_file,
        .set_fid = smb2_set_fid,
        .close = smb2_close_file,
index 74b3a6684383c9bfd08f481c8575932cefa06766..8f1672bb82d56202c3cc9d3d5506ab983d0467f6 100644 (file)
@@ -1098,6 +1098,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
 
        if (oparms->create_options & CREATE_OPTION_READONLY)
                file_attributes |= ATTR_READONLY;
+       if (oparms->create_options & CREATE_OPTION_SPECIAL)
+               file_attributes |= ATTR_SYSTEM;
 
        req->ImpersonationLevel = IL_IMPERSONATION;
        req->DesiredAccess = cpu_to_le32(oparms->desired_access);
index fbe486c285a90aff757a43569330fb32c9bbb50d..e3188abdafd0e144be62064fb7ee47c5e2575dc6 100644 (file)
@@ -352,6 +352,8 @@ struct smb2_tree_disconnect_rsp {
 #define FILE_ATTRIBUTE_OFFLINE                 0x00001000
 #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED     0x00002000
 #define FILE_ATTRIBUTE_ENCRYPTED               0x00004000
+#define FILE_ATTRIBUTE_INTEGRITY_STREAM                0x00008000
+#define FILE_ATTRIBUTE_NO_SCRUB_DATA           0x00020000
 
 /* Oplock levels */
 #define SMB2_OPLOCK_LEVEL_NONE         0x00
index 67e8ce8055def7743fd1204b311eda090b3e1fc1..79dc650c18b26baf2e9a4ca8ac2c67df5ebdc63e 100644 (file)
@@ -82,7 +82,13 @@ extern int smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
 extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
                                const char *from_name, const char *to_name,
                                struct cifs_sb_info *cifs_sb);
-
+extern int smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+                       struct cifs_sb_info *cifs_sb, const unsigned char *path,
+                       char *pbuf, unsigned int *pbytes_written);
+extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+                         struct cifs_sb_info *cifs_sb,
+                         const unsigned char *path, char *pbuf,
+                         unsigned int *pbytes_read);
 extern int smb2_open_file(const unsigned int xid,
                          struct cifs_open_parms *oparms,
                          __u32 *oplock, FILE_ALL_INFO *buf);
index 43eb1367b10353ae1e71a5196b83587df6939160..6c1566366a6613cbc494fe46344c8fd00342a82b 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/random.h>
+#include "cifs_fs_sb.h"
 #include "cifs_unicode.h"
 #include "cifspdu.h"
 #include "cifsglob.h"
index 5ac836a86b1885d4e1766e831a1b56d3a263e277..72a4d10653d6e746919bae66d9fe46316cdaa812 100644 (file)
@@ -28,6 +28,8 @@
 #include "cifsglob.h"
 #include "cifsproto.h"
 #include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
 
 #define MAX_EA_VALUE_SIZE 65535
 #define CIFS_XATTR_DOS_ATTRIB "user.DosAttrib"
@@ -85,8 +87,7 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
                if (pTcon->ses->server->ops->set_EA)
                        rc = pTcon->ses->server->ops->set_EA(xid, pTcon,
                                full_path, ea_name, NULL, (__u16)0,
-                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
-                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_sb->local_nls, cifs_remap(cifs_sb));
        }
 remove_ea_exit:
        kfree(full_path);
@@ -154,8 +155,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
                if (pTcon->ses->server->ops->set_EA)
                        rc = pTcon->ses->server->ops->set_EA(xid, pTcon,
                                full_path, ea_name, ea_value, (__u16)value_size,
-                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
-                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_sb->local_nls, cifs_remap(cifs_sb));
        } else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN)
                   == 0) {
                if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
@@ -165,8 +165,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
                if (pTcon->ses->server->ops->set_EA)
                        rc = pTcon->ses->server->ops->set_EA(xid, pTcon,
                                full_path, ea_name, ea_value, (__u16)value_size,
-                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
-                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_sb->local_nls, cifs_remap(cifs_sb));
        } else if (strncmp(ea_name, CIFS_XATTR_CIFS_ACL,
                        strlen(CIFS_XATTR_CIFS_ACL)) == 0) {
 #ifdef CONFIG_CIFS_ACL
@@ -199,8 +198,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
                                rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
                                        ea_value, (const int)value_size,
                                        ACL_TYPE_ACCESS, cifs_sb->local_nls,
-                                       cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                       cifs_remap(cifs_sb));
                        cifs_dbg(FYI, "set POSIX ACL rc %d\n", rc);
 #else
                        cifs_dbg(FYI, "set POSIX ACL not supported\n");
@@ -212,8 +210,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
                                rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
                                        ea_value, (const int)value_size,
                                        ACL_TYPE_DEFAULT, cifs_sb->local_nls,
-                                       cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                       cifs_remap(cifs_sb));
                        cifs_dbg(FYI, "set POSIX default ACL rc %d\n", rc);
 #else
                        cifs_dbg(FYI, "set default POSIX ACL not supported\n");
@@ -285,8 +282,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
                if (pTcon->ses->server->ops->query_all_EAs)
                        rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon,
                                full_path, ea_name, ea_value, buf_size,
-                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
-                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_sb->local_nls, cifs_remap(cifs_sb));
        } else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) {
                if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
                        goto get_ea_exit;
@@ -295,8 +291,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
                if (pTcon->ses->server->ops->query_all_EAs)
                        rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon,
                                full_path, ea_name, ea_value, buf_size,
-                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
-                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_sb->local_nls, cifs_remap(cifs_sb));
        } else if (strncmp(ea_name, POSIX_ACL_XATTR_ACCESS,
                          strlen(POSIX_ACL_XATTR_ACCESS)) == 0) {
 #ifdef CONFIG_CIFS_POSIX
@@ -304,8 +299,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
                        rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
                                ea_value, buf_size, ACL_TYPE_ACCESS,
                                cifs_sb->local_nls,
-                               cifs_sb->mnt_cifs_flags &
-                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_remap(cifs_sb));
 #else
                cifs_dbg(FYI, "Query POSIX ACL not supported yet\n");
 #endif /* CONFIG_CIFS_POSIX */
@@ -316,8 +310,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
                        rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
                                ea_value, buf_size, ACL_TYPE_DEFAULT,
                                cifs_sb->local_nls,
-                               cifs_sb->mnt_cifs_flags &
-                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_remap(cifs_sb));
 #else
                cifs_dbg(FYI, "Query POSIX default ACL not supported yet\n");
 #endif /* CONFIG_CIFS_POSIX */
@@ -421,8 +414,7 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
        if (pTcon->ses->server->ops->query_all_EAs)
                rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon,
                                full_path, NULL, data, buf_size,
-                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
-                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
+                               cifs_sb->local_nls, cifs_remap(cifs_sb));
 list_ea_exit:
        kfree(full_path);
        free_xid(xid);