Skip to content

Commit 0ac0eb2

Browse files
committed
[FREELDR] Add a mechanism to determine the actual size of a mounted storage filesystem volume (reactos#8423)
CORE-14603 - Each filesystem exposes a `*GetVolumeSize()` routine that returns the volume size it claims it reports (this is based on data from FAT/NTFS BIOS Parameter Blocks, or EXTn/BTRFS superblocks, similarly to what their NT filesystem drivers do). - Given a device ID corresponding to a mountable drive, a top-level `FsGetVolumeSize()` routine attempts to mount the corresponding filesystem. This may fail if the device is not a drive, or if the filesystem is not recognized. If it succeeds, then the per-filesytem `*GetVolumeSize()` is invoked and the corresponding volume size is returned.
1 parent 7cee847 commit 0ac0eb2

File tree

12 files changed

+289
-23
lines changed

12 files changed

+289
-23
lines changed

boot/freeldr/freeldr/include/fs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ FsOpenFile(
5353
IN OPENMODE OpenMode,
5454
OUT PULONG FileId);
5555

56+
ARC_STATUS
57+
FsGetVolumeSize(
58+
_In_ ULONG DeviceId,
59+
_Out_ PULONGLONG VolumeSize);
60+
5661
ULONG FsGetNumPathParts(PCSTR Path);
5762
VOID FsGetFirstNameFromPath(PCHAR Buffer, PCSTR Path);
5863

boot/freeldr/freeldr/include/fs/btrfs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,4 +422,8 @@ typedef struct {
422422
CHAR FileName[RTL_FIELD_SIZE(FILEINFORMATION, FileName)];
423423
} btrfs_file_info, *pbtrfs_file_info;
424424

425+
ULONGLONG
426+
BtrFsGetVolumeSize(
427+
_In_ ULONG DeviceId);
428+
425429
const DEVVTBL* BtrFsMount(ULONG DeviceId);

boot/freeldr/freeldr/include/fs/ext.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,20 @@ typedef struct _ExtSuperBlock
100100
UCHAR DefHashVersion;
101101
UCHAR JournalBackupType;
102102
USHORT GroupDescSize;
103-
UCHAR Reserved[768];
103+
104+
ULONG DefaultMountOpts;
105+
ULONG FirstMetaBg;
106+
ULONG MkfsTime;
107+
ULONG JnlBlocks[17];
108+
109+
/* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */
110+
ULONG BlocksCountHi;
111+
ULONG RBlocksCountHi;
112+
ULONG FreeBlocksCountHi;
113+
114+
UCHAR Reserved[676];
104115
} EXT_SUPER_BLOCK, *PEXT_SUPER_BLOCK;
116+
C_ASSERT(sizeof(EXT_SUPER_BLOCK) == 0x400);
105117

106118
typedef struct _ExtGroupDescriptor
107119
{
@@ -230,4 +242,8 @@ typedef struct _EXT_FILE_INFO
230242
CHAR FileName[RTL_FIELD_SIZE(FILEINFORMATION, FileName)];
231243
} EXT_FILE_INFO, *PEXT_FILE_INFO;
232244

245+
ULONGLONG
246+
ExtGetVolumeSize(
247+
_In_ ULONG DeviceId);
248+
233249
const DEVVTBL* ExtMount(ULONG DeviceId);

boot/freeldr/freeldr/include/fs/fat.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,8 @@ typedef struct _FAT_FILE_INFO
174174
CHAR FileName[RTL_FIELD_SIZE(FILEINFORMATION, FileName)];
175175
} FAT_FILE_INFO, *PFAT_FILE_INFO;
176176

177+
ULONGLONG
178+
FatGetVolumeSize(
179+
_In_ ULONG DeviceId);
180+
177181
const DEVVTBL* FatMount(ULONG DeviceId);

boot/freeldr/freeldr/include/fs/iso.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,8 @@ typedef struct _ISO_FILE_INFO
106106
CHAR FileName[RTL_FIELD_SIZE(FILEINFORMATION, FileName)];
107107
} ISO_FILE_INFO, *PISO_FILE_INFO;
108108

109+
ULONGLONG
110+
IsoGetVolumeSize(
111+
_In_ ULONG DeviceId);
112+
109113
const DEVVTBL* IsoMount(ULONG DeviceId);

boot/freeldr/freeldr/include/fs/ntfs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,4 +258,8 @@ typedef struct _NTFS_FILE_HANDLE
258258
CHAR FileName[RTL_FIELD_SIZE(FILEINFORMATION, FileName)];
259259
} NTFS_FILE_HANDLE, *PNTFS_FILE_HANDLE;
260260

