Fossil SCM
Another update for[9919dfbbaa]: Make sure reallocated buffers always grow, guard all buffer writes by overflow checks (next time use blobs), make sure the `case-sensitive' setting and command-line option are followed, and use brute force to achieve binary (vs.linguistic) file name comparison (only on older versions of Windows).
Commit
fe6ef89f5f9d23e09c899e854d6dc796ababc59fdf14068477e1bee0bd4bd821
Parent
efd79f87f55bcfa…
2 files changed
+2
-2
+57
-18
+2
-2
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -1408,13 +1408,13 @@ | ||
| 1408 | 1408 | closedir(d); |
| 1409 | 1409 | } |
| 1410 | 1410 | fossil_path_free(zNative); |
| 1411 | 1411 | if( zResult==0 ) zResult = fossil_strdup(zPath); |
| 1412 | 1412 | return zResult; |
| 1413 | -#else /* _WIN32 */ | |
| 1413 | +#else /* !_WIN32 */ | |
| 1414 | 1414 | return win32_file_case_preferred_name(zDir,zPath); |
| 1415 | -#endif /* _WIN32 */ | |
| 1415 | +#endif /* !_WIN32 */ | |
| 1416 | 1416 | } |
| 1417 | 1417 | |
| 1418 | 1418 | /* |
| 1419 | 1419 | ** COMMAND: test-case-filename |
| 1420 | 1420 | ** |
| 1421 | 1421 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1408,13 +1408,13 @@ | |
| 1408 | closedir(d); |
| 1409 | } |
| 1410 | fossil_path_free(zNative); |
| 1411 | if( zResult==0 ) zResult = fossil_strdup(zPath); |
| 1412 | return zResult; |
| 1413 | #else /* _WIN32 */ |
| 1414 | return win32_file_case_preferred_name(zDir,zPath); |
| 1415 | #endif /* _WIN32 */ |
| 1416 | } |
| 1417 | |
| 1418 | /* |
| 1419 | ** COMMAND: test-case-filename |
| 1420 | ** |
| 1421 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1408,13 +1408,13 @@ | |
| 1408 | closedir(d); |
| 1409 | } |
| 1410 | fossil_path_free(zNative); |
| 1411 | if( zResult==0 ) zResult = fossil_strdup(zPath); |
| 1412 | return zResult; |
| 1413 | #else /* !_WIN32 */ |
| 1414 | return win32_file_case_preferred_name(zDir,zPath); |
| 1415 | #endif /* !_WIN32 */ |
| 1416 | } |
| 1417 | |
| 1418 | /* |
| 1419 | ** COMMAND: test-case-filename |
| 1420 | ** |
| 1421 |
+57
-18
| --- src/winfile.c | ||
| +++ src/winfile.c | ||
| @@ -293,28 +293,53 @@ | ||
| 293 | 293 | fossil_path_free(zUtf8); |
| 294 | 294 | } |
| 295 | 295 | |
| 296 | 296 | /* Perform case-insensitive comparison of two UTF-16 file names. Try to load the |
| 297 | 297 | ** CompareStringOrdinal() function on Windows Vista and newer, and resort to the |
| 298 | -** lstrcmpiW() function on Windows XP. | |
| 298 | +** RtlEqualUnicodeString() function on Windows XP. | |
| 299 | +** The dance to invoke RtlEqualUnicodeString() is necessary because lstrcmpiW() | |
| 300 | +** performs linguistic comparison, while the former performs binary comparison. | |
| 301 | +** As an example, matching "ß" (U+00DF Latin Small Letter Sharp S) with "ss" is | |
| 302 | +** undesirable in file name comparison, so lstrcmpiW() is only invoked in cases | |
| 303 | +** that are technically impossible and contradicting all known laws of physics. | |
| 299 | 304 | */ |
| 300 | -int win32_compare_filenames_nocase( | |
| 305 | +int win32_filenames_equal_nocase( | |
| 301 | 306 | const wchar_t *fn1, |
| 302 | 307 | const wchar_t *fn2 |
| 303 | 308 | ){ |
| 304 | 309 | static FARPROC fnCompareStringOrdinal; |
| 305 | - static int tried_CompareStringOrdinal; | |
| 306 | - if( !tried_CompareStringOrdinal ){ | |
| 310 | + static FARPROC fnRtlInitUnicodeString; | |
| 311 | + static FARPROC fnRtlEqualUnicodeString; | |
| 312 | + static int loaded_CompareStringOrdinal; | |
| 313 | + static int loaded_RtlUnicodeStringAPIs; | |
| 314 | + if( !loaded_CompareStringOrdinal ){ | |
| 307 | 315 | fnCompareStringOrdinal = |
| 308 | 316 | GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal"); |
| 309 | - tried_CompareStringOrdinal = 1; | |
| 317 | + loaded_CompareStringOrdinal = 1; | |
| 310 | 318 | } |
| 311 | 319 | if( fnCompareStringOrdinal ){ |
| 312 | - return -2 + fnCompareStringOrdinal(fn1,-1,fn2,-1,1); | |
| 313 | - }else{ | |
| 314 | - return lstrcmpiW(fn1,fn2); | |
| 320 | + return fnCompareStringOrdinal(fn1,-1,fn2,-1,1)-2==0; | |
| 321 | + } | |
| 322 | + if( !loaded_RtlUnicodeStringAPIs ){ | |
| 323 | + fnRtlInitUnicodeString = | |
| 324 | + GetProcAddress(GetModuleHandleA("ntdll"),"RtlInitUnicodeString"); | |
| 325 | + fnRtlEqualUnicodeString = | |
| 326 | + GetProcAddress(GetModuleHandleA("ntdll"),"RtlEqualUnicodeString"); | |
| 327 | + loaded_RtlUnicodeStringAPIs = 1; | |
| 328 | + } | |
| 329 | + if( fnRtlInitUnicodeString && fnRtlEqualUnicodeString ){ | |
| 330 | + struct { /* UNICODE_STRING from <ntdef.h> */ | |
| 331 | + unsigned short Length; | |
| 332 | + unsigned short MaximumLength; | |
| 333 | + wchar_t *Buffer; | |
| 334 | + } u1, u2; | |
| 335 | + fnRtlInitUnicodeString(&u1,fn1); | |
| 336 | + fnRtlInitUnicodeString(&u2,fn2); | |
| 337 | + return (unsigned char)fnRtlEqualUnicodeString(&u1,&u2,1); | |
| 315 | 338 | } |
| 339 | + /* In what kind of strange parallel universe are we? */ | |
| 340 | + return lstrcmpiW(fn1,fn2)==0; | |
| 316 | 341 | } |
| 317 | 342 | |
| 318 | 343 | /* Helper macros to deal with directory separators. */ |
| 319 | 344 | #define IS_DIRSEP(s,i) ( s[i]=='/' || s[i]=='\\' ) |
| 320 | 345 | #define NEXT_DIRSEP(s,i) while( s[i] && s[i]!='/' && s[i]!='\\' ){i++;} |
| @@ -347,18 +372,28 @@ | ||
| 347 | 372 | */ |
| 348 | 373 | char *win32_file_case_preferred_name( |
| 349 | 374 | const char *zBase, |
| 350 | 375 | const char *zPath |
| 351 | 376 | ){ |
| 352 | - int cchBase = strlen(zBase); | |
| 353 | - int cchPath = strlen(zPath); | |
| 354 | - int cchBuf = cchBase + cchPath + 2; /* + NULL + optional directory slash */ | |
| 355 | - int cchRes = cchPath + 1; /* + NULL */ | |
| 356 | - char *zBuf = fossil_malloc(cchBuf); | |
| 357 | - char *zRes = fossil_malloc(cchRes); | |
| 358 | - int ncUsed = 0; | |
| 377 | + int cchBase; | |
| 378 | + int cchPath; | |
| 379 | + int cchBuf; | |
| 380 | + int cchRes; | |
| 381 | + char *zBuf; | |
| 382 | + char *zRes; | |
| 383 | + int ncUsed; | |
| 359 | 384 | int i, j; |
| 385 | + if( filenames_are_case_sensitive() ){ | |
| 386 | + return fossil_strdup(zPath); | |
| 387 | + } | |
| 388 | + cchBase = strlen(zBase); | |
| 389 | + cchPath = strlen(zPath); | |
| 390 | + cchBuf = cchBase + cchPath + 2; /* + NULL + optional directory slash */ | |
| 391 | + cchRes = cchPath + 1; /* + NULL */ | |
| 392 | + zBuf = fossil_malloc(cchBuf); | |
| 393 | + zRes = fossil_malloc(cchRes); | |
| 394 | + ncUsed = 0; | |
| 360 | 395 | memcpy(zBuf,zBase,cchBase); |
| 361 | 396 | if( !IS_DIRSEP(zBuf,cchBase-1) ){ |
| 362 | 397 | zBuf[cchBase++]=L'/'; |
| 363 | 398 | } |
| 364 | 399 | memcpy(zBuf+cchBase,zPath,cchPath+1); |
| @@ -372,10 +407,14 @@ | ||
| 372 | 407 | int cchComp; |
| 373 | 408 | char chSep; |
| 374 | 409 | int fDone; |
| 375 | 410 | if( IS_DIRSEP(zBuf,i) ){ |
| 376 | 411 | zRes[ncUsed++] = zBuf[i]; |
| 412 | + if( ncUsed+2>cchRes ){ /* Directory slash + NULL*/ | |
| 413 | + cchRes += 32; /* Overprovisioning. */ | |
| 414 | + zRes = fossil_realloc(zRes,cchRes); | |
| 415 | + } | |
| 377 | 416 | i = j = i+1; |
| 378 | 417 | continue; |
| 379 | 418 | } |
| 380 | 419 | NEXT_DIRSEP(zBuf,j); |
| 381 | 420 | fDone = zBuf[j]==0; |
| @@ -385,20 +424,20 @@ | ||
| 385 | 424 | hFind = FindFirstFileW(wzBuf,&fd); |
| 386 | 425 | if( hFind!=INVALID_HANDLE_VALUE ){ |
| 387 | 426 | wchar_t *wzComp = fossil_utf8_to_path(zComp,0); |
| 388 | 427 | FindClose(hFind); |
| 389 | 428 | /* Test fd.cFileName, not fd.cAlternateFileName (classic 8.3 format). */ |
| 390 | - if( win32_compare_filenames_nocase(wzComp,fd.cFileName)==0 ){ | |
| 429 | + if( win32_filenames_equal_nocase(wzComp,fd.cFileName) ){ | |
| 391 | 430 | zCompBuf = fossil_path_to_utf8(fd.cFileName); |
| 392 | 431 | zComp = zCompBuf; |
| 393 | 432 | } |
| 394 | 433 | fossil_path_free(wzComp); |
| 395 | 434 | } |
| 396 | 435 | fossil_path_free(wzBuf); |
| 397 | 436 | cchComp = strlen(zComp); |
| 398 | - if( ncUsed+cchComp+1>cchRes ){ | |
| 399 | - cchRes = ncUsed + cchComp + 32; /* While at it, add some extra space. */ | |
| 437 | + if( ncUsed+cchComp+1>cchRes ){ /* Current component + NULL */ | |
| 438 | + cchRes += cchComp + 32; /* Overprovisioning. */ | |
| 400 | 439 | zRes = fossil_realloc(zRes,cchRes); |
| 401 | 440 | } |
| 402 | 441 | memcpy(zRes+ncUsed,zComp,cchComp); |
| 403 | 442 | ncUsed += cchComp; |
| 404 | 443 | if( zCompBuf ){ |
| 405 | 444 |
| --- src/winfile.c | |
| +++ src/winfile.c | |
| @@ -293,28 +293,53 @@ | |
| 293 | fossil_path_free(zUtf8); |
| 294 | } |
| 295 | |
| 296 | /* Perform case-insensitive comparison of two UTF-16 file names. Try to load the |
| 297 | ** CompareStringOrdinal() function on Windows Vista and newer, and resort to the |
| 298 | ** lstrcmpiW() function on Windows XP. |
| 299 | */ |
| 300 | int win32_compare_filenames_nocase( |
| 301 | const wchar_t *fn1, |
| 302 | const wchar_t *fn2 |
| 303 | ){ |
| 304 | static FARPROC fnCompareStringOrdinal; |
| 305 | static int tried_CompareStringOrdinal; |
| 306 | if( !tried_CompareStringOrdinal ){ |
| 307 | fnCompareStringOrdinal = |
| 308 | GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal"); |
| 309 | tried_CompareStringOrdinal = 1; |
| 310 | } |
| 311 | if( fnCompareStringOrdinal ){ |
| 312 | return -2 + fnCompareStringOrdinal(fn1,-1,fn2,-1,1); |
| 313 | }else{ |
| 314 | return lstrcmpiW(fn1,fn2); |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | /* Helper macros to deal with directory separators. */ |
| 319 | #define IS_DIRSEP(s,i) ( s[i]=='/' || s[i]=='\\' ) |
| 320 | #define NEXT_DIRSEP(s,i) while( s[i] && s[i]!='/' && s[i]!='\\' ){i++;} |
| @@ -347,18 +372,28 @@ | |
| 347 | */ |
| 348 | char *win32_file_case_preferred_name( |
| 349 | const char *zBase, |
| 350 | const char *zPath |
| 351 | ){ |
| 352 | int cchBase = strlen(zBase); |
| 353 | int cchPath = strlen(zPath); |
| 354 | int cchBuf = cchBase + cchPath + 2; /* + NULL + optional directory slash */ |
| 355 | int cchRes = cchPath + 1; /* + NULL */ |
| 356 | char *zBuf = fossil_malloc(cchBuf); |
| 357 | char *zRes = fossil_malloc(cchRes); |
| 358 | int ncUsed = 0; |
| 359 | int i, j; |
| 360 | memcpy(zBuf,zBase,cchBase); |
| 361 | if( !IS_DIRSEP(zBuf,cchBase-1) ){ |
| 362 | zBuf[cchBase++]=L'/'; |
| 363 | } |
| 364 | memcpy(zBuf+cchBase,zPath,cchPath+1); |
| @@ -372,10 +407,14 @@ | |
| 372 | int cchComp; |
| 373 | char chSep; |
| 374 | int fDone; |
| 375 | if( IS_DIRSEP(zBuf,i) ){ |
| 376 | zRes[ncUsed++] = zBuf[i]; |
| 377 | i = j = i+1; |
| 378 | continue; |
| 379 | } |
| 380 | NEXT_DIRSEP(zBuf,j); |
| 381 | fDone = zBuf[j]==0; |
| @@ -385,20 +424,20 @@ | |
| 385 | hFind = FindFirstFileW(wzBuf,&fd); |
| 386 | if( hFind!=INVALID_HANDLE_VALUE ){ |
| 387 | wchar_t *wzComp = fossil_utf8_to_path(zComp,0); |
| 388 | FindClose(hFind); |
| 389 | /* Test fd.cFileName, not fd.cAlternateFileName (classic 8.3 format). */ |
| 390 | if( win32_compare_filenames_nocase(wzComp,fd.cFileName)==0 ){ |
| 391 | zCompBuf = fossil_path_to_utf8(fd.cFileName); |
| 392 | zComp = zCompBuf; |
| 393 | } |
| 394 | fossil_path_free(wzComp); |
| 395 | } |
| 396 | fossil_path_free(wzBuf); |
| 397 | cchComp = strlen(zComp); |
| 398 | if( ncUsed+cchComp+1>cchRes ){ |
| 399 | cchRes = ncUsed + cchComp + 32; /* While at it, add some extra space. */ |
| 400 | zRes = fossil_realloc(zRes,cchRes); |
| 401 | } |
| 402 | memcpy(zRes+ncUsed,zComp,cchComp); |
| 403 | ncUsed += cchComp; |
| 404 | if( zCompBuf ){ |
| 405 |
| --- src/winfile.c | |
| +++ src/winfile.c | |
| @@ -293,28 +293,53 @@ | |
| 293 | fossil_path_free(zUtf8); |
| 294 | } |
| 295 | |
| 296 | /* Perform case-insensitive comparison of two UTF-16 file names. Try to load the |
| 297 | ** CompareStringOrdinal() function on Windows Vista and newer, and resort to the |
| 298 | ** RtlEqualUnicodeString() function on Windows XP. |
| 299 | ** The dance to invoke RtlEqualUnicodeString() is necessary because lstrcmpiW() |
| 300 | ** performs linguistic comparison, while the former performs binary comparison. |
| 301 | ** As an example, matching "ß" (U+00DF Latin Small Letter Sharp S) with "ss" is |
| 302 | ** undesirable in file name comparison, so lstrcmpiW() is only invoked in cases |
| 303 | ** that are technically impossible and contradicting all known laws of physics. |
| 304 | */ |
| 305 | int win32_filenames_equal_nocase( |
| 306 | const wchar_t *fn1, |
| 307 | const wchar_t *fn2 |
| 308 | ){ |
| 309 | static FARPROC fnCompareStringOrdinal; |
| 310 | static FARPROC fnRtlInitUnicodeString; |
| 311 | static FARPROC fnRtlEqualUnicodeString; |
| 312 | static int loaded_CompareStringOrdinal; |
| 313 | static int loaded_RtlUnicodeStringAPIs; |
| 314 | if( !loaded_CompareStringOrdinal ){ |
| 315 | fnCompareStringOrdinal = |
| 316 | GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal"); |
| 317 | loaded_CompareStringOrdinal = 1; |
| 318 | } |
| 319 | if( fnCompareStringOrdinal ){ |
| 320 | return fnCompareStringOrdinal(fn1,-1,fn2,-1,1)-2==0; |
| 321 | } |
| 322 | if( !loaded_RtlUnicodeStringAPIs ){ |
| 323 | fnRtlInitUnicodeString = |
| 324 | GetProcAddress(GetModuleHandleA("ntdll"),"RtlInitUnicodeString"); |
| 325 | fnRtlEqualUnicodeString = |
| 326 | GetProcAddress(GetModuleHandleA("ntdll"),"RtlEqualUnicodeString"); |
| 327 | loaded_RtlUnicodeStringAPIs = 1; |
| 328 | } |
| 329 | if( fnRtlInitUnicodeString && fnRtlEqualUnicodeString ){ |
| 330 | struct { /* UNICODE_STRING from <ntdef.h> */ |
| 331 | unsigned short Length; |
| 332 | unsigned short MaximumLength; |
| 333 | wchar_t *Buffer; |
| 334 | } u1, u2; |
| 335 | fnRtlInitUnicodeString(&u1,fn1); |
| 336 | fnRtlInitUnicodeString(&u2,fn2); |
| 337 | return (unsigned char)fnRtlEqualUnicodeString(&u1,&u2,1); |
| 338 | } |
| 339 | /* In what kind of strange parallel universe are we? */ |
| 340 | return lstrcmpiW(fn1,fn2)==0; |
| 341 | } |
| 342 | |
| 343 | /* Helper macros to deal with directory separators. */ |
| 344 | #define IS_DIRSEP(s,i) ( s[i]=='/' || s[i]=='\\' ) |
| 345 | #define NEXT_DIRSEP(s,i) while( s[i] && s[i]!='/' && s[i]!='\\' ){i++;} |
| @@ -347,18 +372,28 @@ | |
| 372 | */ |
| 373 | char *win32_file_case_preferred_name( |
| 374 | const char *zBase, |
| 375 | const char *zPath |
| 376 | ){ |
| 377 | int cchBase; |
| 378 | int cchPath; |
| 379 | int cchBuf; |
| 380 | int cchRes; |
| 381 | char *zBuf; |
| 382 | char *zRes; |
| 383 | int ncUsed; |
| 384 | int i, j; |
| 385 | if( filenames_are_case_sensitive() ){ |
| 386 | return fossil_strdup(zPath); |
| 387 | } |
| 388 | cchBase = strlen(zBase); |
| 389 | cchPath = strlen(zPath); |
| 390 | cchBuf = cchBase + cchPath + 2; /* + NULL + optional directory slash */ |
| 391 | cchRes = cchPath + 1; /* + NULL */ |
| 392 | zBuf = fossil_malloc(cchBuf); |
| 393 | zRes = fossil_malloc(cchRes); |
| 394 | ncUsed = 0; |
| 395 | memcpy(zBuf,zBase,cchBase); |
| 396 | if( !IS_DIRSEP(zBuf,cchBase-1) ){ |
| 397 | zBuf[cchBase++]=L'/'; |
| 398 | } |
| 399 | memcpy(zBuf+cchBase,zPath,cchPath+1); |
| @@ -372,10 +407,14 @@ | |
| 407 | int cchComp; |
| 408 | char chSep; |
| 409 | int fDone; |
| 410 | if( IS_DIRSEP(zBuf,i) ){ |
| 411 | zRes[ncUsed++] = zBuf[i]; |
| 412 | if( ncUsed+2>cchRes ){ /* Directory slash + NULL*/ |
| 413 | cchRes += 32; /* Overprovisioning. */ |
| 414 | zRes = fossil_realloc(zRes,cchRes); |
| 415 | } |
| 416 | i = j = i+1; |
| 417 | continue; |
| 418 | } |
| 419 | NEXT_DIRSEP(zBuf,j); |
| 420 | fDone = zBuf[j]==0; |
| @@ -385,20 +424,20 @@ | |
| 424 | hFind = FindFirstFileW(wzBuf,&fd); |
| 425 | if( hFind!=INVALID_HANDLE_VALUE ){ |
| 426 | wchar_t *wzComp = fossil_utf8_to_path(zComp,0); |
| 427 | FindClose(hFind); |
| 428 | /* Test fd.cFileName, not fd.cAlternateFileName (classic 8.3 format). */ |
| 429 | if( win32_filenames_equal_nocase(wzComp,fd.cFileName) ){ |
| 430 | zCompBuf = fossil_path_to_utf8(fd.cFileName); |
| 431 | zComp = zCompBuf; |
| 432 | } |
| 433 | fossil_path_free(wzComp); |
| 434 | } |
| 435 | fossil_path_free(wzBuf); |
| 436 | cchComp = strlen(zComp); |
| 437 | if( ncUsed+cchComp+1>cchRes ){ /* Current component + NULL */ |
| 438 | cchRes += cchComp + 32; /* Overprovisioning. */ |
| 439 | zRes = fossil_realloc(zRes,cchRes); |
| 440 | } |
| 441 | memcpy(zRes+ncUsed,zComp,cchComp); |
| 442 | ncUsed += cchComp; |
| 443 | if( zCompBuf ){ |
| 444 |