From 95c5d0607a54f02551bdd9ecc7f41b1d35b19211 Mon Sep 17 00:00:00 2001
From: Steve French <stfrench@microsoft.com>
Date: Sat, 27 Feb 2021 02:01:46 -0600
Subject: [PATCH] smb3: allow files to be created with backslash in name
Backslash is reserved in Windows (and SMB2/SMB3 by default) but
allowed in POSIX so must be remapped when POSIX extensions are
not enabled.
The default mapping for SMB3 mounts ("SFM") allows mapping backslash
(ie 0x5C in UTF8) to 0xF026 in UCS-2 (using the Unicode remapping
range reserved for these characters), but this was not mapped by
cifs.ko (unlike asterisk, greater than, question mark etc). This patch
fixes that to allow creating files and directories with backslash
in the file or directory name.
Before this patch:
touch "/mnt2/filewith\slash"
would return
touch: setting times of '/mnt2/filewith\slash': Invalid argument
With the patch touch and mkdir with the backslash in the name works.
This problem was found while debugging xfstest generic/453
https://bugzilla.kernel.org/show_bug.cgi?id=210961
Reported-by: Xiaoli Feng <xifeng@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
---
fs/cifs/cifs_unicode.c | 15 ++++++++++-----
fs/cifs/cifs_unicode.h | 3 +++
fs/cifs/cifsglob.h | 5 +----
fs/cifs/dir.c | 18 ++++++++++++------
fs/cifs/misc.c | 2 +-
fs/cifs/smb2misc.c | 18 +++++++++++-------
6 files changed, 38 insertions(+), 23 deletions(-)
@@ -98,6 +98,9 @@ convert_sfm_char(const __u16 src_char, char *target)
case SFM_PERIOD:
*target = '.';
break;
+ case SFM_SLASH:
+ *target = '\\';
+ break;
default:
return false;
}
@@ -431,6 +434,9 @@ static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
case '|':
dest_char = cpu_to_le16(SFM_PIPE);
break;
+ case '\\':
+ dest_char = cpu_to_le16(SFM_SLASH);
+ break;
case '.':
if (end_of_string)
dest_char = cpu_to_le16(SFM_PERIOD);
@@ -443,6 +449,9 @@ static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
else
dest_char = 0;
break;
+ case '/':
+ dest_char = cpu_to_le16(UCS2_SLASH);
+ break;
default:
dest_char = 0;
}
@@ -502,11 +511,7 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
dst_char = convert_to_sfm_char(src_char, end_of_string);
} 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.
- */
+
if (dst_char == 0) {
charlen = cp->char2uni(source + i, srclen - i, &tmp);
dst_char = cpu_to_le16(tmp);
@@ -24,6 +24,9 @@
#define UNIUPR_NOLOWER /* Example to not expand lower case tables */
+/* Unicode encoding of backslash character */
+#define UCS2_SLASH 0x005C
+
/*
* Windows maps these to the user defined 16 bit Unicode range since they are
* reserved symbols (along with \ and /), otherwise illegal to store
@@ -1430,10 +1430,7 @@ CIFS_FILE_SB(struct file *file)
static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb)
{
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
- return '/';
- else
- return '\\';
+ return '/';
}
static inline void
@@ -209,12 +209,18 @@ check_name(struct dentry *direntry, struct cifs_tcon *tcon)
le32_to_cpu(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] == '\\') {
- cifs_dbg(FYI, "Invalid file name\n");
- return -EINVAL;
- }
+ /*
+ * SMB3.1.1 POSIX Extensions, CIFS Unix Extensions and SFM mappings
+ * allow \ in paths (or in latter case remaps \ to 0xF026)
+ */
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) ||
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR))
+ return 0;
+
+ for (i = 0; i < direntry->d_name.len; i++) {
+ if (direntry->d_name.name[i] == '\\') {
+ cifs_dbg(FYI, "Invalid file name\n");
+ return -EINVAL;
}
}
return 0;
@@ -1186,7 +1186,7 @@ int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
goto out;
}
- convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
+ convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); /* BB Check this */
} else
cifs_sb->prepath = NULL;
@@ -476,13 +476,17 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
if (from[0] == '\\')
start_of_path = from + 1;
- /* SMB311 POSIX extensions paths do not include leading slash */
- else if (cifs_sb_master_tlink(cifs_sb) &&
- cifs_sb_master_tcon(cifs_sb)->posix_extensions &&
- (from[0] == '/')) {
- start_of_path = from + 1;
- } else
- start_of_path = from;
+ start_of_path = from;
+ /*
+ * Only old CIFS Unix extensions paths include leading slash
+ * Need to skip if for SMB3.1.1 POSIX Extensions and SMB1/2/3
+ */
+ if (from[0] == '/') {
+ if (((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == false) ||
+ (cifs_sb_master_tlink(cifs_sb) &&
+ (cifs_sb_master_tcon(cifs_sb)->posix_extensions)))
+ start_of_path = from + 1;
+ }
to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len,
cifs_sb->local_nls, map_type);
--
2.27.0