add support for more than one volume
This commit is contained in:
1 parent
529f816166
commit
09c1420a32
5 files changed
+230
-106
No files matched your search
Binary file not shown.
@@ -183,7 +183,7 @@ link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^
|
||||
/OUT:"bin\video.exe" /PDB:"bin\video.pdb" bin\win32.lib obj\libc.obj %SRC%\video\bink2w64.lib obj\video\*.obj || exit /b 1
|
||||
|
||||
:: ---- Mount disk image ----
|
||||
echo Mounting disk.vhd as Z: ...
|
||||
echo Mounting disk.vhd...
|
||||
set DPS=%TEMP%\ntlite_mnt_%RANDOM%.txt
|
||||
(
|
||||
echo select vdisk file="%VHD%"
|
||||
@@ -191,6 +191,9 @@ set DPS=%TEMP%\ntlite_mnt_%RANDOM%.txt
|
||||
echo select partition 1
|
||||
echo format fs=fat32 quick
|
||||
echo assign letter=Z
|
||||
echo select partition 2
|
||||
echo format fs=fat32 quick
|
||||
echo assign letter=Y
|
||||
echo exit
|
||||
) > "%DPS%"
|
||||
diskpart /s "%DPS%"
|
||||
@@ -202,10 +205,12 @@ if not exist "Z:\" (
|
||||
exit /b 1
|
||||
)
|
||||
set DEST=Z:
|
||||
set DEST2=Y:
|
||||
|
||||
:: ---- Copy to ESP layout ----
|
||||
echo Assembling ESP...
|
||||
mkdir "%DEST%\apps" 2>nul
|
||||
mkdir "%DEST2%\apps" 2>nul
|
||||
mkdir "%DEST%\system" 2>nul
|
||||
mkdir "%DEST%\drivers" 2>nul
|
||||
mkdir "%DEST%\EFI\BOOT" 2>nul
|
||||
@@ -223,8 +228,8 @@ copy /y "bin\init.exe" "%DEST%\system\"
|
||||
copy /y "bin\shell_win32.exe" "%DEST%\apps\"
|
||||
copy /y "bin\shell_posix.exe" "%DEST%\apps\"
|
||||
copy /y "bin\shell_native.exe" "%DEST%\apps\"
|
||||
copy /y "bin\async_apc.exe" "%DEST%\apps\"
|
||||
copy /y "bin\async_cancel.exe" "%DEST%\apps\"
|
||||
copy /y "bin\async_apc.exe" "%DEST2%\apps\"
|
||||
copy /y "bin\async_cancel.exe" "%DEST2%\apps\"
|
||||
copy /y "bin\doom.exe" "%DEST%\apps\"
|
||||
copy /y "bin\video.exe" "%DEST%\apps\"
|
||||
copy /y "%SRC%\boot.ini" "%DEST%\"
|
||||
|
||||
+213
-96
@@ -233,8 +233,9 @@ static void FlushFatCache(FAT32_VOL *v);
|
||||
// Driver state
|
||||
// ---------------------------------------------------------------------------
|
||||
struct FAT32_VOL_S {
|
||||
uint8_t DriveIndex;
|
||||
uint32_t PartitionLba;
|
||||
uint8_t DriveIndex; // physical disk DEVICE_OBJECT index
|
||||
uint64_t PartitionLba; // absolute LBA of the mounted volume
|
||||
uint64_t PartitionSize; // sectors, best-effort bounds/documentation
|
||||
uint32_t BytesPerSector;
|
||||
uint32_t SectorsPerCluster;
|
||||
uint32_t BytesPerCluster;
|
||||
@@ -242,11 +243,10 @@ struct FAT32_VOL_S {
|
||||
uint32_t NumFats;
|
||||
uint32_t FatSize; // sectors per FAT copy
|
||||
uint32_t RootCluster;
|
||||
uint32_t DataStart; // absolute LBA of cluster 2
|
||||
uint64_t DataStart; // absolute LBA of cluster 2
|
||||
uint32_t TotalClusters; // usable data clusters
|
||||
uint32_t NextFreeHint; // fix #3: start cluster for next free-scan
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t StartCluster;
|
||||
uint32_t FileSize;
|
||||
@@ -1479,66 +1479,174 @@ static void Fat32CloseDir(IO_FILESYSTEM *fs, void *dirCtx) {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Partition scan (MBR + GPT) — unchanged
|
||||
// Volume scan (MBR + GPT + superfloppy)
|
||||
// ---------------------------------------------------------------------------
|
||||
static const EFI_GUID GUID_EFI_SYSTEM = { 0xC12A7328, 0xF81F, 0x11D2, {0xBA,0x4B,0x00,0xA0,0xC9,0x3E,0xC9,0x3B} };
|
||||
static const EFI_GUID GUID_MS_BASIC_DATA = { 0xEBD0A0A2, 0xB9E5, 0x4433, {0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7} };
|
||||
static const EFI_GUID GUID_LINUX_BASIC_DATA = { 0x0FC63DAF, 0x8483, 0x4772, {0x8E,0x79,0x3D,0x69,0xD8,0x47,0x7D,0xE4} };
|
||||
|
||||
static int IsFat32Vbr(FAT32_VOL *v, uint32_t lba) {
|
||||
#define FAT32_MAX_PARTITIONS_PER_DISK 32
|
||||
|
||||
typedef struct FAT32_PARTITION_CANDIDATE {
|
||||
uint64_t StartLba;
|
||||
uint64_t SectorCount;
|
||||
} FAT32_PARTITION_CANDIDATE;
|
||||
|
||||
static uint32_t ReadLe32(const void* p) {
|
||||
const uint8_t* b = (const uint8_t*)p;
|
||||
return (uint32_t)b[0]
|
||||
| ((uint32_t)b[1] << 8)
|
||||
| ((uint32_t)b[2] << 16)
|
||||
| ((uint32_t)b[3] << 24);
|
||||
}
|
||||
|
||||
static int IsFat32Vbr(FAT32_VOL *v, uint64_t lba) {
|
||||
uint8_t sector[512];
|
||||
if (!KSUCCESS(ReadSectors(v, lba, 1, sector))) return 0;
|
||||
if (sector[510] != 0x55 || sector[511] != 0xAA) return 0;
|
||||
|
||||
BPB *bpb = (BPB *)sector;
|
||||
return bpb->FatSize16 == 0 && bpb->RootEntryCount == 0 && bpb->FatSize32 != 0;
|
||||
if (bpb->BytesPerSector != 512) return 0; // disk I/O layer is 512-byte sector based
|
||||
if (bpb->SectorsPerCluster == 0) return 0;
|
||||
if (bpb->NumFats == 0) return 0;
|
||||
if (bpb->FatSize16 != 0) return 0;
|
||||
if (bpb->RootEntryCount != 0) return 0;
|
||||
if (bpb->FatSize32 == 0) return 0;
|
||||
if (bpb->RootCluster < 2) return 0;
|
||||
|
||||
uint32_t totalSectors = bpb->TotalSectors16 ? bpb->TotalSectors16 : bpb->TotalSectors32;
|
||||
if (totalSectors == 0) return 0;
|
||||
if (totalSectors <= (uint32_t)bpb->ReservedSectors + (uint32_t)bpb->NumFats * bpb->FatSize32)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t FindFat32Partition(FAT32_VOL *v) {
|
||||
uint8_t mbr[512];
|
||||
if (!KSUCCESS(ReadSectors(v, 0, 1, mbr))) return 0;
|
||||
if (mbr[510] != 0x55 || mbr[511] != 0xAA) return 0;
|
||||
|
||||
int isGpt = 0;
|
||||
for (int i = 0; i < 4; i++) if (mbr[0x1BE + i*16 + 4] == 0xEE) { isGpt = 1; break; }
|
||||
|
||||
if (isGpt) {
|
||||
uint8_t gptSector[512];
|
||||
if (!KSUCCESS(ReadSectors(v, 1, 1, gptSector))) return 0;
|
||||
GPT_HEADER *gpt = (GPT_HEADER *)gptSector;
|
||||
if (memcmp(gpt->Signature, "EFI PART", 8) != 0) return 0;
|
||||
|
||||
uint64_t entryLBA = gpt->PartitionEntryLBA;
|
||||
uint32_t numEntries = gpt->NumberOfPartitionEntries;
|
||||
uint32_t entrySize = gpt->SizeOfPartitionEntry;
|
||||
if (entrySize < sizeof(GPT_ENTRY)) return 0;
|
||||
|
||||
uint32_t eps = 512 / entrySize; uint8_t entrySector[512];
|
||||
for (uint32_t i = 0; i < numEntries; i++) {
|
||||
if (i % eps == 0)
|
||||
if (!KSUCCESS(ReadSectors(v, entryLBA + i/eps, 1, entrySector))) return 0;
|
||||
GPT_ENTRY *entry = (GPT_ENTRY *)(entrySector + (i % eps) * entrySize);
|
||||
static const EFI_GUID emptyGuid = {0};
|
||||
if (memcmp(&entry->PartitionTypeGUID, &emptyGuid, sizeof(EFI_GUID)) == 0) continue;
|
||||
if (memcmp(&entry->PartitionTypeGUID, &GUID_EFI_SYSTEM, sizeof(EFI_GUID)) == 0 ||
|
||||
memcmp(&entry->PartitionTypeGUID, &GUID_MS_BASIC_DATA, sizeof(EFI_GUID)) == 0 ||
|
||||
memcmp(&entry->PartitionTypeGUID, &GUID_LINUX_BASIC_DATA, sizeof(EFI_GUID)) == 0) {
|
||||
uint32_t lba = (uint32_t)entry->StartingLBA;
|
||||
if (IsFat32Vbr(v, lba)) return lba;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint8_t *entry = mbr + 0x1BE + i*16;
|
||||
if (entry[4] == 0x0B || entry[4] == 0x0C) {
|
||||
uint32_t lba = *(uint32_t *)(entry + 8);
|
||||
if (IsFat32Vbr(v, lba)) return lba;
|
||||
}
|
||||
static int Fat32CandidateExists(FAT32_PARTITION_CANDIDATE* out,
|
||||
uint32_t count,
|
||||
uint64_t startLba) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if (out[i].StartLba == startLba)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Fat32AddCandidate(FAT32_VOL* probe,
|
||||
FAT32_PARTITION_CANDIDATE* out,
|
||||
uint32_t* count,
|
||||
uint32_t maxCount,
|
||||
uint64_t startLba,
|
||||
uint64_t sectorCount) {
|
||||
if (!out || !count || *count >= maxCount) return;
|
||||
if (Fat32CandidateExists(out, *count, startLba)) return;
|
||||
|
||||
uint64_t diskSectors = IoDiskSectorCount(probe->DriveIndex);
|
||||
if (diskSectors) {
|
||||
if (startLba >= diskSectors) return;
|
||||
if (sectorCount == 0 || startLba + sectorCount > diskSectors)
|
||||
sectorCount = diskSectors - startLba;
|
||||
}
|
||||
|
||||
if (!IsFat32Vbr(probe, startLba)) return;
|
||||
|
||||
out[*count].StartLba = startLba;
|
||||
out[*count].SectorCount = sectorCount;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
static uint32_t Fat32EnumerateDiskVolumes(uint8_t diskIndex,
|
||||
FAT32_PARTITION_CANDIDATE* out,
|
||||
uint32_t maxCount) {
|
||||
if (!out || maxCount == 0) return 0;
|
||||
|
||||
FAT32_VOL probe;
|
||||
memset(&probe, 0, sizeof(probe));
|
||||
probe.DriveIndex = diskIndex;
|
||||
|
||||
uint32_t count = 0;
|
||||
uint8_t mbr[512];
|
||||
if (!KSUCCESS(ReadSectors(&probe, 0, 1, mbr)))
|
||||
return 0;
|
||||
|
||||
// A disk image may be a raw FAT32 volume with no partition table.
|
||||
Fat32AddCandidate(&probe, out, &count, maxCount, 0, IoDiskSectorCount(diskIndex));
|
||||
|
||||
if (mbr[510] != 0x55 || mbr[511] != 0xAA)
|
||||
return count;
|
||||
|
||||
int isGpt = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (mbr[0x1BE + i * 16 + 4] == 0xEE) {
|
||||
isGpt = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isGpt) {
|
||||
uint8_t gptSector[512];
|
||||
if (!KSUCCESS(ReadSectors(&probe, 1, 1, gptSector)))
|
||||
return count;
|
||||
|
||||
GPT_HEADER* gpt = (GPT_HEADER*)gptSector;
|
||||
if (memcmp(gpt->Signature, "EFI PART", 8) != 0)
|
||||
return count;
|
||||
|
||||
uint64_t entryLBA = gpt->PartitionEntryLBA;
|
||||
uint32_t numEntries = gpt->NumberOfPartitionEntries;
|
||||
uint32_t entrySize = gpt->SizeOfPartitionEntry;
|
||||
if (entrySize < sizeof(GPT_ENTRY) || entrySize > 512)
|
||||
return count;
|
||||
|
||||
uint32_t entriesPerSector = 512 / entrySize;
|
||||
if (!entriesPerSector)
|
||||
return count;
|
||||
|
||||
uint8_t entrySector[512];
|
||||
uint64_t loadedSector = UINT64_MAX;
|
||||
static const EFI_GUID emptyGuid = {0};
|
||||
|
||||
for (uint32_t i = 0; i < numEntries && count < maxCount; i++) {
|
||||
uint64_t sectorLba = entryLBA + i / entriesPerSector;
|
||||
if (sectorLba != loadedSector) {
|
||||
if (!KSUCCESS(ReadSectors(&probe, sectorLba, 1, entrySector)))
|
||||
return count;
|
||||
loadedSector = sectorLba;
|
||||
}
|
||||
|
||||
GPT_ENTRY* entry = (GPT_ENTRY*)(entrySector + (i % entriesPerSector) * entrySize);
|
||||
if (memcmp(&entry->PartitionTypeGUID, &emptyGuid, sizeof(EFI_GUID)) == 0)
|
||||
continue;
|
||||
|
||||
if (memcmp(&entry->PartitionTypeGUID, &GUID_EFI_SYSTEM, sizeof(EFI_GUID)) != 0 &&
|
||||
memcmp(&entry->PartitionTypeGUID, &GUID_MS_BASIC_DATA, sizeof(EFI_GUID)) != 0 &&
|
||||
memcmp(&entry->PartitionTypeGUID, &GUID_LINUX_BASIC_DATA, sizeof(EFI_GUID)) != 0)
|
||||
continue;
|
||||
|
||||
uint64_t start = entry->StartingLBA;
|
||||
uint64_t end = entry->EndingLBA;
|
||||
if (end < start) continue;
|
||||
Fat32AddCandidate(&probe, out, &count, maxCount, start, end - start + 1);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4 && count < maxCount; i++) {
|
||||
uint8_t* entry = mbr + 0x1BE + i * 16;
|
||||
uint8_t type = entry[4];
|
||||
uint32_t start = ReadLe32(entry + 8);
|
||||
uint32_t size = ReadLe32(entry + 12);
|
||||
if (!start || !size) continue;
|
||||
|
||||
// FAT32 CHS/LBA plus EFI System Partition. The VBR probe is the
|
||||
// authority, so accepting 0xEF here lets FAT32 ESPs mount too.
|
||||
if (type == 0x0B || type == 0x0C || type == 0xEF)
|
||||
Fat32AddCandidate(&probe, out, &count, maxCount, start, size);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Native async read/write
|
||||
@@ -1990,63 +2098,72 @@ KSTATUS DriverEntry(const HAL_API *hal) {
|
||||
uint32_t mounted = 0;
|
||||
KSTATUS lastStatus = STATUS_NOT_SUPPORTED;
|
||||
|
||||
for (uint8_t drive = 0; drive < FAT32_MAX_VOLUMES; drive++) {
|
||||
if (!IoDiskDevicePresent(drive))
|
||||
for (uint8_t disk = 0; disk < FAT32_MAX_VOLUMES && mounted < FAT32_MAX_VOLUMES; disk++) {
|
||||
if (!IoDiskDevicePresent(disk))
|
||||
continue;
|
||||
|
||||
FAT32_VOL* v = &gVols[drive];
|
||||
memset(v, 0, sizeof(*v));
|
||||
v->DriveIndex = drive;
|
||||
|
||||
uint32_t partLba = FindFat32Partition(v);
|
||||
if (!partLba) {
|
||||
DbgPrint("FAT32: drive %u: no FAT32 volume\n", drive);
|
||||
continue;
|
||||
}
|
||||
v->PartitionLba = partLba;
|
||||
|
||||
uint8_t sector[512];
|
||||
if (!KSUCCESS(ReadSectors(v, partLba, 1, sector))) {
|
||||
lastStatus = STATUS_DEVICE_ERROR;
|
||||
DbgPrint("FAT32: drive %u: failed to read VBR\n", drive);
|
||||
FAT32_PARTITION_CANDIDATE parts[FAT32_MAX_PARTITIONS_PER_DISK];
|
||||
uint32_t partCount = Fat32EnumerateDiskVolumes(disk, parts, FAT32_MAX_PARTITIONS_PER_DISK);
|
||||
if (!partCount) {
|
||||
DbgPrint("FAT32: disk %u: no FAT32 volumes\n", disk);
|
||||
continue;
|
||||
}
|
||||
|
||||
BPB *bpb = (BPB *)sector;
|
||||
if (bpb->FatSize16 != 0 || bpb->RootEntryCount != 0) {
|
||||
lastStatus = STATUS_NOT_SUPPORTED;
|
||||
DbgPrint("FAT32: drive %u: unsupported FAT variant\n", drive);
|
||||
continue;
|
||||
}
|
||||
for (uint32_t part = 0; part < partCount && mounted < FAT32_MAX_VOLUMES; part++) {
|
||||
uint8_t volumeIndex = (uint8_t)mounted;
|
||||
FAT32_VOL* v = &gVols[volumeIndex];
|
||||
memset(v, 0, sizeof(*v));
|
||||
v->DriveIndex = disk;
|
||||
v->PartitionLba = parts[part].StartLba;
|
||||
v->PartitionSize = parts[part].SectorCount;
|
||||
|
||||
v->BytesPerSector = bpb->BytesPerSector;
|
||||
v->SectorsPerCluster = bpb->SectorsPerCluster;
|
||||
v->BytesPerCluster = bpb->BytesPerSector * bpb->SectorsPerCluster;
|
||||
v->ReservedSectors = bpb->ReservedSectors;
|
||||
v->NumFats = bpb->NumFats;
|
||||
v->FatSize = bpb->FatSize32;
|
||||
v->RootCluster = bpb->RootCluster;
|
||||
v->DataStart = partLba + bpb->ReservedSectors + bpb->NumFats * bpb->FatSize32;
|
||||
v->NextFreeHint = 2;
|
||||
uint8_t sector[512];
|
||||
if (!KSUCCESS(ReadSectors(v, v->PartitionLba, 1, sector))) {
|
||||
lastStatus = STATUS_DEVICE_ERROR;
|
||||
DbgPrint("FAT32: disk %u part %u: failed to read VBR\n", disk, part);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t totalSectors = bpb->TotalSectors16 ? bpb->TotalSectors16 : bpb->TotalSectors32;
|
||||
uint32_t dataSectors = totalSectors - bpb->ReservedSectors - bpb->NumFats * bpb->FatSize32;
|
||||
v->TotalClusters = dataSectors / bpb->SectorsPerCluster;
|
||||
BPB *bpb = (BPB *)sector;
|
||||
if (!IsFat32Vbr(v, v->PartitionLba)) {
|
||||
lastStatus = STATUS_NOT_SUPPORTED;
|
||||
DbgPrint("FAT32: disk %u part %u: unsupported FAT variant\n", disk, part);
|
||||
continue;
|
||||
}
|
||||
|
||||
Fat32InitVtable(&gFs[drive], v);
|
||||
v->BytesPerSector = bpb->BytesPerSector;
|
||||
v->SectorsPerCluster = bpb->SectorsPerCluster;
|
||||
v->BytesPerCluster = bpb->BytesPerSector * bpb->SectorsPerCluster;
|
||||
v->ReservedSectors = bpb->ReservedSectors;
|
||||
v->NumFats = bpb->NumFats;
|
||||
v->FatSize = bpb->FatSize32;
|
||||
v->RootCluster = bpb->RootCluster;
|
||||
v->DataStart = v->PartitionLba + bpb->ReservedSectors + (uint64_t)bpb->NumFats * bpb->FatSize32;
|
||||
v->NextFreeHint = 2;
|
||||
|
||||
IO_VOLUME_PARAMS volParams;
|
||||
volParams.DiskIndex = v->DriveIndex;
|
||||
volParams.PartitionLba = partLba;
|
||||
volParams.PartitionSize = totalSectors;
|
||||
uint32_t totalSectors = bpb->TotalSectors16 ? bpb->TotalSectors16 : bpb->TotalSectors32;
|
||||
uint32_t dataSectors = totalSectors - bpb->ReservedSectors - bpb->NumFats * bpb->FatSize32;
|
||||
v->TotalClusters = dataSectors / bpb->SectorsPerCluster;
|
||||
if (v->PartitionSize == 0 || v->PartitionSize > totalSectors)
|
||||
v->PartitionSize = totalSectors;
|
||||
|
||||
KSTATUS status = IoRegisterFilesystem(drive, &gFs[drive], &volParams);
|
||||
if (KSUCCESS(status)) {
|
||||
mounted++;
|
||||
DbgPrint("FAT32: drive %u mounted (clusters=%u).\n", drive, v->TotalClusters);
|
||||
} else {
|
||||
lastStatus = status;
|
||||
DbgPrint("FAT32: drive %u mount failed status=%d.\n", drive, status);
|
||||
Fat32InitVtable(&gFs[volumeIndex], v);
|
||||
|
||||
IO_VOLUME_PARAMS volParams;
|
||||
volParams.DiskIndex = v->DriveIndex;
|
||||
volParams.PartitionLba = v->PartitionLba;
|
||||
volParams.PartitionSize = v->PartitionSize;
|
||||
|
||||
KSTATUS status = IoRegisterFilesystem(volumeIndex, &gFs[volumeIndex], &volParams);
|
||||
if (KSUCCESS(status)) {
|
||||
mounted++;
|
||||
DbgPrint("FAT32: disk %u part %u mounted as drive %u (lba=%llu clusters=%u).\n",
|
||||
disk, part, volumeIndex, (unsigned long long)v->PartitionLba, v->TotalClusters);
|
||||
} else {
|
||||
lastStatus = status;
|
||||
DbgPrint("FAT32: disk %u part %u mount as drive %u failed status=%d.\n",
|
||||
disk, part, volumeIndex, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -16,8 +16,10 @@ set DPS=%TEMP%\ntlite_mkimg_%RANDOM%.txt
|
||||
echo create vdisk file="%VHD%" maximum=256 type=fixed
|
||||
echo select vdisk file="%VHD%"
|
||||
echo attach vdisk
|
||||
echo create partition primary size=150
|
||||
echo format fs=fat32 quick
|
||||
echo create partition primary
|
||||
echo format fs=fat32 label=NTLITE quick
|
||||
echo format fs=fat32 quick
|
||||
echo detach vdisk
|
||||
echo exit
|
||||
) > "%DPS%"
|
||||
|
||||
@@ -197,14 +197,14 @@ That sentence used to be science fiction. Now it is boring. Excellent.
|
||||
|
||||
```text
|
||||
"apps/shell.exe" -> current working directory
|
||||
"0:/apps/shell.exe" -> drive 0
|
||||
"2:/data/config.ini" -> drive 2
|
||||
"C:\apps\doom.exe" -> drive 0, mapping is simple c->0, d->1, etc.
|
||||
"/apps/video.exe" -> drive 0
|
||||
"/1/apps/video.exe" -> drive 1
|
||||
"0:/apps/shell.exe" -> volume 0
|
||||
"2:/data/config.ini" -> volume 2
|
||||
"C:\apps\doom.exe" -> volume 0, mapping is simple c->0, d->1, etc.
|
||||
"/apps/video.exe" -> volume 0
|
||||
"/1/apps/video.exe" -> volume 1
|
||||
```
|
||||
|
||||
Drives `0`-`7` are available. A drive only works if a filesystem is registered on it. The Win32 personality translates drive letters to drive numbers, while the POSIX personality maps drives onto a tree.
|
||||
Volumes `0`-`7` are available. A volume only works if a filesystem is registered on it. The Win32 personality translates drive letters to volume numbers, while the POSIX personality maps volumes onto a tree.
|
||||
|
||||
`0:/` is a mounted namespace, not a Windows path. It merely enjoys cosplay.
|
||||
|
||||
|
||||
Reference in new issue
Block a user