Attachment "win-tclWinFile.c.diff-u" to
ticket [1344540fff]
added by
jfg118
2006-03-11 05:13:59.
--- ../../../tcl8.4.12-src/tcl8.4.12/win/tclWinFile.c 2005-11-15 08:41:41.000000000 -0800
+++ tclWinFile.c 2006-03-10 14:02:35.883563200 -0800
@@ -179,6 +179,7 @@
static int NativeStat(CONST TCHAR *path, Tcl_StatBuf *statPtr, int checkLinks);
static unsigned short NativeStatMode(DWORD attr, int checkLinks, int isExec);
static int NativeIsExec(CONST TCHAR *path);
+static int NativeIsWritable(DWORD attr, CONST TCHAR *path);
static int NativeReadReparse(CONST TCHAR* LinkDirectory,
REPARSE_DATA_BUFFER* buffer);
static int NativeWriteReparse(CONST TCHAR* LinkDirectory,
@@ -1341,7 +1342,7 @@
return -1;
}
- if ((mode & W_OK) && (attr & FILE_ATTRIBUTE_READONLY)) {
+ if ((mode & W_OK) && !NativeIsWritable(attr, nativePath)) {
/*
* File is not writable.
*/
@@ -1368,6 +1369,164 @@
return 0;
}
+ /*
+ *----------------------------------------------------------------------
+ *
+ * NativeIsWritable --
+ *
+ * Determine if a path is writable. On Windows this requires more than a
+ * simple read only flag check for directories because Windows uses this
+ * as a flag to customize the folder options. [Bug 1193497]
+ *
+ * Results:
+ * 1 = writable, 0 = not.
+ *
+ * Side effects:
+ * May smash the last error value (errors are just reported by this
+ * function as non-writable files).
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+NativeIsWritable(
+ DWORD attr,
+ CONST TCHAR *nativePath)
+{
+ BYTE *secDescPtr = 0;
+ DWORD secDescLen = 0;
+ HANDLE hToken = NULL;
+ BOOL isWritable = FALSE;
+
+ /*
+ * One time initialization, dynamically load Windows NT features
+ */
+
+ static const SECURITY_INFORMATION infoBits = OWNER_SECURITY_INFORMATION
+ | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
+
+ static BOOL (WINAPI *getFileSecurityProc)(LPCSTR, SECURITY_INFORMATION,
+ PSECURITY_DESCRIPTOR, DWORD, LPDWORD);
+ static BOOL (WINAPI *openProcessTokenProc)(HANDLE, DWORD, PHANDLE);
+ static BOOL (WINAPI *duplicateTokenProc)(HANDLE, SECURITY_IMPERSONATION_LEVEL,
+ PHANDLE);
+ static VOID (WINAPI *mapGenericMaskProc)(PDWORD, PGENERIC_MAPPING);
+ static BOOL (WINAPI *accessCheckProc)(PSECURITY_DESCRIPTOR, HANDLE, DWORD,
+ PGENERIC_MAPPING, PPRIVILEGE_SET, LPDWORD, LPDWORD, LPBOOL);
+
+ static int initialized = 0;
+
+ if (!initialized) {
+ TCL_DECLARE_MUTEX(writableMutex)
+ Tcl_MutexLock(&writableMutex);
+ if (!initialized) {
+ HINSTANCE hInstance = LoadLibrary("Advapi32");
+ if (hInstance != NULL) {
+ getFileSecurityProc = (BOOL (WINAPI *)(LPCSTR,
+ SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, DWORD, LPDWORD))
+ GetProcAddress(hInstance, tclWinProcs->useWide ?
+ "GetFileSecurityW" : "GetFileSecurityA");
+ openProcessTokenProc = (BOOL (WINAPI *)(HANDLE, DWORD,
+ PHANDLE)) GetProcAddress(hInstance, "OpenProcessToken");
+ duplicateTokenProc = (BOOL (WINAPI *)(HANDLE,
+ SECURITY_IMPERSONATION_LEVEL, PHANDLE))
+ GetProcAddress(hInstance, "DuplicateToken");
+ mapGenericMaskProc = (VOID (WINAPI *)(PDWORD,
+ PGENERIC_MAPPING)) GetProcAddress(hInstance,
+ "MapGenericMask");
+ accessCheckProc = (BOOL (WINAPI *)(PSECURITY_DESCRIPTOR,
+ HANDLE, DWORD, PGENERIC_MAPPING, PPRIVILEGE_SET,
+ LPDWORD, LPDWORD, LPBOOL)) GetProcAddress(hInstance,
+ "AccessCheck");
+ if (getFileSecurityProc && openProcessTokenProc
+ && duplicateTokenProc && mapGenericMaskProc
+ && accessCheckProc) {
+ initialized = 1;
+ }
+ }
+ if (!initialized)
+ initialized = -1;
+ }
+ Tcl_MutexUnlock(&writableMutex);
+ }
+
+ /*
+ * Windows NT features not available, default back to readonly flag
+ */
+ if (initialized < 0)
+ return (attr & FILE_ATTRIBUTE_READONLY) ? 0 : 1;
+
+ /*
+ * If this is not a directory and it is marked read-only then just return
+ * not writable. No need to check the ACL.
+ */
+ if (!(attr & FILE_ATTRIBUTE_DIRECTORY) && (attr & FILE_ATTRIBUTE_READONLY))
+ return 0;
+
+ /*
+ * Read the security descriptor for the file or directory. Note the
+ * first call obtains the size of the security descriptor.
+ */
+
+ if (!getFileSecurityProc(nativePath, infoBits, NULL, 0, &secDescLen)) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ DWORD secDescLen2 = 0;
+ secDescPtr = (BYTE *) ckalloc(secDescLen);
+ if (!getFileSecurityProc(nativePath, infoBits, secDescPtr,
+ secDescLen, &secDescLen2)
+ || (secDescLen < secDescLen2)) {
+ goto done;
+ }
+ } else {
+ goto done;
+ }
+ }
+
+ /*
+ * Get the security identity of the process and (if successful) use it
+ * with the security descriptor of the file to find out if the file really
+ * is writable.
+ */
+ if (openProcessTokenProc(GetCurrentProcess(), TOKEN_DUPLICATE, &hToken)) {
+ HANDLE hIToken = NULL;
+ BOOL status = duplicateTokenProc(hToken, SecurityIdentification,
+ &hIToken);
+
+ CloseHandle(hToken);
+ if (status) {
+ PRIVILEGE_SET privilegeSet;
+ DWORD privilegeSetLength = sizeof(privilegeSet);
+ DWORD granted; /* unused */
+ DWORD accessDesired = ACCESS_WRITE;
+
+ /* Initialize generic mapping structure to map all. */
+ GENERIC_MAPPING GenericMapping;
+ memset(&GenericMapping, 0xff, sizeof(GENERIC_MAPPING));
+ GenericMapping.GenericRead = ACCESS_READ;
+ GenericMapping.GenericWrite = ACCESS_WRITE;
+ GenericMapping.GenericExecute = 0;
+ GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE;
+ mapGenericMaskProc(&accessDesired, &GenericMapping);
+
+ accessCheckProc(secDescPtr, hIToken, accessDesired,
+ &GenericMapping, &privilegeSet, &privilegeSetLength, &granted,
+ &isWritable);
+ CloseHandle(hIToken);
+ }
+ }
+
+ /*
+ * Release any temporary space we may be using and convert the writability
+ * value into a boolean for return.
+ */
+
+ done:
+ if (secDescPtr) {
+ ckfree(secDescPtr);
+ }
+ return isWritable != 0;
+}
+
+
/*
*----------------------------------------------------------------------
*