* The Shell.. for a long time we dreamed of having a compatible, properly working...
[reactos.git] / reactos / lib / fslib / vfatlib / vfatlib.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS VFAT filesystem library
4 * FILE: lib\fslib\vfatlib\vfatlib.c
5 * PURPOSE: Main API
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISIONS:
8 * CSH 05/04-2003 Created
9 */
10 #include "vfatlib.h"
11
12 #define NDEBUG
13 #include <debug.h>
14
15 PFMIFSCALLBACK ChkdskCallback = NULL;
16 PVOID FsCheckMemQueue;
17 ULONG FsCheckFlags;
18 ULONG FsCheckTotalFiles;
19
20 NTSTATUS
21 NTAPI
22 VfatFormat(IN PUNICODE_STRING DriveRoot,
23 IN FMIFS_MEDIA_FLAG MediaFlag,
24 IN PUNICODE_STRING Label,
25 IN BOOLEAN QuickFormat,
26 IN ULONG ClusterSize,
27 IN PFMIFSCALLBACK Callback)
28 {
29 OBJECT_ATTRIBUTES ObjectAttributes;
30 DISK_GEOMETRY DiskGeometry;
31 IO_STATUS_BLOCK Iosb;
32 HANDLE FileHandle;
33 PARTITION_INFORMATION PartitionInfo;
34 FORMAT_CONTEXT Context;
35 NTSTATUS Status, LockStatus;
36
37 DPRINT("VfatFormat(DriveRoot '%wZ')\n", DriveRoot);
38
39 Context.TotalSectorCount = 0;
40 Context.CurrentSectorCount = 0;
41 Context.Callback = Callback;
42 Context.Success = FALSE;
43 Context.Percent = 0;
44
45 InitializeObjectAttributes(&ObjectAttributes,
46 DriveRoot,
47 0,
48 NULL,
49 NULL);
50
51 Status = NtOpenFile(&FileHandle,
52 FILE_GENERIC_READ | FILE_GENERIC_WRITE,
53 &ObjectAttributes,
54 &Iosb,
55 FILE_SHARE_READ,
56 FILE_SYNCHRONOUS_IO_ALERT);
57 if (!NT_SUCCESS(Status))
58 {
59 DPRINT1("NtOpenFile() failed with status 0x%.08x\n", Status);
60 return Status;
61 }
62
63 Status = NtDeviceIoControlFile(FileHandle,
64 NULL,
65 NULL,
66 NULL,
67 &Iosb,
68 IOCTL_DISK_GET_DRIVE_GEOMETRY,
69 NULL,
70 0,
71 &DiskGeometry,
72 sizeof(DISK_GEOMETRY));
73 if (!NT_SUCCESS(Status))
74 {
75 DPRINT("IOCTL_DISK_GET_DRIVE_GEOMETRY failed with status 0x%.08x\n", Status);
76 NtClose(FileHandle);
77 return Status;
78 }
79
80 if (DiskGeometry.MediaType == FixedMedia)
81 {
82 DPRINT("Cylinders %I64d\n", DiskGeometry.Cylinders.QuadPart);
83 DPRINT("TracksPerCylinder %ld\n", DiskGeometry.TracksPerCylinder);
84 DPRINT("SectorsPerTrack %ld\n", DiskGeometry.SectorsPerTrack);
85 DPRINT("BytesPerSector %ld\n", DiskGeometry.BytesPerSector);
86 DPRINT("DiskSize %I64d\n",
87 DiskGeometry.Cylinders.QuadPart *
88 (ULONGLONG)DiskGeometry.TracksPerCylinder *
89 (ULONGLONG)DiskGeometry.SectorsPerTrack *
90 (ULONGLONG)DiskGeometry.BytesPerSector);
91
92 Status = NtDeviceIoControlFile(FileHandle,
93 NULL,
94 NULL,
95 NULL,
96 &Iosb,
97 IOCTL_DISK_GET_PARTITION_INFO,
98 NULL,
99 0,
100 &PartitionInfo,
101 sizeof(PARTITION_INFORMATION));
102 if (!NT_SUCCESS(Status))
103 {
104 DPRINT("IOCTL_DISK_GET_PARTITION_INFO failed with status 0x%.08x\n", Status);
105 NtClose(FileHandle);
106 return Status;
107 }
108
109 /*
110 * FIXME: This is a hack!
111 * Partitioning software MUST set the correct number of hidden sectors!
112 */
113 PartitionInfo.HiddenSectors = DiskGeometry.SectorsPerTrack;
114 }
115 else
116 {
117 PartitionInfo.PartitionType = 0;
118 PartitionInfo.StartingOffset.QuadPart = 0ULL;
119 PartitionInfo.PartitionLength.QuadPart =
120 DiskGeometry.Cylinders.QuadPart *
121 (ULONGLONG)DiskGeometry.TracksPerCylinder *
122 (ULONGLONG)DiskGeometry.SectorsPerTrack *
123 (ULONGLONG)DiskGeometry.BytesPerSector;
124 PartitionInfo.HiddenSectors = 0;
125 PartitionInfo.PartitionNumber = 0;
126 PartitionInfo.BootIndicator = FALSE;
127 PartitionInfo.RewritePartition = FALSE;
128 PartitionInfo.RecognizedPartition = FALSE;
129 }
130
131 /* If it already has a FAT FS, we'll use that type.
132 * If it doesn't, we will determine the FAT type based on size and offset */
133 if (PartitionInfo.PartitionType != PARTITION_FAT_12 &&
134 PartitionInfo.PartitionType != PARTITION_FAT_16 &&
135 PartitionInfo.PartitionType != PARTITION_HUGE &&
136 PartitionInfo.PartitionType != PARTITION_XINT13 &&
137 PartitionInfo.PartitionType != PARTITION_FAT32 &&
138 PartitionInfo.PartitionType != PARTITION_FAT32_XINT13)
139 {
140 /* Determine the correct type based upon size and offset (copied from usetup) */
141 if (PartitionInfo.PartitionLength.QuadPart < (4200LL * 1024LL))
142 {
143 /* FAT12 CHS partition (disk is smaller than 4.1MB) */
144 PartitionInfo.PartitionType = PARTITION_FAT_12;
145 }
146 else if (PartitionInfo.StartingOffset.QuadPart < (1024LL * 255LL * 63LL * 512LL))
147 {
148 /* Partition starts below the 8.4GB boundary ==> CHS partition */
149
150 if (PartitionInfo.PartitionLength.QuadPart < (32LL * 1024LL * 1024LL))
151 {
152 /* FAT16 CHS partition (partiton size < 32MB) */
153 PartitionInfo.PartitionType = PARTITION_FAT_16;
154 }
155 else if (PartitionInfo.PartitionLength.QuadPart < (512LL * 1024LL * 1024LL))
156 {
157 /* FAT16 CHS partition (partition size < 512MB) */
158 PartitionInfo.PartitionType = PARTITION_HUGE;
159 }
160 else
161 {
162 /* FAT32 CHS partition (partition size >= 512MB) */
163 PartitionInfo.PartitionType = PARTITION_FAT32;
164 }
165 }
166 else
167 {
168 /* Partition starts above the 8.4GB boundary ==> LBA partition */
169
170 if (PartitionInfo.PartitionLength.QuadPart < (512LL * 1024LL * 1024LL))
171 {
172 /* FAT16 LBA partition (partition size < 512MB) */
173 PartitionInfo.PartitionType = PARTITION_XINT13;
174 }
175 else
176 {
177 /* FAT32 LBA partition (partition size >= 512MB) */
178 PartitionInfo.PartitionType = PARTITION_FAT32_XINT13;
179 }
180 }
181 }
182
183 DPRINT("PartitionType 0x%x\n", PartitionInfo.PartitionType);
184 DPRINT("StartingOffset %I64d\n", PartitionInfo.StartingOffset.QuadPart);
185 DPRINT("PartitionLength %I64d\n", PartitionInfo.PartitionLength.QuadPart);
186 DPRINT("HiddenSectors %lu\n", PartitionInfo.HiddenSectors);
187 DPRINT("PartitionNumber %d\n", PartitionInfo.PartitionNumber);
188 DPRINT("BootIndicator 0x%x\n", PartitionInfo.BootIndicator);
189 DPRINT("RewritePartition %d\n", PartitionInfo.RewritePartition);
190 DPRINT("RecognizedPartition %d\n", PartitionInfo.RecognizedPartition);
191
192 if (Callback != NULL)
193 {
194 Context.Percent = 0;
195 Callback (PROGRESS, 0, (PVOID)&Context.Percent);
196 }
197
198 LockStatus = NtFsControlFile(FileHandle,
199 NULL,
200 NULL,
201 NULL,
202 &Iosb,
203 FSCTL_LOCK_VOLUME,
204 NULL,
205 0,
206 NULL,
207 0);
208 if (!NT_SUCCESS(LockStatus))
209 {
210 DPRINT1("WARNING: Failed to lock volume for formatting! Format may fail! (Status: 0x%x)\n", LockStatus);
211 }
212
213 if (PartitionInfo.PartitionType == PARTITION_FAT_12)
214 {
215 /* FAT12 */
216 Status = Fat12Format(FileHandle,
217 &PartitionInfo,
218 &DiskGeometry,
219 Label,
220 QuickFormat,
221 ClusterSize,
222 &Context);
223 }
224 else if (PartitionInfo.PartitionType == PARTITION_FAT_16 ||
225 PartitionInfo.PartitionType == PARTITION_HUGE ||
226 PartitionInfo.PartitionType == PARTITION_XINT13)
227 {
228 /* FAT16 */
229 Status = Fat16Format(FileHandle,
230 &PartitionInfo,
231 &DiskGeometry,
232 Label,
233 QuickFormat,
234 ClusterSize,
235 &Context);
236 }
237 else if (PartitionInfo.PartitionType == PARTITION_FAT32 ||
238 PartitionInfo.PartitionType == PARTITION_FAT32_XINT13)
239 {
240 /* FAT32 */
241 Status = Fat32Format(FileHandle,
242 &PartitionInfo,
243 &DiskGeometry,
244 Label,
245 QuickFormat,
246 ClusterSize,
247 &Context);
248 }
249 else
250 {
251 Status = STATUS_INVALID_PARAMETER;
252 }
253
254 /* Attempt to dismount formatted volume */
255 LockStatus = NtFsControlFile(FileHandle,
256 NULL,
257 NULL,
258 NULL,
259 &Iosb,
260 FSCTL_DISMOUNT_VOLUME,
261 NULL,
262 0,
263 NULL,
264 0);
265 if (!NT_SUCCESS(LockStatus))
266 {
267 DPRINT1("Failed to umount volume (Status: 0x%x)\n", LockStatus);
268 }
269
270
271 LockStatus = NtFsControlFile(FileHandle,
272 NULL,
273 NULL,
274 NULL,
275 &Iosb,
276 FSCTL_UNLOCK_VOLUME,
277 NULL,
278 0,
279 NULL,
280 0);
281 if (!NT_SUCCESS(LockStatus))
282 {
283 DPRINT1("Failed to unlock volume (Status: 0x%x)\n", LockStatus);
284 }
285
286 NtClose(FileHandle);
287
288 if (Callback != NULL)
289 {
290 Context.Success = (BOOLEAN)(NT_SUCCESS(Status));
291 Callback (DONE, 0, (PVOID)&Context.Success);
292 }
293
294 DPRINT("VfatFormat() done. Status 0x%.08x\n", Status);
295
296 return Status;
297 }
298
299
300 VOID
301 UpdateProgress(PFORMAT_CONTEXT Context,
302 ULONG Increment)
303 {
304 ULONG NewPercent;
305
306 Context->CurrentSectorCount += (ULONGLONG)Increment;
307
308
309 NewPercent = (Context->CurrentSectorCount * 100ULL) / Context->TotalSectorCount;
310
311 if (NewPercent > Context->Percent)
312 {
313 Context->Percent = NewPercent;
314 if (Context->Callback != NULL)
315 {
316 Context->Callback (PROGRESS, 0, &Context->Percent);
317 }
318 }
319 }
320
321
322 VOID
323 VfatPrint(PCHAR Format, ...)
324 {
325 TEXTOUTPUT TextOut;
326 CHAR TextBuf[512];
327 va_list valist;
328
329 va_start(valist, Format);
330 _vsnprintf(TextBuf, sizeof(TextBuf), Format, valist);
331 va_end(valist);
332
333 /* Prepare parameters */
334 TextOut.Lines = 1;
335 TextOut.Output = TextBuf;
336
337 /* Do the callback */
338 if (ChkdskCallback)
339 ChkdskCallback(OUTPUT, 0, &TextOut);
340 }
341
342
343 NTSTATUS
344 WINAPI
345 VfatChkdsk(IN PUNICODE_STRING DriveRoot,
346 IN BOOLEAN FixErrors,
347 IN BOOLEAN Verbose,
348 IN BOOLEAN CheckOnlyIfDirty,
349 IN BOOLEAN ScanDrive,
350 IN PFMIFSCALLBACK Callback)
351 {
352 #if 0
353 BOOLEAN verify;
354 BOOLEAN salvage_files;
355 #endif
356 //ULONG free_clusters;
357 //DOS_FS fs;
358
359 /* Store callback pointer */
360 ChkdskCallback = Callback;
361 FsCheckMemQueue = NULL;
362
363 /* Set parameters */
364 FsCheckFlags = 0;
365 if (Verbose)
366 FsCheckFlags |= FSCHECK_VERBOSE;
367
368 FsCheckTotalFiles = 0;
369
370 #if 0
371 verify = TRUE;
372 salvage_files = TRUE;
373
374 /* Open filesystem */
375 fs_open(DriveRoot,FixErrors);
376
377 if (CheckOnlyIfDirty && !fs_isdirty())
378 {
379 /* No need to check FS */
380 return fs_close(FALSE);
381 }
382
383 read_boot(&fs);
384 if (verify)
385 VfatPrint("Starting check/repair pass.\n");
386
387 while (read_fat(&fs), scan_root(&fs))
388 qfree(&FsCheckMemQueue);
389
390 if (ScanDrive)
391 fix_bad(&fs);
392
393 if (salvage_files)
394 reclaim_file(&fs);
395 else
396 reclaim_free(&fs);
397
398 free_clusters = update_free(&fs);
399 file_unused();
400 qfree(&FsCheckMemQueue);
401 if (verify)
402 {
403 VfatPrint("Starting verification pass.\n");
404 read_fat(&fs);
405 scan_root(&fs);
406 reclaim_free(&fs);
407 qfree(&FsCheckMemQueue);
408 }
409
410 if (fs_changed())
411 {
412 if (FixErrors)
413 {
414 if (FsCheckFlags & FSCHECK_INTERACTIVE)
415 FixErrors = get_key("yn","Perform changes ? (y/n)") == 'y';
416 else
417 VfatPrint("Performing changes.\n");
418 }
419 else
420 {
421 VfatPrint("Leaving file system unchanged.\n");
422 }
423 }
424
425 VfatPrint("%wZ: %u files, %lu/%lu clusters\n", DriveRoot,
426 FsCheckTotalFiles, fs.clusters - free_clusters, fs.clusters );
427
428 if (FixErrors)
429 {
430 /* Dismount the volume */
431 fs_dismount();
432
433 /* Unlock the volume */
434 fs_lock(FALSE);
435 }
436
437 /* Close the volume */
438 return fs_close(FixErrors) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
439 #else
440 return STATUS_SUCCESS;
441 #endif
442 }
443
444 /* EOF */