summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2014-05-09 12:03:58 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2014-05-09 12:03:58 (GMT)
commitc9eee4b29ab5763b1c99d721139fa5b7180721ba (patch)
tree69dbd10c630d1f3b12ea46d522ce6e486f506d6e
parent2f3a2a3308b055bd58c2b4cd80aa0202d913ada2 (diff)
parent435b75fc24a85e806029ac4159863e6bf87b6412 (diff)
downloadtcl-c9eee4b29ab5763b1c99d721139fa5b7180721ba.zip
tcl-c9eee4b29ab5763b1c99d721139fa5b7180721ba.tar.gz
tcl-c9eee4b29ab5763b1c99d721139fa5b7180721ba.tar.bz2
Fix [3389978]: Support for paths longer than MAX_PATH on Windows.
-rw-r--r--tests/winFCmd.test4
-rw-r--r--win/tclWinFile.c85
2 files changed, 67 insertions, 22 deletions
diff --git a/tests/winFCmd.test b/tests/winFCmd.test
index bd50328..28257c6 100644
--- a/tests/winFCmd.test
+++ b/tests/winFCmd.test
@@ -1385,10 +1385,10 @@ test winFCmd-19.5 {Windows extended path names} -constraints nt -setup {
list [catch {
set f [open $tmpfile [list WRONLY CREAT]]
close $f
- } res] errormsg ;#$res
+ } res] $res
} -cleanup {
catch {file delete $tmpfile}
-} -result [list 1 errormsg]
+} -result [list 0 {}]
test winFCmd-19.6 {Windows extended path names} -constraints nt -setup {
set tmpfile [file join $::env(TEMP) tcl[string repeat x 248].tmp]
set tmpfile //?/[file normalize $tmpfile]
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index fc0ac9e..5761eeb 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -2897,10 +2897,10 @@ ClientData
TclNativeCreateNativeRep(
Tcl_Obj *pathPtr)
{
- char *nativePathPtr, *str;
- Tcl_DString ds;
+ WCHAR *nativePathPtr;
+ const char *str;
Tcl_Obj *validPathPtr;
- int len, i = 2;
+ int len;
WCHAR *wp;
if (TclFSCwdIsNative()) {
@@ -2927,27 +2927,72 @@ TclNativeCreateNativeRep(
}
str = Tcl_GetStringFromObj(validPathPtr, &len);
- Tcl_WinUtfToTChar(str, len, &ds);
- len = Tcl_DStringLength(&ds) + sizeof(WCHAR);
- wp = (WCHAR *) Tcl_DStringValue(&ds);
- for (i=sizeof(WCHAR); i<len; ++wp,i+=sizeof(WCHAR)) {
- if ( (*wp < ' ') || wcschr(L"\"*<>|", *wp) ){
- if (!*wp){
- /* See bug [3118489]: NUL in filenames */
- Tcl_DecrRefCount(validPathPtr);
- Tcl_DStringFree(&ds);
- return NULL;
- }
+
+ if (strlen(str)!=len) {
+ /* String contains NUL-bytes. This is invalid. */
+ return 0;
+ }
+ /* Let MultiByteToWideChar check for other invalid sequences, like
+ * 0xC0 0x80 (== overlong NUL). See bug [3118489]: NUL in filenames */
+ len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, 0, 0);
+ if (len==0) {
+ return 0;
+ }
+ /* Overallocate 6 chars, making some room for extended paths */
+ wp = nativePathPtr = ckalloc( (len+6) * sizeof(WCHAR) );
+ if (nativePathPtr==0) {
+ return 0;
+ }
+ MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, nativePathPtr, len);
+ /*
+ ** If path starts with "//?/" or "\\?\" (extended path), translate
+ ** any slashes to backslashes but leave the '?' intact
+ */
+ if ((str[0]=='\\' || str[0]=='/') && (str[1]=='\\' || str[1]=='/')
+ && str[2]=='?' && (str[3]=='\\' || str[3]=='/')) {
+ wp[0] = wp[1] = wp[3] = '\\';
+ str += 4;
+ wp += 4;
+ }
+ /*
+ ** If there is no "\\?\" prefix but there is a drive or UNC
+ ** path prefix and the path is larger than MAX_PATH chars,
+ ** no Win32 API function can handle that unless it is
+ ** prefixed with the extended path prefix. See:
+ ** <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath>
+ **/
+ if (((str[0]>='A'&&str[0]<='Z') || (str[0]>='a'&&str[0]<='z'))
+ && str[1]==':' && (str[2]=='\\' || str[2]=='/')) {
+ if (wp==nativePathPtr && len>MAX_PATH) {
+ memmove(wp+4, wp, len*sizeof(WCHAR));
+ memcpy(wp, L"\\\\?\\", 4*sizeof(WCHAR));
+ wp += 4;
+ }
+ /*
+ ** If (remainder of) path starts with "<drive>:/" or "<drive>:\",
+ ** leave the ':' intact but translate the backslash to a slash.
+ */
+ wp[2] = '\\';
+ wp += 3;
+ } else if (wp==nativePathPtr && len>MAX_PATH
+ && (str[0]=='\\' || str[0]=='/')
+ && (str[1]=='\\' || str[1]=='/') && str[2]!='?') {
+ memmove(wp+6, wp, len*sizeof(WCHAR));
+ memcpy(wp, L"\\\\?\\UNC", 7*sizeof(WCHAR));
+ wp += 7;
+ }
+ /*
+ ** In the remainder of the path, translate invalid characters to
+ ** characters in the Unicode private use area.
+ */
+ while (*wp != '\0') {
+ if ((*wp < ' ') || wcschr(L"\"*:<>?|", *wp)) {
*wp |= 0xF000;
- }else if (*wp=='/') {
+ } else if (*wp == '/') {
*wp = '\\';
}
+ ++wp;
}
- Tcl_DecrRefCount(validPathPtr);
- nativePathPtr = ckalloc(len);
- memcpy(nativePathPtr, Tcl_DStringValue(&ds), (size_t) len);
-
- Tcl_DStringFree(&ds);
return nativePathPtr;
}