@@ -64,10 +64,11 @@ sanitize_label_string (WCHAR *s)
6464 /* Linux does not skip leading spaces. */
6565 return sanitize_string (s, L' \0 ' , L' ' , L' _' , [] (WCHAR c) -> bool
6666 {
67- /* Labels may contain characters not allowed in filenames.
68- Linux replaces spaces with \x20 which is not an option here. */
69- return !((0 <= c && c <= L' ' ) || c == L' :' || c == L' /' || c == L' \\ '
70- || c == L' "' );
67+ /* Labels may contain characters not allowed in filenames. Also
68+ replace '#' to avoid that duplicate markers introduce new
69+ duplicates. Linux replaces spaces with \x20 which is not an
70+ option here. */
71+ return !(c == L' /' || c == L' \\ ' || c == L' #' );
7172 }
7273 );
7374}
@@ -304,8 +305,7 @@ partition_to_label_or_uuid(bool uuid, const UNICODE_STRING *drive_uname,
304305 const NTFS_VOLUME_DATA_BUFFER *nvdb =
305306 reinterpret_cast <const NTFS_VOLUME_DATA_BUFFER *>(ioctl_buf);
306307 if (uuid && DeviceIoControl (volhdl, FSCTL_GET_NTFS_VOLUME_DATA, nullptr , 0 ,
307- ioctl_buf, NT_MAX_PATH, &bytes_read, nullptr )
308- && nvdb->VolumeSerialNumber .QuadPart )
308+ ioctl_buf, NT_MAX_PATH, &bytes_read, nullptr ))
309309 {
310310 /* Print without any separator as on Linux. */
311311 __small_sprintf (name, " %016X" , nvdb->VolumeSerialNumber .QuadPart );
@@ -327,13 +327,9 @@ partition_to_label_or_uuid(bool uuid, const UNICODE_STRING *drive_uname,
327327 FILE_FS_VOLUME_INFORMATION *ffvi =
328328 reinterpret_cast <FILE_FS_VOLUME_INFORMATION *>(ioctl_buf);
329329 if (uuid)
330- {
331- if (!ffvi->VolumeSerialNumber )
332- return false ;
333- /* Print with separator as on Linux. */
334- __small_sprintf (name, " %04x-%04x" , ffvi->VolumeSerialNumber >> 16 ,
335- ffvi->VolumeSerialNumber & 0xffff );
336- }
330+ /* Print with separator as on Linux. */
331+ __small_sprintf (name, " %04x-%04x" , ffvi->VolumeSerialNumber >> 16 ,
332+ ffvi->VolumeSerialNumber & 0xffff );
337333 else
338334 {
339335 /* Label is not null terminated. */
@@ -361,6 +357,20 @@ by_id_compare_name (const void *a, const void *b)
361357 return strcmp (ap->name , bp->name );
362358}
363359
360+ static int
361+ by_id_compare_name_drive_part (const void *a, const void *b)
362+ {
363+ const by_id_entry *ap = reinterpret_cast <const by_id_entry *>(a);
364+ const by_id_entry *bp = reinterpret_cast <const by_id_entry *>(b);
365+ int cmp = strcmp (ap->name , bp->name );
366+ if (cmp)
367+ return cmp;
368+ cmp = ap->drive - bp->drive ;
369+ if (cmp)
370+ return cmp;
371+ return ap->part - bp->part ;
372+ }
373+
364374static by_id_entry *
365375by_id_realloc (by_id_entry *p, size_t n)
366376{
@@ -610,21 +620,23 @@ get_by_id_table (by_id_entry * &table, fhandler_dev_disk::dev_disk_location loc)
610620 if (!table)
611621 return (errno_set ? -1 : 0 );
612622
613- /* Sort by name and remove duplicates. */
614- qsort (table, table_size, sizeof (*table), by_id_compare_name);
623+ /* Sort by {name, drive, part} to ensure stable sort order. */
624+ qsort (table, table_size, sizeof (*table), by_id_compare_name_drive_part);
625+ /* Mark duplicate names. */
615626 for (unsigned i = 0 ; i < table_size; i++)
616627 {
617628 unsigned j = i + 1 ;
618629 while (j < table_size && !strcmp (table[i].name , table[j].name ))
619630 j++;
620631 if (j == i + 1 )
621632 continue ;
622- /* Duplicate(s) found, remove all entries with this name. */
623- debug_printf (" removing duplicates %d-%d: '%s'" , i, j - 1 , table[i].name );
624- if (j < table_size)
625- memmove (table + i, table + j, (table_size - j) * sizeof (*table));
626- table_size -= j - i;
627- i--;
633+ /* Duplicate(s) found, append "#N" to all entries. This never
634+ introduces new duplicates because '#' never occurs in the
635+ original names. */
636+ debug_printf (" mark duplicates %u-%u of '%s'" , i, j - 1 , table[i].name );
637+ size_t len = strlen (table[i].name );
638+ for (unsigned k = i; k < j; k++)
639+ __small_sprintf (table[k].name + len, " #%u" , k - i);
628640 }
629641
630642 debug_printf (" table_size: %d" , table_size);
0 commit comments