13c2a2e06bb4625a55812b03d459b5794baa1413
[reactos.git] / reactos / lib / fslib / vfatlib / fat32.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS VFAT filesystem library
4 * FILE: fat32.c
5 * PURPOSE: Fat32 support
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Eric Kohl (ekohl@rz-online.de)
8 * REVISIONS:
9 * EK 05/04-2003 Created
10 */
11 #include "vfatlib.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 static ULONG
17 GetShiftCount(ULONG Value)
18 {
19 ULONG i = 1;
20 while (Value > 0)
21 {
22 i++;
23 Value /= 2;
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 Fat32WriteBootSector (IN HANDLE FileHandle,
52 IN PFAT32_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 SECTORSIZE);
64 if (NewBootSector == NULL)
65 return(STATUS_INSUFFICIENT_RESOURCES);
66
67 /* Zero the new bootsector */
68 memset(NewBootSector, 0, SECTORSIZE);
69
70 /* Copy FAT32 BPB to new bootsector */
71 memcpy((NewBootSector + 3),
72 &BootSector->OEMName[0],
73 87); /* FAT32 BPB length (up to (not including) Res2) */
74
75 /* Write sector 0 */
76 FileOffset.QuadPart = 0ULL;
77 Status = NtWriteFile(FileHandle,
78 NULL,
79 NULL,
80 NULL,
81 &IoStatusBlock,
82 NewBootSector,
83 SECTORSIZE,
84 &FileOffset,
85 NULL);
86 if (!NT_SUCCESS(Status))
87 {
88 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
89 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
90 return Status;
91 }
92
93 UpdateProgress (Context, 1);
94
95 /* Write backup boot sector */
96 if (BootSector->BootBackup != 0x0000)
97 {
98 FileOffset.QuadPart = (ULONGLONG)((ULONG) BootSector->BootBackup * SECTORSIZE);
99 Status = NtWriteFile(FileHandle,
100 NULL,
101 NULL,
102 NULL,
103 &IoStatusBlock,
104 NewBootSector,
105 SECTORSIZE,
106 &FileOffset,
107 NULL);
108 if (!NT_SUCCESS(Status))
109 {
110 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
111 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
112 return Status;
113 }
114
115 UpdateProgress (Context, 1);
116 }
117
118 /* Free the new boot sector */
119 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
120
121 return Status;
122 }
123
124
125 static NTSTATUS
126 Fat32WriteFsInfo (IN HANDLE FileHandle,
127 IN PFAT32_BOOT_SECTOR BootSector,
128 IN OUT PFORMAT_CONTEXT Context)
129 {
130 IO_STATUS_BLOCK IoStatusBlock;
131 NTSTATUS Status;
132 PFAT32_FSINFO FsInfo;
133 LARGE_INTEGER FileOffset;
134
135 /* Allocate buffer for new sector */
136 FsInfo = (PFAT32_FSINFO)RtlAllocateHeap(RtlGetProcessHeap(),
137 0,
138 BootSector->BytesPerSector);
139 if (FsInfo == NULL)
140 return(STATUS_INSUFFICIENT_RESOURCES);
141
142 /* Zero the new sector */
143 memset(FsInfo, 0, BootSector->BytesPerSector);
144
145 FsInfo->LeadSig = 0x41615252;
146 FsInfo->StrucSig = 0x61417272;
147 FsInfo->FreeCount = 0xffffffff;
148 FsInfo->NextFree = 0xffffffff;
149 FsInfo->TrailSig = 0xaa550000;
150
151 /* Write sector */
152 FileOffset.QuadPart = BootSector->FSInfoSector * BootSector->BytesPerSector;
153 Status = NtWriteFile(FileHandle,
154 NULL,
155 NULL,
156 NULL,
157 &IoStatusBlock,
158 FsInfo,
159 BootSector->BytesPerSector,
160 &FileOffset,
161 NULL);
162 if (!NT_SUCCESS(Status))
163 {
164 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
165 RtlFreeHeap(RtlGetProcessHeap(), 0, FsInfo);
166 return(Status);
167 }
168
169 UpdateProgress (Context, 1);
170
171 /* Free the new sector buffer */
172 RtlFreeHeap(RtlGetProcessHeap(), 0, FsInfo);
173
174 return(Status);
175 }
176
177
178 static NTSTATUS
179 Fat32WriteFAT (IN HANDLE FileHandle,
180 IN ULONG SectorOffset,
181 IN PFAT32_BOOT_SECTOR BootSector,
182 IN OUT PFORMAT_CONTEXT Context)
183 {
184 IO_STATUS_BLOCK IoStatusBlock;
185 NTSTATUS Status;
186 PUCHAR Buffer;
187 LARGE_INTEGER FileOffset;
188 ULONG i;
189 ULONG Sectors;
190
191 /* Allocate buffer */
192 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
193 0,
194 64 * 1024);
195 if (Buffer == NULL)
196 return(STATUS_INSUFFICIENT_RESOURCES);
197
198 /* Zero the buffer */
199 memset(Buffer, 0, 64 * 1024);
200
201 /* FAT cluster 0 */
202 Buffer[0] = 0xf8; /* Media type */
203 Buffer[1] = 0xff;
204 Buffer[2] = 0xff;
205 Buffer[3] = 0x0f;
206 /* FAT cluster 1 */
207 Buffer[4] = 0xff; /* Clean shutdown, no disk read/write errors, end-of-cluster (EOC) mark */
208 Buffer[5] = 0xff;
209 Buffer[6] = 0xff;
210 Buffer[7] = 0x0f;
211 /* FAT cluster 2 */
212 Buffer[8] = 0xff; /* End of root directory */
213 Buffer[9] = 0xff;
214 Buffer[10] = 0xff;
215 Buffer[11] = 0x0f;
216
217 /* Write first sector of the FAT */
218 FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors) * BootSector->BytesPerSector;
219 Status = NtWriteFile(FileHandle,
220 NULL,
221 NULL,
222 NULL,
223 &IoStatusBlock,
224 Buffer,
225 BootSector->BytesPerSector,
226 &FileOffset,
227 NULL);
228 if (!NT_SUCCESS(Status))
229 {
230 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
231 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
232 return(Status);
233 }
234
235 UpdateProgress (Context, 1);
236
237 /* Zero the begin of the buffer */
238 memset(Buffer, 0, 12);
239
240 /* Zero the rest of the FAT */
241 Sectors = 64 * 1024 / BootSector->BytesPerSector;
242 for (i = 1; i < BootSector->FATSectors32; i += Sectors)
243 {
244 /* Zero some sectors of the FAT */
245 FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector;
246
247 if ((BootSector->FATSectors32 - i) <= Sectors)
248 {
249 Sectors = BootSector->FATSectors32 - i;
250 }
251
252 Status = NtWriteFile(FileHandle,
253 NULL,
254 NULL,
255 NULL,
256 &IoStatusBlock,
257 Buffer,
258 Sectors * BootSector->BytesPerSector,
259 &FileOffset,
260 NULL);
261 if (!NT_SUCCESS(Status))
262 {
263 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
264 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
265 return(Status);
266 }
267
268 UpdateProgress (Context, Sectors);
269 }
270
271 /* Free the buffer */
272 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
273
274 return(Status);
275 }
276
277
278 static NTSTATUS
279 Fat32WriteRootDirectory (IN HANDLE FileHandle,
280 IN PFAT32_BOOT_SECTOR BootSector,
281 IN OUT PFORMAT_CONTEXT Context)
282 {
283 IO_STATUS_BLOCK IoStatusBlock;
284 NTSTATUS Status;
285 PUCHAR Buffer;
286 LARGE_INTEGER FileOffset;
287 ULONGLONG FirstDataSector;
288 ULONGLONG FirstRootDirSector;
289
290 /* Allocate buffer for the cluster */
291 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
292 0,
293 BootSector->SectorsPerCluster * BootSector->BytesPerSector);
294 if (Buffer == NULL)
295 return(STATUS_INSUFFICIENT_RESOURCES);
296
297 /* Zero the buffer */
298 memset(Buffer, 0, BootSector->SectorsPerCluster * BootSector->BytesPerSector);
299
300 DPRINT("BootSector->ReservedSectors = %lu\n", BootSector->ReservedSectors);
301 DPRINT("BootSector->FATSectors32 = %lu\n", BootSector->FATSectors32);
302 DPRINT("BootSector->RootCluster = %lu\n", BootSector->RootCluster);
303 DPRINT("BootSector->SectorsPerCluster = %lu\n", BootSector->SectorsPerCluster);
304
305 /* Write cluster */
306 FirstDataSector = BootSector->ReservedSectors +
307 (BootSector->FATCount * BootSector->FATSectors32) + 0 /* RootDirSectors */;
308
309 DPRINT("FirstDataSector = %lu\n", FirstDataSector);
310
311 FirstRootDirSector = ((BootSector->RootCluster - 2) * BootSector->SectorsPerCluster) + FirstDataSector;
312 FileOffset.QuadPart = FirstRootDirSector * BootSector->BytesPerSector;
313
314 DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector);
315 DPRINT("FileOffset = %lu\n", FileOffset.QuadPart);
316
317 Status = NtWriteFile(FileHandle,
318 NULL,
319 NULL,
320 NULL,
321 &IoStatusBlock,
322 Buffer,
323 BootSector->SectorsPerCluster * BootSector->BytesPerSector,
324 &FileOffset,
325 NULL);
326 if (!NT_SUCCESS(Status))
327 {
328 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
329 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
330 return(Status);
331 }
332
333 UpdateProgress (Context, (ULONG)BootSector->SectorsPerCluster);
334
335 /* Free the buffer */
336 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
337
338 return(Status);
339 }
340
341
342 NTSTATUS
343 Fat32Format (HANDLE FileHandle,
344 PPARTITION_INFORMATION PartitionInfo,
345 PDISK_GEOMETRY DiskGeometry,
346 PUNICODE_STRING Label,
347 BOOLEAN QuickFormat,
348 ULONG ClusterSize,
349 PFORMAT_CONTEXT Context)
350 {
351 FAT32_BOOT_SECTOR BootSector;
352 OEM_STRING VolumeLabel;
353 ULONG RootDirSectors;
354 ULONG TmpVal1;
355 ULONG TmpVal2;
356 NTSTATUS Status;
357
358 /* Calculate cluster size */
359 if (ClusterSize == 0)
360 {
361 if (PartitionInfo->PartitionLength.QuadPart < 8LL * 1024LL * 1024LL * 1024LL)
362 {
363 /* Partition < 8GB ==> 4KB Cluster */
364 ClusterSize = 4096;
365 }
366 else if (PartitionInfo->PartitionLength.QuadPart < 16LL * 1024LL * 1024LL * 1024LL)
367 {
368 /* Partition 8GB - 16GB ==> 8KB Cluster */
369 ClusterSize = 8192;
370 }
371 else if (PartitionInfo->PartitionLength.QuadPart < 32LL * 1024LL * 1024LL * 1024LL)
372 {
373 /* Partition 16GB - 32GB ==> 16KB Cluster */
374 ClusterSize = 16384;
375 }
376 else
377 {
378 /* Partition >= 32GB ==> 32KB Cluster */
379 ClusterSize = 32768;
380 }
381 }
382
383 memset(&BootSector, 0, sizeof(FAT32_BOOT_SECTOR));
384 memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
385 BootSector.BytesPerSector = DiskGeometry->BytesPerSector;
386 BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector;
387 BootSector.ReservedSectors = 32;
388 BootSector.FATCount = 2;
389 BootSector.RootEntries = 0;
390 BootSector.Sectors = 0;
391 BootSector.Media = 0xf8;
392 BootSector.FATSectors = 0;
393 BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack;
394 BootSector.Heads = DiskGeometry->TracksPerCylinder;
395 BootSector.HiddenSectors = PartitionInfo->HiddenSectors;
396 BootSector.SectorsHuge = PartitionInfo->PartitionLength.QuadPart >>
397 GetShiftCount(BootSector.BytesPerSector); /* Use shifting to avoid 64-bit division */
398 BootSector.FATSectors32 = 0; /* Set later */
399 BootSector.ExtFlag = 0; /* Mirror all FATs */
400 BootSector.FSVersion = 0x0000; /* 0:0 */
401 BootSector.RootCluster = 2;
402 BootSector.FSInfoSector = 1;
403 BootSector.BootBackup = 6;
404 BootSector.Drive = 0xff; /* No BIOS boot drive available */
405 BootSector.ExtBootSignature = 0x29;
406 BootSector.VolumeID = CalcVolumeSerialNumber ();
407 if ((Label == NULL) || (Label->Buffer == NULL))
408 {
409 memcpy(&BootSector.VolumeLabel[0], "NO NAME ", 11);
410 }
411 else
412 {
413 RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE);
414 memset(&BootSector.VolumeLabel[0], ' ', 11);
415 memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
416 VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
417 RtlFreeOemString(&VolumeLabel);
418 }
419 memcpy(&BootSector.SysType[0], "FAT32 ", 8);
420
421 RootDirSectors = ((BootSector.RootEntries * 32) +
422 (BootSector.BytesPerSector - 1)) / BootSector.BytesPerSector;
423
424 /* Calculate number of FAT sectors */
425 /* (BytesPerSector / 4) FAT entries (32bit) fit into one sector */
426 TmpVal1 = BootSector.SectorsHuge - BootSector.ReservedSectors;
427 TmpVal2 = ((BootSector.BytesPerSector / 4) * BootSector.SectorsPerCluster) + BootSector.FATCount;
428 BootSector.FATSectors32 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
429 DPRINT("FATSectors32 = %lu\n", BootSector.FATSectors32);
430
431 /* Init context data */
432 Context->TotalSectorCount =
433 2 + (BootSector.FATSectors32 * BootSector.FATCount) + BootSector.SectorsPerCluster;
434
435 Status = Fat32WriteBootSector (FileHandle,
436 &BootSector,
437 Context);
438 if (!NT_SUCCESS(Status))
439 {
440 DPRINT("Fat32WriteBootSector() failed with status 0x%.08x\n", Status);
441 return Status;
442 }
443
444 Status = Fat32WriteFsInfo (FileHandle,
445 &BootSector,
446 Context);
447 if (!NT_SUCCESS(Status))
448 {
449 DPRINT("Fat32WriteFsInfo() failed with status 0x%.08x\n", Status);
450 return Status;
451 }
452
453 /* Write first FAT copy */
454 Status = Fat32WriteFAT (FileHandle,
455 0,
456 &BootSector,
457 Context);
458 if (!NT_SUCCESS(Status))
459 {
460 DPRINT("Fat32WriteFAT() failed with status 0x%.08x\n", Status);
461 return Status;
462 }
463
464 /* Write second FAT copy */
465 Status = Fat32WriteFAT (FileHandle,
466 BootSector.FATSectors32,
467 &BootSector,
468 Context);
469 if (!NT_SUCCESS(Status))
470 {
471 DPRINT("Fat32WriteFAT() failed with status 0x%.08x.\n", Status);
472 return Status;
473 }
474
475 Status = Fat32WriteRootDirectory (FileHandle,
476 &BootSector,
477 Context);
478 if (!NT_SUCCESS(Status))
479 {
480 DPRINT("Fat32WriteRootDirectory() failed with status 0x%.08x\n", Status);
481 }
482
483 if (!QuickFormat)
484 {
485 /* FIXME: Fill remaining sectors */
486 }
487
488 return Status;
489 }
490
491 /* EOF */