[PATCH] cifs: Add new mount parm mapchars
Steve French [Fri, 29 Apr 2005 05:41:05 +0000 (22:41 -0700)]
For handling seven special characters that shells use for filenames.

This first parts implements conversions from Unicode.

Signed-off-by: Steve French
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

fs/cifs/CHANGES
fs/cifs/README
fs/cifs/TODO
fs/cifs/cifs_fs_sb.h
fs/cifs/cifsproto.h
fs/cifs/connect.c
fs/cifs/misc.c
fs/cifs/readdir.c

index ce16b0a..412b6d2 100644 (file)
@@ -7,7 +7,9 @@ as new protocol extensions. Do not send Get/Set calls for POSIX ACLs
 unless server explicitly claims to support them in CIFS Unix extensions
 POSIX ACL capability bit. Fix packet signing when multiuser mounting with
 different users from the same client to the same server. Fix oops in
-cifs_close.
+cifs_close. Add mount option for remapping reserved characters in
+filenames (also allow recognizing files with created by SFU which have any
+of these seven reserved characters to be recognized).
 
 Version 1.31
 ------------
index 0f20edc..83e0545 100644 (file)
@@ -376,6 +376,17 @@ A partial list of the supported mount options follows:
                attributes) to the server (default) e.g. via setfattr 
                and getfattr utilities. 
   nouser_xattr  Do not allow getfattr/setfattr to get/set xattrs 
+  mapchars      Translate the seven reserved characters 
+                       *?<>|:\ 
+               to the remap range (above 0xF000), which also
+               allows the CIFS client to recognize files created with
+               such characters by Windows's POSIX emulation. This can
+               also be useful when mounting to most versions of Samba
+               (which also forbids creating and opening files
+               whose names contain any of these seven characters).
+               This has no effect if the server does not support
+               Unicode on the wire.
+ nomapchars     Do not translate any of these seven characters (default).
                
 The mount.cifs mount helper also accepts a few mount options before -o
 including:
index f4e3e1f..a692274 100644 (file)
@@ -1,4 +1,4 @@
-version 1.22 July 30, 2004 
+version 1.32 April 3, 2005
 
 A Partial List of Missing Features
 ==================================
@@ -14,7 +14,7 @@ b) Better pam/winbind integration (e.g. to handle uid mapping
 better)
 
 c) multi-user mounts - multiplexed sessionsetups over single vc
-(ie tcp session) - prettying up needed, and more testing needed
+(ie tcp session) - more testing needed
 
 d) Kerberos/SPNEGO session setup support - (started)
 
@@ -67,12 +67,15 @@ q) implement support for security and trusted categories of xattrs
 
 r) Implement O_DIRECT flag on open (already supported on mount)
 
-KNOWN BUGS (updated December 10, 2004)
+KNOWN BUGS (updated April 3, 2005)
 ====================================
+See http://bugzilla.samba.org - search on product "CifsVFS" for
+current bug list.
+
 1) existing symbolic links (Windows reparse points) are recognized but
 can not be created remotely. They are implemented for Samba and those that
-support the CIFS Unix extensions but Samba has a bug currently handling
-symlink text beginning with slash
+support the CIFS Unix extensions, although earlier versions of Samba
+overly restrict the pathnames.
 2) follow_link and readdir code does not follow dfs junctions
 but recognizes them
 3) create of new files to FAT partitions on Windows servers can
@@ -98,7 +101,5 @@ there are some easy changes that can be done to parallelize sequential writes,
 and when signing is disabled to request larger read sizes (larger than 
 negotiated size) and send larger write sizes to modern servers.
 
-4) More exhaustively test the recently added NT4 support against various
-NT4 service pack levels, and fix cifs_setattr for setting file times and 
-size to fall back to level 1 when error invalid level returned.
-
+4) More exhaustively test against less common servers.  More testing
+against Windows 9x, Windows ME servers.
index 77da902..ec00d61 100644 (file)
@@ -23,6 +23,7 @@
 #define CIFS_MOUNT_SERVER_INUM  4 /* inode numbers from uniqueid from server */
 #define CIFS_MOUNT_DIRECT_IO    8 /* do not write nor read through page cache */
 #define CIFS_MOUNT_NO_XATTR  0x10 /* if set - disable xattr support */
+#define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */
 
 struct cifs_sb_info {
        struct cifsTconInfo *tcon;      /* primary mount */
index dd95c2b..b486ba7 100644 (file)
@@ -212,6 +212,8 @@ extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
 extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
                        const unsigned char *searchName, __u64 * inode_number,
                        const struct nls_table *nls_codepage);
+extern int cifs_convertUCSpath(char *target, const __u16 *source, int maxlen,
+                       const struct nls_table * codepage);
 #endif /* CONFIG_CIFS_EXPERIMENTAL */
 
 extern int CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
