diff options
Diffstat (limited to 'win/tclWinTest.c')
| -rw-r--r-- | win/tclWinTest.c | 682 |
1 files changed, 589 insertions, 93 deletions
diff --git a/win/tclWinTest.c b/win/tclWinTest.c index 8d290a8..e493fbf 100644 --- a/win/tclWinTest.c +++ b/win/tclWinTest.c @@ -1,39 +1,56 @@ -/* +/* * tclWinTest.c -- * * Contains commands for platform specific tests on Windows. * * Copyright (c) 1996 Sun Microsystems, Inc. * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclWinTest.c,v 1.5 2000/11/21 21:33:43 andreas_kupries Exp $ + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ -#include "tclWinInt.h" +#include "tclInt.h" /* - * Forward declarations of procedures defined later in this file: + * For TestplatformChmod on Windows */ -int TclplatformtestInit _ANSI_ARGS_((Tcl_Interp *interp)); -static int TesteventloopCmd _ANSI_ARGS_((ClientData dummy, - Tcl_Interp *interp, int argc, char **argv)); -static int TestvolumetypeCmd _ANSI_ARGS_((ClientData dummy, - Tcl_Interp *interp, int objc, - Tcl_Obj *CONST objv[])); -static int TestwinclockCmd _ANSI_ARGS_(( ClientData dummy, - Tcl_Interp* interp, - int objc, - Tcl_Obj *CONST objv[] )); +#ifdef __WIN32__ +#include <aclapi.h> +#endif + +/* + * MinGW 3.4.2 does not define this. + */ +#ifndef INHERITED_ACE +#define INHERITED_ACE (0x10) +#endif + +/* + * Forward declarations of functions defined later in this file: + */ + +int TclplatformtestInit(Tcl_Interp *interp); +static int TesteventloopCmd(ClientData dummy, Tcl_Interp *interp, + int argc, const char **argv); +static int TestvolumetypeCmd(ClientData dummy, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int TestwinclockCmd(ClientData dummy, Tcl_Interp* interp, + int objc, Tcl_Obj *const objv[]); +static int TestwinsleepCmd(ClientData dummy, Tcl_Interp* interp, + int objc, Tcl_Obj *const objv[]); +static Tcl_ObjCmdProc TestExceptionCmd; +static int TestplatformChmod(const char *nativePath, int pmode); +static int TestchmodCmd(ClientData dummy, + Tcl_Interp *interp, int argc, const char **argv); /* *---------------------------------------------------------------------- * * TclplatformtestInit -- * - * Defines commands that test platform specific functionality for - * Unix platforms. + * Defines commands that test platform specific functionality for Windows + * platforms. * * Results: * A standard Tcl result. @@ -45,19 +62,20 @@ static int TestwinclockCmd _ANSI_ARGS_(( ClientData dummy, */ int -TclplatformtestInit(interp) - Tcl_Interp *interp; /* Interpreter to add commands to. */ +TclplatformtestInit( + Tcl_Interp *interp) /* Interpreter to add commands to. */ { /* * Add commands for platform specific tests for Windows here. */ - Tcl_CreateCommand(interp, "testeventloop", TesteventloopCmd, - (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "testchmod", TestchmodCmd, NULL, NULL); + Tcl_CreateCommand(interp, "testeventloop", TesteventloopCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "testvolumetype", TestvolumetypeCmd, - (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateObjCommand(interp, "testwinclock", TestwinclockCmd, - (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + NULL, NULL); + Tcl_CreateObjCommand(interp, "testwinclock", TestwinclockCmd, NULL, NULL); + Tcl_CreateObjCommand(interp, "testwinsleep", TestwinsleepCmd, NULL, NULL); + Tcl_CreateObjCommand(interp, "testexcept", TestExceptionCmd, NULL, NULL); return TCL_OK; } @@ -66,9 +84,9 @@ TclplatformtestInit(interp) * * TesteventloopCmd -- * - * This procedure implements the "testeventloop" command. It is - * used to test the Tcl notifier from an "external" event loop - * (i.e. not Tcl_DoOneEvent()). + * This function implements the "testeventloop" command. It is used to + * test the Tcl notifier from an "external" event loop (i.e. not + * Tcl_DoOneEvent()). * * Results: * A standard Tcl result. @@ -80,27 +98,25 @@ TclplatformtestInit(interp) */ static int -TesteventloopCmd(clientData, interp, argc, argv) - ClientData clientData; /* Not used. */ - Tcl_Interp *interp; /* Current interpreter. */ - int argc; /* Number of arguments. */ - char **argv; /* Argument strings. */ +TesteventloopCmd( + ClientData clientData, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int argc, /* Number of arguments. */ + const char **argv) /* Argument strings. */ { - static int *framePtr = NULL; /* Pointer to integer on stack frame of - * innermost invocation of the "wait" - * subcommand. */ + static int *framePtr = NULL;/* Pointer to integer on stack frame of + * innermost invocation of the "wait" + * subcommand. */ - if (argc < 2) { + if (argc < 2) { Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0], - " option ... \"", (char *) NULL); - return TCL_ERROR; + " option ... \"", NULL); + return TCL_ERROR; } if (strcmp(argv[1], "done") == 0) { *framePtr = 1; } else if (strcmp(argv[1], "wait") == 0) { - int *oldFramePtr; - int done; - MSG msg; + int *oldFramePtr, done; int oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); /* @@ -111,19 +127,21 @@ TesteventloopCmd(clientData, interp, argc, argv) framePtr = &done; /* - * Enter a standard Windows event loop until the flag changes. - * Note that we do not explicitly call Tcl_ServiceEvent(). + * Enter a standard Windows event loop until the flag changes. Note + * that we do not explicitly call Tcl_ServiceEvent(). */ done = 0; while (!done) { + MSG msg; + if (!GetMessage(&msg, NULL, 0, 0)) { /* - * The application is exiting, so repost the quit message - * and start unwinding. + * The application is exiting, so repost the quit message and + * start unwinding. */ - PostQuitMessage(msg.wParam); + PostQuitMessage((int) msg.wParam); break; } TranslateMessage(&msg); @@ -133,7 +151,7 @@ TesteventloopCmd(clientData, interp, argc, argv) framePtr = oldFramePtr; } else { Tcl_AppendResult(interp, "bad option \"", argv[1], - "\": must be done or wait", (char *) NULL); + "\": must be done or wait", NULL); return TCL_ERROR; } return TCL_OK; @@ -144,8 +162,8 @@ TesteventloopCmd(clientData, interp, argc, argv) * * Testvolumetype -- * - * This procedure implements the "testvolumetype" command. It is - * used to check the volume type (FAT, NTFS) of a volume. + * This function implements the "testvolumetype" command. It is used to + * check the volume type (FAT, NTFS) of a volume. * * Results: * A standard Tcl result. @@ -157,11 +175,11 @@ TesteventloopCmd(clientData, interp, argc, argv) */ static int -TestvolumetypeCmd(clientData, interp, objc, objv) - ClientData clientData; /* Not used. */ - Tcl_Interp *interp; /* Current interpreter. */ - int objc; /* Number of arguments. */ - Tcl_Obj *CONST objv[]; /* Argument objects. */ +TestvolumetypeCmd( + ClientData clientData, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { #define VOL_BUF_SIZE 32 int found; @@ -170,23 +188,24 @@ TestvolumetypeCmd(clientData, interp, objc, objv) if (objc > 2) { Tcl_WrongNumArgs(interp, 1, objv, "?name?"); - return TCL_ERROR; + return TCL_ERROR; } if (objc == 2) { /* - * path has to be really a proper volume, but we don't - * get query APIs for that until NT5 + * path has to be really a proper volume, but we don't get query APIs + * for that until NT5 */ + path = Tcl_GetString(objv[1]); } else { path = NULL; } - found = GetVolumeInformationA(path, NULL, 0, NULL, NULL, - NULL, volType, VOL_BUF_SIZE); + found = GetVolumeInformationA(path, NULL, 0, NULL, NULL, NULL, volType, + VOL_BUF_SIZE); if (found == 0) { Tcl_AppendResult(interp, "could not get volume type for \"", - (path?path:""), "\"", (char *) NULL); + (path?path:""), "\"", NULL); TclWinConvertError(GetLastError()); return TCL_ERROR; } @@ -198,11 +217,11 @@ TestvolumetypeCmd(clientData, interp, objc, objv) /* *---------------------------------------------------------------------- * - * TestclockCmd -- + * TestwinclockCmd -- * - * Command that returns the seconds and microseconds portions of - * the system clock and of the Tcl clock so that they can be - * compared to validate that the Tcl clock is staying in sync. + * Command that returns the seconds and microseconds portions of the + * system clock and of the Tcl clock so that they can be compared to + * validate that the Tcl clock is staying in sync. * * Usage: * testclock @@ -211,9 +230,9 @@ TestvolumetypeCmd(clientData, interp, objc, objv) * None. * * Results: - * Returns a standard Tcl result comprising a four-element list: - * the seconds and microseconds portions of the system clock, - * and the seconds and microseconds portions of the Tcl clock. + * Returns a standard Tcl result comprising a four-element list: the + * seconds and microseconds portions of the system clock, and the seconds + * and microseconds portions of the Tcl clock. * * Side effects: * None. @@ -222,46 +241,523 @@ TestvolumetypeCmd(clientData, interp, objc, objv) */ static int -TestwinclockCmd( ClientData dummy, - /* Unused */ - Tcl_Interp* interp, - /* Tcl interpreter */ - int objc, - /* Argument count */ - Tcl_Obj *CONST objv[] ) - /* Argument vector */ +TestwinclockCmd( + ClientData dummy, /* Unused */ + Tcl_Interp* interp, /* Tcl interpreter */ + int objc, /* Argument count */ + Tcl_Obj *const objv[]) /* Argument vector */ { - CONST static FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE }; - /* The Posix epoch, expressed as a - * Windows FILETIME */ + static const FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE }; + /* The Posix epoch, expressed as a Windows + * FILETIME */ Tcl_Time tclTime; /* Tcl clock */ FILETIME sysTime; /* System clock */ - Tcl_Obj* result; /* Result of the command */ + Tcl_Obj *result; /* Result of the command */ LARGE_INTEGER t1, t2; + LARGE_INTEGER p1, p2; - if ( objc != 1 ) { - Tcl_WrongNumArgs( interp, 1, objv, "" ); + if (objc != 1) { + Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } - TclpGetTime( &tclTime ); - GetSystemTimeAsFileTime( &sysTime ); + QueryPerformanceCounter(&p1); + + Tcl_GetTime(&tclTime); + GetSystemTimeAsFileTime(&sysTime); t1.LowPart = posixEpoch.dwLowDateTime; t1.HighPart = posixEpoch.dwHighDateTime; t2.LowPart = sysTime.dwLowDateTime; t2.HighPart = sysTime.dwHighDateTime; t2.QuadPart -= t1.QuadPart; + QueryPerformanceCounter(&p2); + result = Tcl_NewObj(); - Tcl_ListObjAppendElement - ( interp, result, Tcl_NewIntObj( (int) (t2.QuadPart / 10000000 ) ) ); - Tcl_ListObjAppendElement - ( interp, result, - Tcl_NewIntObj( (int) ( (t2.QuadPart / 10 ) % 1000000 ) ) ); - Tcl_ListObjAppendElement( interp, result, Tcl_NewIntObj( tclTime.sec ) ); - Tcl_ListObjAppendElement( interp, result, Tcl_NewIntObj( tclTime.usec ) ); + Tcl_ListObjAppendElement(interp, result, + Tcl_NewIntObj((int) (t2.QuadPart / 10000000))); + Tcl_ListObjAppendElement(interp, result, + Tcl_NewIntObj((int) ((t2.QuadPart / 10) % 1000000))); + Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(tclTime.sec)); + Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(tclTime.usec)); + + Tcl_ListObjAppendElement(interp, result, Tcl_NewWideIntObj(p1.QuadPart)); + Tcl_ListObjAppendElement(interp, result, Tcl_NewWideIntObj(p2.QuadPart)); + + Tcl_SetObjResult(interp, result); + + return TCL_OK; +} + +static int +TestwinsleepCmd( + ClientData clientData, /* Unused */ + Tcl_Interp* interp, /* Tcl interpreter */ + int objc, /* Parameter count */ + Tcl_Obj *const * objv) /* Parameter vector */ +{ + int ms; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "ms"); + return TCL_ERROR; + } + if (Tcl_GetIntFromObj(interp, objv[1], &ms) != TCL_OK) { + return TCL_ERROR; + } + Sleep((DWORD) ms); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TestExceptionCmd -- + * + * Causes this process to end with the named exception. Used for testing + * Tcl_WaitPid(). + * + * Usage: + * testexcept <type> + * + * Parameters: + * Type of exception. + * + * Results: + * None, this process closes now and doesn't return. + * + * Side effects: + * This Tcl process closes, hard... Bang! + * + *---------------------------------------------------------------------- + */ + +static int +TestExceptionCmd( + ClientData dummy, /* Unused */ + Tcl_Interp* interp, /* Tcl interpreter */ + int objc, /* Argument count */ + Tcl_Obj *const objv[]) /* Argument vector */ +{ + static const char *cmds[] = { + "access_violation", "datatype_misalignment", "array_bounds", + "float_denormal", "float_divbyzero", "float_inexact", + "float_invalidop", "float_overflow", "float_stack", "float_underflow", + "int_divbyzero", "int_overflow", "private_instruction", "inpageerror", + "illegal_instruction", "noncontinue", "stack_overflow", + "invalid_disp", "guard_page", "invalid_handle", "ctrl+c", + NULL + }; + static const DWORD exceptions[] = { + EXCEPTION_ACCESS_VIOLATION, EXCEPTION_DATATYPE_MISALIGNMENT, + EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_FLT_DENORMAL_OPERAND, + EXCEPTION_FLT_DIVIDE_BY_ZERO, EXCEPTION_FLT_INEXACT_RESULT, + EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW, + EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, + EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, + EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_IN_PAGE_ERROR, + EXCEPTION_ILLEGAL_INSTRUCTION, EXCEPTION_NONCONTINUABLE_EXCEPTION, + EXCEPTION_STACK_OVERFLOW, EXCEPTION_INVALID_DISPOSITION, + EXCEPTION_GUARD_PAGE, EXCEPTION_INVALID_HANDLE, CONTROL_C_EXIT + }; + int cmd; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 0, objv, "<type-of-exception>"); + return TCL_ERROR; + } + if (Tcl_GetIndexFromObj(interp, objv[1], cmds, "command", 0, + &cmd) != TCL_OK) { + return TCL_ERROR; + } + + /* + * Make sure the GPF dialog doesn't popup. + */ - Tcl_SetObjResult( interp, result ); + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + /* + * As Tcl does not handle structured exceptions, this falls all the way + * back up the instruction stack to the C run-time portion that called + * main() where the process will now be terminated with this exception + * code by the default handler the C run-time provides. + */ + + /* SMASH! */ + RaiseException(exceptions[cmd], EXCEPTION_NONCONTINUABLE, 0, NULL); + + /* NOTREACHED */ return TCL_OK; } + +static int +TestplatformChmod( + const char *nativePath, + int pmode) +{ + typedef DWORD (WINAPI *getSidLengthRequiredDef)(UCHAR); + typedef BOOL (WINAPI *initializeSidDef)(PSID, PSID_IDENTIFIER_AUTHORITY, + BYTE); + typedef PDWORD (WINAPI *getSidSubAuthorityDef)(PSID, DWORD); + typedef DWORD (WINAPI *setNamedSecurityInfoADef)(IN LPSTR, + IN SE_OBJECT_TYPE, IN SECURITY_INFORMATION, IN PSID, IN PSID, + IN PACL, IN PACL); + typedef BOOL (WINAPI *getAceDef)(PACL, DWORD, LPVOID *); + typedef BOOL (WINAPI *addAceDef)(PACL, DWORD, DWORD, LPVOID, DWORD); + typedef BOOL (WINAPI *equalSidDef)(PSID, PSID); + typedef BOOL (WINAPI *addAccessDeniedAceDef)(PACL, DWORD, DWORD, PSID); + typedef BOOL (WINAPI *initializeAclDef)(PACL, DWORD, DWORD); + typedef DWORD (WINAPI *getLengthSidDef)(PSID); + typedef BOOL (WINAPI *getAclInformationDef)(PACL, LPVOID, DWORD, + ACL_INFORMATION_CLASS); + typedef BOOL (WINAPI *getSecurityDescriptorDaclDef)(PSECURITY_DESCRIPTOR, + LPBOOL, PACL *, LPBOOL); + typedef BOOL (WINAPI *lookupAccountNameADef)(LPCSTR, LPCSTR, PSID, + PDWORD, LPSTR, LPDWORD, PSID_NAME_USE); + typedef BOOL (WINAPI *getFileSecurityADef)(LPCSTR, SECURITY_INFORMATION, + PSECURITY_DESCRIPTOR, DWORD, LPDWORD); + + static const SECURITY_INFORMATION infoBits = OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; + static const DWORD readOnlyMask = FILE_DELETE_CHILD | FILE_ADD_FILE + | FILE_ADD_SUBDIRECTORY | FILE_WRITE_EA | FILE_APPEND_DATA + | FILE_WRITE_DATA | DELETE; + + /* + * References to security functions (only available on NT and later). + */ + + static getSidLengthRequiredDef getSidLengthRequiredProc; + static initializeSidDef initializeSidProc; + static getSidSubAuthorityDef getSidSubAuthorityProc; + static setNamedSecurityInfoADef setNamedSecurityInfoProc; + static getAceDef getAceProc; + static addAceDef addAceProc; + static equalSidDef equalSidProc; + static addAccessDeniedAceDef addAccessDeniedAceProc; + static initializeAclDef initializeAclProc; + static getLengthSidDef getLengthSidProc; + static getAclInformationDef getAclInformationProc; + static getSecurityDescriptorDaclDef getSecurityDescriptorDaclProc; + static lookupAccountNameADef lookupAccountNameProc; + static getFileSecurityADef getFileSecurityProc; + static int initialized = 0; + + const BOOL set_readOnly = !(pmode & 0222); + BOOL acl_readOnly_found = FALSE, curAclPresent, curAclDefaulted; + SID_IDENTIFIER_AUTHORITY userSidAuthority = { + SECURITY_WORLD_SID_AUTHORITY + }; + BYTE *secDesc = 0; + DWORD secDescLen, attr, newAclSize; + ACL_SIZE_INFORMATION ACLSize; + PACL curAcl, newAcl = 0; + WORD j; + SID *userSid = 0; + TCHAR *userDomain = 0; + int res = 0; + + /* + * One time initialization, dynamically load Windows NT features + */ + + if (!initialized) { + TCL_DECLARE_MUTEX(initializeMutex) + Tcl_MutexLock(&initializeMutex); + if (!initialized) { + HINSTANCE hInstance = LoadLibrary("Advapi32"); + + if (hInstance != NULL) { + setNamedSecurityInfoProc = (setNamedSecurityInfoADef) + GetProcAddress(hInstance, "SetNamedSecurityInfoA"); + getFileSecurityProc = (getFileSecurityADef) + GetProcAddress(hInstance, "GetFileSecurityA"); + getAceProc = (getAceDef) + GetProcAddress(hInstance, "GetAce"); + addAceProc = (addAceDef) + GetProcAddress(hInstance, "AddAce"); + equalSidProc = (equalSidDef) + GetProcAddress(hInstance, "EqualSid"); + addAccessDeniedAceProc = (addAccessDeniedAceDef) + GetProcAddress(hInstance, "AddAccessDeniedAce"); + initializeAclProc = (initializeAclDef) + GetProcAddress(hInstance, "InitializeAcl"); + getLengthSidProc = (getLengthSidDef) + GetProcAddress(hInstance, "GetLengthSid"); + getAclInformationProc = (getAclInformationDef) + GetProcAddress(hInstance, "GetAclInformation"); + getSecurityDescriptorDaclProc = (getSecurityDescriptorDaclDef) + GetProcAddress(hInstance, "GetSecurityDescriptorDacl"); + lookupAccountNameProc = (lookupAccountNameADef) + GetProcAddress(hInstance, "LookupAccountNameA"); + getSidLengthRequiredProc = (getSidLengthRequiredDef) + GetProcAddress(hInstance, "GetSidLengthRequired"); + initializeSidProc = (initializeSidDef) + GetProcAddress(hInstance, "InitializeSid"); + getSidSubAuthorityProc = (getSidSubAuthorityDef) + GetProcAddress(hInstance, "GetSidSubAuthority"); + + if (setNamedSecurityInfoProc && getAceProc && addAceProc + && equalSidProc && addAccessDeniedAceProc + && initializeAclProc && getLengthSidProc + && getAclInformationProc + && getSecurityDescriptorDaclProc + && lookupAccountNameProc && getFileSecurityProc + && getSidLengthRequiredProc && initializeSidProc + && getSidSubAuthorityProc) { + initialized = 1; + } + } + if (!initialized) { + initialized = -1; + } + } + Tcl_MutexUnlock(&initializeMutex); + } + + /* + * Process the chmod request. + */ + + attr = GetFileAttributes(nativePath); + + /* + * nativePath not found + */ + + if (attr == 0xffffffff) { + res = -1; + goto done; + } + + /* + * If no ACL API is present or nativePath is not a directory, there is no + * special handling. + */ + + if (initialized < 0 || !(attr & FILE_ATTRIBUTE_DIRECTORY)) { + goto done; + } + + /* + * Set the result to error, if the ACL change is successful it will be + * reset to 0. + */ + + res = -1; + + /* + * Read the security descriptor for the directory. Note the first call + * obtains the size of the security descriptor. + */ + + if (!getFileSecurityProc(nativePath, infoBits, NULL, 0, &secDescLen)) { + DWORD secDescLen2 = 0; + + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + goto done; + } + + secDesc = (BYTE *) ckalloc(secDescLen); + if (!getFileSecurityProc(nativePath, infoBits, + (PSECURITY_DESCRIPTOR) secDesc, secDescLen, &secDescLen2) + || (secDescLen < secDescLen2)) { + goto done; + } + } + + /* + * Get the World SID. + */ + + userSid = (SID *) ckalloc(getSidLengthRequiredProc((UCHAR) 1)); + initializeSidProc(userSid, &userSidAuthority, (BYTE) 1); + *(getSidSubAuthorityProc(userSid, 0)) = SECURITY_WORLD_RID; + + /* + * If curAclPresent == false then curAcl and curAclDefaulted not valid. + */ + + if (!getSecurityDescriptorDaclProc((PSECURITY_DESCRIPTOR) secDesc, + &curAclPresent, &curAcl, &curAclDefaulted)) { + goto done; + } + if (!curAclPresent || !curAcl) { + ACLSize.AclBytesInUse = 0; + ACLSize.AceCount = 0; + } else if (!getAclInformationProc(curAcl, &ACLSize, sizeof(ACLSize), + AclSizeInformation)) { + goto done; + } + + /* + * Allocate memory for the new ACL. + */ + + newAclSize = ACLSize.AclBytesInUse + sizeof(ACCESS_DENIED_ACE) + + getLengthSidProc(userSid) - sizeof(DWORD); + newAcl = (ACL *) ckalloc(newAclSize); + + /* + * Initialize the new ACL. + */ + + if (!initializeAclProc(newAcl, newAclSize, ACL_REVISION)) { + goto done; + } + + /* + * Add denied to make readonly, this will be known as a "read-only tag". + */ + + if (set_readOnly && !addAccessDeniedAceProc(newAcl, ACL_REVISION, + readOnlyMask, userSid)) { + goto done; + } + + acl_readOnly_found = FALSE; + for (j = 0; j < ACLSize.AceCount; j++) { + LPVOID pACE2; + ACE_HEADER *phACE2; + + if (!getAceProc(curAcl, j, &pACE2)) { + goto done; + } + + phACE2 = (ACE_HEADER *) pACE2; + + /* + * Do NOT propagate inherited ACEs. + */ + + if (phACE2->AceFlags & INHERITED_ACE) { + continue; + } + + /* + * Skip the "read-only tag" restriction (either added above, or it is + * being removed). + */ + + if (phACE2->AceType == ACCESS_DENIED_ACE_TYPE) { + ACCESS_DENIED_ACE *pACEd = (ACCESS_DENIED_ACE *) phACE2; + + if (pACEd->Mask == readOnlyMask + && equalSidProc(userSid, (PSID) &pACEd->SidStart)) { + acl_readOnly_found = TRUE; + continue; + } + } + + /* + * Copy the current ACE from the old to the new ACL. + */ + + if (!addAceProc(newAcl, ACL_REVISION, MAXDWORD, (PACL *)pACE2, + ((PACE_HEADER) pACE2)->AceSize)) { + goto done; + } + } + + /* + * Apply the new ACL. + */ + + if (set_readOnly == acl_readOnly_found || setNamedSecurityInfoProc( + (LPSTR) nativePath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, + NULL, NULL, newAcl, NULL) == ERROR_SUCCESS) { + res = 0; + } + + done: + if (secDesc) { + ckfree((char *) secDesc); + } + if (newAcl) { + ckfree((char *) newAcl); + } + if (userSid) { + ckfree((char *) userSid); + } + if (userDomain) { + ckfree(userDomain); + } + + if (res != 0) { + return res; + } + + /* + * Run normal chmod command. + */ + + return chmod(nativePath, pmode); +} + +/* + *--------------------------------------------------------------------------- + * + * TestchmodCmd -- + * + * Implements the "testchmod" cmd. Used when testing "file" command. The + * only attribute used by the Windows platform is the user write flag; if + * this is not set, the file is made read-only. Otherwise, the file is + * made read-write. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Changes permissions of specified files. + * + *--------------------------------------------------------------------------- + */ + +static int +TestchmodCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int argc, /* Number of arguments. */ + const char **argv) /* Argument strings. */ +{ + int i, mode; + char *rest; + + if (argc < 2) { + usage: + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " mode file ?file ...?", NULL); + return TCL_ERROR; + } + + mode = (int) strtol(argv[1], &rest, 8); + if ((rest == argv[1]) || (*rest != '\0')) { + goto usage; + } + + for (i = 2; i < argc; i++) { + Tcl_DString buffer; + const char *translated; + + translated = Tcl_TranslateFileName(interp, argv[i], &buffer); + if (translated == NULL) { + return TCL_ERROR; + } + if (TestplatformChmod(translated, mode) != 0) { + Tcl_AppendResult(interp, translated, ": ", Tcl_PosixError(interp), + NULL); + return TCL_ERROR; + } + Tcl_DStringFree(&buffer); + } + return TCL_OK; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |
