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).

florian 2024-10-19 14:03 trunk
Commit fe6ef89f5f9d23e09c899e854d6dc796ababc59fdf14068477e1bee0bd4bd821
2 files changed +2 -2 +57 -18
+2 -2
--- src/file.c
+++ src/file.c
@@ -1408,13 +1408,13 @@
14081408
closedir(d);
14091409
}
14101410
fossil_path_free(zNative);
14111411
if( zResult==0 ) zResult = fossil_strdup(zPath);
14121412
return zResult;
1413
-#else /* _WIN32 */
1413
+#else /* !_WIN32 */
14141414
return win32_file_case_preferred_name(zDir,zPath);
1415
-#endif /* _WIN32 */
1415
+#endif /* !_WIN32 */
14161416
}
14171417
14181418
/*
14191419
** COMMAND: test-case-filename
14201420
**
14211421
--- 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 @@
293293
fossil_path_free(zUtf8);
294294
}
295295
296296
/* Perform case-insensitive comparison of two UTF-16 file names. Try to load the
297297
** 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.
299304
*/
300
-int win32_compare_filenames_nocase(
305
+int win32_filenames_equal_nocase(
301306
const wchar_t *fn1,
302307
const wchar_t *fn2
303308
){
304309
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 ){
307315
fnCompareStringOrdinal =
308316
GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal");
309
- tried_CompareStringOrdinal = 1;
317
+ loaded_CompareStringOrdinal = 1;
310318
}
311319
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);
315338
}
339
+ /* In what kind of strange parallel universe are we? */
340
+ return lstrcmpiW(fn1,fn2)==0;
316341
}
317342
318343
/* Helper macros to deal with directory separators. */
319344
#define IS_DIRSEP(s,i) ( s[i]=='/' || s[i]=='\\' )
320345
#define NEXT_DIRSEP(s,i) while( s[i] && s[i]!='/' && s[i]!='\\' ){i++;}
@@ -347,18 +372,28 @@
347372
*/
348373
char *win32_file_case_preferred_name(
349374
const char *zBase,
350375
const char *zPath
351376
){
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;
359384
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;
360395
memcpy(zBuf,zBase,cchBase);
361396
if( !IS_DIRSEP(zBuf,cchBase-1) ){
362397
zBuf[cchBase++]=L'/';
363398
}
364399
memcpy(zBuf+cchBase,zPath,cchPath+1);
@@ -372,10 +407,14 @@
372407
int cchComp;
373408
char chSep;
374409
int fDone;
375410
if( IS_DIRSEP(zBuf,i) ){
376411
zRes[ncUsed++] = zBuf[i];
412
+ if( ncUsed+2>cchRes ){ /* Directory slash + NULL*/
413
+ cchRes += 32; /* Overprovisioning. */
414
+ zRes = fossil_realloc(zRes,cchRes);
415
+ }
377416
i = j = i+1;
378417
continue;
379418
}
380419
NEXT_DIRSEP(zBuf,j);
381420
fDone = zBuf[j]==0;
@@ -385,20 +424,20 @@
385424
hFind = FindFirstFileW(wzBuf,&fd);
386425
if( hFind!=INVALID_HANDLE_VALUE ){
387426
wchar_t *wzComp = fossil_utf8_to_path(zComp,0);
388427
FindClose(hFind);
389428
/* 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) ){
391430
zCompBuf = fossil_path_to_utf8(fd.cFileName);
392431
zComp = zCompBuf;
393432
}
394433
fossil_path_free(wzComp);
395434
}
396435
fossil_path_free(wzBuf);
397436
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. */
400439
zRes = fossil_realloc(zRes,cchRes);
401440
}
402441
memcpy(zRes+ncUsed,zComp,cchComp);
403442
ncUsed += cchComp;
404443
if( zCompBuf ){
405444
--- 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

Keyboard Shortcuts

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