261+
ULONGLONG
262+
NtfsGetVolumeSize(
263+
_In_ ULONG DeviceId);
264+
261265
const DEVVTBL* NtfsMount(ULONG DeviceId);

boot/freeldr/freeldr/lib/fs/btrfs.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,24 @@ ARC_STATUS BtrFsSeek(ULONG FileId, LARGE_INTEGER *Position, SEEKMODE SeekMode)
13321332
return ESUCCESS;
13331333
}
13341334

1335+
1336+
/**
1337+
* @brief
1338+
* Returns the size of the BTRFS volume laid on the storage media device
1339+
* opened via @p DeviceId.
1340+
**/
1341+
ULONGLONG
1342+
BtrFsGetVolumeSize(
1343+
_In_ ULONG DeviceId)
1344+
{
1345+
PBTRFS_INFO Volume = BtrFsVolumes[DeviceId];
1346+
ASSERT(Volume);
1347+
1348+
/* NOTE: BytesPerSector is given by Volume->SuperBlock.sectorsize */
1349+
return Volume->SuperBlock.total_bytes;
1350+
}
1351+
1352+
13351353
const DEVVTBL BtrFsFuncTable =
13361354
{
13371355
BtrFsClose,

boot/freeldr/freeldr/lib/fs/ext.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,13 @@ BOOLEAN ExtReadSuperBlock(PEXT_VOLUME_INFO Volume)
702702
TRACE("DefHashVersion: %d\n", SuperBlock->DefHashVersion);
703703
TRACE("JournalBackupType: %d\n", SuperBlock->JournalBackupType);
704704
TRACE("GroupDescSize: %d\n", SuperBlock->GroupDescSize);
705+
TRACE("DefaultMountOpts: %d\n", SuperBlock->DefaultMountOpts);
706+
TRACE("FirstMetaBg: %d\n", SuperBlock->FirstMetaBg);
707+
TRACE("MkfsTime: %d\n", SuperBlock->MkfsTime);
708+
// ULONG JnlBlocks[17];
709+
TRACE("BlocksCountHi: %d\n", SuperBlock->BlocksCountHi);
710+
TRACE("RBlocksCountHi: %d\n", SuperBlock->RBlocksCountHi);
711+
TRACE("FreeBlocksCountHi: %d\n", SuperBlock->FreeBlocksCountHi);
705712

706713
//
707714
// Check the super block magic
@@ -1387,6 +1394,30 @@ ARC_STATUS ExtSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
13871394
return ESUCCESS;
13881395
}
13891396

1397+
1398+
/**
1399+
* @brief
1400+
* Returns the size of the EXT2/3/4 volume laid on the storage media device
1401+
* opened via @p DeviceId.
1402+
**/
1403+
ULONGLONG
1404+
ExtGetVolumeSize(
1405+
_In_ ULONG DeviceId)
1406+
{
1407+
PEXT_VOLUME_INFO Volume;
1408+
ULARGE_INTEGER BlocksCount;
1409+
1410+
Volume = ExtVolumes[DeviceId];
1411+
ASSERT(Volume);
1412+
1413+
BlocksCount.LowPart = Volume->SuperBlock->BlocksCountLo;
1414+
BlocksCount.HighPart = Volume->SuperBlock->BlocksCountHi;
1415+
1416+
/* NOTE: Volume->BlockSizeInBytes == Volume->BlockSizeInSectors * Volume->BytesPerSector */
1417+
return BlocksCount.QuadPart * Volume->BlockSizeInBytes;
1418+
}
1419+
1420+
13901421
const DEVVTBL ExtFuncTable =
13911422
{
13921423
ExtClose,

boot/freeldr/freeldr/lib/fs/fat.c

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,13 @@ typedef struct _FAT_VOLUME_INFO
4747
ULONG RootDirSectors; /* Number of sectors of the root directory (non-fat32) */
4848
ULONG RootDirStartCluster; /* Starting cluster number of the root directory (fat32 only) */
4949
ULONG DataSectorStart; /* Starting sector of the data area */
50+
// ULONG NumberOfClusters; /* Number of clusters of the data area */
5051
ULONG DeviceId;
52+
ULONG TotalSectors; /* Total number of sectors on the volume */
5153
UINT16 BytesPerSector; /* Number of bytes per sector */
52-
UINT8 FatType; /* FAT12, FAT16, FAT32, FATX16 or FATX32 */
53-
UINT8 NumberOfFats; /* Number of FAT tables */
5454
UINT8 SectorsPerCluster; /* Number of sectors per cluster */
55+
UINT8 NumberOfFats; /* Number of FAT tables */
56+
UINT8 FatType; /* FAT12, FAT16, FAT32, FATX16 or FATX32 */
5557
} FAT_VOLUME_INFO;
5658

