summaryrefslogtreecommitdiffstats
path: root/win/tclWinFile.c
diff options
context:
space:
mode:
Diffstat (limited to 'win/tclWinFile.c')
-rw-r--r--win/tclWinFile.c128
1 files changed, 100 insertions, 28 deletions
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index f69ad23..25c6ea4 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -17,7 +17,11 @@
#include <winioctl.h>
#include <shlobj.h>
#include <lm.h> /* For TclpGetUserHome(). */
+#include <userenv.h> /* For TclpGetUserHome(). */
+#ifdef _MSC_VER
+# pragma comment(lib, "userenv.lib")
+#endif
/*
* The number of 100-ns intervals between the Windows system epoch (1601-01-01
* on the proleptic Gregorian calendar) and the Posix epoch (1970-01-01).
@@ -172,7 +176,7 @@ static int WinLink(const TCHAR *LinkSource,
const TCHAR *LinkTarget, int linkAction);
static int WinSymLinkDirectory(const TCHAR *LinkDirectory,
const TCHAR *LinkTarget);
-MODULE_SCOPE void tclWinDebugPanic(const char *format, ...);
+MODULE_SCOPE TCL_NORETURN void tclWinDebugPanic(const char *format, ...);
/*
*--------------------------------------------------------------------
@@ -668,7 +672,7 @@ NativeReadReparse(
HANDLE hFile;
DWORD returnedLength;
- hFile = CreateFile(linkDirPath, desiredAccess, 0, NULL, OPEN_EXISTING,
+ hFile = CreateFile(linkDirPath, desiredAccess, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
@@ -789,7 +793,7 @@ NativeWriteReparse(
*----------------------------------------------------------------------
*/
-void
+TCL_NORETURN void
tclWinDebugPanic(
const char *format, ...)
{
@@ -1241,9 +1245,9 @@ WinIsReserved(
if ((path[0] == 'c' || path[0] == 'C')
&& (path[1] == 'o' || path[1] == 'O')) {
if ((path[2] == 'm' || path[2] == 'M')
- && path[3] >= '1' && path[3] <= '4') {
+ && path[3] >= '1' && path[3] <= '9') {
/*
- * May have match for 'com[1-4]:?', which is a serial port.
+ * May have match for 'com[1-9]:?', which is a serial port.
*/
if (path[4] == '\0') {
@@ -1262,9 +1266,9 @@ WinIsReserved(
} else if ((path[0] == 'l' || path[0] == 'L')
&& (path[1] == 'p' || path[1] == 'P')
&& (path[2] == 't' || path[2] == 'T')) {
- if (path[3] >= '1' && path[3] <= '3') {
+ if (path[3] >= '1' && path[3] <= '9') {
/*
- * May have match for 'lpt[1-3]:?'
+ * May have match for 'lpt[1-9]:?'
*/
if (path[4] == '\0') {
@@ -1461,12 +1465,16 @@ TclpGetUserHome(
} else {
/*
* User exists but has no home dir. Return
- * "{Windows Drive}:/users/default".
+ * "{GetProfilesDirectory}/<user>".
*/
-
- GetWindowsDirectoryW(buf, MAX_PATH);
- Tcl_UniCharToUtfDString(buf, 2, bufferPtr);
- TclDStringAppendLiteral(bufferPtr, "/users/default");
+ DWORD i, size = MAX_PATH;
+ GetProfilesDirectoryW(buf, &size);
+ for (i = 0; i < size; ++i){
+ if (buf[i] == '\\') buf[i] = '/';
+ }
+ Tcl_UniCharToUtfDString(buf, size-1, bufferPtr);
+ Tcl_DStringAppend(bufferPtr, "/", -1);
+ Tcl_DStringAppend(bufferPtr, name, -1);
}
result = Tcl_DStringValue(bufferPtr);
NetApiBufferFree((void *) uiPtr);
@@ -1816,6 +1824,9 @@ TclpObjChdir(
nativePath = Tcl_FSGetNativePath(pathPtr);
+ if (!nativePath) {
+ return -1;
+ }
result = SetCurrentDirectory(nativePath);
if (result == 0) {
@@ -2894,10 +2905,11 @@ ClientData
TclNativeCreateNativeRep(
Tcl_Obj *pathPtr)
{
- char *nativePathPtr, *str;
- Tcl_DString ds;
+ WCHAR *nativePathPtr = NULL;
+ const char *str;
Tcl_Obj *validPathPtr;
- int len;
+ size_t len;
+ WCHAR *wp;
if (TclFSCwdIsNative()) {
/*
@@ -2910,6 +2922,7 @@ TclNativeCreateNativeRep(
if (validPathPtr == NULL) {
return NULL;
}
+ /* refCount of validPathPtr was already incremented in Tcl_FSGetTranslatedPath */
} else {
/*
* Make sure the normalized path is set.
@@ -2919,26 +2932,85 @@ TclNativeCreateNativeRep(
if (validPathPtr == NULL) {
return NULL;
}
+ /* validPathPtr returned from Tcl_FSGetNormalizedPath is owned by Tcl, so incr refCount here */
Tcl_IncrRefCount(validPathPtr);
}
- str = Tcl_GetStringFromObj(validPathPtr, &len);
- if (str[0] == '/' && str[1] == '/' && str[2] == '?' && str[3] == '/') {
- char *p;
+ str = Tcl_GetString(validPathPtr);
+ len = validPathPtr->length;
- for (p = str; p && *p; ++p) {
- if (*p == '/') {
- *p = '\\';
- }
+ if (strlen(str)!=(unsigned int)len) {
+ /* String contains NUL-bytes. This is invalid. */
+ goto done;
+ }
+ /* For a reserved device, strip a possible postfix ':' */
+ len = WinIsReserved(str);
+ if (len == 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) {
+ goto done;
+ }
+ }
+ /* Overallocate 6 chars, making some room for extended paths */
+ wp = nativePathPtr = ckalloc( (len+6) * sizeof(WCHAR) );
+ if (nativePathPtr==0) {
+ goto done;
+ }
+ MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, nativePathPtr, len+1);
+ /*
+ ** 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]==':') {
+ if (wp==nativePathPtr && len>MAX_PATH && (str[2]=='\\' || str[2]=='/')) {
+ memmove(wp+4, wp, len*sizeof(WCHAR));
+ memcpy(wp, L"\\\\?\\", 4*sizeof(WCHAR));
+ wp += 4;
+ }
+ /*
+ ** If (remainder of) path starts with "<drive>:",
+ ** leave the ':' intact.
+ */
+ wp += 2;
+ } 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 == '/') {
+ *wp = '\\';
}
+ ++wp;
}
- Tcl_WinUtfToTChar(str, len, &ds);
- len = Tcl_DStringLength(&ds) + sizeof(WCHAR);
- Tcl_DecrRefCount(validPathPtr);
- nativePathPtr = ckalloc(len);
- memcpy(nativePathPtr, Tcl_DStringValue(&ds), (size_t) len);
- Tcl_DStringFree(&ds);
+ done:
+
+ TclDecrRefCount(validPathPtr);
return nativePathPtr;
}