aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve French <smfrench@gmail.com>2014-09-25 14:20:05 -0400
committerSteve French <smfrench@gmail.com>2014-10-16 16:20:20 -0400
commitb693855fe67314d501aae74b9adff8788eb2fd82 (patch)
treeae47f2dd9b4c4fb73c6592d22916416cdd3f4871
parentc22870ea2deb2841402133909cfa707a2c0b12ed (diff)
Allow conversion of characters in Mac remap range. Part 1
This allows directory listings to Mac to display filenames correctly which have been created with illegal (to Windows) characters in their filename. It does not allow converting the other direction yet ie opening files with these characters (followon patch). There are seven reserved characters that need to be remapped when mounting to Windows, Mac (or any server without Unix Extensions) which are valid in POSIX but not in the other OS. : \ < > ? * | We used the normal UCS-2 remap range for this in order to convert this to/from UTF8 as did Windows Services for Unix (basically add 0xF000 to any of the 7 reserved characters), at least when the "mapchars" mount option was specified. Mac used a very slightly different "Services for Mac" remap range 0xF021 through 0xF027. The attached patch allows cifs.ko (the kernel client) to read directories on macs containing files with these characters and display their names properly. In theory this even might be useful on mounts to Samba when the vfs_catia or new "vfs_fruit" module is loaded. Currently the 7 reserved characters look very strange in directory listings from cifs.ko to Mac server. This patch allows these file name characters to be read (requires specifying mapchars on mount). Two additional changes are needed: 1) Make it more automatic: a way of detecting enough info so that we know to try to always remap these characters or not. Various have suggested that the SFM approach be made the default when the server does not support POSIX Unix extensions (cifs mounts to Samba for example) so need to make SFM remapping the default unless mapchars (SFU style mapping) specified on mount or no mapping explicitly requested or no mapping needed (cifs mounts to Samba). 2) Adding a patch to map the characters the other direction (ie UTF-8 to UCS-2 on open). This patch does it for translating readdir entries (ie UCS-2 to UTF-8) Signed-off-by: Steve French <smfrench@gmail.com> Reviewed-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
-rw-r--r--fs/cifs/cifs_fs_sb.h1
-rw-r--r--fs/cifs/cifs_unicode.c95
-rw-r--r--fs/cifs/cifs_unicode.h30
-rw-r--r--fs/cifs/cifsencrypt.c2
-rw-r--r--fs/cifs/readdir.c12
5 files changed, 107 insertions, 33 deletions
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
index 9409fa10bd5c..3182273a3407 100644
--- a/fs/cifs/cifs_fs_sb.h
+++ b/fs/cifs/cifs_fs_sb.h
@@ -45,6 +45,7 @@
45#define CIFS_MOUNT_POSIXACL 0x100000 /* mirror of MS_POSIXACL in mnt_cifs_flags */ 45#define CIFS_MOUNT_POSIXACL 0x100000 /* mirror of MS_POSIXACL in mnt_cifs_flags */
46#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */ 46#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */
47#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */ 47#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */
48#define CIFS_MOUNT_MAP_SFM_CHR 0x800000 /* SFM/MAC mapping for illegal chars */
48 49
49struct cifs_sb_info { 50struct cifs_sb_info {
50 struct rb_root tlink_tree; 51 struct rb_root tlink_tree;
diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c
index 15e9505aa35f..a479cc552617 100644
--- a/fs/cifs/cifs_unicode.c
+++ b/fs/cifs/cifs_unicode.c
@@ -61,26 +61,10 @@ cifs_utf16_bytes(const __le16 *from, int maxbytes,
61 return outlen; 61 return outlen;
62} 62}
63 63
64/* 64/* Convert character using the SFU - "Services for Unix" remapping range */
65 * cifs_mapchar - convert a host-endian char to proper char in codepage 65static bool
66 * @target - where converted character should be copied 66convert_sfu_char(const __u16 src_char, char *target)
67 * @src_char - 2 byte host-endian source character
68 * @cp - codepage to which character should be converted
69 * @mapchar - should character be mapped according to mapchars mount option?
70 *
71 * This function handles the conversion of a single character. It is the
72 * responsibility of the caller to ensure that the target buffer is large
73 * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE).
74 */
75static int
76cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
77 bool mapchar)
78{ 67{
79 int len = 1;
80
81 if (!mapchar)
82 goto cp_convert;
83
84 /* 68 /*
85 * BB: Cannot handle remapping UNI_SLASH until all the calls to 69 * BB: Cannot handle remapping UNI_SLASH until all the calls to
86 * build_path_from_dentry are modified, as they use slash as 70 * build_path_from_dentry are modified, as they use slash as
@@ -106,19 +90,74 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
106 *target = '<'; 90 *target = '<';
107 break; 91 break;
108 default: 92 default:
109 goto cp_convert; 93 return false;
110 } 94 }
95 return true;
96}
111 97
112out: 98/* Convert character using the SFM - "Services for Mac" remapping range */
113 return len; 99static bool
100convert_sfm_char(const __u16 src_char, char *target)
101{
102 switch (src_char) {
103 case SFM_COLON:
104 *target = ':';
105 break;
106 case SFM_ASTERISK:
107 *target = '*';
108 break;
109 case SFM_QUESTION:
110 *target = '?';
111 break;
112 case SFM_PIPE:
113 *target = '|';
114 break;
115 case SFM_GRTRTHAN:
116 *target = '>';
117 break;
118 case SFM_LESSTHAN:
119 *target = '<';
120 break;
121 case SFM_SLASH:
122 *target = '\\';
123 break;
124 default:
125 return false;
126 }
127 return true;
128}
129
130
131/*
132 * cifs_mapchar - convert a host-endian char to proper char in codepage
133 * @target - where converted character should be copied
134 * @src_char - 2 byte host-endian source character
135 * @cp - codepage to which character should be converted
136 * @map_type - How should the 7 NTFS/SMB reserved characters be mapped to UCS2?
137 *
138 * This function handles the conversion of a single character. It is the
139 * responsibility of the caller to ensure that the target buffer is large
140 * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE).
141 */
142static int
143cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
144 int maptype)
145{
146 int len = 1;
147
148 if ((maptype == SFM_MAP_UNI_RSVD) && convert_sfm_char(src_char, target))
149 return len;
150 else if ((maptype == SFU_MAP_UNI_RSVD) &&
151 convert_sfu_char(src_char, target))
152 return len;
114 153
115cp_convert: 154 /* if character not one of seven in special remap set */
116 len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); 155 len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);
117 if (len <= 0) { 156 if (len <= 0) {
118 *target = '?'; 157 *target = '?';
119 len = 1; 158 len = 1;
120 } 159 }
121 goto out; 160 return len;
122} 161}
123 162
124/* 163/*
@@ -145,7 +184,7 @@ cp_convert:
145 */ 184 */
146int 185int
147cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, 186cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
148 const struct nls_table *codepage, bool mapchar) 187 const struct nls_table *codepage, int map_type)
149{ 188{
150 int i, charlen, safelen; 189 int i, charlen, safelen;
151 int outlen = 0; 190 int outlen = 0;
@@ -172,13 +211,13 @@ cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
172 * conversion bleed into the null terminator 211 * conversion bleed into the null terminator
173 */ 212 */
174 if (outlen >= safelen) { 213 if (outlen >= safelen) {
175 charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar); 214 charlen = cifs_mapchar(tmp, ftmp, codepage, map_type);
176 if ((outlen + charlen) > (tolen - nullsize)) 215 if ((outlen + charlen) > (tolen - nullsize))
177 break; 216 break;
178 } 217 }
179 218
180 /* put converted char into 'to' buffer */ 219 /* put converted char into 'to' buffer */
181 charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); 220 charlen = cifs_mapchar(&to[outlen], ftmp, codepage, map_type);
182 outlen += charlen; 221 outlen += charlen;
183 } 222 }
184 223
@@ -267,7 +306,7 @@ cifs_strndup_from_utf16(const char *src, const int maxlen,
267 if (!dst) 306 if (!dst)
268 return NULL; 307 return NULL;
269 cifs_from_utf16(dst, (__le16 *) src, len, maxlen, codepage, 308 cifs_from_utf16(dst, (__le16 *) src, len, maxlen, codepage,
270 false); 309 NO_MAP_UNI_RSVD);
271 } else { 310 } else {
272 len = strnlen(src, maxlen); 311 len = strnlen(src, maxlen);
273 len++; 312 len++;
diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h
index d8eac3b6cefb..30af32b55a94 100644
--- a/fs/cifs/cifs_unicode.h
+++ b/fs/cifs/cifs_unicode.h
@@ -52,6 +52,34 @@
52#define UNI_PIPE (__u16) ('|' + 0xF000) 52#define UNI_PIPE (__u16) ('|' + 0xF000)
53#define UNI_SLASH (__u16) ('\\' + 0xF000) 53#define UNI_SLASH (__u16) ('\\' + 0xF000)
54 54
55/*
56 * Macs use an older "SFM" mapping of the symbols above. Fortunately it does
57 * not conflict (although almost does) with the mapping above.
58 */
59
60#define SFM_ASTERISK ((__u16) 0xF021)
61#define SFM_QUESTION ((__u16) 0xF025)
62#define SFM_COLON ((__u16) 0xF022)
63#define SFM_GRTRTHAN ((__u16) 0xF024)
64#define SFM_LESSTHAN ((__u16) 0xF023)
65#define SFM_PIPE ((__u16) 0xF027)
66#define SFM_SLASH ((__u16) 0xF026)
67
68/*
69 * Mapping mechanism to use when one of the seven reserved characters is
70 * encountered. We can only map using one of the mechanisms at a time
71 * since otherwise readdir could return directory entries which we would
72 * not be able to open
73 *
74 * NO_MAP_UNI_RSVD = do not perform any remapping of the character
75 * SFM_MAP_UNI_RSVD = map reserved characters using SFM scheme (MAC compatible)
76 * SFU_MAP_UNI_RSVD = map reserved characters ala SFU ("mapchars" option)
77 *
78 */
79#define NO_MAP_UNI_RSVD 0
80#define SFM_MAP_UNI_RSVD 1
81#define SFU_MAP_UNI_RSVD 2
82
55/* Just define what we want from uniupr.h. We don't want to define the tables 83/* Just define what we want from uniupr.h. We don't want to define the tables
56 * in each source file. 84 * in each source file.
57 */ 85 */
@@ -75,7 +103,7 @@ extern const struct UniCaseRange CifsUniLowerRange[];
75 103
76#ifdef __KERNEL__ 104#ifdef __KERNEL__
77int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, 105int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
78 const struct nls_table *codepage, bool mapchar); 106 const struct nls_table *cp, int map_type);
79int cifs_utf16_bytes(const __le16 *from, int maxbytes, 107int cifs_utf16_bytes(const __le16 *from, int maxbytes,
80 const struct nls_table *codepage); 108 const struct nls_table *codepage);
81int cifs_strtoUTF16(__le16 *, const char *, int, const struct nls_table *); 109int cifs_strtoUTF16(__le16 *, const char *, int, const struct nls_table *);
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index 4934347321d3..4ac7445e6ec7 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -431,7 +431,7 @@ find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp)
431 return -ENOMEM; 431 return -ENOMEM;
432 cifs_from_utf16(ses->domainName, 432 cifs_from_utf16(ses->domainName,
433 (__le16 *)blobptr, attrsize, attrsize, 433 (__le16 *)blobptr, attrsize, attrsize,
434 nls_cp, false); 434 nls_cp, NO_MAP_UNI_RSVD);
435 break; 435 break;
436 } 436 }
437 } 437 }
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index d2141f101382..5bf3d0a746f8 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -704,15 +704,21 @@ static int cifs_filldir(char *find_entry, struct file *file,
704 704
705 if (file_info->srch_inf.unicode) { 705 if (file_info->srch_inf.unicode) {
706 struct nls_table *nlt = cifs_sb->local_nls; 706 struct nls_table *nlt = cifs_sb->local_nls;
707 int map_type;
708
709 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR)
710 map_type = SFM_MAP_UNI_RSVD;
711 else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
712 map_type = SFU_MAP_UNI_RSVD;
713 else
714 map_type = NO_MAP_UNI_RSVD;
707 715
708 name.name = scratch_buf; 716 name.name = scratch_buf;
709 name.len = 717 name.len =
710 cifs_from_utf16((char *)name.name, (__le16 *)de.name, 718 cifs_from_utf16((char *)name.name, (__le16 *)de.name,
711 UNICODE_NAME_MAX, 719 UNICODE_NAME_MAX,
712 min_t(size_t, de.namelen, 720 min_t(size_t, de.namelen,
713 (size_t)max_len), nlt, 721 (size_t)max_len), nlt, map_type);
714 cifs_sb->mnt_cifs_flags &
715 CIFS_MOUNT_MAP_SPECIAL_CHR);
716 name.len -= nls_nullsize(nlt); 722 name.len -= nls_nullsize(nlt);
717 } else { 723 } else {
718 name.name = de.name; 724 name.name = de.name;