index 814e709..3d036bf 100644 (file)
@@ -72,6 +72,7 @@ struct smb_vol {
        unsigned no_xattr:1;   /* set if xattr (EA) support should be disabled*/
        unsigned server_ino:1; /* use inode numbers from server ie UniqueId */
        unsigned direct_io:1;
+       unsigned remap:1;   /* set to remap seven reserved chars in filenames */
        unsigned int rsize;
        unsigned int wsize;
        unsigned int sockopt;
@@ -771,6 +772,10 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol)
                        vol->noperm = 0;
                } else if (strnicmp(data, "noperm", 6) == 0) {
                        vol->noperm = 1;
+               } else if (strnicmp(data, "mapchars", 8) == 0) {
+                       vol->remap = 1;
+               } else if (strnicmp(data, "nomapchars", 10) == 0) {
+                       vol->remap = 0;
                } else if (strnicmp(data, "setuids", 7) == 0) {
                        vol->setuids = 1;
                } else if (strnicmp(data, "nosetuids", 9) == 0) {
@@ -1421,6 +1426,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
                if(volume_info.server_ino)
                        cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
+               if(volume_info.remap)
+                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
                if(volume_info.no_xattr)
                        cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
                if(volume_info.direct_io) {
index 7b38d30..f2a0260 100644 (file)
@@ -514,3 +514,72 @@ dump_smb(struct smb_hdr *smb_buf, int smb_buf_length)
        printk( " | %s\n", debug_line);
        return;
 }
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+/* Windows maps these to the user defined 16 bit Unicode range since they are
+   reserved symbols (along with \ and /), otherwise illegal to store
+   in filenames in NTFS */
+#define UNI_ASTERIK     cpu_to_le16('*' + 0xF000)
+#define UNI_QUESTION    cpu_to_le16('?' + 0xF000)
+#define UNI_COLON       cpu_to_le16(':' + 0xF000)
+#define UNI_GRTRTHAN    cpu_to_le16('>' + 0xF000)
+#define UNI_LESSTHAN    cpu_to_le16('<' + 0xF000)
+#define UNI_PIPE        cpu_to_le16('|' + 0xF000)
+#define UNI_SLASH       cpu_to_le16('\\' + 0xF000)
+
+/* Convert 16 bit Unicode pathname from wire format to string in current code
+   page.  Conversion may involve remapping up the seven characters that are
+   only legal in POSIX-like OS (if they are present in the string). Path
+   names are little endian 16 bit Unicode on the wire */
+int
+cifs_convertUCSpath(char *target, const __le16 * source, int maxlen,
+                   const struct nls_table * cp)
+{
+       int i,j,len;
+       wchar_t src_char;
+
+       for(i = 0, j = 0; i < maxlen; i++) {
+               src_char = le16_to_cpu(source[i]);
+               switch (src_char) {
+                       case 0:
+                               goto cUCS_out; /* BB check this BB */
+                       case UNI_COLON:
+                               target[j] = ':';
+                               break;
+                       case UNI_ASTERIK:
+                               target[j] = '*';
+                               break;
+                       case UNI_QUESTION:
+                               target[j] = '?';
+                               break;
+                       case UNI_SLASH:
+                               target[j] = '\\'; /* BB check this - is there risk here of converting path sep BB */
+                               break;
+                       case UNI_PIPE:
+                               target[j] = '|';
+                               break;
+                       case UNI_GRTRTHAN:
+                               target[j] = '>';
+                               break;
+                       case UNI_LESSTHAN:
+                               target[j] = '<';
+                       default: 
+                               len = cp->uni2char(src_char, &target[j], 
+                                               NLS_MAX_CHARSET_SIZE);
+                               if(len > 0) {
+                                       j += len;
+                                       continue;
+                               } else {
+                                       target[j] = '?';
+                               }
+               }
+               j++;
+               /* check to make sure we do not overrun callers allocated temp buffer */
+               if(j >= (2 * NAME_MAX))
+                       break;
+       }
+cUCS_out:
+       target[j] = 0;
+       return j;
+}
+#endif /* CIFS_EXPERIMENTAL */
index 07838a5..4a33add 100644 (file)
@@ -600,7 +600,14 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst,
        if(unicode) {
                /* BB fixme - test with long names */
                /* Note converted filename can be longer than in unicode */
-               pqst->len = cifs_strfromUCS_le((char *)pqst->name,(wchar_t *)filename,len/2,nlt);
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+               if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
+                       pqst->len = cifs_convertUCSpath((char *)pqst->name,
+                                       (__le16 *)filename, len/2, nlt);
+               else
+#endif /* CIFS_EXPERIMENTAL */
+                       pqst->len = cifs_strfromUCS_le((char *)pqst->name,
+                                       (wchar_t *)filename,len/2,nlt);
        } else {
                pqst->name = filename;
                pqst->len = len;
@@ -829,7 +836,11 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
                end_of_smb = cifsFile->srch_inf.ntwrk_buf_start +
                        smbCalcSize((struct smb_hdr *)
                                    cifsFile->srch_inf.ntwrk_buf_start);
-               tmp_buf = kmalloc(NAME_MAX+1,GFP_KERNEL);
+               /* To be safe - for UCS to UTF-8 with strings loaded
+               with the rare long characters alloc more to account for
+               such multibyte target UTF-8 characters. cifs_unicode.c,
+               which actually does the conversion, has the same limit */
+               tmp_buf = kmalloc((2 * NAME_MAX) + 4, GFP_KERNEL);
                for(i=0;(i<num_to_fill) && (rc == 0);i++) {
                        if(current_entry == NULL) {
                                /* evaluate whether this case is an error */