10104668d056351749654f48cd89deeb85235617
[reactos.git] / reactos / lib / fslib / vfatxlib / fatx.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS VFATX filesystem library
4 * FILE: fatx.c
5 * PURPOSE: Fatx support
6 * PROGRAMMERS:
7 * REVISIONS:
8 */
9 #include "vfatxlib.h"
10
11 #define NDEBUG
12 #include <debug.h>
13
14 static ULONG
15 GetShiftCount(IN ULONG Value)
16 {
17 ULONG i = 1;
18
19 while (Value > 0)
20 {
21 i++;
22 Value /= 2;
23 }
24
25 return i - 2;
26 }
27
28
29 static ULONG
30 CalcVolumeSerialNumber(VOID)
31 {
32 LARGE_INTEGER SystemTime;
33 TIME_FIELDS TimeFields;
34 ULONG Serial;
35 PUCHAR Buffer;
36
37 NtQuerySystemTime (&SystemTime);
38 RtlTimeToTimeFields (&SystemTime, &TimeFields);
39
40 Buffer = (PUCHAR)&Serial;
41 Buffer[0] = (UCHAR)(TimeFields.Year & 0xFF) + (UCHAR)(TimeFields.Hour & 0xFF);
42 Buffer[1] = (UCHAR)(TimeFields.Year >> 8) + (UCHAR)(TimeFields.Minute & 0xFF);
43 Buffer[2] = (UCHAR)(TimeFields.Month & 0xFF) + (UCHAR)(TimeFields.Second & 0xFF);
44 Buffer[3] = (UCHAR)(TimeFields.Day & 0xFF) + (UCHAR)(TimeFields.Milliseconds & 0xFF);
45
46 return Serial;
47 }
48
49
50 static NTSTATUS
51 FatxWriteBootSector(IN HANDLE FileHandle,
52 IN PFATX_BOOT_SECTOR BootSector,
53 IN OUT PFORMAT_CONTEXT Context)
54 {
55 IO_STATUS_BLOCK IoStatusBlock;
56 NTSTATUS Status;
57 PUCHAR NewBootSector;
58 LARGE_INTEGER FileOffset;
59
60 /* Allocate buffer for new bootsector */
61 NewBootSector = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
62 0,
63 sizeof(FATX_BOOT_SECTOR));
64 if (NewBootSector == NULL)
65 return STATUS_INSUFFICIENT_RESOURCES;
66
67 /* Zero the new bootsector */
68 memset(NewBootSector, 0, sizeof(FATX_BOOT_SECTOR));
69
70 /* Copy FAT16 BPB to new bootsector */
71 memcpy(NewBootSector, BootSector, 18); /* FAT16 BPB length (up to (not including) Res2) */
72
73 /* Write sector 0 */
74 FileOffset.QuadPart = 0ULL;
75 Status = NtWriteFile(FileHandle,
76 NULL,
77 NULL,
78 NULL,
79 &IoStatusBlock,
80 NewBootSector,
81 sizeof(FATX_BOOT_SECTOR),
82 &FileOffset,
83 NULL);
84 if (!NT_SUCCESS(Status))
85 {
86 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
87 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
88 return Status;
89 }
90
91 VfatxUpdateProgress(Context, 1);
92
93 /* Free the new boot sector */
94 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
95
96 return Status;
97 }
98
99
100 static NTSTATUS
101 Fatx16WriteFAT(IN HANDLE FileHandle,
102 IN ULONG SectorOffset,
103 IN ULONG FATSectors,
104 IN OUT PFORMAT_CONTEXT Context)
105 {
106 IO_STATUS_BLOCK IoStatusBlock;
107 NTSTATUS Status;
108 PUCHAR Buffer;
109 LARGE_INTEGER FileOffset;
110 ULONG i;
111 ULONG Sectors;
112
113 /* Allocate buffer */
114 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
115 0,
116 32 * 1024);
117 if (Buffer == NULL)
118 return STATUS_INSUFFICIENT_RESOURCES;
119
120 /* Zero the buffer */
121 memset(Buffer, 0, 32 * 1024);
122
123 /* FAT cluster 0 */
124 Buffer[0] = 0xf8; /* Media type */
125 Buffer[1] = 0xff;
126
127 /* FAT cluster 1 */
128 Buffer[2] = 0xff; /* Clean shutdown, no disk read/write errors, end-of-cluster (EOC) mark */
129 Buffer[3] = 0xff;
130
131 /* Write first sector of the FAT */
132 FileOffset.QuadPart = (SectorOffset * 512) + sizeof(FATX_BOOT_SECTOR);
133 Status = NtWriteFile(FileHandle,
134 NULL,
135 NULL,
136 NULL,
137 &IoStatusBlock,
138 Buffer,
139 512,
140 &FileOffset,
141 NULL);
142 if (!NT_SUCCESS(Status))
143 {
144 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
145 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
146 return Status;
147 }
148
149 VfatxUpdateProgress(Context, 1);
150
151 /* Zero the begin of the buffer */
152 memset(Buffer, 0, 4);
153
154 /* Zero the rest of the FAT */
155 Sectors = 32 * 1024 / 512;
156 for (i = 1; i < FATSectors; i += Sectors)
157 {
158 /* Zero some sectors of the FAT */
159 FileOffset.QuadPart = (SectorOffset + i) * 512 + sizeof(FATX_BOOT_SECTOR) ;
160 if ((FATSectors - i) <= Sectors)
161 {
162 Sectors = FATSectors - i;
163 }
164
165 Status = NtWriteFile(FileHandle,
166 NULL,
167 NULL,
168 NULL,
169 &IoStatusBlock,
170 Buffer,
171 Sectors * 512,
172 &FileOffset,
173 NULL);
174 if (!NT_SUCCESS(Status))
175 {
176 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
177 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
178 return Status;
179 }
180
181 VfatxUpdateProgress(Context, Sectors);
182 }
183
184 /* Free the buffer */
185 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
186
187 return Status;
188 }
189
190 static NTSTATUS
191 Fatx32WriteFAT(IN HANDLE FileHandle,
192 IN ULONG SectorOffset,
193 IN ULONG FATSectors,
194 IN OUT PFORMAT_CONTEXT Context)
195 {
196 IO_STATUS_BLOCK IoStatusBlock;
197 NTSTATUS Status;
198 PUCHAR Buffer;
199 LARGE_INTEGER FileOffset;
200 ULONG i;
201 ULONG Sectors;
202
203 /* Allocate buffer */
204 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
205 0,
206 64 * 1024);
207 if (Buffer == NULL)
208 return STATUS_INSUFFICIENT_RESOURCES;
209
210 /* Zero the buffer */
211 memset(Buffer, 0, 64 * 1024);
212
213 /* FAT cluster 0 */
214 Buffer[0] = 0xf8; /* Media type */
215 Buffer[1] = 0xff;
216 Buffer[2] = 0xff;
217 Buffer[3] = 0x0f;
218
219 /* FAT cluster 1 */
220 Buffer[4] = 0xff; /* Clean shutdown, no disk read/write errors, end-of-cluster (EOC) mark */
221 Buffer[5] = 0xff;
222 Buffer[6] = 0xff;
223 Buffer[7] = 0x0f;
224
225 /* Write first sector of the FAT */
226 FileOffset.QuadPart = (SectorOffset * 512) + sizeof(FATX_BOOT_SECTOR);
227 Status = NtWriteFile(FileHandle,
228 NULL,
229 NULL,
230 NULL,
231 &IoStatusBlock,
232 Buffer,
233 512,
234 &FileOffset,
235 NULL);
236 if (!NT_SUCCESS(Status))
237 {
238 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
239 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
240 return Status;
241 }
242
243 VfatxUpdateProgress(Context, 1);
244
245 /* Zero the begin of the buffer */
246 memset(Buffer, 0, 8);
247
248 /* Zero the rest of the FAT */
249 Sectors = 64 * 1024 / 512;
250 for (i = 1; i < FATSectors; i += Sectors)
251 {
252 /* Zero some sectors of the FAT */
253 FileOffset.QuadPart = (SectorOffset + i) * 512 + sizeof(FATX_BOOT_SECTOR);
254
255 if ((FATSectors - i) <= Sectors)
256 {
257 Sectors = FATSectors - i;
258 }
259
260 Status = NtWriteFile(FileHandle,
261 NULL,
262 NULL,
263 NULL,
264 &IoStatusBlock,
265 Buffer,
266 Sectors * 512,
267 &FileOffset,
268 NULL);
269 if (!NT_SUCCESS(Status))
270 {
271 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
272 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
273 return Status;
274 }
275
276 VfatxUpdateProgress(Context, Sectors);
277 }
278
279 /* Free the buffer */
280 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
281
282 return Status;
283 }
284
285 static NTSTATUS
286 FatxWriteRootDirectory(IN HANDLE FileHandle,
287 IN ULONG FATSectors,
288 IN OUT PFORMAT_CONTEXT Context)
289 {
290 IO_STATUS_BLOCK IoStatusBlock;
291 NTSTATUS Status = STATUS_SUCCESS;
292 PUCHAR Buffer;
293 LARGE_INTEGER FileOffset;
294 ULONG FirstRootDirSector;
295 ULONG RootDirSectors;
296
297 /* Write cluster */
298 RootDirSectors = 256 * 64 / 512;
299 FirstRootDirSector = sizeof(FATX_BOOT_SECTOR) / 512 + FATSectors;
300
301 DPRINT("RootDirSectors = %lu\n", RootDirSectors);
302 DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector);
303
304 /* Allocate buffer for the cluster */
305 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
306 0,
307 RootDirSectors * 512);
308 if (Buffer == NULL)
309 return STATUS_INSUFFICIENT_RESOURCES;
310
311 /* Zero the buffer */
312 memset(Buffer, 0xff, RootDirSectors * 512);
313
314 /* Zero some sectors of the root directory */
315 FileOffset.QuadPart = FirstRootDirSector * 512;
316
317 Status = NtWriteFile(FileHandle,
318 NULL,
319 NULL,
320 NULL,
321 &IoStatusBlock,
322 Buffer,
323 RootDirSectors * 512,
324 &FileOffset,
325 NULL);
326 if (!NT_SUCCESS(Status))
327 {
328 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
329 }
330
331 /* Free the buffer */
332 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
333
334 return Status;
335 }
336
337
338 NTSTATUS
339 FatxFormat(IN HANDLE FileHandle,
340 IN PPARTITION_INFORMATION PartitionInfo,
341 IN PDISK_GEOMETRY DiskGeometry,
342 IN BOOLEAN QuickFormat,
343 IN OUT PFORMAT_CONTEXT Context)
344 {
345 FATX_BOOT_SECTOR BootSector;
346 ULONGLONG SectorCount;
347 ULONG ClusterCount;
348 ULONG RootDirSectors;
349 ULONG FATSectors;
350 NTSTATUS Status;
351
352 SectorCount = PartitionInfo->PartitionLength.QuadPart >> GetShiftCount(512); /* Use shifting to avoid 64-bit division */
353
354 memset(&BootSector, 0, sizeof(FATX_BOOT_SECTOR));
355 memcpy(&BootSector.SysType[0], "FATX", 4);
356 BootSector.SectorsPerCluster = 32;
357 BootSector.FATCount = 1;
358 BootSector.VolumeID = CalcVolumeSerialNumber();
359 RootDirSectors = 256 * 64 / 512;
360
361 /* Calculate number of FAT sectors */
362 ClusterCount = SectorCount >> GetShiftCount(32);
363
364 if (ClusterCount > 65525)
365 {
366 FATSectors = (((ClusterCount * 4) + 4095) & ~4095) >> GetShiftCount(512);
367 }
368 else
369 {
370 FATSectors = (((ClusterCount * 2) + 4095) & ~4095) >> GetShiftCount(512);
371 }
372 DPRINT("FATSectors = %hu\n", FATSectors);
373
374 /* Init context data */
375 if (QuickFormat)
376 {
377 Context->TotalSectorCount =
378 1 + FATSectors + RootDirSectors;
379 }
380 else
381 {
382 Context->TotalSectorCount = SectorCount;
383 }
384
385 Status = FatxWriteBootSector(FileHandle,
386 &BootSector,
387 Context);
388 if (!NT_SUCCESS(Status))
389 {
390 DPRINT("FatxWriteBootSector() failed with status 0x%.08x\n", Status);
391 return Status;
392 }
393
394 /* Write first FAT copy */
395 if (ClusterCount > 65525)
396 {
397 Status = Fatx32WriteFAT(FileHandle,
398 0,
399 FATSectors,
400 Context);
401 }
402 else
403 {
404 Status = Fatx16WriteFAT(FileHandle,
405 0,
406 FATSectors,
407 Context);
408 }
409
410 if (!NT_SUCCESS(Status))
411 {
412 DPRINT("FatxWriteFAT() failed with status 0x%.08x\n", Status);
413 return Status;
414 }
415
416 Status = FatxWriteRootDirectory(FileHandle,
417 FATSectors,
418 Context);
419 if (!NT_SUCCESS(Status))
420 {
421 DPRINT("FatxWriteRootDirectory() failed with status 0x%.08x\n", Status);
422 }
423
424 if (!QuickFormat)
425 {
426 /* FIXME: Fill remaining sectors */
427 }
428
429 return Status;
430 }