[THEMES]
[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 LockStatus = NtFsControlFile(FileHandle,
255 NULL,
256 NULL,
257 NULL,
258 &Iosb,
259 FSCTL_UNLOCK_VOLUME,
260 NULL,
261 0,
262 NULL,
263 0);
264 if (!NT_SUCCESS(LockStatus))
265 {
266 DPRINT1("Failed to unlock volume (Status: 0x%x)\n", LockStatus);
267 }
268
269 NtClose(FileHandle);
270
271 if (Callback != NULL)
272 {
273 Context.Success = (BOOLEAN)(NT_SUCCESS(Status));
274 Callback (DONE, 0, (PVOID)&Context.Success);
275 }
276
277 DPRINT("VfatFormat() done. Status 0x%.08x\n", Status);
278
279 return Status;
280 }
281
282
283 VOID
284 UpdateProgress(PFORMAT_CONTEXT Context,
285 ULONG Increment)
286 {
287 ULONG NewPercent;
288
289 Context->CurrentSectorCount += (ULONGLONG)Increment;
290
291
292 NewPercent = (Context->CurrentSectorCount * 100ULL) / Context->TotalSectorCount;
293
294 if (NewPercent > Context->Percent)
295 {
296 Context->Percent = NewPercent;
297 if (Context->Callback != NULL)
298 {
299 Context->Callback (PROGRESS, 0, &Context->Percent);
300 }
301 }
302 }
303
304
305 VOID
306 VfatPrint(PCHAR Format, ...)
307 {
308 TEXTOUTPUT TextOut;
309 CHAR TextBuf[512];
310 va_list valist;
311
312 va_start(valist, Format);
313 _vsnprintf(TextBuf, sizeof(TextBuf), Format, valist);
314 va_end(valist);
315
316 /* Prepare parameters */
317 TextOut.Lines = 1;
318 TextOut.Output = TextBuf;
319
320 /* Do the callback */
321 if (ChkdskCallback)
322 ChkdskCallback(OUTPUT, 0, &TextOut);
323 }
324
325
326 NTSTATUS
327 WINAPI
328 VfatChkdsk(IN PUNICODE_STRING DriveRoot,
329 IN BOOLEAN FixErrors,
330 IN BOOLEAN Verbose,
331 IN BOOLEAN CheckOnlyIfDirty,
332 IN BOOLEAN ScanDrive,
333 IN PFMIFSCALLBACK Callback)
334 {
335 #if 0
336 BOOLEAN verify;
337 BOOLEAN salvage_files;
338 #endif
339 //ULONG free_clusters;
340 //DOS_FS fs;
341
342 /* Store callback pointer */
343 ChkdskCallback = Callback;
344 FsCheckMemQueue = NULL;
345
346 /* Set parameters */
347 FsCheckFlags = 0;
348 if (Verbose)
349 FsCheckFlags |= FSCHECK_VERBOSE;
350
351 FsCheckTotalFiles = 0;
352
353 #if 0
354 verify = TRUE;
355 salvage_files = TRUE;
356
357 /* Open filesystem */
358 fs_open(DriveRoot,FixErrors);
359
360 if (CheckOnlyIfDirty && !fs_isdirty())
361 {
362 /* No need to check FS */
363 return fs_close(FALSE);
364 }
365
366 read_boot(&fs);
367 if (verify)
368 VfatPrint("Starting check/repair pass.\n");
369
370 while (read_fat(&fs), scan_root(&fs))
371 qfree(&FsCheckMemQueue);
372
373 if (ScanDrive)
374 fix_bad(&fs);
375
376 if (salvage_files)
377 reclaim_file(&fs);
378 else
379 reclaim_free(&fs);
380
381 free_clusters = update_free(&fs);
382 file_unused();
383 qfree(&FsCheckMemQueue);
384 if (verify)
385 {
386 VfatPrint("Starting verification pass.\n");
387 read_fat(&fs);
388 scan_root(&fs);
389 reclaim_free(&fs);
390 qfree(&FsCheckMemQueue);
391 }
392
393 if (fs_changed())
394 {
395 if (FixErrors)
396 {
397 if (FsCheckFlags & FSCHECK_INTERACTIVE)
398 FixErrors = get_key("yn","Perform changes ? (y/n)") == 'y';
399 else
400 VfatPrint("Performing changes.\n");
401 }
402 else
403 {
404 VfatPrint("Leaving file system unchanged.\n");
405 }
406 }
407
408 VfatPrint("%wZ: %u files, %lu/%lu clusters\n", DriveRoot,
409 FsCheckTotalFiles, fs.clusters - free_clusters, fs.clusters );
410
411 if (FixErrors)
412 {
413 /* Dismount the volume */
414 fs_dismount();
415
416 /* Unlock the volume */
417 fs_lock(FALSE);
418 }
419
420 /* Close the volume */
421 return fs_close(FixErrors) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
422 #else
423 return STATUS_SUCCESS;
424 #endif
425 }
426
427 /* EOF */