From patchwork Thu Sep 18 01:18:33 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steve French X-Patchwork-Id: 4927661 Return-Path: X-Original-To: patchwork-cifs-client@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id E99F99F2EC for ; Thu, 18 Sep 2014 01:17:55 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 6E0232015D for ; Thu, 18 Sep 2014 01:18:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4B54320127 for ; Thu, 18 Sep 2014 01:18:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754253AbaIRBSy (ORCPT ); Wed, 17 Sep 2014 21:18:54 -0400 Received: from mail-qc0-f173.google.com ([209.85.216.173]:35816 "EHLO mail-qc0-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754154AbaIRBSy (ORCPT ); Wed, 17 Sep 2014 21:18:54 -0400 Received: by mail-qc0-f173.google.com with SMTP id i8so262742qcq.32 for ; Wed, 17 Sep 2014 18:18:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:from:date:message-id:subject:to:content-type; bh=saqlNy3XIhOl0hnn3JCDDGS7YEjlbW8FV6VCAbT8S+Y=; b=Np/wF0iS+ChahxEpxzNxlqMPKv7gGulRmgDW5Ptc2zHAJs4AqEJgneDmDSUMNfkqJ3 Se6E8cKMT+ki7HYg7l7GAkdjetGMNgkwJk98mg+SIuVGafrX59GZSujAKuib5pmyaA3+ RMSxBYV8Cnw4mqEqygnftJ1BuAl+rg5I28gfTK0WjGMjKQCjG1IHopMIsKSZUsAe1Kx9 arBGS8e473jdpVivnI7jBH0MRKZArP3rZyVS0Xjm0a22/pnWpOIx4k2TMic3qGHGPGOj SRD/BADYKW1U7A6Benf+3JQjpvbwUyKzA/Onn2wvaIS7q2kWhvlyF+RcNN7ezks4yjwH BVVw== X-Received: by 10.229.183.130 with SMTP id cg2mr2302947qcb.17.1411003133319; Wed, 17 Sep 2014 18:18:53 -0700 (PDT) MIME-Version: 1.0 Received: by 10.140.32.116 with HTTP; Wed, 17 Sep 2014 18:18:33 -0700 (PDT) From: Steve French Date: Wed, 17 Sep 2014 20:18:33 -0500 Message-ID: Subject: resending patch as git format patch To: "linux-cifs@vger.kernel.org" Sender: linux-cifs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-cifs@vger.kernel.org X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, T_TVD_MIME_EPI,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From f6f9178542189237d2451c7a2f697b13c68abf3d Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 17 Sep 2014 20:12:33 -0500 Subject: [PATCH] [CIFS] Allow conversion of characters in Mac range (SFM-style) 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. 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 use 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). 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 module is loaded and we are mounted with smb3. 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 that we are mounted to a Mac so we know to try to always remap these characters (at least for smb2.1 mounts to them and eventually smb3 when they support that) 2) A deterministic way of deciding when to use the normal mapchars approach vs. the mac mappings when creating a file with any of the seven characters in its name. It may be ok in some cases to be able to translate file names in both formats properly when listing directories - but when creating files we have to use the same mechanism and decide when to use which, especially on SMB3 mounts (which do not have an option for posix pathnames). Will need to decouple turning mac character mapping on/off from mapchars mount option. Signed-off-by: Steve French --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/cifs_unicode.c | 96 +++++++++++++++++++++++++++++++++++--------------- fs/cifs/cifs_unicode.h | 15 +++++++- fs/cifs/cifsencrypt.c | 2 +- fs/cifs/cifsglob.h | 3 +- fs/cifs/connect.c | 4 ++- fs/cifs/readdir.c | 4 ++- 7 files changed, 92 insertions(+), 33 deletions(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 9409fa1..8c0b3ff 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -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_MAC_CHR 0x800000 /* remap illegal chars :*?<> ala SFM */ struct cifs_sb_info { struct rb_root tlink_tree; diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 15e9505..bda9c70 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, 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) +/* Convert character using the SFU - "Services for Unix" remapping range */ +static bool +convert_sfu_char(const __u16 src_char, char *target) { - int len = 1; - - if (!mapchar) - goto cp_convert; - /* * BB: Cannot handle remapping UNI_SLASH until all the calls to * build_path_from_dentry are modified, as they use slash as @@ -106,19 +90,73 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, *target = '<'; break; default: - goto cp_convert; + return false; } + return true; +} -out: - return len; +/* 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; +} + + +/* + * 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, bool map_mac_char) +{ + int len = 1; + + if (mapchar && convert_sfu_char(src_char, target)) + return len; + + if (map_mac_char && convert_sfm_char(src_char, target)) + return len; -cp_convert: len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); if (len <= 0) { *target = '?'; len = 1; } - goto out; + return len; } /* @@ -145,7 +183,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, bool mapchar, bool mapmacchar) { int i, charlen, safelen; int outlen = 0; @@ -172,13 +210,15 @@ 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, mapchar, + mapmacchar); 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, mapchar, + mapmacchar); outlen += charlen; } @@ -267,7 +307,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); + false, false); } else { len = strnlen(src, maxlen); len++; diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index d8eac3b..1270077 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -52,6 +52,19 @@ #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) + /* Just define what we want from uniupr.h. We don't want to define the tables * in each source file. */ @@ -75,7 +88,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, bool mapchar, bool mapmacchars); 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 *); diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 4934347..cdeb80b 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) return -ENOMEM; cifs_from_utf16(ses->domainName, (__le16 *)blobptr, attrsize, attrsize, - nls_cp, false); + nls_cp, false, false); break; } } diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 25b8392..53652a2 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -498,7 +498,8 @@ 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_NO_XATTR | \ + CIFS_MOUNT_MAP_SPECIAL_CHR | CIFS_MOUNT_MAP_MAC_CHR | \ CIFS_MOUNT_UNX_EMUL | CIFS_MOUNT_NO_BRL | \ CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_OVERR_UID | \ CIFS_MOUNT_OVERR_GID | CIFS_MOUNT_DYNPERM | \ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 36ca204..b1755a9 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3196,8 +3196,10 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info, cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; if (pvolume_info->server_ino) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; - if (pvolume_info->remap) + if (pvolume_info->remap) { cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_MAC_CHR; + } if (pvolume_info->no_xattr) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; if (pvolume_info->sfu_emul) diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index b334a89..3867d1a 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -716,7 +716,9 @@ static int cifs_filldir(char *find_entry, struct file *file, min_t(size_t, de.namelen, (size_t)max_len), nlt, cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + CIFS_MOUNT_MAP_SPECIAL_CHR, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_MAC_CHR); name.len -= nls_nullsize(nlt); } else { name.name = de.name; -- 1.9.1