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