| [25] | 1 | /* |
|---|
| 2 | * tclWinTest.c -- |
|---|
| 3 | * |
|---|
| 4 | * Contains commands for platform specific tests on Windows. |
|---|
| 5 | * |
|---|
| 6 | * Copyright (c) 1996 Sun Microsystems, Inc. |
|---|
| 7 | * |
|---|
| 8 | * See the file "license.terms" for information on usage and redistribution of |
|---|
| 9 | * this file, and for a DISCLAIMER OF ALL WARRANTIES. |
|---|
| 10 | * |
|---|
| 11 | * RCS: @(#) $Id: tclWinTest.c,v 1.22 2007/12/13 15:28:44 dgp Exp $ |
|---|
| 12 | */ |
|---|
| 13 | |
|---|
| 14 | #include "tclInt.h" |
|---|
| 15 | |
|---|
| 16 | /* |
|---|
| 17 | * For TestplatformChmod on Windows |
|---|
| 18 | */ |
|---|
| 19 | #ifdef __WIN32__ |
|---|
| 20 | #include <aclapi.h> |
|---|
| 21 | #endif |
|---|
| 22 | |
|---|
| 23 | /* |
|---|
| 24 | * MinGW 3.4.2 does not define this. |
|---|
| 25 | */ |
|---|
| 26 | #ifndef INHERITED_ACE |
|---|
| 27 | #define INHERITED_ACE (0x10) |
|---|
| 28 | #endif |
|---|
| 29 | |
|---|
| 30 | /* |
|---|
| 31 | * Forward declarations of functions defined later in this file: |
|---|
| 32 | */ |
|---|
| 33 | |
|---|
| 34 | int TclplatformtestInit(Tcl_Interp *interp); |
|---|
| 35 | static int TesteventloopCmd(ClientData dummy, Tcl_Interp *interp, |
|---|
| 36 | int argc, const char **argv); |
|---|
| 37 | static int TestvolumetypeCmd(ClientData dummy, |
|---|
| 38 | Tcl_Interp *interp, int objc, |
|---|
| 39 | Tcl_Obj *const objv[]); |
|---|
| 40 | static int TestwinclockCmd(ClientData dummy, Tcl_Interp* interp, |
|---|
| 41 | int objc, Tcl_Obj *const objv[]); |
|---|
| 42 | static int TestwinsleepCmd(ClientData dummy, Tcl_Interp* interp, |
|---|
| 43 | int objc, Tcl_Obj *const objv[]); |
|---|
| 44 | static Tcl_ObjCmdProc TestExceptionCmd; |
|---|
| 45 | static int TestwincpuidCmd(ClientData dummy, Tcl_Interp* interp, |
|---|
| 46 | int objc, Tcl_Obj *const objv[]); |
|---|
| 47 | static int TestplatformChmod(const char *nativePath, int pmode); |
|---|
| 48 | static int TestchmodCmd(ClientData dummy, |
|---|
| 49 | Tcl_Interp *interp, int argc, const char **argv); |
|---|
| 50 | |
|---|
| 51 | /* |
|---|
| 52 | *---------------------------------------------------------------------- |
|---|
| 53 | * |
|---|
| 54 | * TclplatformtestInit -- |
|---|
| 55 | * |
|---|
| 56 | * Defines commands that test platform specific functionality for Windows |
|---|
| 57 | * platforms. |
|---|
| 58 | * |
|---|
| 59 | * Results: |
|---|
| 60 | * A standard Tcl result. |
|---|
| 61 | * |
|---|
| 62 | * Side effects: |
|---|
| 63 | * Defines new commands. |
|---|
| 64 | * |
|---|
| 65 | *---------------------------------------------------------------------- |
|---|
| 66 | */ |
|---|
| 67 | |
|---|
| 68 | int |
|---|
| 69 | TclplatformtestInit( |
|---|
| 70 | Tcl_Interp *interp) /* Interpreter to add commands to. */ |
|---|
| 71 | { |
|---|
| 72 | /* |
|---|
| 73 | * Add commands for platform specific tests for Windows here. |
|---|
| 74 | */ |
|---|
| 75 | |
|---|
| 76 | Tcl_CreateCommand(interp, "testchmod", TestchmodCmd, NULL, NULL); |
|---|
| 77 | Tcl_CreateCommand(interp, "testeventloop", TesteventloopCmd, NULL, NULL); |
|---|
| 78 | Tcl_CreateObjCommand(interp, "testvolumetype", TestvolumetypeCmd, |
|---|
| 79 | NULL, NULL); |
|---|
| 80 | Tcl_CreateObjCommand(interp, "testwinclock", TestwinclockCmd, NULL, NULL); |
|---|
| 81 | Tcl_CreateObjCommand(interp, "testwincpuid", TestwincpuidCmd, NULL, NULL); |
|---|
| 82 | Tcl_CreateObjCommand(interp, "testwinsleep", TestwinsleepCmd, NULL, NULL); |
|---|
| 83 | Tcl_CreateObjCommand(interp, "testexcept", TestExceptionCmd, NULL, NULL); |
|---|
| 84 | return TCL_OK; |
|---|
| 85 | } |
|---|
| 86 | |
|---|
| 87 | /* |
|---|
| 88 | *---------------------------------------------------------------------- |
|---|
| 89 | * |
|---|
| 90 | * TesteventloopCmd -- |
|---|
| 91 | * |
|---|
| 92 | * This function implements the "testeventloop" command. It is used to |
|---|
| 93 | * test the Tcl notifier from an "external" event loop (i.e. not |
|---|
| 94 | * Tcl_DoOneEvent()). |
|---|
| 95 | * |
|---|
| 96 | * Results: |
|---|
| 97 | * A standard Tcl result. |
|---|
| 98 | * |
|---|
| 99 | * Side effects: |
|---|
| 100 | * None. |
|---|
| 101 | * |
|---|
| 102 | *---------------------------------------------------------------------- |
|---|
| 103 | */ |
|---|
| 104 | |
|---|
| 105 | static int |
|---|
| 106 | TesteventloopCmd( |
|---|
| 107 | ClientData clientData, /* Not used. */ |
|---|
| 108 | Tcl_Interp *interp, /* Current interpreter. */ |
|---|
| 109 | int argc, /* Number of arguments. */ |
|---|
| 110 | const char **argv) /* Argument strings. */ |
|---|
| 111 | { |
|---|
| 112 | static int *framePtr = NULL;/* Pointer to integer on stack frame of |
|---|
| 113 | * innermost invocation of the "wait" |
|---|
| 114 | * subcommand. */ |
|---|
| 115 | |
|---|
| 116 | if (argc < 2) { |
|---|
| 117 | Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0], |
|---|
| 118 | " option ... \"", NULL); |
|---|
| 119 | return TCL_ERROR; |
|---|
| 120 | } |
|---|
| 121 | if (strcmp(argv[1], "done") == 0) { |
|---|
| 122 | *framePtr = 1; |
|---|
| 123 | } else if (strcmp(argv[1], "wait") == 0) { |
|---|
| 124 | int *oldFramePtr, done; |
|---|
| 125 | int oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); |
|---|
| 126 | |
|---|
| 127 | /* |
|---|
| 128 | * Save the old stack frame pointer and set up the current frame. |
|---|
| 129 | */ |
|---|
| 130 | |
|---|
| 131 | oldFramePtr = framePtr; |
|---|
| 132 | framePtr = &done; |
|---|
| 133 | |
|---|
| 134 | /* |
|---|
| 135 | * Enter a standard Windows event loop until the flag changes. Note |
|---|
| 136 | * that we do not explicitly call Tcl_ServiceEvent(). |
|---|
| 137 | */ |
|---|
| 138 | |
|---|
| 139 | done = 0; |
|---|
| 140 | while (!done) { |
|---|
| 141 | MSG msg; |
|---|
| 142 | |
|---|
| 143 | if (!GetMessage(&msg, NULL, 0, 0)) { |
|---|
| 144 | /* |
|---|
| 145 | * The application is exiting, so repost the quit message and |
|---|
| 146 | * start unwinding. |
|---|
| 147 | */ |
|---|
| 148 | |
|---|
| 149 | PostQuitMessage((int) msg.wParam); |
|---|
| 150 | break; |
|---|
| 151 | } |
|---|
| 152 | TranslateMessage(&msg); |
|---|
| 153 | DispatchMessage(&msg); |
|---|
| 154 | } |
|---|
| 155 | (void) Tcl_SetServiceMode(oldMode); |
|---|
| 156 | framePtr = oldFramePtr; |
|---|
| 157 | } else { |
|---|
| 158 | Tcl_AppendResult(interp, "bad option \"", argv[1], |
|---|
| 159 | "\": must be done or wait", NULL); |
|---|
| 160 | return TCL_ERROR; |
|---|
| 161 | } |
|---|
| 162 | return TCL_OK; |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | /* |
|---|
| 166 | *---------------------------------------------------------------------- |
|---|
| 167 | * |
|---|
| 168 | * Testvolumetype -- |
|---|
| 169 | * |
|---|
| 170 | * This function implements the "testvolumetype" command. It is used to |
|---|
| 171 | * check the volume type (FAT, NTFS) of a volume. |
|---|
| 172 | * |
|---|
| 173 | * Results: |
|---|
| 174 | * A standard Tcl result. |
|---|
| 175 | * |
|---|
| 176 | * Side effects: |
|---|
| 177 | * None. |
|---|
| 178 | * |
|---|
| 179 | *---------------------------------------------------------------------- |
|---|
| 180 | */ |
|---|
| 181 | |
|---|
| 182 | static int |
|---|
| 183 | TestvolumetypeCmd( |
|---|
| 184 | ClientData clientData, /* Not used. */ |
|---|
| 185 | Tcl_Interp *interp, /* Current interpreter. */ |
|---|
| 186 | int objc, /* Number of arguments. */ |
|---|
| 187 | Tcl_Obj *const objv[]) /* Argument objects. */ |
|---|
| 188 | { |
|---|
| 189 | #define VOL_BUF_SIZE 32 |
|---|
| 190 | int found; |
|---|
| 191 | char volType[VOL_BUF_SIZE]; |
|---|
| 192 | char *path; |
|---|
| 193 | |
|---|
| 194 | if (objc > 2) { |
|---|
| 195 | Tcl_WrongNumArgs(interp, 1, objv, "?name?"); |
|---|
| 196 | return TCL_ERROR; |
|---|
| 197 | } |
|---|
| 198 | if (objc == 2) { |
|---|
| 199 | /* |
|---|
| 200 | * path has to be really a proper volume, but we don't get query APIs |
|---|
| 201 | * for that until NT5 |
|---|
| 202 | */ |
|---|
| 203 | |
|---|
| 204 | path = Tcl_GetString(objv[1]); |
|---|
| 205 | } else { |
|---|
| 206 | path = NULL; |
|---|
| 207 | } |
|---|
| 208 | found = GetVolumeInformationA(path, NULL, 0, NULL, NULL, NULL, volType, |
|---|
| 209 | VOL_BUF_SIZE); |
|---|
| 210 | |
|---|
| 211 | if (found == 0) { |
|---|
| 212 | Tcl_AppendResult(interp, "could not get volume type for \"", |
|---|
| 213 | (path?path:""), "\"", NULL); |
|---|
| 214 | TclWinConvertError(GetLastError()); |
|---|
| 215 | return TCL_ERROR; |
|---|
| 216 | } |
|---|
| 217 | Tcl_SetResult(interp, volType, TCL_VOLATILE); |
|---|
| 218 | return TCL_OK; |
|---|
| 219 | #undef VOL_BUF_SIZE |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | /* |
|---|
| 223 | *---------------------------------------------------------------------- |
|---|
| 224 | * |
|---|
| 225 | * TestwinclockCmd -- |
|---|
| 226 | * |
|---|
| 227 | * Command that returns the seconds and microseconds portions of the |
|---|
| 228 | * system clock and of the Tcl clock so that they can be compared to |
|---|
| 229 | * validate that the Tcl clock is staying in sync. |
|---|
| 230 | * |
|---|
| 231 | * Usage: |
|---|
| 232 | * testclock |
|---|
| 233 | * |
|---|
| 234 | * Parameters: |
|---|
| 235 | * None. |
|---|
| 236 | * |
|---|
| 237 | * Results: |
|---|
| 238 | * Returns a standard Tcl result comprising a four-element list: the |
|---|
| 239 | * seconds and microseconds portions of the system clock, and the seconds |
|---|
| 240 | * and microseconds portions of the Tcl clock. |
|---|
| 241 | * |
|---|
| 242 | * Side effects: |
|---|
| 243 | * None. |
|---|
| 244 | * |
|---|
| 245 | *---------------------------------------------------------------------- |
|---|
| 246 | */ |
|---|
| 247 | |
|---|
| 248 | static int |
|---|
| 249 | TestwinclockCmd( |
|---|
| 250 | ClientData dummy, /* Unused */ |
|---|
| 251 | Tcl_Interp* interp, /* Tcl interpreter */ |
|---|
| 252 | int objc, /* Argument count */ |
|---|
| 253 | Tcl_Obj *const objv[]) /* Argument vector */ |
|---|
| 254 | { |
|---|
| 255 | static const FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE }; |
|---|
| 256 | /* The Posix epoch, expressed as a Windows |
|---|
| 257 | * FILETIME */ |
|---|
| 258 | Tcl_Time tclTime; /* Tcl clock */ |
|---|
| 259 | FILETIME sysTime; /* System clock */ |
|---|
| 260 | Tcl_Obj *result; /* Result of the command */ |
|---|
| 261 | LARGE_INTEGER t1, t2; |
|---|
| 262 | LARGE_INTEGER p1, p2; |
|---|
| 263 | |
|---|
| 264 | if (objc != 1) { |
|---|
| 265 | Tcl_WrongNumArgs(interp, 1, objv, ""); |
|---|
| 266 | return TCL_ERROR; |
|---|
| 267 | } |
|---|
| 268 | |
|---|
| 269 | QueryPerformanceCounter(&p1); |
|---|
| 270 | |
|---|
| 271 | Tcl_GetTime(&tclTime); |
|---|
| 272 | GetSystemTimeAsFileTime(&sysTime); |
|---|
| 273 | t1.LowPart = posixEpoch.dwLowDateTime; |
|---|
| 274 | t1.HighPart = posixEpoch.dwHighDateTime; |
|---|
| 275 | t2.LowPart = sysTime.dwLowDateTime; |
|---|
| 276 | t2.HighPart = sysTime.dwHighDateTime; |
|---|
| 277 | t2.QuadPart -= t1.QuadPart; |
|---|
| 278 | |
|---|
| 279 | QueryPerformanceCounter(&p2); |
|---|
| 280 | |
|---|
| 281 | result = Tcl_NewObj(); |
|---|
| 282 | Tcl_ListObjAppendElement(interp, result, |
|---|
| 283 | Tcl_NewIntObj((int) (t2.QuadPart / 10000000))); |
|---|
| 284 | Tcl_ListObjAppendElement(interp, result, |
|---|
| 285 | Tcl_NewIntObj((int) ((t2.QuadPart / 10) % 1000000))); |
|---|
| 286 | Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(tclTime.sec)); |
|---|
| 287 | Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(tclTime.usec)); |
|---|
| 288 | |
|---|
| 289 | Tcl_ListObjAppendElement(interp, result, Tcl_NewWideIntObj(p1.QuadPart)); |
|---|
| 290 | Tcl_ListObjAppendElement(interp, result, Tcl_NewWideIntObj(p2.QuadPart)); |
|---|
| 291 | |
|---|
| 292 | Tcl_SetObjResult(interp, result); |
|---|
| 293 | |
|---|
| 294 | return TCL_OK; |
|---|
| 295 | } |
|---|
| 296 | |
|---|
| 297 | /* |
|---|
| 298 | *---------------------------------------------------------------------- |
|---|
| 299 | * |
|---|
| 300 | * TestwincpuidCmd -- |
|---|
| 301 | * |
|---|
| 302 | * Retrieves CPU ID information. |
|---|
| 303 | * |
|---|
| 304 | * Usage: |
|---|
| 305 | * testwincpuid <eax> |
|---|
| 306 | * |
|---|
| 307 | * Parameters: |
|---|
| 308 | * eax - The value to pass in the EAX register to a CPUID instruction. |
|---|
| 309 | * |
|---|
| 310 | * Results: |
|---|
| 311 | * Returns a four-element list containing the values from the EAX, EBX, |
|---|
| 312 | * ECX and EDX registers returned from the CPUID instruction. |
|---|
| 313 | * |
|---|
| 314 | * Side effects: |
|---|
| 315 | * None. |
|---|
| 316 | * |
|---|
| 317 | *---------------------------------------------------------------------- |
|---|
| 318 | */ |
|---|
| 319 | |
|---|
| 320 | static int |
|---|
| 321 | TestwincpuidCmd( |
|---|
| 322 | ClientData dummy, |
|---|
| 323 | Tcl_Interp* interp, /* Tcl interpreter */ |
|---|
| 324 | int objc, /* Parameter count */ |
|---|
| 325 | Tcl_Obj *const * objv) /* Parameter vector */ |
|---|
| 326 | { |
|---|
| 327 | int status, index, i; |
|---|
| 328 | unsigned int regs[4]; |
|---|
| 329 | Tcl_Obj *regsObjs[4]; |
|---|
| 330 | |
|---|
| 331 | if (objc != 2) { |
|---|
| 332 | Tcl_WrongNumArgs(interp, 1, objv, "eax"); |
|---|
| 333 | return TCL_ERROR; |
|---|
| 334 | } |
|---|
| 335 | if (Tcl_GetIntFromObj(interp, objv[1], &index) != TCL_OK) { |
|---|
| 336 | return TCL_ERROR; |
|---|
| 337 | } |
|---|
| 338 | status = TclWinCPUID((unsigned) index, regs); |
|---|
| 339 | if (status != TCL_OK) { |
|---|
| 340 | Tcl_SetObjResult(interp, |
|---|
| 341 | Tcl_NewStringObj("operation not available", -1)); |
|---|
| 342 | return status; |
|---|
| 343 | } |
|---|
| 344 | for (i=0 ; i<4 ; ++i) { |
|---|
| 345 | regsObjs[i] = Tcl_NewIntObj((int) regs[i]); |
|---|
| 346 | } |
|---|
| 347 | Tcl_SetObjResult(interp, Tcl_NewListObj(4, regsObjs)); |
|---|
| 348 | return TCL_OK; |
|---|
| 349 | } |
|---|
| 350 | |
|---|
| 351 | /* |
|---|
| 352 | *---------------------------------------------------------------------- |
|---|
| 353 | * |
|---|
| 354 | * TestwinsleepCmd -- |
|---|
| 355 | * |
|---|
| 356 | * Causes this process to wait for the given number of milliseconds by |
|---|
| 357 | * means of a direct call to Sleep. |
|---|
| 358 | * |
|---|
| 359 | * Usage: |
|---|
| 360 | * testwinsleep <n> |
|---|
| 361 | * |
|---|
| 362 | * Parameters: |
|---|
| 363 | * n - the number of milliseconds to sleep |
|---|
| 364 | * |
|---|
| 365 | * Results: |
|---|
| 366 | * None. |
|---|
| 367 | * |
|---|
| 368 | * Side effects: |
|---|
| 369 | * Sleeps for the requisite number of milliseconds. |
|---|
| 370 | * |
|---|
| 371 | *---------------------------------------------------------------------- |
|---|
| 372 | */ |
|---|
| 373 | |
|---|
| 374 | static int |
|---|
| 375 | TestwinsleepCmd( |
|---|
| 376 | ClientData clientData, /* Unused */ |
|---|
| 377 | Tcl_Interp* interp, /* Tcl interpreter */ |
|---|
| 378 | int objc, /* Parameter count */ |
|---|
| 379 | Tcl_Obj *const * objv) /* Parameter vector */ |
|---|
| 380 | { |
|---|
| 381 | int ms; |
|---|
| 382 | |
|---|
| 383 | if (objc != 2) { |
|---|
| 384 | Tcl_WrongNumArgs(interp, 1, objv, "ms"); |
|---|
| 385 | return TCL_ERROR; |
|---|
| 386 | } |
|---|
| 387 | if (Tcl_GetIntFromObj(interp, objv[1], &ms) != TCL_OK) { |
|---|
| 388 | return TCL_ERROR; |
|---|
| 389 | } |
|---|
| 390 | Sleep((DWORD) ms); |
|---|
| 391 | return TCL_OK; |
|---|
| 392 | } |
|---|
| 393 | |
|---|
| 394 | /* |
|---|
| 395 | *---------------------------------------------------------------------- |
|---|
| 396 | * |
|---|
| 397 | * TestExceptionCmd -- |
|---|
| 398 | * |
|---|
| 399 | * Causes this process to end with the named exception. Used for testing |
|---|
| 400 | * Tcl_WaitPid(). |
|---|
| 401 | * |
|---|
| 402 | * Usage: |
|---|
| 403 | * testexcept <type> |
|---|
| 404 | * |
|---|
| 405 | * Parameters: |
|---|
| 406 | * Type of exception. |
|---|
| 407 | * |
|---|
| 408 | * Results: |
|---|
| 409 | * None, this process closes now and doesn't return. |
|---|
| 410 | * |
|---|
| 411 | * Side effects: |
|---|
| 412 | * This Tcl process closes, hard... Bang! |
|---|
| 413 | * |
|---|
| 414 | *---------------------------------------------------------------------- |
|---|
| 415 | */ |
|---|
| 416 | |
|---|
| 417 | static int |
|---|
| 418 | TestExceptionCmd( |
|---|
| 419 | ClientData dummy, /* Unused */ |
|---|
| 420 | Tcl_Interp* interp, /* Tcl interpreter */ |
|---|
| 421 | int objc, /* Argument count */ |
|---|
| 422 | Tcl_Obj *const objv[]) /* Argument vector */ |
|---|
| 423 | { |
|---|
| 424 | static const char *cmds[] = { |
|---|
| 425 | "access_violation", "datatype_misalignment", "array_bounds", |
|---|
| 426 | "float_denormal", "float_divbyzero", "float_inexact", |
|---|
| 427 | "float_invalidop", "float_overflow", "float_stack", "float_underflow", |
|---|
| 428 | "int_divbyzero", "int_overflow", "private_instruction", "inpageerror", |
|---|
| 429 | "illegal_instruction", "noncontinue", "stack_overflow", |
|---|
| 430 | "invalid_disp", "guard_page", "invalid_handle", "ctrl+c", |
|---|
| 431 | NULL |
|---|
| 432 | }; |
|---|
| 433 | static DWORD exceptions[] = { |
|---|
| 434 | EXCEPTION_ACCESS_VIOLATION, EXCEPTION_DATATYPE_MISALIGNMENT, |
|---|
| 435 | EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_FLT_DENORMAL_OPERAND, |
|---|
| 436 | EXCEPTION_FLT_DIVIDE_BY_ZERO, EXCEPTION_FLT_INEXACT_RESULT, |
|---|
| 437 | EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW, |
|---|
| 438 | EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, |
|---|
| 439 | EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, |
|---|
| 440 | EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_IN_PAGE_ERROR, |
|---|
| 441 | EXCEPTION_ILLEGAL_INSTRUCTION, EXCEPTION_NONCONTINUABLE_EXCEPTION, |
|---|
| 442 | EXCEPTION_STACK_OVERFLOW, EXCEPTION_INVALID_DISPOSITION, |
|---|
| 443 | EXCEPTION_GUARD_PAGE, EXCEPTION_INVALID_HANDLE, CONTROL_C_EXIT |
|---|
| 444 | }; |
|---|
| 445 | int cmd; |
|---|
| 446 | |
|---|
| 447 | if (objc != 2) { |
|---|
| 448 | Tcl_WrongNumArgs(interp, 0, objv, "<type-of-exception>"); |
|---|
| 449 | return TCL_ERROR; |
|---|
| 450 | } |
|---|
| 451 | if (Tcl_GetIndexFromObj(interp, objv[1], cmds, "command", 0, |
|---|
| 452 | &cmd) != TCL_OK) { |
|---|
| 453 | return TCL_ERROR; |
|---|
| 454 | } |
|---|
| 455 | |
|---|
| 456 | /* |
|---|
| 457 | * Make sure the GPF dialog doesn't popup. |
|---|
| 458 | */ |
|---|
| 459 | |
|---|
| 460 | SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); |
|---|
| 461 | |
|---|
| 462 | /* |
|---|
| 463 | * As Tcl does not handle structured exceptions, this falls all the way |
|---|
| 464 | * back up the instruction stack to the C run-time portion that called |
|---|
| 465 | * main() where the process will now be terminated with this exception |
|---|
| 466 | * code by the default handler the C run-time provides. |
|---|
| 467 | */ |
|---|
| 468 | |
|---|
| 469 | /* SMASH! */ |
|---|
| 470 | RaiseException(exceptions[cmd], EXCEPTION_NONCONTINUABLE, 0, NULL); |
|---|
| 471 | |
|---|
| 472 | /* NOTREACHED */ |
|---|
| 473 | return TCL_OK; |
|---|
| 474 | } |
|---|
| 475 | |
|---|
| 476 | static int |
|---|
| 477 | TestplatformChmod( |
|---|
| 478 | const char *nativePath, |
|---|
| 479 | int pmode) |
|---|
| 480 | { |
|---|
| 481 | typedef DWORD (WINAPI *getSidLengthRequiredDef)(UCHAR); |
|---|
| 482 | typedef BOOL (WINAPI *initializeSidDef)(PSID, PSID_IDENTIFIER_AUTHORITY, |
|---|
| 483 | BYTE); |
|---|
| 484 | typedef PDWORD (WINAPI *getSidSubAuthorityDef)(PSID, DWORD); |
|---|
| 485 | typedef DWORD (WINAPI *setNamedSecurityInfoADef)(IN LPSTR, |
|---|
| 486 | IN SE_OBJECT_TYPE, IN SECURITY_INFORMATION, IN PSID, IN PSID, |
|---|
| 487 | IN PACL, IN PACL); |
|---|
| 488 | typedef BOOL (WINAPI *getAceDef)(PACL, DWORD, LPVOID *); |
|---|
| 489 | typedef BOOL (WINAPI *addAceDef)(PACL, DWORD, DWORD, LPVOID, DWORD); |
|---|
| 490 | typedef BOOL (WINAPI *equalSidDef)(PSID, PSID); |
|---|
| 491 | typedef BOOL (WINAPI *addAccessDeniedAceDef)(PACL, DWORD, DWORD, PSID); |
|---|
| 492 | typedef BOOL (WINAPI *initializeAclDef)(PACL, DWORD, DWORD); |
|---|
| 493 | typedef DWORD (WINAPI *getLengthSidDef)(PSID); |
|---|
| 494 | typedef BOOL (WINAPI *getAclInformationDef)(PACL, LPVOID, DWORD, |
|---|
| 495 | ACL_INFORMATION_CLASS); |
|---|
| 496 | typedef BOOL (WINAPI *getSecurityDescriptorDaclDef)(PSECURITY_DESCRIPTOR, |
|---|
| 497 | LPBOOL, PACL *, LPBOOL); |
|---|
| 498 | typedef BOOL (WINAPI *lookupAccountNameADef)(LPCSTR, LPCSTR, PSID, |
|---|
| 499 | PDWORD, LPSTR, LPDWORD, PSID_NAME_USE); |
|---|
| 500 | typedef BOOL (WINAPI *getFileSecurityADef)(LPCSTR, SECURITY_INFORMATION, |
|---|
| 501 | PSECURITY_DESCRIPTOR, DWORD, LPDWORD); |
|---|
| 502 | |
|---|
| 503 | static const SECURITY_INFORMATION infoBits = OWNER_SECURITY_INFORMATION |
|---|
| 504 | | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; |
|---|
| 505 | static const DWORD readOnlyMask = FILE_DELETE_CHILD | FILE_ADD_FILE |
|---|
| 506 | | FILE_ADD_SUBDIRECTORY | FILE_WRITE_EA | FILE_APPEND_DATA |
|---|
| 507 | | FILE_WRITE_DATA | DELETE; |
|---|
| 508 | |
|---|
| 509 | /* |
|---|
| 510 | * References to security functions (only available on NT and later). |
|---|
| 511 | */ |
|---|
| 512 | |
|---|
| 513 | static getSidLengthRequiredDef getSidLengthRequiredProc; |
|---|
| 514 | static initializeSidDef initializeSidProc; |
|---|
| 515 | static getSidSubAuthorityDef getSidSubAuthorityProc; |
|---|
| 516 | static setNamedSecurityInfoADef setNamedSecurityInfoProc; |
|---|
| 517 | static getAceDef getAceProc; |
|---|
| 518 | static addAceDef addAceProc; |
|---|
| 519 | static equalSidDef equalSidProc; |
|---|
| 520 | static addAccessDeniedAceDef addAccessDeniedAceProc; |
|---|
| 521 | static initializeAclDef initializeAclProc; |
|---|
| 522 | static getLengthSidDef getLengthSidProc; |
|---|
| 523 | static getAclInformationDef getAclInformationProc; |
|---|
| 524 | static getSecurityDescriptorDaclDef getSecurityDescriptorDaclProc; |
|---|
| 525 | static lookupAccountNameADef lookupAccountNameProc; |
|---|
| 526 | static getFileSecurityADef getFileSecurityProc; |
|---|
| 527 | static int initialized = 0; |
|---|
| 528 | |
|---|
| 529 | const BOOL set_readOnly = !(pmode & 0222); |
|---|
| 530 | BOOL acl_readOnly_found = FALSE, curAclPresent, curAclDefaulted; |
|---|
| 531 | SID_IDENTIFIER_AUTHORITY userSidAuthority = { |
|---|
| 532 | SECURITY_WORLD_SID_AUTHORITY |
|---|
| 533 | }; |
|---|
| 534 | BYTE *secDesc = 0; |
|---|
| 535 | DWORD secDescLen, attr, newAclSize; |
|---|
| 536 | ACL_SIZE_INFORMATION ACLSize; |
|---|
| 537 | PACL curAcl, newAcl = 0; |
|---|
| 538 | WORD j; |
|---|
| 539 | SID *userSid = 0; |
|---|
| 540 | TCHAR *userDomain = 0; |
|---|
| 541 | int res = 0; |
|---|
| 542 | |
|---|
| 543 | /* |
|---|
| 544 | * One time initialization, dynamically load Windows NT features |
|---|
| 545 | */ |
|---|
| 546 | |
|---|
| 547 | if (!initialized) { |
|---|
| 548 | TCL_DECLARE_MUTEX(initializeMutex) |
|---|
| 549 | Tcl_MutexLock(&initializeMutex); |
|---|
| 550 | if (!initialized) { |
|---|
| 551 | HINSTANCE hInstance = LoadLibrary("Advapi32"); |
|---|
| 552 | |
|---|
| 553 | if (hInstance != NULL) { |
|---|
| 554 | setNamedSecurityInfoProc = (setNamedSecurityInfoADef) |
|---|
| 555 | GetProcAddress(hInstance, "SetNamedSecurityInfoA"); |
|---|
| 556 | getFileSecurityProc = (getFileSecurityADef) |
|---|
| 557 | GetProcAddress(hInstance, "GetFileSecurityA"); |
|---|
| 558 | getAceProc = (getAceDef) |
|---|
| 559 | GetProcAddress(hInstance, "GetAce"); |
|---|
| 560 | addAceProc = (addAceDef) |
|---|
| 561 | GetProcAddress(hInstance, "AddAce"); |
|---|
| 562 | equalSidProc = (equalSidDef) |
|---|
| 563 | GetProcAddress(hInstance, "EqualSid"); |
|---|
| 564 | addAccessDeniedAceProc = (addAccessDeniedAceDef) |
|---|
| 565 | GetProcAddress(hInstance, "AddAccessDeniedAce"); |
|---|
| 566 | initializeAclProc = (initializeAclDef) |
|---|
| 567 | GetProcAddress(hInstance, "InitializeAcl"); |
|---|
| 568 | getLengthSidProc = (getLengthSidDef) |
|---|
| 569 | GetProcAddress(hInstance, "GetLengthSid"); |
|---|
| 570 | getAclInformationProc = (getAclInformationDef) |
|---|
| 571 | GetProcAddress(hInstance, "GetAclInformation"); |
|---|
| 572 | getSecurityDescriptorDaclProc = (getSecurityDescriptorDaclDef) |
|---|
| 573 | GetProcAddress(hInstance, "GetSecurityDescriptorDacl"); |
|---|
| 574 | lookupAccountNameProc = (lookupAccountNameADef) |
|---|
| 575 | GetProcAddress(hInstance, "LookupAccountNameA"); |
|---|
| 576 | getSidLengthRequiredProc = (getSidLengthRequiredDef) |
|---|
| 577 | GetProcAddress(hInstance, "GetSidLengthRequired"); |
|---|
| 578 | initializeSidProc = (initializeSidDef) |
|---|
| 579 | GetProcAddress(hInstance, "InitializeSid"); |
|---|
| 580 | getSidSubAuthorityProc = (getSidSubAuthorityDef) |
|---|
| 581 | GetProcAddress(hInstance, "GetSidSubAuthority"); |
|---|
| 582 | |
|---|
| 583 | if (setNamedSecurityInfoProc && getAceProc && addAceProc |
|---|
| 584 | && equalSidProc && addAccessDeniedAceProc |
|---|
| 585 | && initializeAclProc && getLengthSidProc |
|---|
| 586 | && getAclInformationProc |
|---|
| 587 | && getSecurityDescriptorDaclProc |
|---|
| 588 | && lookupAccountNameProc && getFileSecurityProc |
|---|
| 589 | && getSidLengthRequiredProc && initializeSidProc |
|---|
| 590 | && getSidSubAuthorityProc) { |
|---|
| 591 | initialized = 1; |
|---|
| 592 | } |
|---|
| 593 | } |
|---|
| 594 | if (!initialized) { |
|---|
| 595 | initialized = -1; |
|---|
| 596 | } |
|---|
| 597 | } |
|---|
| 598 | Tcl_MutexUnlock(&initializeMutex); |
|---|
| 599 | } |
|---|
| 600 | |
|---|
| 601 | /* |
|---|
| 602 | * Process the chmod request. |
|---|
| 603 | */ |
|---|
| 604 | |
|---|
| 605 | attr = GetFileAttributes(nativePath); |
|---|
| 606 | |
|---|
| 607 | /* |
|---|
| 608 | * nativePath not found |
|---|
| 609 | */ |
|---|
| 610 | |
|---|
| 611 | if (attr == 0xffffffff) { |
|---|
| 612 | res = -1; |
|---|
| 613 | goto done; |
|---|
| 614 | } |
|---|
| 615 | |
|---|
| 616 | /* |
|---|
| 617 | * If no ACL API is present or nativePath is not a directory, there is no |
|---|
| 618 | * special handling. |
|---|
| 619 | */ |
|---|
| 620 | |
|---|
| 621 | if (initialized < 0 || !(attr & FILE_ATTRIBUTE_DIRECTORY)) { |
|---|
| 622 | goto done; |
|---|
| 623 | } |
|---|
| 624 | |
|---|
| 625 | /* |
|---|
| 626 | * Set the result to error, if the ACL change is successful it will be |
|---|
| 627 | * reset to 0. |
|---|
| 628 | */ |
|---|
| 629 | |
|---|
| 630 | res = -1; |
|---|
| 631 | |
|---|
| 632 | /* |
|---|
| 633 | * Read the security descriptor for the directory. Note the first call |
|---|
| 634 | * obtains the size of the security descriptor. |
|---|
| 635 | */ |
|---|
| 636 | |
|---|
| 637 | if (!getFileSecurityProc(nativePath, infoBits, NULL, 0, &secDescLen)) { |
|---|
| 638 | DWORD secDescLen2 = 0; |
|---|
| 639 | |
|---|
| 640 | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { |
|---|
| 641 | goto done; |
|---|
| 642 | } |
|---|
| 643 | |
|---|
| 644 | secDesc = (BYTE *) ckalloc(secDescLen); |
|---|
| 645 | if (!getFileSecurityProc(nativePath, infoBits, |
|---|
| 646 | (PSECURITY_DESCRIPTOR) secDesc, secDescLen, &secDescLen2) |
|---|
| 647 | || (secDescLen < secDescLen2)) { |
|---|
| 648 | goto done; |
|---|
| 649 | } |
|---|
| 650 | } |
|---|
| 651 | |
|---|
| 652 | /* |
|---|
| 653 | * Get the World SID. |
|---|
| 654 | */ |
|---|
| 655 | |
|---|
| 656 | userSid = (SID *) ckalloc(getSidLengthRequiredProc((UCHAR) 1)); |
|---|
| 657 | initializeSidProc(userSid, &userSidAuthority, (BYTE) 1); |
|---|
| 658 | *(getSidSubAuthorityProc(userSid, 0)) = SECURITY_WORLD_RID; |
|---|
| 659 | |
|---|
| 660 | /* |
|---|
| 661 | * If curAclPresent == false then curAcl and curAclDefaulted not valid. |
|---|
| 662 | */ |
|---|
| 663 | |
|---|
| 664 | if (!getSecurityDescriptorDaclProc((PSECURITY_DESCRIPTOR) secDesc, |
|---|
| 665 | &curAclPresent, &curAcl, &curAclDefaulted)) { |
|---|
| 666 | goto done; |
|---|
| 667 | } |
|---|
| 668 | if (!curAclPresent || !curAcl) { |
|---|
| 669 | ACLSize.AclBytesInUse = 0; |
|---|
| 670 | ACLSize.AceCount = 0; |
|---|
| 671 | } else if (!getAclInformationProc(curAcl, &ACLSize, sizeof(ACLSize), |
|---|
| 672 | AclSizeInformation)) { |
|---|
| 673 | goto done; |
|---|
| 674 | } |
|---|
| 675 | |
|---|
| 676 | /* |
|---|
| 677 | * Allocate memory for the new ACL. |
|---|
| 678 | */ |
|---|
| 679 | |
|---|
| 680 | newAclSize = ACLSize.AclBytesInUse + sizeof(ACCESS_DENIED_ACE) |
|---|
| 681 | + getLengthSidProc(userSid) - sizeof(DWORD); |
|---|
| 682 | newAcl = (ACL *) ckalloc(newAclSize); |
|---|
| 683 | |
|---|
| 684 | /* |
|---|
| 685 | * Initialize the new ACL. |
|---|
| 686 | */ |
|---|
| 687 | |
|---|
| 688 | if (!initializeAclProc(newAcl, newAclSize, ACL_REVISION)) { |
|---|
| 689 | goto done; |
|---|
| 690 | } |
|---|
| 691 | |
|---|
| 692 | /* |
|---|
| 693 | * Add denied to make readonly, this will be known as a "read-only tag". |
|---|
| 694 | */ |
|---|
| 695 | |
|---|
| 696 | if (set_readOnly && !addAccessDeniedAceProc(newAcl, ACL_REVISION, |
|---|
| 697 | readOnlyMask, userSid)) { |
|---|
| 698 | goto done; |
|---|
| 699 | } |
|---|
| 700 | |
|---|
| 701 | acl_readOnly_found = FALSE; |
|---|
| 702 | for (j = 0; j < ACLSize.AceCount; j++) { |
|---|
| 703 | PACL *pACE2; |
|---|
| 704 | ACE_HEADER *phACE2; |
|---|
| 705 | |
|---|
| 706 | if (!getAceProc(curAcl, j, (LPVOID *) &pACE2)) { |
|---|
| 707 | goto done; |
|---|
| 708 | } |
|---|
| 709 | |
|---|
| 710 | phACE2 = (ACE_HEADER *) pACE2; |
|---|
| 711 | |
|---|
| 712 | /* |
|---|
| 713 | * Do NOT propagate inherited ACEs. |
|---|
| 714 | */ |
|---|
| 715 | |
|---|
| 716 | if (phACE2->AceFlags & INHERITED_ACE) { |
|---|
| 717 | continue; |
|---|
| 718 | } |
|---|
| 719 | |
|---|
| 720 | /* |
|---|
| 721 | * Skip the "read-only tag" restriction (either added above, or it is |
|---|
| 722 | * being removed). |
|---|
| 723 | */ |
|---|
| 724 | |
|---|
| 725 | if (phACE2->AceType == ACCESS_DENIED_ACE_TYPE) { |
|---|
| 726 | ACCESS_DENIED_ACE *pACEd = (ACCESS_DENIED_ACE *) phACE2; |
|---|
| 727 | |
|---|
| 728 | if (pACEd->Mask == readOnlyMask |
|---|
| 729 | && equalSidProc(userSid, (PSID) &pACEd->SidStart)) { |
|---|
| 730 | acl_readOnly_found = TRUE; |
|---|
| 731 | continue; |
|---|
| 732 | } |
|---|
| 733 | } |
|---|
| 734 | |
|---|
| 735 | /* |
|---|
| 736 | * Copy the current ACE from the old to the new ACL. |
|---|
| 737 | */ |
|---|
| 738 | |
|---|
| 739 | if (!addAceProc(newAcl, ACL_REVISION, MAXDWORD, pACE2, |
|---|
| 740 | ((PACE_HEADER) pACE2)->AceSize)) { |
|---|
| 741 | goto done; |
|---|
| 742 | } |
|---|
| 743 | } |
|---|
| 744 | |
|---|
| 745 | /* |
|---|
| 746 | * Apply the new ACL. |
|---|
| 747 | */ |
|---|
| 748 | |
|---|
| 749 | if (set_readOnly == acl_readOnly_found || setNamedSecurityInfoProc( |
|---|
| 750 | (LPSTR) nativePath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, |
|---|
| 751 | NULL, NULL, newAcl, NULL) == ERROR_SUCCESS) { |
|---|
| 752 | res = 0; |
|---|
| 753 | } |
|---|
| 754 | |
|---|
| 755 | done: |
|---|
| 756 | if (secDesc) { |
|---|
| 757 | ckfree(secDesc); |
|---|
| 758 | } |
|---|
| 759 | if (newAcl) { |
|---|
| 760 | ckfree((char *) newAcl); |
|---|
| 761 | } |
|---|
| 762 | if (userSid) { |
|---|
| 763 | ckfree((char *) userSid); |
|---|
| 764 | } |
|---|
| 765 | if (userDomain) { |
|---|
| 766 | ckfree(userDomain); |
|---|
| 767 | } |
|---|
| 768 | |
|---|
| 769 | if (res != 0) { |
|---|
| 770 | return res; |
|---|
| 771 | } |
|---|
| 772 | |
|---|
| 773 | /* |
|---|
| 774 | * Run normal chmod command. |
|---|
| 775 | */ |
|---|
| 776 | |
|---|
| 777 | return chmod(nativePath, pmode); |
|---|
| 778 | } |
|---|
| 779 | |
|---|
| 780 | /* |
|---|
| 781 | *--------------------------------------------------------------------------- |
|---|
| 782 | * |
|---|
| 783 | * TestchmodCmd -- |
|---|
| 784 | * |
|---|
| 785 | * Implements the "testchmod" cmd. Used when testing "file" command. The |
|---|
| 786 | * only attribute used by the Windows platform is the user write flag; if |
|---|
| 787 | * this is not set, the file is made read-only. Otherwise, the file is |
|---|
| 788 | * made read-write. |
|---|
| 789 | * |
|---|
| 790 | * Results: |
|---|
| 791 | * A standard Tcl result. |
|---|
| 792 | * |
|---|
| 793 | * Side effects: |
|---|
| 794 | * Changes permissions of specified files. |
|---|
| 795 | * |
|---|
| 796 | *--------------------------------------------------------------------------- |
|---|
| 797 | */ |
|---|
| 798 | |
|---|
| 799 | static int |
|---|
| 800 | TestchmodCmd( |
|---|
| 801 | ClientData dummy, /* Not used. */ |
|---|
| 802 | Tcl_Interp *interp, /* Current interpreter. */ |
|---|
| 803 | int argc, /* Number of arguments. */ |
|---|
| 804 | const char **argv) /* Argument strings. */ |
|---|
| 805 | { |
|---|
| 806 | int i, mode; |
|---|
| 807 | char *rest; |
|---|
| 808 | |
|---|
| 809 | if (argc < 2) { |
|---|
| 810 | usage: |
|---|
| 811 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
|---|
| 812 | " mode file ?file ...?", NULL); |
|---|
| 813 | return TCL_ERROR; |
|---|
| 814 | } |
|---|
| 815 | |
|---|
| 816 | mode = (int) strtol(argv[1], &rest, 8); |
|---|
| 817 | if ((rest == argv[1]) || (*rest != '\0')) { |
|---|
| 818 | goto usage; |
|---|
| 819 | } |
|---|
| 820 | |
|---|
| 821 | for (i = 2; i < argc; i++) { |
|---|
| 822 | Tcl_DString buffer; |
|---|
| 823 | const char *translated; |
|---|
| 824 | |
|---|
| 825 | translated = Tcl_TranslateFileName(interp, argv[i], &buffer); |
|---|
| 826 | if (translated == NULL) { |
|---|
| 827 | return TCL_ERROR; |
|---|
| 828 | } |
|---|
| 829 | if (TestplatformChmod(translated, mode) != 0) { |
|---|
| 830 | Tcl_AppendResult(interp, translated, ": ", Tcl_PosixError(interp), |
|---|
| 831 | NULL); |
|---|
| 832 | return TCL_ERROR; |
|---|
| 833 | } |
|---|
| 834 | Tcl_DStringFree(&buffer); |
|---|
| 835 | } |
|---|
| 836 | return TCL_OK; |
|---|
| 837 | } |
|---|
| 838 | |
|---|
| 839 | /* |
|---|
| 840 | * Local Variables: |
|---|
| 841 | * mode: c |
|---|
| 842 | * c-basic-offset: 4 |
|---|
| 843 | * fill-column: 78 |
|---|
| 844 | * End: |
|---|
| 845 | */ |
|---|