[SHELL32] SHChangeNotify: Use tree for CDirectoryList (#6784)
[reactos.git] / base / setup / lib / fsutil.c
1 /*
2 * PROJECT: ReactOS Setup Library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Filesystem Format and ChkDsk support functions.
5 * COPYRIGHT: Copyright 2003-2019 Casper S. Hornstrup (chorns@users.sourceforge.net)
6 * Copyright 2017-2020 Hermes Belusca-Maito
7 */
8
9 //
10 // See also: https://git.reactos.org/?p=reactos.git;a=blob;f=reactos/dll/win32/fmifs/init.c;h=e895f5ef9cae4806123f6bbdd3dfed37ec1c8d33;hb=b9db9a4e377a2055f635b2fb69fef4e1750d219c
11 // for how to get FS providers in a dynamic way. In the (near) future we may
12 // consider merging some of this code with us into a fmifs / fsutil / fslib library...
13 //
14
15 /* INCLUDES *****************************************************************/
16
17 #include "precomp.h"
18
19 #include "partlist.h"
20 #include "fsrec.h"
21 #include "bootcode.h"
22 #include "fsutil.h"
23
24 #include <fslib/vfatlib.h>
25 #include <fslib/btrfslib.h>
26 // #include <fslib/ext2lib.h>
27 // #include <fslib/ntfslib.h>
28
29 #define NDEBUG
30 #include <debug.h>
31
32
33 /* TYPEDEFS *****************************************************************/
34
35 #include <pshpack1.h>
36 typedef struct _FAT_BOOTSECTOR
37 {
38 UCHAR JumpBoot[3]; // Jump instruction to boot code
39 CHAR OemName[8]; // "MSWIN4.1" for MS formatted volumes
40 USHORT BytesPerSector; // Bytes per sector
41 UCHAR SectorsPerCluster; // Number of sectors in a cluster
42 USHORT ReservedSectors; // Reserved sectors, usually 1 (the bootsector)
43 UCHAR NumberOfFats; // Number of FAT tables
44 USHORT RootDirEntries; // Number of root directory entries (fat12/16)
45 USHORT TotalSectors; // Number of total sectors on the drive, 16-bit
46 UCHAR MediaDescriptor; // Media descriptor byte
47 USHORT SectorsPerFat; // Sectors per FAT table (fat12/16)
48 USHORT SectorsPerTrack; // Number of sectors in a track
49 USHORT NumberOfHeads; // Number of heads on the disk
50 ULONG HiddenSectors; // Hidden sectors (sectors before the partition start like the partition table)
51 ULONG TotalSectorsBig; // This field is the new 32-bit total count of sectors on the volume
52 UCHAR DriveNumber; // Int 0x13 drive number (e.g. 0x80)
53 UCHAR Reserved1; // Reserved (used by Windows NT). Code that formats FAT volumes should always set this byte to 0.
54 UCHAR BootSignature; // Extended boot signature (0x29). This is a signature byte that indicates that the following three fields in the boot sector are present.
55 ULONG VolumeSerialNumber; // Volume serial number
56 CHAR VolumeLabel[11]; // Volume label. This field matches the 11-byte volume label recorded in the root directory
57 CHAR FileSystemType[8]; // One of the strings "FAT12 ", "FAT16 ", or "FAT "
58
59 UCHAR BootCodeAndData[448]; // The remainder of the boot sector
60
61 USHORT BootSectorMagic; // 0xAA55
62
63 } FAT_BOOTSECTOR, *PFAT_BOOTSECTOR;
64 C_ASSERT(sizeof(FAT_BOOTSECTOR) == FAT_BOOTSECTOR_SIZE);
65
66 typedef struct _FAT32_BOOTSECTOR
67 {
68 UCHAR JumpBoot[3]; // Jump instruction to boot code
69 CHAR OemName[8]; // "MSWIN4.1" for MS formatted volumes
70 USHORT BytesPerSector; // Bytes per sector
71 UCHAR SectorsPerCluster; // Number of sectors in a cluster
72 USHORT ReservedSectors; // Reserved sectors, usually 1 (the bootsector)
73 UCHAR NumberOfFats; // Number of FAT tables
74 USHORT RootDirEntries; // Number of root directory entries (fat12/16)
75 USHORT TotalSectors; // Number of total sectors on the drive, 16-bit
76 UCHAR MediaDescriptor; // Media descriptor byte
77 USHORT SectorsPerFat; // Sectors per FAT table (fat12/16)
78 USHORT SectorsPerTrack; // Number of sectors in a track
79 USHORT NumberOfHeads; // Number of heads on the disk
80 ULONG HiddenSectors; // Hidden sectors (sectors before the partition start like the partition table)
81 ULONG TotalSectorsBig; // This field is the new 32-bit total count of sectors on the volume
82 ULONG SectorsPerFatBig; // This field is the FAT32 32-bit count of sectors occupied by ONE FAT. BPB_FATSz16 must be 0
83 USHORT ExtendedFlags; // Extended flags (fat32)
84 USHORT FileSystemVersion; // File system version (fat32)
85 ULONG RootDirStartCluster; // Starting cluster of the root directory (fat32)
86 USHORT FsInfo; // Sector number of FSINFO structure in the reserved area of the FAT32 volume. Usually 1.
87 USHORT BackupBootSector; // If non-zero, indicates the sector number in the reserved area of the volume of a copy of the boot record. Usually 6.
88 UCHAR Reserved[12]; // Reserved for future expansion
89 UCHAR DriveNumber; // Int 0x13 drive number (e.g. 0x80)
90 UCHAR Reserved1; // Reserved (used by Windows NT). Code that formats FAT volumes should always set this byte to 0.
91 UCHAR BootSignature; // Extended boot signature (0x29). This is a signature byte that indicates that the following three fields in the boot sector are present.
92 ULONG VolumeSerialNumber; // Volume serial number
93 CHAR VolumeLabel[11]; // Volume label. This field matches the 11-byte volume label recorded in the root directory
94 CHAR FileSystemType[8]; // Always set to the string "FAT32 "
95
96 UCHAR BootCodeAndData[420]; // The remainder of the boot sector
97
98 USHORT BootSectorMagic; // 0xAA55
99
100 } FAT32_BOOTSECTOR, *PFAT32_BOOTSECTOR;
101 C_ASSERT(sizeof(FAT32_BOOTSECTOR) == FAT32_BOOTSECTOR_SIZE);
102
103 typedef struct _BTRFS_BOOTSECTOR
104 {
105 UCHAR JumpBoot[3];
106 UCHAR ChunkMapSize;
107 UCHAR BootDrive;
108 ULONGLONG PartitionStartLBA;
109 UCHAR Fill[1521]; // 1536 - 15
110 USHORT BootSectorMagic;
111 } BTRFS_BOOTSECTOR, *PBTRFS_BOOTSECTOR;
112 C_ASSERT(sizeof(BTRFS_BOOTSECTOR) == BTRFS_BOOTSECTOR_SIZE);
113
114 // TODO: Add more bootsector structures!
115
116 #include <poppack.h>
117
118
119 /* LOCALS *******************************************************************/
120
121 /** IFS_PROVIDER **/
122 typedef struct _FILE_SYSTEM
123 {
124 PCWSTR FileSystemName;
125 PULIB_FORMAT FormatFunc;
126 PULIB_CHKDSK ChkdskFunc;
127 } FILE_SYSTEM, *PFILE_SYSTEM;
128
129 /* The list of file systems on which we can install ReactOS */
130 static FILE_SYSTEM RegisteredFileSystems[] =
131 {
132 /* NOTE: The FAT formatter will automatically
133 * determine whether to use FAT12/16 or FAT32. */
134 { L"FAT" , VfatFormat, VfatChkdsk },
135 { L"FAT32", VfatFormat, VfatChkdsk },
136 #if 0
137 { L"FATX" , VfatxFormat, VfatxChkdsk },
138 { L"NTFS" , NtfsFormat, NtfsChkdsk },
139 #endif
140 { L"BTRFS", BtrfsFormat, BtrfsChkdsk },
141 #if 0
142 { L"EXT2" , Ext2Format, Ext2Chkdsk },
143 { L"EXT3" , Ext2Format, Ext2Chkdsk },
144 { L"EXT4" , Ext2Format, Ext2Chkdsk },
145 { L"FFS" , FfsFormat , FfsChkdsk },
146 { L"REISERFS", ReiserfsFormat, ReiserfsChkdsk },
147 #endif
148 };
149
150
151 /* FUNCTIONS ****************************************************************/
152
153 /** QueryAvailableFileSystemFormat() **/
154 BOOLEAN
155 GetRegisteredFileSystems(
156 IN ULONG Index,
157 OUT PCWSTR* FileSystemName)
158 {
159 if (Index >= ARRAYSIZE(RegisteredFileSystems))
160 return FALSE;
161
162 *FileSystemName = RegisteredFileSystems[Index].FileSystemName;
163
164 return TRUE;
165 }
166
167
168 /** GetProvider() **/
169 static PFILE_SYSTEM
170 GetFileSystemByName(
171 IN PCWSTR FileSystemName)
172 {
173 #if 0 // Reenable when the list of registered FSes will again be dynamic
174
175 PLIST_ENTRY ListEntry;
176 PFILE_SYSTEM_ITEM Item;
177
178 ListEntry = List->ListHead.Flink;
179 while (ListEntry != &List->ListHead)
180 {
181 Item = CONTAINING_RECORD(ListEntry, FILE_SYSTEM_ITEM, ListEntry);
182 if (Item->FileSystemName &&
183 (wcsicmp(FileSystemName, Item->FileSystemName) == 0))
184 {
185 return Item;
186 }
187
188 ListEntry = ListEntry->Flink;
189 }
190
191 #else
192
193 ULONG Count = ARRAYSIZE(RegisteredFileSystems);
194 PFILE_SYSTEM FileSystems = RegisteredFileSystems;
195
196 ASSERT(FileSystems && Count != 0);
197
198 while (Count--)
199 {
200 if (FileSystems->FileSystemName &&
201 (wcsicmp(FileSystemName, FileSystems->FileSystemName) == 0))
202 {
203 return FileSystems;
204 }
205
206 ++FileSystems;
207 }
208
209 #endif
210
211 return NULL;
212 }
213
214
215 /** ChkdskEx() **/
216 NTSTATUS
217 ChkdskFileSystem_UStr(
218 IN PUNICODE_STRING DriveRoot,
219 IN PCWSTR FileSystemName,
220 IN BOOLEAN FixErrors,
221 IN BOOLEAN Verbose,
222 IN BOOLEAN CheckOnlyIfDirty,
223 IN BOOLEAN ScanDrive,
224 IN PFMIFSCALLBACK Callback)
225 {
226 PFILE_SYSTEM FileSystem;
227 NTSTATUS Status;
228 BOOLEAN Success;
229
230 FileSystem = GetFileSystemByName(FileSystemName);
231
232 if (!FileSystem || !FileSystem->ChkdskFunc)
233 {
234 // Success = FALSE;
235 // Callback(DONE, 0, &Success);
236 return STATUS_NOT_SUPPORTED;
237 }
238
239 Status = STATUS_SUCCESS;
240 Success = FileSystem->ChkdskFunc(DriveRoot,
241 Callback,
242 FixErrors,
243 Verbose,
244 CheckOnlyIfDirty,
245 ScanDrive,
246 NULL,
247 NULL,
248 NULL,
249 NULL,
250 (PULONG)&Status);
251 if (!Success)
252 DPRINT1("ChkdskFunc() failed with Status 0x%lx\n", Status);
253
254 // Callback(DONE, 0, &Success);
255
256 return Status;
257 }
258
259 NTSTATUS
260 ChkdskFileSystem(
261 IN PCWSTR DriveRoot,
262 IN PCWSTR FileSystemName,
263 IN BOOLEAN FixErrors,
264 IN BOOLEAN Verbose,
265 IN BOOLEAN CheckOnlyIfDirty,
266 IN BOOLEAN ScanDrive,
267 IN PFMIFSCALLBACK Callback)
268 {
269 UNICODE_STRING DriveRootU;
270
271 RtlInitUnicodeString(&DriveRootU, DriveRoot);
272 return ChkdskFileSystem_UStr(&DriveRootU,
273 FileSystemName,
274 FixErrors,
275 Verbose,
276 CheckOnlyIfDirty,
277 ScanDrive,
278 Callback);
279 }
280
281
282 /** FormatEx() **/
283 NTSTATUS
284 FormatFileSystem_UStr(
285 IN PUNICODE_STRING DriveRoot,
286 IN PCWSTR FileSystemName,
287 IN FMIFS_MEDIA_FLAG MediaFlag,
288 IN PUNICODE_STRING Label,
289 IN BOOLEAN QuickFormat,
290 IN ULONG ClusterSize,
291 IN PFMIFSCALLBACK Callback)
292 {
293 PFILE_SYSTEM FileSystem;
294 BOOLEAN Success;
295 BOOLEAN BackwardCompatible = FALSE; // Default to latest FS versions.
296 MEDIA_TYPE MediaType;
297
298 FileSystem = GetFileSystemByName(FileSystemName);
299
300 if (!FileSystem || !FileSystem->FormatFunc)
301 {
302 // Success = FALSE;
303 // Callback(DONE, 0, &Success);
304 return STATUS_NOT_SUPPORTED;
305 }
306
307 /* Set the BackwardCompatible flag in case we format with older FAT12/16 */
308 if (wcsicmp(FileSystemName, L"FAT") == 0)
309 BackwardCompatible = TRUE;
310 // else if (wcsicmp(FileSystemName, L"FAT32") == 0)
311 // BackwardCompatible = FALSE;
312
313 /* Convert the FMIFS MediaFlag to a NT MediaType */
314 // FIXME: Actually covert all the possible flags.
315 switch (MediaFlag)
316 {
317 case FMIFS_FLOPPY:
318 MediaType = F5_320_1024; // FIXME: This is hardfixed!
319 break;
320 case FMIFS_REMOVABLE:
321 MediaType = RemovableMedia;
322 break;
323 case FMIFS_HARDDISK:
324 MediaType = FixedMedia;
325 break;
326 default:
327 DPRINT1("Unknown FMIFS MediaFlag %d, converting 1-to-1 to NT MediaType\n",
328 MediaFlag);
329 MediaType = (MEDIA_TYPE)MediaFlag;
330 break;
331 }
332
333 Success = FileSystem->FormatFunc(DriveRoot,
334 Callback,
335 QuickFormat,
336 BackwardCompatible,
337 MediaType,
338 Label,
339 ClusterSize);
340 if (!Success)
341 DPRINT1("FormatFunc() failed\n");
342
343 // Callback(DONE, 0, &Success);
344
345 return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
346 }
347
348 NTSTATUS
349 FormatFileSystem(
350 IN PCWSTR DriveRoot,
351 IN PCWSTR FileSystemName,
352 IN FMIFS_MEDIA_FLAG MediaFlag,
353 IN PCWSTR Label,
354 IN BOOLEAN QuickFormat,
355 IN ULONG ClusterSize,
356 IN PFMIFSCALLBACK Callback)
357 {
358 UNICODE_STRING DriveRootU;
359 UNICODE_STRING LabelU;
360
361 RtlInitUnicodeString(&DriveRootU, DriveRoot);
362 RtlInitUnicodeString(&LabelU, Label);
363
364 return FormatFileSystem_UStr(&DriveRootU,
365 FileSystemName,
366 MediaFlag,
367 &LabelU,
368 QuickFormat,
369 ClusterSize,
370 Callback);
371 }
372
373
374 //
375 // Bootsector routines
376 //
377
378 NTSTATUS
379 InstallFatBootCode(
380 IN PCWSTR SrcPath, // FAT12/16 bootsector source file (on the installation medium)
381 IN HANDLE DstPath, // Where to save the bootsector built from the source + partition information
382 IN HANDLE RootPartition) // Partition holding the (old) FAT12/16 information
383 {
384 NTSTATUS Status;
385 UNICODE_STRING Name;
386 IO_STATUS_BLOCK IoStatusBlock;
387 LARGE_INTEGER FileOffset;
388 BOOTCODE OrigBootSector = {0};
389 BOOTCODE NewBootSector = {0};
390
391 /* Allocate and read the current original partition bootsector */
392 Status = ReadBootCodeByHandle(&OrigBootSector,
393 RootPartition,
394 FAT_BOOTSECTOR_SIZE);
395 if (!NT_SUCCESS(Status))
396 return Status;
397
398 /* Allocate and read the new bootsector from SrcPath */
399 RtlInitUnicodeString(&Name, SrcPath);
400 Status = ReadBootCodeFromFile(&NewBootSector,
401 &Name,
402 FAT_BOOTSECTOR_SIZE);
403 if (!NT_SUCCESS(Status))
404 {
405 FreeBootCode(&OrigBootSector);
406 return Status;
407 }
408
409 /* Adjust the bootsector (copy a part of the FAT12/16 BPB) */
410 RtlCopyMemory(&((PFAT_BOOTSECTOR)NewBootSector.BootCode)->OemName,
411 &((PFAT_BOOTSECTOR)OrigBootSector.BootCode)->OemName,
412 FIELD_OFFSET(FAT_BOOTSECTOR, BootCodeAndData) -
413 FIELD_OFFSET(FAT_BOOTSECTOR, OemName));
414
415 /* Free the original bootsector */
416 FreeBootCode(&OrigBootSector);
417
418 /* Write the new bootsector to DstPath */
419 FileOffset.QuadPart = 0ULL;
420 Status = NtWriteFile(DstPath,
421 NULL,
422 NULL,
423 NULL,
424 &IoStatusBlock,
425 NewBootSector.BootCode,
426 NewBootSector.Length,
427 &FileOffset,
428 NULL);
429
430 /* Free the new bootsector */
431 FreeBootCode(&NewBootSector);
432
433 return Status;
434 }
435
436 NTSTATUS
437 InstallFat32BootCode(
438 IN PCWSTR SrcPath, // FAT32 bootsector source file (on the installation medium)
439 IN HANDLE DstPath, // Where to save the bootsector built from the source + partition information
440 IN HANDLE RootPartition) // Partition holding the (old) FAT32 information
441 {
442 NTSTATUS Status;
443 UNICODE_STRING Name;
444 IO_STATUS_BLOCK IoStatusBlock;
445 LARGE_INTEGER FileOffset;
446 USHORT BackupBootSector = 0;
447 BOOTCODE OrigBootSector = {0};
448 BOOTCODE NewBootSector = {0};
449
450 /* Allocate and read the current original partition bootsector */
451 Status = ReadBootCodeByHandle(&OrigBootSector,
452 RootPartition,
453 FAT32_BOOTSECTOR_SIZE);
454 if (!NT_SUCCESS(Status))
455 return Status;
456
457 /* Allocate and read the new bootsector (2 sectors) from SrcPath */
458 RtlInitUnicodeString(&Name, SrcPath);
459 Status = ReadBootCodeFromFile(&NewBootSector,
460 &Name,
461 2 * FAT32_BOOTSECTOR_SIZE);
462 if (!NT_SUCCESS(Status))
463 {
464 FreeBootCode(&OrigBootSector);
465 return Status;
466 }
467
468 /* Adjust the bootsector (copy a part of the FAT32 BPB) */
469 RtlCopyMemory(&((PFAT32_BOOTSECTOR)NewBootSector.BootCode)->OemName,
470 &((PFAT32_BOOTSECTOR)OrigBootSector.BootCode)->OemName,
471 FIELD_OFFSET(FAT32_BOOTSECTOR, BootCodeAndData) -
472 FIELD_OFFSET(FAT32_BOOTSECTOR, OemName));
473
474 /*
475 * We know we copy the boot code to a file only when DstPath != RootPartition,
476 * otherwise the boot code is copied to the specified root partition.
477 */
478 if (DstPath != RootPartition)
479 {
480 /* Copy to a file: Disable the backup bootsector */
481 ((PFAT32_BOOTSECTOR)NewBootSector.BootCode)->BackupBootSector = 0;
482 }
483 else
484 {
485 /* Copy to a disk: Get the location of the backup bootsector */
486 BackupBootSector = ((PFAT32_BOOTSECTOR)OrigBootSector.BootCode)->BackupBootSector;
487 }
488
489 /* Free the original bootsector */
490 FreeBootCode(&OrigBootSector);
491
492 /* Write the first sector of the new bootcode to DstPath sector 0 */
493 FileOffset.QuadPart = 0ULL;
494 Status = NtWriteFile(DstPath,
495 NULL,
496 NULL,
497 NULL,
498 &IoStatusBlock,
499 NewBootSector.BootCode,
500 FAT32_BOOTSECTOR_SIZE,
501 &FileOffset,
502 NULL);
503 if (!NT_SUCCESS(Status))
504 {
505 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
506 FreeBootCode(&NewBootSector);
507 return Status;
508 }
509
510 if (DstPath == RootPartition)
511 {
512 /* Copy to a disk: Write the backup bootsector */
513 if ((BackupBootSector != 0x0000) && (BackupBootSector != 0xFFFF))
514 {
515 FileOffset.QuadPart = (ULONGLONG)((ULONG)BackupBootSector * FAT32_BOOTSECTOR_SIZE);
516 Status = NtWriteFile(DstPath,
517 NULL,
518 NULL,
519 NULL,
520 &IoStatusBlock,
521 NewBootSector.BootCode,
522 FAT32_BOOTSECTOR_SIZE,
523 &FileOffset,
524 NULL);
525 if (!NT_SUCCESS(Status))
526 {
527 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
528 FreeBootCode(&NewBootSector);
529 return Status;
530 }
531 }
532 }
533
534 /* Write the second sector of the new bootcode to boot disk sector 14 */
535 // FileOffset.QuadPart = (ULONGLONG)(14 * FAT32_BOOTSECTOR_SIZE);
536 FileOffset.QuadPart = 14 * FAT32_BOOTSECTOR_SIZE;
537 Status = NtWriteFile(DstPath, // or really RootPartition ???
538 NULL,
539 NULL,
540 NULL,
541 &IoStatusBlock,
542 ((PUCHAR)NewBootSector.BootCode + FAT32_BOOTSECTOR_SIZE),
543 FAT32_BOOTSECTOR_SIZE,
544 &FileOffset,
545 NULL);
546 if (!NT_SUCCESS(Status))
547 {
548 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
549 }
550
551 /* Free the new bootsector */
552 FreeBootCode(&NewBootSector);
553
554 return Status;
555 }
556
557 NTSTATUS
558 InstallBtrfsBootCode(
559 IN PCWSTR SrcPath, // BTRFS bootsector source file (on the installation medium)
560 IN HANDLE DstPath, // Where to save the bootsector built from the source + partition information
561 IN HANDLE RootPartition) // Partition holding the (old) BTRFS information
562 {
563 NTSTATUS Status;
564 NTSTATUS LockStatus;
565 UNICODE_STRING Name;
566 IO_STATUS_BLOCK IoStatusBlock;
567 LARGE_INTEGER FileOffset;
568 PARTITION_INFORMATION_EX PartInfo;
569 BOOTCODE NewBootSector = {0};
570
571 /* Allocate and read the new bootsector from SrcPath */
572 RtlInitUnicodeString(&Name, SrcPath);
573 Status = ReadBootCodeFromFile(&NewBootSector,
574 &Name,
575 BTRFS_BOOTSECTOR_SIZE);
576 if (!NT_SUCCESS(Status))
577 return Status;
578
579 /*
580 * The BTRFS driver requires the volume to be locked in order to modify
581 * the first sectors of the partition, even though they are outside the
582 * file-system space / in the reserved area (they are situated before
583 * the super-block at 0x1000) and is in principle allowed by the NT
584 * storage stack.
585 * So we lock here in order to write the bootsector at sector 0.
586 * If locking fails, we ignore and continue nonetheless.
587 */
588 LockStatus = NtFsControlFile(DstPath,
589 NULL,
590 NULL,
591 NULL,
592 &IoStatusBlock,
593 FSCTL_LOCK_VOLUME,
594 NULL,
595 0,
596 NULL,
597 0);
598 if (!NT_SUCCESS(LockStatus))
599 {
600 DPRINT1("WARNING: Failed to lock BTRFS volume for writing bootsector! Operations may fail! (Status 0x%lx)\n", LockStatus);
601 }
602
603 /* Obtain partition info and write it to the bootsector */
604 Status = NtDeviceIoControlFile(RootPartition,
605 NULL,
606 NULL,
607 NULL,
608 &IoStatusBlock,
609 IOCTL_DISK_GET_PARTITION_INFO_EX,
610 NULL,
611 0,
612 &PartInfo,
613 sizeof(PartInfo));
614 if (!NT_SUCCESS(Status))
615 {
616 DPRINT1("IOCTL_DISK_GET_PARTITION_INFO_EX failed (Status %lx)\n", Status);
617 goto Quit;
618 }
619
620 /* Write new bootsector to RootPath */
621 ((PBTRFS_BOOTSECTOR)NewBootSector.BootCode)->PartitionStartLBA =
622 PartInfo.StartingOffset.QuadPart / SECTORSIZE;
623
624 /* Write sector 0 */
625 FileOffset.QuadPart = 0ULL;
626 Status = NtWriteFile(DstPath,
627 NULL,
628 NULL,
629 NULL,
630 &IoStatusBlock,
631 NewBootSector.BootCode,
632 NewBootSector.Length,
633 &FileOffset,
634 NULL);
635 if (!NT_SUCCESS(Status))
636 {
637 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
638 goto Quit;
639 }
640
641 Quit:
642 /* Unlock the volume */
643 LockStatus = NtFsControlFile(DstPath,
644 NULL,
645 NULL,
646 NULL,
647 &IoStatusBlock,
648 FSCTL_UNLOCK_VOLUME,
649 NULL,
650 0,
651 NULL,
652 0);
653 if (!NT_SUCCESS(LockStatus))
654 {
655 DPRINT1("Failed to unlock BTRFS volume (Status 0x%lx)\n", LockStatus);
656 }
657
658 /* Free the new bootsector */
659 FreeBootCode(&NewBootSector);
660
661 return Status;
662 }
663
664
665 //
666 // Formatting routines
667 //
668
669 NTSTATUS
670 ChkdskPartition(
671 IN PPARTENTRY PartEntry,
672 IN BOOLEAN FixErrors,
673 IN BOOLEAN Verbose,
674 IN BOOLEAN CheckOnlyIfDirty,
675 IN BOOLEAN ScanDrive,
676 IN PFMIFSCALLBACK Callback)
677 {
678 NTSTATUS Status;
679 PDISKENTRY DiskEntry = PartEntry->DiskEntry;
680 // UNICODE_STRING PartitionRootPath;
681 WCHAR PartitionRootPath[MAX_PATH]; // PathBuffer
682
683 ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
684
685 /* HACK: Do not try to check a partition with an unknown filesystem */
686 if (!*PartEntry->FileSystem)
687 {
688 PartEntry->NeedsCheck = FALSE;
689 return STATUS_SUCCESS;
690 }
691
692 /* Set PartitionRootPath */
693 RtlStringCchPrintfW(PartitionRootPath, ARRAYSIZE(PartitionRootPath),
694 L"\\Device\\Harddisk%lu\\Partition%lu",
695 DiskEntry->DiskNumber,
696 PartEntry->PartitionNumber);
697 DPRINT("PartitionRootPath: %S\n", PartitionRootPath);
698
699 /* Check the partition */
700 Status = ChkdskFileSystem(PartitionRootPath,
701 PartEntry->FileSystem,
702 FixErrors,
703 Verbose,
704 CheckOnlyIfDirty,
705 ScanDrive,
706 Callback);
707 if (!NT_SUCCESS(Status))
708 return Status;
709
710 PartEntry->NeedsCheck = FALSE;
711 return STATUS_SUCCESS;
712 }
713
714 NTSTATUS
715 FormatPartition(
716 IN PPARTENTRY PartEntry,
717 IN PCWSTR FileSystemName,
718 IN FMIFS_MEDIA_FLAG MediaFlag,
719 IN PCWSTR Label,
720 IN BOOLEAN QuickFormat,
721 IN ULONG ClusterSize,
722 IN PFMIFSCALLBACK Callback)
723 {
724 NTSTATUS Status;
725 PDISKENTRY DiskEntry = PartEntry->DiskEntry;
726 UCHAR PartitionType;
727 // UNICODE_STRING PartitionRootPath;
728 WCHAR PartitionRootPath[MAX_PATH]; // PathBuffer
729
730 ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
731
732 if (!FileSystemName || !*FileSystemName)
733 {
734 DPRINT1("No file system specified?\n");
735 return STATUS_UNRECOGNIZED_VOLUME;
736 }
737
738 /*
739 * Prepare the partition for formatting (for MBR disks, reset the
740 * partition type), and adjust the filesystem name in case of FAT
741 * vs. FAT32, depending on the geometry of the partition.
742 */
743
744 // FIXME: Do this only if QuickFormat == FALSE? What about FAT handling?
745
746 /*
747 * Retrieve a partition type as a hint only. It will be used to determine
748 * whether to actually use FAT12/16 or FAT32 filesystem, depending on the
749 * geometry of the partition. If the partition resides on an MBR disk,
750 * the partition style will be reset to this value as well, unless the
751 * partition is OEM.
752 */
753 PartitionType = FileSystemToMBRPartitionType(FileSystemName,
754 PartEntry->StartSector.QuadPart,
755 PartEntry->SectorCount.QuadPart);
756 if (PartitionType == PARTITION_ENTRY_UNUSED)
757 {
758 /* Unknown file system */
759 DPRINT1("Unknown file system '%S'\n", FileSystemName);
760 return STATUS_UNRECOGNIZED_VOLUME;
761 }
762
763 /* Reset the MBR partition type, unless this is an OEM partition */
764 if (DiskEntry->DiskStyle == PARTITION_STYLE_MBR)
765 {
766 if (!IsOEMPartition(PartEntry->PartitionType))
767 SetMBRPartitionType(PartEntry, PartitionType);
768 }
769
770 /*
771 * Adjust the filesystem name in case of FAT vs. FAT32, according to
772 * the type of partition returned by FileSystemToMBRPartitionType().
773 */
774 if (wcsicmp(FileSystemName, L"FAT") == 0)
775 {
776 if ((PartitionType == PARTITION_FAT32) ||
777 (PartitionType == PARTITION_FAT32_XINT13))
778 {
779 FileSystemName = L"FAT32";
780 }
781 }
782
783 /* Commit the partition changes to the disk */
784 Status = WritePartitions(DiskEntry);
785 if (!NT_SUCCESS(Status))
786 {
787 DPRINT1("WritePartitions(disk %lu) failed, Status 0x%08lx\n",
788 DiskEntry->DiskNumber, Status);
789 return STATUS_PARTITION_FAILURE;
790 }
791
792 /* Set PartitionRootPath */
793 RtlStringCchPrintfW(PartitionRootPath, ARRAYSIZE(PartitionRootPath),
794 L"\\Device\\Harddisk%lu\\Partition%lu",
795 DiskEntry->DiskNumber,
796 PartEntry->PartitionNumber);
797 DPRINT("PartitionRootPath: %S\n", PartitionRootPath);
798
799 /* Format the partition */
800 Status = FormatFileSystem(PartitionRootPath,
801 FileSystemName,
802 MediaFlag,
803 Label,
804 QuickFormat,
805 ClusterSize,
806 Callback);
807 if (!NT_SUCCESS(Status))
808 return Status;
809
810 //
811 // TODO: Here, call a partlist.c function that update the actual
812 // FS name and the label fields of the volume.
813 //
814 PartEntry->FormatState = Formatted;
815
816 /* Set the new partition's file system proper */
817 RtlStringCbCopyW(PartEntry->FileSystem,
818 sizeof(PartEntry->FileSystem),
819 FileSystemName);
820
821 PartEntry->New = FALSE;
822
823 return STATUS_SUCCESS;
824 }
825
826 /* EOF */