diff -r 000000000000 -r bde4ae8d615e os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinTest.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinTest.c Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,832 @@ +/* + * 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.8.2.6 2006/03/27 23:30:54 patthoyts Exp $ + */ + +#define USE_COMPAT_CONST +#include "tclWinInt.h" + +/* + * For TestplatformChmod on Windows + */ +#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 procedures defined later in this file: + */ +int TclplatformtestInit _ANSI_ARGS_((Tcl_Interp *interp)); +static int TesteventloopCmd _ANSI_ARGS_((ClientData dummy, + Tcl_Interp *interp, int argc, CONST84 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[] )); +static int TestwinsleepCmd _ANSI_ARGS_(( ClientData dummy, + Tcl_Interp* interp, + int objc, + Tcl_Obj *CONST objv[] )); +static Tcl_ObjCmdProc TestExceptionCmd; +static int TestwincpuidCmd _ANSI_ARGS_(( ClientData dummy, + Tcl_Interp* interp, + int objc, + Tcl_Obj *CONST objv[] )); +static int TestplatformChmod _ANSI_ARGS_((CONST char *nativePath, + int pmode)); +static int TestchmodCmd _ANSI_ARGS_((ClientData dummy, + Tcl_Interp *interp, int argc, CONST84 char **argv)); + + +/* + *---------------------------------------------------------------------- + * + * TclplatformtestInit -- + * + * Defines commands that test platform specific functionality for + * Windows platforms. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Defines new commands. + * + *---------------------------------------------------------------------- + */ + +int +TclplatformtestInit(interp) + Tcl_Interp *interp; /* Interpreter to add commands to. */ +{ + /* + * Add commands for platform specific tests for Windows here. + */ + + Tcl_CreateCommand(interp, "testchmod", TestchmodCmd, + (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "testeventloop", TesteventloopCmd, + (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateObjCommand(interp, "testvolumetype", TestvolumetypeCmd, + (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateObjCommand(interp, "testwinclock", TestwinclockCmd, + (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateObjCommand(interp, "testwincpuid", TestwincpuidCmd, + (ClientData) 0, (Tcl_CmdDeleteProc*) NULL ); + Tcl_CreateObjCommand(interp, "testwinsleep", TestwinsleepCmd, + (ClientData) 0, (Tcl_CmdDeleteProc *) NULL ); + Tcl_CreateObjCommand(interp, "testexcept", TestExceptionCmd, + (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * 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()). + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +TesteventloopCmd(clientData, interp, argc, argv) + ClientData clientData; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int argc; /* Number of arguments. */ + CONST84 char **argv; /* Argument strings. */ +{ + static int *framePtr = NULL; /* Pointer to integer on stack frame of + * innermost invocation of the "wait" + * subcommand. */ + + if (argc < 2) { + Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0], + " option ... \"", (char *) 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 oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + + /* + * Save the old stack frame pointer and set up the current frame. + */ + + oldFramePtr = framePtr; + framePtr = &done; + + /* + * Enter a standard Windows event loop until the flag changes. + * Note that we do not explicitly call Tcl_ServiceEvent(). + */ + + done = 0; + while (!done) { + if (!GetMessage(&msg, NULL, 0, 0)) { + /* + * The application is exiting, so repost the quit message + * and start unwinding. + */ + + PostQuitMessage((int)msg.wParam); + break; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + (void) Tcl_SetServiceMode(oldMode); + framePtr = oldFramePtr; + } else { + Tcl_AppendResult(interp, "bad option \"", argv[1], + "\": must be done or wait", (char *) NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Testvolumetype -- + * + * This procedure implements the "testvolumetype" command. It is + * used to check the volume type (FAT, NTFS) of a volume. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +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. */ +{ +#define VOL_BUF_SIZE 32 + int found; + char volType[VOL_BUF_SIZE]; + char *path; + + if (objc > 2) { + Tcl_WrongNumArgs(interp, 1, objv, "?name?"); + 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 = Tcl_GetString(objv[1]); + } else { + path = NULL; + } + 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); + TclWinConvertError(GetLastError()); + return TCL_ERROR; + } + Tcl_SetResult(interp, volType, TCL_VOLATILE); + return TCL_OK; +#undef VOL_BUF_SIZE +} + +/* + *---------------------------------------------------------------------- + * + * 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. + * + * Usage: + * testclock + * + * Parameters: + * 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. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +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 */ + Tcl_Time tclTime; /* Tcl clock */ + FILETIME sysTime; /* System clock */ + Tcl_Obj* result; /* Result of the command */ + LARGE_INTEGER t1, t2; + LARGE_INTEGER p1, p2; + + if ( objc != 1 ) { + Tcl_WrongNumArgs( interp, 1, objv, "" ); + return TCL_ERROR; + } + + 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_NewWideIntObj( p1.QuadPart ) ); + Tcl_ListObjAppendElement( interp, result, Tcl_NewWideIntObj( p2.QuadPart ) ); + + Tcl_SetObjResult( interp, result ); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TestwincpuidCmd -- + * + * Retrieves CPU ID information. + * + * Usage: + * testwincpuid <eax> + * + * Parameters: + * eax - The value to pass in the EAX register to a CPUID instruction. + * + * Results: + * Returns a four-element list containing the values from the + * EAX, EBX, ECX and EDX registers returned from the CPUID instruction. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +TestwincpuidCmd( ClientData dummy, + Tcl_Interp* interp, /* Tcl interpreter */ + int objc, /* Parameter count */ + Tcl_Obj *CONST * objv ) /* Parameter vector */ +{ + int status; + int index; + unsigned int regs[4]; + Tcl_Obj * regsObjs[4]; + int i; + + if ( objc != 2 ) { + Tcl_WrongNumArgs( interp, 1, objv, "eax" ); + return TCL_ERROR; + } + if ( Tcl_GetIntFromObj( interp, objv[1], &index ) != TCL_OK ) { + return TCL_ERROR; + } + status = TclWinCPUID( (unsigned int) index, regs ); + if ( status != TCL_OK ) { + Tcl_SetObjResult( interp, Tcl_NewStringObj( "operation not available", + -1 ) ); + return status; + } + for ( i = 0; i < 4; ++i ) { + regsObjs[i] = Tcl_NewIntObj( (int) regs[i] ); + } + Tcl_SetObjResult( interp, Tcl_NewListObj( 4, regsObjs ) ); + return TCL_OK; + +} + +/* + *---------------------------------------------------------------------- + * + * TestwinsleepCmd -- + * + * Causes this process to wait for the given number of milliseconds + * by means of a direct call to Sleep. + * + * Usage: + * testwinsleep <n> + * + * Parameters: + * n - the number of milliseconds to sleep + * + * Results: + * None. + * + * Side effects: + * Sleeps for the requisite number of milliseconds. + * + *---------------------------------------------------------------------- + */ + +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 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 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. + */ + + 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) +{ + SID_IDENTIFIER_AUTHORITY userSidAuthority = + { SECURITY_WORLD_SID_AUTHORITY }; + + typedef DWORD (WINAPI *getSidLengthRequiredDef) ( UCHAR ); + typedef BOOL (WINAPI *initializeSidDef) ( PSID, + PSID_IDENTIFIER_AUTHORITY, BYTE ); + typedef PDWORD (WINAPI *getSidSubAuthorityDef) ( PSID, DWORD ); + + static getSidLengthRequiredDef getSidLengthRequiredProc; + static initializeSidDef initializeSidProc; + static getSidSubAuthorityDef getSidSubAuthorityProc; + 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; + + BYTE *secDesc = 0; + DWORD secDescLen; + + const BOOL set_readOnly = !(pmode & 0222); + BOOL acl_readOnly_found = FALSE; + + ACL_SIZE_INFORMATION ACLSize; + BOOL curAclPresent, curAclDefaulted; + PACL curAcl; + PACL newAcl = 0; + DWORD newAclSize; + + WORD j; + + SID *userSid = 0; + TCHAR *userDomain = NULL; + + DWORD attr; + + int res = 0; + + /* + * One time initialization, dynamically load Windows NT features + */ + 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 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; + 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)) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + DWORD secDescLen2 = 0; + secDesc = (BYTE *) ckalloc(secDescLen); + if (!getFileSecurityProc(nativePath, infoBits, + (PSECURITY_DESCRIPTOR)secDesc, + secDescLen, &secDescLen2) + || (secDescLen < secDescLen2)) { + goto done; + } + } else { + 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(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++) { + PACL *pACE2; + ACE_HEADER *phACE2; + if (! getAceProc (curAcl, j, (LPVOID*) &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, 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(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. Otehrwise, the + * file is made read-write. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Changes permissions of specified files. + * + *--------------------------------------------------------------------------- + */ + +static int +TestchmodCmd(dummy, interp, argc, argv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int argc; /* Number of arguments. */ + CONST84 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; +}