5759
PFAT_VOLUME_INFO FatVolumes[MAX_FDS];
@@ -134,6 +136,27 @@ VOID FatSwapFatXDirEntry(PFATX_DIRENTRY Obj)
134136
SW(Obj, LastAccessDate);
135137
}
136138

139+
/**
140+
* @brief
141+
* Returns the number of clusters of the data area of the FAT volume.
142+
* This value is computed by taking the total sectors on the disk, subtracting
143+
* up to the first file area sector, then dividing by the sectors per cluster count.
144+
*
145+
* @note
146+
* Relies on the FAT_VOLUME_INFO fields already computed by FatOpenVolume().
147+
*
148+
* @see
149+
* https://github.com/reactos/reactos/blob/435482912c6dc0aaa4371e37b7336b2b1291e65f/drivers/filesystems/fastfat/fat.h#L460
150+
* https://github.com/reactos/reactos/blob/435482912c6dc0aaa4371e37b7336b2b1291e65f/drivers/filesystems/vfatfs/fsctl.c#L24
151+
**/
152+
FORCEINLINE
153+
ULONG
154+
FatNumberOfClusters(
155+
_In_ PFAT_VOLUME_INFO Volume)
156+
{
157+
return ((Volume->TotalSectors - Volume->DataSectorStart) / Volume->SectorsPerCluster);
158+
}
159+
137160
BOOLEAN FatOpenVolume(PFAT_VOLUME_INFO Volume, PFAT_BOOTSECTOR BootSector, ULONGLONG PartitionSectorCount)
138161
{
139162
char ErrMsg[80];
@@ -254,21 +277,38 @@ BOOLEAN FatOpenVolume(PFAT_VOLUME_INFO Volume, PFAT_BOOTSECTOR BootSector, ULONG
254277
return FALSE;
255278
}
256279

257-
//
258-
// Get the sectors per FAT,
259-
// root directory starting sector,
260-
// and data sector start
261-
//
262280
if (ISFATX(Volume->FatType))
263281
{
264-
Volume->BytesPerSector = 512;
282+
Volume->TotalSectors = PartitionSectorCount;
283+
}
284+
else
285+
{
286+
/*
287+
* After DOS 4.0, at least one of TotalSectors or TotalSectorsBig will be zero.
288+
* In DOS version 3.2 or before, both of these might contain some value,
289+
* because, before 3.2, there was no TotalSectorsBig entry. Thus some disks
290+
* might have an unexpected value in the field, and we will use TotalSectorsBig
291+
* only if TotalSectors equals zero.
292+
*/
293+
C_ASSERT(FIELD_OFFSET(FAT_BOOTSECTOR, TotalSectors) ==
294+
FIELD_OFFSET(FAT32_BOOTSECTOR, TotalSectors));
295+
C_ASSERT(FIELD_OFFSET(FAT_BOOTSECTOR, TotalSectorsBig) ==
296+
FIELD_OFFSET(FAT32_BOOTSECTOR, TotalSectorsBig));
297+
Volume->TotalSectors = (FatVolumeBootSector->TotalSectors ? FatVolumeBootSector->TotalSectors
298+
: FatVolumeBootSector->TotalSectorsBig);
299+
}
300+
301+
/* Get the sectors per FAT, root directory sector start, and data sector start */
302+
if (ISFATX(Volume->FatType))
303+
{
304+
Volume->BytesPerSector = SECTOR_SIZE;
265305
Volume->SectorsPerCluster = SWAPD(FatXVolumeBootSector->SectorsPerCluster);
266-
Volume->FatSectorStart = (0x1000 / Volume->BytesPerSector);
306+
Volume->FatSectorStart = 4096 / Volume->BytesPerSector;
267307
Volume->ActiveFatSectorStart = Volume->FatSectorStart;
268-
Volume->NumberOfFats = 1;
308+
Volume->NumberOfFats = 1; // FatXVolumeBootSector->NumberOfFats;
269309
FatSize = (ULONG)(PartitionSectorCount / Volume->SectorsPerCluster *
270310
(Volume->FatType == FATX16 ? 2 : 4));
271-
Volume->SectorsPerFat = ROUND_UP(FatSize, 0x1000) / Volume->BytesPerSector;
311+
Volume->SectorsPerFat = ROUND_UP(FatSize, 4096) / Volume->BytesPerSector;
272312

273313
Volume->RootDirSectorStart = Volume->FatSectorStart + Volume->NumberOfFats * Volume->SectorsPerFat;
274314
Volume->RootDirSectors = FatXVolumeBootSector->SectorsPerCluster;
@@ -286,6 +326,7 @@ BOOLEAN FatOpenVolume(PFAT_VOLUME_INFO Volume, PFAT_BOOTSECTOR BootSector, ULONG
286326

287327
Volume->RootDirSectorStart = Volume->FatSectorStart + Volume->NumberOfFats * Volume->SectorsPerFat;
288328
Volume->RootDirSectors = ((FatVolumeBootSector->RootDirEntries * 32) + (Volume->BytesPerSector - 1)) / Volume->BytesPerSector;
329+
// Volume->RootDirSectors = (FatVolumeBootSector->RootDirEntries * sizeof(DIRENT) / Volume->BytesPerSector);
289330

290331
Volume->DataSectorStart = Volume->RootDirSectorStart + Volume->RootDirSectors;
291332
}
@@ -294,10 +335,12 @@ BOOLEAN FatOpenVolume(PFAT_VOLUME_INFO Volume, PFAT_BOOTSECTOR BootSector, ULONG
294335
Volume->BytesPerSector = Fat32VolumeBootSector->BytesPerSector;
295336
Volume->SectorsPerCluster = Fat32VolumeBootSector->SectorsPerCluster;
296337
Volume->FatSectorStart = Fat32VolumeBootSector->ReservedSectors;
297-
Volume->ActiveFatSectorStart = Volume->FatSectorStart +
298-
((Fat32VolumeBootSector->ExtendedFlags & 0x80) ? ((Fat32VolumeBootSector->ExtendedFlags & 0x0f) * Fat32VolumeBootSector->SectorsPerFatBig) : 0);
338+
Volume->ActiveFatSectorStart =
339+
Volume->FatSectorStart + ((Fat32VolumeBootSector->ExtendedFlags & 0x80) ?
340+
((Fat32VolumeBootSector->ExtendedFlags & 0x0f) * Fat32VolumeBootSector->SectorsPerFatBig) : 0);
299341
Volume->NumberOfFats = Fat32VolumeBootSector->NumberOfFats;
300342
Volume->SectorsPerFat = Fat32VolumeBootSector->SectorsPerFatBig;
343+
// Volume->SectorsPerFat = (Fat32VolumeBootSector->SectorsPerFat ? Fat32VolumeBootSector->SectorsPerFat : Fat32VolumeBootSector->SectorsPerFatBig);
301344

302345
Volume->RootDirStartCluster = Fat32VolumeBootSector->RootDirStartCluster;
303346
Volume->DataSectorStart = Volume->FatSectorStart + Volume->NumberOfFats * Volume->SectorsPerFat;
@@ -312,6 +355,7 @@ BOOLEAN FatOpenVolume(PFAT_VOLUME_INFO Volume, PFAT_BOOTSECTOR BootSector, ULONG
312355
return FALSE;
313356
}
314357
}
358+
// Volume->NumberOfClusters = FatNumberOfClusters(Volume);
315359

316360
Volume->FatCacheSize = min(Volume->SectorsPerFat, FAT_MAX_CACHE_SIZE / Volume->BytesPerSector);
317361
TRACE("FAT cache is %d sectors, %d bytes\n", Volume->FatCacheSize, Volume->FatCacheSize * Volume->BytesPerSector);
@@ -1562,6 +1606,22 @@ ARC_STATUS FatSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
15621606
return ESUCCESS;
15631607
}
15641608

1609+
1610+
/**
1611+
* @brief
1612+
* Returns the size of the FAT volume laid on the storage media device
1613+
* opened via @p DeviceId.
1614+
**/
1615+
ULONGLONG
1616+
FatGetVolumeSize(
1617+
_In_ ULONG DeviceId)
1618+
{
1619+
PFAT_VOLUME_INFO Volume = FatVolumes[DeviceId];
1620+
ASSERT(Volume);
1621+
return (ULONGLONG)Volume->TotalSectors * Volume->BytesPerSector;
1622+
}
1623+
1624+
15651625
const DEVVTBL FatFuncTable =
15661626
{
15671627
FatClose,
@@ -1585,7 +1645,7 @@ const DEVVTBL FatXFuncTable =
15851645
const DEVVTBL* FatMount(ULONG DeviceId)
15861646
{
15871647
PFAT_VOLUME_INFO Volume;
1588-
UCHAR Buffer[512];
1648+
UCHAR Buffer[SECTOR_SIZE];
15891649
PFAT_BOOTSECTOR BootSector = (PFAT_BOOTSECTOR)Buffer;
15901650
PFAT32_BOOTSECTOR BootSector32 = (PFAT32_BOOTSECTOR)Buffer;
15911651
PFATX_BOOTSECTOR BootSectorX = (PFATX_BOOTSECTOR)Buffer;

boot/freeldr/freeldr/lib/fs/fs.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,83 @@ FsOpenFile(
532532
return ArcOpen(FullPath, OpenMode, FileId);
533533
}
534534

535+
/**
536+
* @brief
537+
* Returns the (useful) size of a file-system volume laid on the
538+
* storage media device opened via @p DeviceId.
539+
**/
540+
ARC_STATUS
541+
FsGetVolumeSize(
542+
_In_ ULONG DeviceId,
543+
_Out_ PULONGLONG VolumeSize)
544+
{
545+
DEVICE* pDevice;
546+
const DEVVTBL* FuncTable;
547+
PCWSTR ServiceName;
548+
ULONGLONG (*pFsGetVolumeSize)(_In_ ULONG DeviceId);
549+
550+
/* Check whether this file actually references a device */
551+
pDevice = FsGetDeviceById(DeviceId);
552+
if (!pDevice)
553+
return EBADF; /* It doesn't, fail */
554+
555+
*VolumeSize = 0;
556+
557+
// TODO: Mount the device, if not already done?
558+
#if 1 // FIXME: TEMP HACK!
559+
if (!pDevice->FileFuncTable && (pDevice->ReferenceCount <= 1))
560+
{
561+
for (ULONG fs = 0; fs < _countof(FileSystems); ++fs)
562+
{
563+
pDevice->FileFuncTable = FileSystems[fs](DeviceId);
564+
if (pDevice->FileFuncTable)
565+
break;
566+
}
567+
}
568+
#endif
569+
// TODO: Use a virtual table function member for easily handling other file systems.
570+
571+
/* Check that the device is mounted (non-NULL FileFuncTable)
572+
* and there is an associated "service" (file-system) name. */
573+
FuncTable = pDevice->FileFuncTable;
574+
ServiceName = (FuncTable && FuncTable->ServiceName)
575+
? FuncTable->ServiceName : NULL;
576+
if (!ServiceName)
577+
{
578+
ERR("Non-mounted device %lu\n", DeviceId);
579+
return ENXIO;
580+
}
581+
582+
/* HACK: Since we don't currently use a virtual table function member,
583+
* we instead do a manual lookup over the file-system name.
584+
* The ordering follows that of the FileSystems[] table. */
585+
#ifndef _M_ARM
586+
if (!_wcsicmp(ServiceName, L"cdfs"))
587+
pFsGetVolumeSize = IsoGetVolumeSize;
588+
else
589+
#endif
590+
if (!_wcsicmp(ServiceName, L"fastfat") || !_wcsicmp(ServiceName, L"vfatfs"))
591+
pFsGetVolumeSize = FatGetVolumeSize;
592+
else
593+
if (!_wcsicmp(ServiceName, L"btrfs"))
594+
pFsGetVolumeSize = BtrFsGetVolumeSize;
595+
else
596+
#ifndef _M_ARM
597+
if (!_wcsicmp(ServiceName, L"ntfs"))
598+
pFsGetVolumeSize = NtfsGetVolumeSize;
599+
else
600+
if (!_wcsicmp(ServiceName, L"ext2fs"))
601+
pFsGetVolumeSize = ExtGetVolumeSize;
602+
else
603+
#endif
604+
{
605+
ERR("Unsupported file system '%S' on storage device %lu\n", ServiceName, DeviceId);
606+
return EIO; // ENXIO;
607+
}
608+
*VolumeSize = pFsGetVolumeSize(DeviceId);
609+
return ESUCCESS;
610+
}
611+
535612
/*
536613
* FsGetNumPathParts()
537614
* This function parses a path in the form of dir1\dir2\file1.ext

0 commit comments

Comments
 (0)