[TRANSLATIONS] Malay translation by Henry Tang Ih. CORE-10414
[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 else
110 {
111 PartitionInfo.PartitionType = 0;
112 PartitionInfo.StartingOffset.QuadPart = 0ULL;
113 PartitionInfo.PartitionLength.QuadPart =
114 DiskGeometry.Cylinders.QuadPart *
115 (ULONGLONG)DiskGeometry.TracksPerCylinder *
116 (ULONGLONG)DiskGeometry.SectorsPerTrack *
117 (ULONGLONG)DiskGeometry.BytesPerSector;
118 PartitionInfo.HiddenSectors = 0;
119 PartitionInfo.PartitionNumber = 0;
120 PartitionInfo.BootIndicator = FALSE;
121 PartitionInfo.RewritePartition = FALSE;
122 PartitionInfo.RecognizedPartition = FALSE;
123 }
124
125 /* If it already has a FAT FS, we'll use that type.
126 * If it doesn't, we will determine the FAT type based on size and offset */
127 if (PartitionInfo.PartitionType != PARTITION_FAT_12 &&
128 PartitionInfo.PartitionType != PARTITION_FAT_16 &&
129 PartitionInfo.PartitionType != PARTITION_HUGE &&
130 PartitionInfo.PartitionType != PARTITION_XINT13 &&
131 PartitionInfo.PartitionType != PARTITION_FAT32 &&
132 PartitionInfo.PartitionType != PARTITION_FAT32_XINT13)
133 {
134 /* Determine the correct type based upon size and offset (copied from usetup) */
135 if (PartitionInfo.PartitionLength.QuadPart < (4200LL * 1024LL))
136 {
137 /* FAT12 CHS partition (disk is smaller than 4.1MB) */
138 PartitionInfo.PartitionType = PARTITION_FAT_12;
139 }
140 else if (PartitionInfo.StartingOffset.QuadPart < (1024LL * 255LL * 63LL * 512LL))
141 {
142 /* Partition starts below the 8.4GB boundary ==> CHS partition */
143
144 if (PartitionInfo.PartitionLength.QuadPart < (32LL * 1024LL * 1024LL))
145 {
146 /* FAT16 CHS partition (partiton size < 32MB) */
147 PartitionInfo.PartitionType = PARTITION_FAT_16;
148 }
149 else if (PartitionInfo.PartitionLength.QuadPart < (512LL * 1024LL * 1024LL))
150 {
151 /* FAT16 CHS partition (partition size < 512MB) */
152 PartitionInfo.PartitionType = PARTITION_HUGE;
153 }
154 else
155 {
156 /* FAT32 CHS partition (partition size >= 512MB) */
157 PartitionInfo.PartitionType = PARTITION_FAT32;
158 }
159 }
160 else
161 {
162 /* Partition starts above the 8.4GB boundary ==> LBA partition */
163
164 if (PartitionInfo.PartitionLength.QuadPart < (512LL * 1024LL * 1024LL))
165 {
166 /* FAT16 LBA partition (partition size < 512MB) */
167 PartitionInfo.PartitionType = PARTITION_XINT13;
168 }
169 else
170 {
171 /* FAT32 LBA partition (partition size >= 512MB) */
172 PartitionInfo.PartitionType = PARTITION_FAT32_XINT13;
173 }
174 }
175 }
176
177 DPRINT("PartitionType 0x%x\n", PartitionInfo.PartitionType);
178 DPRINT("StartingOffset %I64d\n", PartitionInfo.StartingOffset.QuadPart);
179 DPRINT("PartitionLength %I64d\n", PartitionInfo.PartitionLength.QuadPart);
180 DPRINT("HiddenSectors %lu\n", PartitionInfo.HiddenSectors);
181 DPRINT("PartitionNumber %d\n", PartitionInfo.PartitionNumber);
182 DPRINT("BootIndicator 0x%x\n", PartitionInfo.BootIndicator);
183 DPRINT("RewritePartition %d\n", PartitionInfo.RewritePartition);
184 DPRINT("RecognizedPartition %d\n", PartitionInfo.RecognizedPartition);
185
186 if (Callback != NULL)
187 {
188 Context.Percent = 0;
189 Callback (PROGRESS, 0, (PVOID)&Context.Percent);
190 }
191
192 LockStatus = NtFsControlFile(FileHandle,
193 NULL,
194 NULL,
195 NULL,
196 &Iosb,
197 FSCTL_LOCK_VOLUME,
198 NULL,
199 0,
200 NULL,
201 0);
202 if (!NT_SUCCESS(LockStatus))
203 {
204 DPRINT1("WARNING: Failed to lock volume for formatting! Format may fail! (Status: 0x%x)\n", LockStatus);
205 }
206
207 if (PartitionInfo.PartitionType == PARTITION_FAT_12)
208 {
209 /* FAT12 */
210 Status = Fat12Format(FileHandle,
211 &PartitionInfo,
212 &DiskGeometry,
213 Label,
214 QuickFormat,
215 ClusterSize,
216 &Context);
217 }
218 else if (PartitionInfo.PartitionType == PARTITION_FAT_16 ||
219 PartitionInfo.PartitionType == PARTITION_HUGE ||
220 PartitionInfo.PartitionType == PARTITION_XINT13)
221 {
222 /* FAT16 */
223 Status = Fat16Format(FileHandle,
224 &PartitionInfo,
225 &DiskGeometry,
226 Label,
227 QuickFormat,
228 ClusterSize,
229 &Context);
230 }
231 else if (PartitionInfo.PartitionType == PARTITION_FAT32 ||
232 PartitionInfo.PartitionType == PARTITION_FAT32_XINT13)
233 {
234 /* FAT32 */
235 Status = Fat32Format(FileHandle,
236 &PartitionInfo,
237 &DiskGeometry,
238 Label,
239 QuickFormat,
240 ClusterSize,
241 &Context);
242 }
243 else
244 {
245 Status = STATUS_INVALID_PARAMETER;
246 }
247
248 /* Attempt to dismount formatted volume */
249 LockStatus = NtFsControlFile(FileHandle,
250 NULL,
251 NULL,
252 NULL,
253 &Iosb,
254 FSCTL_DISMOUNT_VOLUME,
255 NULL,
256 0,
257 NULL,
258 0);
259 if (!NT_SUCCESS(LockStatus))
260 {
261 DPRINT1("Failed to umount volume (Status: 0x%x)\n", LockStatus);
262 }
263
264
265 LockStatus = NtFsControlFile(FileHandle,
266 NULL,
267 NULL,
268 NULL,
269 &Iosb,
270 FSCTL_UNLOCK_VOLUME,
271 NULL,
272 0,
273 NULL,
274 0);
275 if (!NT_SUCCESS(LockStatus))
276 {
277 DPRINT1("Failed to unlock volume (Status: 0x%x)\n", LockStatus);
278 }
279
280 NtClose(FileHandle);
281
282 if (Callback != NULL)
283 {
284 Context.Success = (BOOLEAN)(NT_SUCCESS(Status));
285 Callback (DONE, 0, (PVOID)&Context.Success);
286 }
287
288 DPRINT("VfatFormat() done. Status 0x%.08x\n", Status);
289
290 return Status;
291 }
292
293
294 VOID
295 UpdateProgress(PFORMAT_CONTEXT Context,
296 ULONG Increment)
297 {
298 ULONG NewPercent;
299
300 Context->CurrentSectorCount += (ULONGLONG)Increment;
301
302
303 NewPercent = (Context->CurrentSectorCount * 100ULL) / Context->TotalSectorCount;
304
305 if (NewPercent > Context->Percent)
306 {
307 Context->Percent = NewPercent;
308 if (Context->Callback != NULL)
309 {
310 Context->Callback (PROGRESS, 0, &Context->Percent);
311 }
312 }
313 }
314
315
316 VOID
317 VfatPrint(PCHAR Format, ...)
318 {
319 TEXTOUTPUT TextOut;
320 CHAR TextBuf[512];
321 va_list valist;
322
323 va_start(valist, Format);
324 _vsnprintf(TextBuf, sizeof(TextBuf), Format, valist);
325 va_end(valist);
326
327 /* Prepare parameters */
328 TextOut.Lines = 1;
329 TextOut.Output = TextBuf;
330
331 /* Do the callback */
332 if (ChkdskCallback)
333 ChkdskCallback(OUTPUT, 0, &TextOut);
334 }
335
336
337 NTSTATUS
338 WINAPI
339 VfatChkdsk(IN PUNICODE_STRING DriveRoot,
340 IN BOOLEAN FixErrors,
341 IN BOOLEAN Verbose,
342 IN BOOLEAN CheckOnlyIfDirty,
343 IN BOOLEAN ScanDrive,
344 IN PFMIFSCALLBACK Callback)
345 {
346 #if 0
347 BOOLEAN verify;
348 BOOLEAN salvage_files;
349 #endif
350 //ULONG free_clusters;
351 //DOS_FS fs;
352
353 /* Store callback pointer */
354 ChkdskCallback = Callback;
355 FsCheckMemQueue = NULL;
356
357 /* Set parameters */
358 FsCheckFlags = 0;
359 if (Verbose)
360 FsCheckFlags |= FSCHECK_VERBOSE;
361
362 FsCheckTotalFiles = 0;
363
364 #if 0
365 verify = TRUE;
366 salvage_files = TRUE;
367
368 /* Open filesystem */
369 fs_open(DriveRoot,FixErrors);
370
371 if (CheckOnlyIfDirty && !fs_isdirty())
372 {
373 /* No need to check FS */
374 return fs_close(FALSE);
375 }
376
377 read_boot(&fs);
378 if (verify)
379 VfatPrint("Starting check/repair pass.\n");
380
381 while (read_fat(&fs), scan_root(&fs))
382 qfree(&FsCheckMemQueue);
383
384 if (ScanDrive)
385 fix_bad(&fs);
386
387 if (salvage_files)
388 reclaim_file(&fs);
389 else
390 reclaim_free(&fs);
391
392 free_clusters = update_free(&fs);
393 file_unused();
394 qfree(&FsCheckMemQueue);
395 if (verify)
396 {
397 VfatPrint("Starting verification pass.\n");
398 read_fat(&fs);
399 scan_root(&fs);
400 reclaim_free(&fs);
401 qfree(&FsCheckMemQueue);
402 }
403
404 if (fs_changed())
405 {
406 if (FixErrors)
407 {
408 if (FsCheckFlags & FSCHECK_INTERACTIVE)
409 FixErrors = get_key("yn","Perform changes ? (y/n)") == 'y';
410 else
411 VfatPrint("Performing changes.\n");
412 }
413 else
414 {
415 VfatPrint("Leaving file system unchanged.\n");
416 }
417 }
418
419 VfatPrint("%wZ: %u files, %lu/%lu clusters\n", DriveRoot,
420 FsCheckTotalFiles, fs.clusters - free_clusters, fs.clusters );
421
422 if (FixErrors)
423 {
424 /* Dismount the volume */
425 fs_dismount();
426
427 /* Unlock the volume */
428 fs_lock(FALSE);
429 }
430
431 /* Close the volume */
432 return fs_close(FixErrors) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
433 #else
434 return STATUS_SUCCESS;
435 #endif
436 }
437
438 /* EOF */