Fossil SCM
Implement file_access() function such that it accepts paths>MAX_PATH. Implementation copied (with some simplifications) from Tcl 8.6
Commit
ba4b3ac1d29720ab4b7635a06cdd44f10cf30f7e
Parent
29f023fe536c49b…
1 file changed
+205
-12
+205
-12
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -87,11 +87,11 @@ | ||
| 87 | 87 | ULARGE_INTEGER ull; |
| 88 | 88 | ull.LowPart = attr.ftLastWriteTime.dwLowDateTime; |
| 89 | 89 | ull.HighPart = attr.ftLastWriteTime.dwHighDateTime; |
| 90 | 90 | buf->st_mode = (attr.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)? |
| 91 | 91 | S_IFDIR:S_IFREG; |
| 92 | - buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | buf->attr.nFileSizeLow; | |
| 92 | + buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; | |
| 93 | 93 | buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL; |
| 94 | 94 | } |
| 95 | 95 | #endif |
| 96 | 96 | fossil_filename_free(zMbcs); |
| 97 | 97 | return rc; |
| @@ -318,22 +318,215 @@ | ||
| 318 | 318 | /* |
| 319 | 319 | ** Wrapper around the access() system call. |
| 320 | 320 | */ |
| 321 | 321 | int file_access(const char *zFilename, int flags){ |
| 322 | 322 | #ifdef _WIN32 |
| 323 | + SECURITY_DESCRIPTOR *sdPtr = NULL; | |
| 324 | + unsigned long size; | |
| 325 | + PSID pSid = 0; | |
| 326 | + BOOL SidDefaulted; | |
| 327 | + SID_IDENTIFIER_AUTHORITY samba_unmapped = {{0, 0, 0, 0, 0, 22}}; | |
| 328 | + GENERIC_MAPPING genMap; | |
| 329 | + HANDLE hToken = NULL; | |
| 330 | + DWORD desiredAccess = 0, grantedAccess = 0; | |
| 331 | + BOOL accessYesNo = FALSE; | |
| 332 | + PRIVILEGE_SET privSet; | |
| 333 | + DWORD privSetSize = sizeof(PRIVILEGE_SET); | |
| 334 | + int error, rc = 0; | |
| 335 | + DWORD attr; | |
| 323 | 336 | wchar_t *zMbcs = fossil_utf8_to_filename(zFilename); |
| 324 | - int rc; | |
| 325 | - if( memcmp(zMbcs, L"\\\\?\\", 8)==0 ){ | |
| 326 | - /* Unfortunately, _waccess cannot handle extended prefixes. */ | |
| 327 | - if( memcmp(zMbcs+4, "UNC\\", 8)==0 ){ | |
| 328 | - zMbcs[6] = '\\'; | |
| 329 | - rc = _waccess(zMbcs+6, flags); | |
| 330 | - }else{ | |
| 331 | - rc = _waccess(zMbcs+4, flags); | |
| 332 | - } | |
| 333 | - }else{ | |
| 334 | - rc = _waccess(zMbcs, flags); | |
| 337 | + | |
| 338 | + attr = GetFileAttributesW(zMbcs); | |
| 339 | + | |
| 340 | + if (attr == INVALID_FILE_ATTRIBUTES) { | |
| 341 | + /* | |
| 342 | + * File might not exist. | |
| 343 | + */ | |
| 344 | + | |
| 345 | + DWORD lasterror = GetLastError(); | |
| 346 | + if (lasterror != ERROR_SHARING_VIOLATION) { | |
| 347 | + fossil_filename_free(zMbcs); | |
| 348 | + return -1; | |
| 349 | + } | |
| 350 | + } | |
| 351 | + | |
| 352 | + if (flags == F_OK) { | |
| 353 | + /* | |
| 354 | + * File exists, nothing else to check. | |
| 355 | + */ | |
| 356 | + | |
| 357 | + fossil_filename_free(zMbcs); | |
| 358 | + return 0; | |
| 359 | + } | |
| 360 | + | |
| 361 | + if ((flags & W_OK) | |
| 362 | + && (attr & FILE_ATTRIBUTE_READONLY) | |
| 363 | + && !(attr & FILE_ATTRIBUTE_DIRECTORY)) { | |
| 364 | + /* | |
| 365 | + * The attributes say the file is not writable. If the file is a | |
| 366 | + * regular file (i.e., not a directory), then the file is not | |
| 367 | + * writable, full stop. For directories, the read-only bit is | |
| 368 | + * (mostly) ignored by Windows, so we can't ascertain anything about | |
| 369 | + * directory access from the attrib data. However, if we have the | |
| 370 | + * advanced 'getFileSecurityProc', then more robust ACL checks | |
| 371 | + * will be done below. | |
| 372 | + */ | |
| 373 | + | |
| 374 | + fossil_filename_free(zMbcs); | |
| 375 | + return -1; | |
| 376 | + } | |
| 377 | + | |
| 378 | + /* | |
| 379 | + * It looks as if the permissions are ok, but if we are on NT, 2000 or XP, | |
| 380 | + * we have a more complex permissions structure so we try to check that. | |
| 381 | + * The code below is remarkably complex for such a simple thing as finding | |
| 382 | + * what permissions the OS has set for a file. | |
| 383 | + */ | |
| 384 | + | |
| 385 | + /* | |
| 386 | + * First find out how big the buffer needs to be. | |
| 387 | + */ | |
| 388 | + | |
| 389 | + size = 0; | |
| 390 | + GetFileSecurityW(zMbcs, | |
| 391 | + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | |
| 392 | + | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, | |
| 393 | + 0, 0, &size); | |
| 394 | + | |
| 395 | + /* | |
| 396 | + * Should have failed with ERROR_INSUFFICIENT_BUFFER | |
| 397 | + */ | |
| 398 | + | |
| 399 | + error = GetLastError(); | |
| 400 | + if (error != ERROR_INSUFFICIENT_BUFFER) { | |
| 401 | + /* | |
| 402 | + * Most likely case is ERROR_ACCESS_DENIED, which we will convert | |
| 403 | + * to EACCES - just what we want! | |
| 404 | + */ | |
| 405 | + | |
| 406 | + fossil_filename_free(zMbcs); | |
| 407 | + return -1; | |
| 408 | + } | |
| 409 | + | |
| 410 | + /* | |
| 411 | + * Now size contains the size of buffer needed. | |
| 412 | + */ | |
| 413 | + | |
| 414 | + sdPtr = (SECURITY_DESCRIPTOR *) HeapAlloc(GetProcessHeap(), 0, size); | |
| 415 | + | |
| 416 | + if (sdPtr == NULL) { | |
| 417 | + goto accessError; | |
| 418 | + } | |
| 419 | + | |
| 420 | + /* | |
| 421 | + * Call GetFileSecurity() for real. | |
| 422 | + */ | |
| 423 | + | |
| 424 | + if (!GetFileSecurityW(zMbcs, | |
| 425 | + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | |
| 426 | + | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, | |
| 427 | + sdPtr, size, &size)) { | |
| 428 | + /* | |
| 429 | + * Error getting owner SD | |
| 430 | + */ | |
| 431 | + | |
| 432 | + goto accessError; | |
| 433 | + } | |
| 434 | + | |
| 435 | + /* | |
| 436 | + * As of Samba 3.0.23 (10-Jul-2006), unmapped users and groups are | |
| 437 | + * assigned to SID domains S-1-22-1 and S-1-22-2, where "22" is the | |
| 438 | + * top-level authority. If the file owner and group is unmapped then | |
| 439 | + * the ACL access check below will only test against world access, | |
| 440 | + * which is likely to be more restrictive than the actual access | |
| 441 | + * restrictions. Since the ACL tests are more likely wrong than | |
| 442 | + * right, skip them. Moreover, the unix owner access permissions are | |
| 443 | + * usually mapped to the Windows attributes, so if the user is the | |
| 444 | + * file owner then the attrib checks above are correct (as far as they | |
| 445 | + * go). | |
| 446 | + */ | |
| 447 | + | |
| 448 | + if(!GetSecurityDescriptorOwner(sdPtr,&pSid,&SidDefaulted) || | |
| 449 | + memcmp(GetSidIdentifierAuthority(pSid),&samba_unmapped, | |
| 450 | + sizeof(SID_IDENTIFIER_AUTHORITY))==0) { | |
| 451 | + HeapFree(GetProcessHeap(), 0, sdPtr); | |
| 452 | + fossil_filename_free(zMbcs); | |
| 453 | + return 0; /* Attrib tests say access allowed. */ | |
| 454 | + } | |
| 455 | + | |
| 456 | + /* | |
| 457 | + * Perform security impersonation of the user and open the resulting | |
| 458 | + * thread token. | |
| 459 | + */ | |
| 460 | + | |
| 461 | + if (!ImpersonateSelf(SecurityImpersonation)) { | |
| 462 | + /* | |
| 463 | + * Unable to perform security impersonation. | |
| 464 | + */ | |
| 465 | + | |
| 466 | + goto accessError; | |
| 467 | + } | |
| 468 | + if (!OpenThreadToken(GetCurrentThread(), | |
| 469 | + TOKEN_DUPLICATE | TOKEN_QUERY, FALSE, &hToken)) { | |
| 470 | + /* | |
| 471 | + * Unable to get current thread's token. | |
| 472 | + */ | |
| 473 | + | |
| 474 | + goto accessError; | |
| 475 | + } | |
| 476 | + | |
| 477 | + RevertToSelf(); | |
| 478 | + | |
| 479 | + /* | |
| 480 | + * Setup desiredAccess according to the access priveleges we are | |
| 481 | + * checking. | |
| 482 | + */ | |
| 483 | + | |
| 484 | + if (flags & R_OK) { | |
| 485 | + desiredAccess |= FILE_GENERIC_READ; | |
| 486 | + } | |
| 487 | + if (flags & W_OK) { | |
| 488 | + desiredAccess |= FILE_GENERIC_WRITE; | |
| 489 | + } | |
| 490 | + | |
| 491 | + memset(&genMap, 0x0, sizeof(GENERIC_MAPPING)); | |
| 492 | + genMap.GenericRead = FILE_GENERIC_READ; | |
| 493 | + genMap.GenericWrite = FILE_GENERIC_WRITE; | |
| 494 | + genMap.GenericExecute = FILE_GENERIC_EXECUTE; | |
| 495 | + genMap.GenericAll = FILE_ALL_ACCESS; | |
| 496 | + | |
| 497 | + /* | |
| 498 | + * Perform access check using the token. | |
| 499 | + */ | |
| 500 | + | |
| 501 | + if (!AccessCheck(sdPtr, hToken, desiredAccess, | |
| 502 | + &genMap, &privSet, &privSetSize, &grantedAccess, | |
| 503 | + &accessYesNo)) { | |
| 504 | + /* | |
| 505 | + * Unable to perform access check. | |
| 506 | + */ | |
| 507 | + | |
| 508 | + accessError: | |
| 509 | + if (sdPtr != NULL) { | |
| 510 | + HeapFree(GetProcessHeap(), 0, sdPtr); | |
| 511 | + } | |
| 512 | + if (hToken != NULL) { | |
| 513 | + CloseHandle(hToken); | |
| 514 | + } | |
| 515 | + fossil_filename_free(zMbcs); | |
| 516 | + return -1; | |
| 517 | + } | |
| 518 | + | |
| 519 | + /* | |
| 520 | + * Clean up. | |
| 521 | + */ | |
| 522 | + | |
| 523 | + HeapFree(GetProcessHeap(), 0, sdPtr); | |
| 524 | + CloseHandle(hToken); | |
| 525 | + if (!accessYesNo) { | |
| 526 | + fossil_filename_free(zMbcs); | |
| 527 | + return -1; | |
| 335 | 528 | } |
| 336 | 529 | #else |
| 337 | 530 | char *zMbcs = fossil_utf8_to_filename(zFilename); |
| 338 | 531 | int rc = access(zMbcs, flags); |
| 339 | 532 | #endif |
| 340 | 533 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -87,11 +87,11 @@ | |
| 87 | ULARGE_INTEGER ull; |
| 88 | ull.LowPart = attr.ftLastWriteTime.dwLowDateTime; |
| 89 | ull.HighPart = attr.ftLastWriteTime.dwHighDateTime; |
| 90 | buf->st_mode = (attr.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)? |
| 91 | S_IFDIR:S_IFREG; |
| 92 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | buf->attr.nFileSizeLow; |
| 93 | buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL; |
| 94 | } |
| 95 | #endif |
| 96 | fossil_filename_free(zMbcs); |
| 97 | return rc; |
| @@ -318,22 +318,215 @@ | |
| 318 | /* |
| 319 | ** Wrapper around the access() system call. |
| 320 | */ |
| 321 | int file_access(const char *zFilename, int flags){ |
| 322 | #ifdef _WIN32 |
| 323 | wchar_t *zMbcs = fossil_utf8_to_filename(zFilename); |
| 324 | int rc; |
| 325 | if( memcmp(zMbcs, L"\\\\?\\", 8)==0 ){ |
| 326 | /* Unfortunately, _waccess cannot handle extended prefixes. */ |
| 327 | if( memcmp(zMbcs+4, "UNC\\", 8)==0 ){ |
| 328 | zMbcs[6] = '\\'; |
| 329 | rc = _waccess(zMbcs+6, flags); |
| 330 | }else{ |
| 331 | rc = _waccess(zMbcs+4, flags); |
| 332 | } |
| 333 | }else{ |
| 334 | rc = _waccess(zMbcs, flags); |
| 335 | } |
| 336 | #else |
| 337 | char *zMbcs = fossil_utf8_to_filename(zFilename); |
| 338 | int rc = access(zMbcs, flags); |
| 339 | #endif |
| 340 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -87,11 +87,11 @@ | |
| 87 | ULARGE_INTEGER ull; |
| 88 | ull.LowPart = attr.ftLastWriteTime.dwLowDateTime; |
| 89 | ull.HighPart = attr.ftLastWriteTime.dwHighDateTime; |
| 90 | buf->st_mode = (attr.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)? |
| 91 | S_IFDIR:S_IFREG; |
| 92 | buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; |
| 93 | buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL; |
| 94 | } |
| 95 | #endif |
| 96 | fossil_filename_free(zMbcs); |
| 97 | return rc; |
| @@ -318,22 +318,215 @@ | |
| 318 | /* |
| 319 | ** Wrapper around the access() system call. |
| 320 | */ |
| 321 | int file_access(const char *zFilename, int flags){ |
| 322 | #ifdef _WIN32 |
| 323 | SECURITY_DESCRIPTOR *sdPtr = NULL; |
| 324 | unsigned long size; |
| 325 | PSID pSid = 0; |
| 326 | BOOL SidDefaulted; |
| 327 | SID_IDENTIFIER_AUTHORITY samba_unmapped = {{0, 0, 0, 0, 0, 22}}; |
| 328 | GENERIC_MAPPING genMap; |
| 329 | HANDLE hToken = NULL; |
| 330 | DWORD desiredAccess = 0, grantedAccess = 0; |
| 331 | BOOL accessYesNo = FALSE; |
| 332 | PRIVILEGE_SET privSet; |
| 333 | DWORD privSetSize = sizeof(PRIVILEGE_SET); |
| 334 | int error, rc = 0; |
| 335 | DWORD attr; |
| 336 | wchar_t *zMbcs = fossil_utf8_to_filename(zFilename); |
| 337 | |
| 338 | attr = GetFileAttributesW(zMbcs); |
| 339 | |
| 340 | if (attr == INVALID_FILE_ATTRIBUTES) { |
| 341 | /* |
| 342 | * File might not exist. |
| 343 | */ |
| 344 | |
| 345 | DWORD lasterror = GetLastError(); |
| 346 | if (lasterror != ERROR_SHARING_VIOLATION) { |
| 347 | fossil_filename_free(zMbcs); |
| 348 | return -1; |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | if (flags == F_OK) { |
| 353 | /* |
| 354 | * File exists, nothing else to check. |
| 355 | */ |
| 356 | |
| 357 | fossil_filename_free(zMbcs); |
| 358 | return 0; |
| 359 | } |
| 360 | |
| 361 | if ((flags & W_OK) |
| 362 | && (attr & FILE_ATTRIBUTE_READONLY) |
| 363 | && !(attr & FILE_ATTRIBUTE_DIRECTORY)) { |
| 364 | /* |
| 365 | * The attributes say the file is not writable. If the file is a |
| 366 | * regular file (i.e., not a directory), then the file is not |
| 367 | * writable, full stop. For directories, the read-only bit is |
| 368 | * (mostly) ignored by Windows, so we can't ascertain anything about |
| 369 | * directory access from the attrib data. However, if we have the |
| 370 | * advanced 'getFileSecurityProc', then more robust ACL checks |
| 371 | * will be done below. |
| 372 | */ |
| 373 | |
| 374 | fossil_filename_free(zMbcs); |
| 375 | return -1; |
| 376 | } |
| 377 | |
| 378 | /* |
| 379 | * It looks as if the permissions are ok, but if we are on NT, 2000 or XP, |
| 380 | * we have a more complex permissions structure so we try to check that. |
| 381 | * The code below is remarkably complex for such a simple thing as finding |
| 382 | * what permissions the OS has set for a file. |
| 383 | */ |
| 384 | |
| 385 | /* |
| 386 | * First find out how big the buffer needs to be. |
| 387 | */ |
| 388 | |
| 389 | size = 0; |
| 390 | GetFileSecurityW(zMbcs, |
| 391 | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
| 392 | | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, |
| 393 | 0, 0, &size); |
| 394 | |
| 395 | /* |
| 396 | * Should have failed with ERROR_INSUFFICIENT_BUFFER |
| 397 | */ |
| 398 | |
| 399 | error = GetLastError(); |
| 400 | if (error != ERROR_INSUFFICIENT_BUFFER) { |
| 401 | /* |
| 402 | * Most likely case is ERROR_ACCESS_DENIED, which we will convert |
| 403 | * to EACCES - just what we want! |
| 404 | */ |
| 405 | |
| 406 | fossil_filename_free(zMbcs); |
| 407 | return -1; |
| 408 | } |
| 409 | |
| 410 | /* |
| 411 | * Now size contains the size of buffer needed. |
| 412 | */ |
| 413 | |
| 414 | sdPtr = (SECURITY_DESCRIPTOR *) HeapAlloc(GetProcessHeap(), 0, size); |
| 415 | |
| 416 | if (sdPtr == NULL) { |
| 417 | goto accessError; |
| 418 | } |
| 419 | |
| 420 | /* |
| 421 | * Call GetFileSecurity() for real. |
| 422 | */ |
| 423 | |
| 424 | if (!GetFileSecurityW(zMbcs, |
| 425 | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
| 426 | | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, |
| 427 | sdPtr, size, &size)) { |
| 428 | /* |
| 429 | * Error getting owner SD |
| 430 | */ |
| 431 | |
| 432 | goto accessError; |
| 433 | } |
| 434 | |
| 435 | /* |
| 436 | * As of Samba 3.0.23 (10-Jul-2006), unmapped users and groups are |
| 437 | * assigned to SID domains S-1-22-1 and S-1-22-2, where "22" is the |
| 438 | * top-level authority. If the file owner and group is unmapped then |
| 439 | * the ACL access check below will only test against world access, |
| 440 | * which is likely to be more restrictive than the actual access |
| 441 | * restrictions. Since the ACL tests are more likely wrong than |
| 442 | * right, skip them. Moreover, the unix owner access permissions are |
| 443 | * usually mapped to the Windows attributes, so if the user is the |
| 444 | * file owner then the attrib checks above are correct (as far as they |
| 445 | * go). |
| 446 | */ |
| 447 | |
| 448 | if(!GetSecurityDescriptorOwner(sdPtr,&pSid,&SidDefaulted) || |
| 449 | memcmp(GetSidIdentifierAuthority(pSid),&samba_unmapped, |
| 450 | sizeof(SID_IDENTIFIER_AUTHORITY))==0) { |
| 451 | HeapFree(GetProcessHeap(), 0, sdPtr); |
| 452 | fossil_filename_free(zMbcs); |
| 453 | return 0; /* Attrib tests say access allowed. */ |
| 454 | } |
| 455 | |
| 456 | /* |
| 457 | * Perform security impersonation of the user and open the resulting |
| 458 | * thread token. |
| 459 | */ |
| 460 | |
| 461 | if (!ImpersonateSelf(SecurityImpersonation)) { |
| 462 | /* |
| 463 | * Unable to perform security impersonation. |
| 464 | */ |
| 465 | |
| 466 | goto accessError; |
| 467 | } |
| 468 | if (!OpenThreadToken(GetCurrentThread(), |
| 469 | TOKEN_DUPLICATE | TOKEN_QUERY, FALSE, &hToken)) { |
| 470 | /* |
| 471 | * Unable to get current thread's token. |
| 472 | */ |
| 473 | |
| 474 | goto accessError; |
| 475 | } |
| 476 | |
| 477 | RevertToSelf(); |
| 478 | |
| 479 | /* |
| 480 | * Setup desiredAccess according to the access priveleges we are |
| 481 | * checking. |
| 482 | */ |
| 483 | |
| 484 | if (flags & R_OK) { |
| 485 | desiredAccess |= FILE_GENERIC_READ; |
| 486 | } |
| 487 | if (flags & W_OK) { |
| 488 | desiredAccess |= FILE_GENERIC_WRITE; |
| 489 | } |
| 490 | |
| 491 | memset(&genMap, 0x0, sizeof(GENERIC_MAPPING)); |
| 492 | genMap.GenericRead = FILE_GENERIC_READ; |
| 493 | genMap.GenericWrite = FILE_GENERIC_WRITE; |
| 494 | genMap.GenericExecute = FILE_GENERIC_EXECUTE; |
| 495 | genMap.GenericAll = FILE_ALL_ACCESS; |
| 496 | |
| 497 | /* |
| 498 | * Perform access check using the token. |
| 499 | */ |
| 500 | |
| 501 | if (!AccessCheck(sdPtr, hToken, desiredAccess, |
| 502 | &genMap, &privSet, &privSetSize, &grantedAccess, |
| 503 | &accessYesNo)) { |
| 504 | /* |
| 505 | * Unable to perform access check. |
| 506 | */ |
| 507 | |
| 508 | accessError: |
| 509 | if (sdPtr != NULL) { |
| 510 | HeapFree(GetProcessHeap(), 0, sdPtr); |
| 511 | } |
| 512 | if (hToken != NULL) { |
| 513 | CloseHandle(hToken); |
| 514 | } |
| 515 | fossil_filename_free(zMbcs); |
| 516 | return -1; |
| 517 | } |
| 518 | |
| 519 | /* |
| 520 | * Clean up. |
| 521 | */ |
| 522 | |
| 523 | HeapFree(GetProcessHeap(), 0, sdPtr); |
| 524 | CloseHandle(hToken); |
| 525 | if (!accessYesNo) { |
| 526 | fossil_filename_free(zMbcs); |
| 527 | return -1; |
| 528 | } |
| 529 | #else |
| 530 | char *zMbcs = fossil_utf8_to_filename(zFilename); |
| 531 | int rc = access(zMbcs, flags); |
| 532 | #endif |
| 533 |