Attachment "writable.patch" to
ticket [1193497fff]
added by
dkf
2005-10-21 15:48:14.
Index: tclWinFile.c
===================================================================
RCS file: /cvsroot/tcl/tcl/win/tclWinFile.c,v
retrieving revision 1.77
diff -u -r1.77 tclWinFile.c
--- tclWinFile.c 31 Aug 2005 15:12:18 -0000 1.77
+++ tclWinFile.c 21 Oct 2005 08:40:33 -0000
@@ -186,6 +186,7 @@
int checkLinks);
static unsigned short NativeStatMode(DWORD attr, int checkLinks, int isExec);
static int NativeIsExec(CONST TCHAR *path);
+static int NativeIsWritable(CONST TCHAR *path);
static int NativeReadReparse(CONST TCHAR *LinkDirectory,
REPARSE_DATA_BUFFER* buffer);
static int NativeWriteReparse(CONST TCHAR *LinkDirectory,
@@ -1541,7 +1542,7 @@
return -1;
}
- if ((mode & W_OK) && (attr & FILE_ATTRIBUTE_READONLY)) {
+ if ((mode & W_OK) && !NativeIsWritable(nativePath)) {
/*
* File is not writable.
*/
@@ -1780,6 +1781,112 @@
/*
*----------------------------------------------------------------------
*
+ * 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(
+ CONST TCHAR *nativePath)
+{
+ char secDescStaticBuffer[1024], *secDescPtr = secDescStaticBuffer;
+ DWORD secDescLen;
+ HANDLE hToken = NULL;
+ int isWritable = 0; /* Any non-zero value indicates writability. */
+
+#define FILE_SEC_INFO_BITS \
+ OWNER_SECURITY_INFORMATION \
+ | GROUP_SECURITY_INFORMATION \
+ | DACL_SECURITY_INFORMATION
+
+ /*
+ * If we're using the wide version of the Win32 interface, we're not (in
+ * practice) on a platform that needs any of the security-descriptor
+ * managing anyway. So just use the old code.
+ */
+
+ if (tclWinProcs->useWide) {
+ int attr = (*tclWinProcs->getFileAttributesProc)(nativePath);
+
+ isWritable = attr & FILE_ATTRIBUTE_READONLY;
+ goto done;
+ }
+
+ /*
+ * Read the security descriptor for the file or directory. Note that this
+ * may take two syscalls if we have to allocate a larger buffer.
+ */
+
+ if (!GetFileSecurity(nativePath, FILE_SEC_INFO_BITS, secDescPtr,
+ sizeof(secDescStaticBuffer), &secDescLen)) {
+ goto done;
+ }
+ if (sizeof(secDescStaticBuffer) < secDescLen) {
+ DWORD secDescLen2;
+
+ secDescPtr = (char *) ckalloc(secDescLen);
+ if (!GetFileSecurity(nativePath, FILE_SEC_INFO_BITS, secDescPtr,
+ secDescLen, &secDescLen2) || (secDescLen < secDescLen2)) {
+ 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 (OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, &hToken)) {
+ HANDLE hIToken = NULL;
+ BOOL status = DuplicateToken(hToken, SecurityIdentification, &hIToken);
+
+ CloseHandle(hToken);
+ if (status) {
+ GENERIC_MAPPING mapping = {
+ FILE_GENERIC_READ,
+ FILE_GENERIC_WRITE,
+ FILE_GENERIC_EXECUTE,
+ FILE_ALL_ACCESS
+ };
+ char privBuf[sizeof(PRIVILEGE_SET)+3*sizeof(LUID_AND_ATTRIBUTES)];
+ DWORD privLength = sizeof(privBuf);
+ DWORD granted; /* dummy */
+
+ isWritable = AccessCheck(secDescPtr, hIToken, FILE_WRITE_DATA,
+ &mapping, (PPRIVILEGE_SET) privBuf, &privLength, &granted,
+ &status);
+ CloseHandle(hIToken);
+ }
+ }
+
+ /*
+ * Release any temporary space we may be using and convert the writability
+ * value into a boolean for return.
+ */
+
+ done:
+ if (secDescPtr != secDescStaticBuffer) {
+ ckfree(secDescPtr);
+ }
+ return isWritable != 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclpObjChdir --
*
* This function replaces the library version of chdir().