summaryrefslogtreecommitdiffstats
path: root/win
diff options
context:
space:
mode:
authorvincentdarley <vincentdarley>2006-03-09 11:30:28 (GMT)
committervincentdarley <vincentdarley>2006-03-09 11:30:28 (GMT)
commit0f8885eba3047270f8d0d21e93cddc8d2358807e (patch)
tree027636376eae811cf8dab0f8597c26aeaa979d31 /win
parent9dcbe336bddd91bb7fab7be8e33ce7fed7f229ce (diff)
downloadtcl-0f8885eba3047270f8d0d21e93cddc8d2358807e.zip
tcl-0f8885eba3047270f8d0d21e93cddc8d2358807e.tar.gz
tcl-0f8885eba3047270f8d0d21e93cddc8d2358807e.tar.bz2
fix to file writable in certain XP directories
Diffstat (limited to 'win')
-rw-r--r--win/tclWinFile.c155
1 files changed, 153 insertions, 2 deletions
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index f65e1c1..1284671 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinFile.c,v 1.82 2006/01/12 16:19:04 vincentdarley Exp $
+ * RCS: @(#) $Id: tclWinFile.c,v 1.83 2006/03/09 11:30:29 vincentdarley Exp $
*/
/* #define _WIN32_WINNT 0x0500 */
@@ -188,6 +188,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,
@@ -1546,7 +1547,7 @@ NativeAccess(
return -1;
}
- if ((mode & W_OK) && (attr & FILE_ATTRIBUTE_READONLY)) {
+ if ((mode & W_OK) && !NativeIsWritable(attr, nativePath)) {
/*
* File is not writable.
*/
@@ -1789,6 +1790,156 @@ NativeIsExec(
/*
*----------------------------------------------------------------------
*
+ * 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;
+
+ /*
+ * 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclpObjChdir --
*
* This function replaces the library version of chdir().