Hopefully fail to break anything in the process of syncing with trunk (r47786)
[reactos.git] / 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;
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 DPRINT("PartitionType 0x%x\n", PartitionInfo.PartitionType);
132 DPRINT("StartingOffset %I64d\n", PartitionInfo.StartingOffset.QuadPart);
133 DPRINT("PartitionLength %I64d\n", PartitionInfo.PartitionLength.QuadPart);
134 DPRINT("HiddenSectors %lu\n", PartitionInfo.HiddenSectors);
135 DPRINT("PartitionNumber %d\n", PartitionInfo.PartitionNumber);
136 DPRINT("BootIndicator 0x%x\n", PartitionInfo.BootIndicator);
137 DPRINT("RewritePartition %d\n", PartitionInfo.RewritePartition);
138 DPRINT("RecognizedPartition %d\n", PartitionInfo.RecognizedPartition);
139
140 if (Callback != NULL)
141 {
142 Context.Percent = 0;
143 Callback (PROGRESS, 0, (PVOID)&Context.Percent);
144 }
145
146 if (PartitionInfo.PartitionType == PARTITION_FAT_12)
147 {
148 /* FAT12 */
149 Status = Fat12Format(FileHandle,
150 &PartitionInfo,
151 &DiskGeometry,
152 Label,
153 QuickFormat,
154 ClusterSize,
155 &Context);
156 }
157 else if (PartitionInfo.PartitionType == PARTITION_FAT_16 ||
158 PartitionInfo.PartitionType == PARTITION_HUGE ||
159 PartitionInfo.PartitionType == PARTITION_XINT13)
160 {
161 /* FAT16 */
162 Status = Fat16Format(FileHandle,
163 &PartitionInfo,
164 &DiskGeometry,
165 Label,
166 QuickFormat,
167 ClusterSize,
168 &Context);
169 }
170 else if (PartitionInfo.PartitionType == PARTITION_FAT32 ||
171 PartitionInfo.PartitionType == PARTITION_FAT32_XINT13)
172 {
173 /* FAT32 */
174 Status = Fat32Format(FileHandle,
175 &PartitionInfo,
176 &DiskGeometry,
177 Label,
178 QuickFormat,
179 ClusterSize,
180 &Context);
181 }
182 else
183 {
184 Status = STATUS_INVALID_PARAMETER;
185 }
186
187 NtClose(FileHandle);
188
189 if (Callback != NULL)
190 {
191 Context.Success = (BOOLEAN)(NT_SUCCESS(Status));
192 Callback (DONE, 0, (PVOID)&Context.Success);
193 }
194
195 DPRINT("VfatFormat() done. Status 0x%.08x\n", Status);
196
197 return Status;
198 }
199
200
201 VOID
202 UpdateProgress(PFORMAT_CONTEXT Context,
203 ULONG Increment)
204 {
205 ULONG NewPercent;
206
207 Context->CurrentSectorCount += (ULONGLONG)Increment;
208
209
210 NewPercent = (Context->CurrentSectorCount * 100ULL) / Context->TotalSectorCount;
211
212 if (NewPercent > Context->Percent)
213 {
214 Context->Percent = NewPercent;
215 Context->Callback (PROGRESS, 0, &Context->Percent);
216 }
217 }
218
219
220 VOID
221 VfatPrint(PCHAR Format, ...)
222 {
223 TEXTOUTPUT TextOut;
224 CHAR TextBuf[512];
225 va_list valist;
226
227 va_start(valist, Format);
228 _vsnprintf(TextBuf, sizeof(TextBuf), Format, valist);
229 va_end(valist);
230
231 /* Prepare parameters */
232 TextOut.Lines = 1;
233 TextOut.Output = TextBuf;
234
235 /* Do the callback */
236 if (ChkdskCallback)
237 ChkdskCallback(OUTPUT, 0, &TextOut);
238 }
239
240
241 NTSTATUS
242 WINAPI
243 VfatChkdsk(IN PUNICODE_STRING DriveRoot,
244 IN BOOLEAN FixErrors,
245 IN BOOLEAN Verbose,
246 IN BOOLEAN CheckOnlyIfDirty,
247 IN BOOLEAN ScanDrive,
248 IN PFMIFSCALLBACK Callback)
249 {
250 BOOLEAN verify, salvage_files;
251 //ULONG free_clusters;
252 //DOS_FS fs;
253
254 /* Store callback pointer */
255 ChkdskCallback = Callback;
256 FsCheckMemQueue = NULL;
257
258 /* Set parameters */
259 FsCheckFlags = 0;
260 if (Verbose)
261 FsCheckFlags |= FSCHECK_VERBOSE;
262
263 FsCheckTotalFiles = 0;
264
265 verify = TRUE;
266 salvage_files = TRUE;
267 #if 0
268 /* Open filesystem */
269 fs_open(DriveRoot,FixErrors);
270
271 if (CheckOnlyIfDirty && !fs_isdirty())
272 {
273 /* No need to check FS */
274 return fs_close(FALSE);
275 }
276
277 read_boot(&fs);
278 if (verify)
279 VfatPrint("Starting check/repair pass.\n");
280
281 while (read_fat(&fs), scan_root(&fs))
282 qfree(&FsCheckMemQueue);
283
284 if (ScanDrive)
285 fix_bad(&fs);
286
287 if (salvage_files)
288 reclaim_file(&fs);
289 else
290 reclaim_free(&fs);
291
292 free_clusters = update_free(&fs);
293 file_unused();
294 qfree(&FsCheckMemQueue);
295 if (verify)
296 {
297 VfatPrint("Starting verification pass.\n");
298 read_fat(&fs);
299 scan_root(&fs);
300 reclaim_free(&fs);
301 qfree(&FsCheckMemQueue);
302 }
303
304 if (fs_changed())
305 {
306 if (FixErrors)
307 {
308 if (FsCheckFlags & FSCHECK_INTERACTIVE)
309 FixErrors = get_key("yn","Perform changes ? (y/n)") == 'y';
310 else
311 VfatPrint("Performing changes.\n");
312 }
313 else
314 {
315 VfatPrint("Leaving file system unchanged.\n");
316 }
317 }
318
319 VfatPrint("%wZ: %u files, %lu/%lu clusters\n", DriveRoot,
320 FsCheckTotalFiles, fs.clusters - free_clusters, fs.clusters );
321
322 if (FixErrors)
323 {
324 /* Dismount the volume */
325 fs_dismount();
326
327 /* Unlock the volume */
328 fs_lock(FALSE);
329 }
330
331 /* Close the volume */
332 return fs_close(FixErrors) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
333 #else
334 return STATUS_SUCCESS;
335 #endif
336 }
337
338 /* EOF */