@@ -346,7 +346,7 @@ static void ConvertDirent(const struct dirent* entry, struct DirectoryEntry* out
346
346
// We use Marshal.PtrToStringAnsi on the managed side, which takes a pointer to
347
347
// the start of the unmanaged string. Give the caller back a pointer to the
348
348
// location of the start of the string that exists in their own byte buffer.
349
- outputEntry -> Name = entry -> d_name ;
349
+ outputEntry -> Name = strdup ( entry -> d_name ) ;
350
350
#if !defined(DT_UNKNOWN )
351
351
// AIX has no d_type, and since we can't get the directory that goes with
352
352
// the filename from ReadDir, we can't stat the file. Return unknown and
@@ -380,12 +380,22 @@ int32_t SystemNative_GetReadDirRBufferSize(void)
380
380
#endif
381
381
}
382
382
383
- #if READDIR_SORT
384
- static int cmpstring (const void * p1 , const void * p2 )
383
+ static int CompareByName (const void * p1 , const void * p2 )
385
384
{
386
- return strcmp (((struct dirent * ) p1 )-> d_name , ((struct dirent * ) p2 )-> d_name );
385
+ const struct DirectoryEntry * directoryEntry1 = (const struct DirectoryEntry * )p1 ;
386
+ const struct DirectoryEntry * directoryEntry2 = (const struct DirectoryEntry * )p2 ;
387
+
388
+ // Sort NULL values to the end of the array. This can happen when
389
+ // a file is deleted while GetFiles is called.
390
+ if (directoryEntry1 -> Name == directoryEntry2 -> Name )
391
+ return 0 ;
392
+ if (directoryEntry1 -> Name == NULL )
393
+ return 1 ;
394
+ if (directoryEntry2 -> Name == NULL )
395
+ return -1 ;
396
+
397
+ return strcmp (directoryEntry1 -> Name , directoryEntry2 -> Name );
387
398
}
388
- #endif
389
399
390
400
// To reduce the number of string copies, the caller of this function is responsible to ensure the memory
391
401
// referenced by outputEntry remains valid until it is read.
@@ -453,51 +463,55 @@ int32_t SystemNative_ReadDirR(struct DIRWrapper* dirWrapper, uint8_t* buffer, in
453
463
(void )buffer ; // unused
454
464
(void )bufferSize ; // unused
455
465
errno = 0 ;
456
-
457
- #if READDIR_SORT
458
- struct dirent * entry ;
466
+ bool endOfEntries = false;
459
467
460
468
if (!dirWrapper -> result )
461
469
{
470
+ struct dirent * entry ;
462
471
size_t numEntries = 0 ;
463
- while ((entry = readdir (dirWrapper -> dir ))) numEntries ++ ;
472
+ while ((entry = readdir (dirWrapper -> dir )))
473
+ numEntries ++ ;
464
474
if (numEntries )
465
475
{
466
- dirWrapper -> result = malloc (numEntries * sizeof (struct dirent ));
476
+ // Use calloc to ensure the array is zero-initialized.
477
+ dirWrapper -> result = calloc (numEntries , sizeof (struct DirectoryEntry ));
467
478
dirWrapper -> curIndex = 0 ;
479
+
468
480
#if HAVE_REWINDDIR
469
481
rewinddir (dirWrapper -> dir );
470
482
#else
471
483
closedir (dirWrapper -> dir );
472
484
dirWrapper -> dir = opendir (dirWrapper -> dirPath );
473
485
#endif
486
+
487
+ // If we iterate fewer entries than exist because some files were deleted
488
+ // since the time we computed numEntries above, that will be fine. Those
489
+ // extra entries will be zero-initialized and will be sorted to the end
490
+ // of the array by the qsort below.
474
491
size_t index = 0 ;
475
492
while ((entry = readdir (dirWrapper -> dir )) && index < numEntries )
476
493
{
477
- memcpy ( & (( struct dirent * ) dirWrapper -> result ) [index ], entry , sizeof ( struct dirent ) );
494
+ ConvertDirent ( entry , & dirWrapper -> result [index ]);
478
495
index ++ ;
479
496
}
480
497
481
- qsort ( dirWrapper -> result , numEntries , sizeof ( struct dirent ), cmpstring ) ;
482
- dirWrapper -> numEntries = index ;
498
+ dirWrapper -> numEntries = index ;
499
+ qsort ( dirWrapper -> result , dirWrapper -> numEntries , sizeof ( * dirWrapper -> result ), CompareByName );
483
500
}
484
501
}
485
502
486
503
if (dirWrapper -> curIndex < dirWrapper -> numEntries )
487
504
{
488
- entry = & (( struct dirent * ) dirWrapper -> result ) [dirWrapper -> curIndex ];
505
+ * outputEntry = dirWrapper -> result [dirWrapper -> curIndex ];
489
506
dirWrapper -> curIndex ++ ;
490
507
}
491
-
492
508
else
493
- entry = NULL ;
494
-
495
- #else
496
- struct dirent * entry = readdir (dirWrapper -> dir );
497
- #endif
509
+ {
510
+ endOfEntries = true;
511
+ }
498
512
499
513
// 0 returned with null result -> end-of-stream
500
- if (entry == NULL )
514
+ if (endOfEntries )
501
515
{
502
516
memset (outputEntry , 0 , sizeof (* outputEntry )); // managed out param must be initialized
503
517
@@ -510,7 +524,7 @@ int32_t SystemNative_ReadDirR(struct DIRWrapper* dirWrapper, uint8_t* buffer, in
510
524
return -1 ;
511
525
}
512
526
#endif
513
- ConvertDirent ( entry , outputEntry );
527
+
514
528
return 0 ;
515
529
}
516
530
@@ -523,13 +537,11 @@ struct DIRWrapper* SystemNative_OpenDir(const char* path)
523
537
524
538
struct DIRWrapper * ret = (struct DIRWrapper * )malloc (sizeof (struct DIRWrapper ));
525
539
ret -> dir = dir ;
526
- #if READDIR_SORT
527
540
ret -> result = NULL ;
528
541
ret -> curIndex = 0 ;
529
542
ret -> numEntries = 0 ;
530
543
#if !HAVE_REWINDDIR
531
544
ret -> dirPath = strdup (path );
532
- #endif
533
545
#endif
534
546
return ret ;
535
547
}
@@ -538,16 +550,24 @@ int32_t SystemNative_CloseDir(struct DIRWrapper* dirWrapper)
538
550
{
539
551
assert (dirWrapper != NULL );
540
552
int32_t ret = closedir (dirWrapper -> dir );
541
- #if READDIR_SORT
553
+
542
554
if (dirWrapper -> result )
543
- free (dirWrapper -> result );
555
+ {
556
+ for (size_t i = 0 ; i < dirWrapper -> numEntries ; i ++ )
557
+ {
558
+ free (dirWrapper -> result [i ].Name );
559
+ }
560
+
561
+ free (dirWrapper -> result );
562
+ }
544
563
dirWrapper -> result = NULL ;
564
+
545
565
#if !HAVE_REWINDDIR
546
566
if (dirWrapper -> dirPath )
547
567
free (dirWrapper -> dirPath );
548
568
#endif
549
569
free (dirWrapper );
550
- #endif
570
+
551
571
return ret ;
552
572
}
553
573
0 commit comments