Fossil SCM

Implement file_access() function such that it accepts paths>MAX_PATH. Implementation copied (with some simplifications) from Tcl 8.6

jan.nijtmans 2013-12-12 14:27 win32-longpath
Commit ba4b3ac1d29720ab4b7635a06cdd44f10cf30f7e
1 file changed +205 -12
+205 -12
--- src/file.c
+++ src/file.c
@@ -87,11 +87,11 @@
8787
ULARGE_INTEGER ull;
8888
ull.LowPart = attr.ftLastWriteTime.dwLowDateTime;
8989
ull.HighPart = attr.ftLastWriteTime.dwHighDateTime;
9090
buf->st_mode = (attr.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)?
9191
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;
9393
buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL;
9494
}
9595
#endif
9696
fossil_filename_free(zMbcs);
9797
return rc;
@@ -318,22 +318,215 @@
318318
/*
319319
** Wrapper around the access() system call.
320320
*/
321321
int file_access(const char *zFilename, int flags){
322322
#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;
323336
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;
335528
}
336529
#else
337530
char *zMbcs = fossil_utf8_to_filename(zFilename);
338531
int rc = access(zMbcs, flags);
339532
#endif
340533
--- 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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button