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
GetShiftCount(IN ULONG Value)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
CalcVolumeSerialNumber(VOID)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
FatxWriteBootSector(IN HANDLE FileHandle,IN PFATX_BOOT_SECTOR BootSector,IN OUT PFORMAT_CONTEXT Context)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
Fatx16WriteFAT(IN HANDLE FileHandle,IN ULONG SectorOffset,IN ULONG FATSectors,IN OUT PFORMAT_CONTEXT Context)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
Fatx32WriteFAT(IN HANDLE FileHandle,IN ULONG SectorOffset,IN ULONG FATSectors,IN OUT PFORMAT_CONTEXT Context)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
FatxWriteRootDirectory(IN HANDLE FileHandle,IN ULONG FATSectors,IN OUT PFORMAT_CONTEXT Context)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
FatxFormat(IN HANDLE FileHandle,IN PPARTITION_INFORMATION PartitionInfo,IN PDISK_GEOMETRY DiskGeometry,IN BOOLEAN QuickFormat,IN OUT PFORMAT_CONTEXT Context)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