1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS VFAT filesystem library
4 * FILE: fat16.c
5 * PURPOSE: Fat16 support
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Eric Kohl
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include "vfatlib.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17
18 /* FUNCTIONS ******************************************************************/
19
20 static NTSTATUS
Fat16WriteBootSector(IN HANDLE FileHandle,IN PFAT16_BOOT_SECTOR BootSector,IN OUT PFORMAT_CONTEXT Context)21 Fat16WriteBootSector(IN HANDLE FileHandle,
22 IN PFAT16_BOOT_SECTOR BootSector,
23 IN OUT PFORMAT_CONTEXT Context)
24 {
25 IO_STATUS_BLOCK IoStatusBlock;
26 NTSTATUS Status;
27 PFAT16_BOOT_SECTOR NewBootSector;
28 LARGE_INTEGER FileOffset;
29
30 /* Allocate buffer for new bootsector */
31 NewBootSector = (PFAT16_BOOT_SECTOR)RtlAllocateHeap(RtlGetProcessHeap(),
32 0,
33 BootSector->BytesPerSector);
34 if (NewBootSector == NULL)
35 return STATUS_INSUFFICIENT_RESOURCES;
36
37 /* Zero the new bootsector */
38 RtlZeroMemory(NewBootSector, BootSector->BytesPerSector);
39
40 /* Copy FAT16 BPB to new bootsector */
41 memcpy(NewBootSector, BootSector,
42 FIELD_OFFSET(FAT16_BOOT_SECTOR, Res2) - FIELD_OFFSET(FAT16_BOOT_SECTOR, Jump));
43 /* FAT16 BPB length (up to (not including) Res2) */
44
45 /* Write the boot sector signature */
46 NewBootSector->Signature1 = 0xAA550000;
47
48 /* Write sector 0 */
49 FileOffset.QuadPart = 0ULL;
50 Status = NtWriteFile(FileHandle,
51 NULL,
52 NULL,
53 NULL,
54 &IoStatusBlock,
55 NewBootSector,
56 BootSector->BytesPerSector,
57 &FileOffset,
58 NULL);
59 if (!NT_SUCCESS(Status))
60 {
61 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
62 goto done;
63 }
64
65 UpdateProgress(Context, 1);
66
67 done:
68 /* Free the buffer */
69 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
70 return Status;
71 }
72
73
74 static NTSTATUS
Fat16WriteFAT(IN HANDLE FileHandle,IN ULONG SectorOffset,IN PFAT16_BOOT_SECTOR BootSector,IN OUT PFORMAT_CONTEXT Context)75 Fat16WriteFAT(IN HANDLE FileHandle,
76 IN ULONG SectorOffset,
77 IN PFAT16_BOOT_SECTOR BootSector,
78 IN OUT PFORMAT_CONTEXT Context)
79 {
80 IO_STATUS_BLOCK IoStatusBlock;
81 NTSTATUS Status;
82 PUCHAR Buffer;
83 LARGE_INTEGER FileOffset;
84 ULONG i;
85 ULONG Sectors;
86
87 /* Allocate buffer */
88 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
89 0,
90 32 * 1024);
91 if (Buffer == NULL)
92 return STATUS_INSUFFICIENT_RESOURCES;
93
94 /* Zero the buffer */
95 RtlZeroMemory(Buffer, 32 * 1024);
96
97 /* FAT cluster 0 */
98 Buffer[0] = 0xf8; /* Media type */
99 Buffer[1] = 0xff;
100
101 /* FAT cluster 1 */
102 Buffer[2] = 0xff; /* Clean shutdown, no disk read/write errors, end-of-cluster (EOC) mark */
103 Buffer[3] = 0xff;
104
105 /* Write first sector of the FAT */
106 FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors) * BootSector->BytesPerSector;
107 Status = NtWriteFile(FileHandle,
108 NULL,
109 NULL,
110 NULL,
111 &IoStatusBlock,
112 Buffer,
113 BootSector->BytesPerSector,
114 &FileOffset,
115 NULL);
116 if (!NT_SUCCESS(Status))
117 {
118 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
119 goto done;
120 }
121
122 UpdateProgress(Context, 1);
123
124 /* Zero the begin of the buffer */
125 RtlZeroMemory(Buffer, 4);
126
127 /* Zero the rest of the FAT */
128 Sectors = 32 * 1024 / BootSector->BytesPerSector;
129 for (i = 1; i < (ULONG)BootSector->FATSectors; i += Sectors)
130 {
131 /* Zero some sectors of the FAT */
132 FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector;
133
134 if (((ULONG)BootSector->FATSectors - i) <= Sectors)
135 {
136 Sectors = (ULONG)BootSector->FATSectors - i;
137 }
138
139 Status = NtWriteFile(FileHandle,
140 NULL,
141 NULL,
142 NULL,
143 &IoStatusBlock,
144 Buffer,
145 Sectors * BootSector->BytesPerSector,
146 &FileOffset,
147 NULL);
148 if (!NT_SUCCESS(Status))
149 {
150 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
151 goto done;
152 }
153
154 UpdateProgress(Context, Sectors);
155 }
156
157 done:
158 /* Free the buffer */
159 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
160 return Status;
161 }
162
163
164 static NTSTATUS
Fat16WriteRootDirectory(IN HANDLE FileHandle,IN PFAT16_BOOT_SECTOR BootSector,IN OUT PFORMAT_CONTEXT Context)165 Fat16WriteRootDirectory(IN HANDLE FileHandle,
166 IN PFAT16_BOOT_SECTOR BootSector,
167 IN OUT PFORMAT_CONTEXT Context)
168 {
169 IO_STATUS_BLOCK IoStatusBlock;
170 NTSTATUS Status = STATUS_SUCCESS;
171 PUCHAR Buffer;
172 LARGE_INTEGER FileOffset;
173 ULONG FirstRootDirSector;
174 ULONG RootDirSectors;
175 ULONG Sectors;
176 ULONG i;
177
178 DPRINT("BootSector->ReservedSectors = %hu\n", BootSector->ReservedSectors);
179 DPRINT("BootSector->FATSectors = %hu\n", BootSector->FATSectors);
180 DPRINT("BootSector->SectorsPerCluster = %u\n", BootSector->SectorsPerCluster);
181
182 /* Write cluster */
183 RootDirSectors = ((BootSector->RootEntries * 32) +
184 (BootSector->BytesPerSector - 1)) / BootSector->BytesPerSector;
185 FirstRootDirSector =
186 BootSector->ReservedSectors + (BootSector->FATCount * BootSector->FATSectors);
187
188 DPRINT("RootDirSectors = %lu\n", RootDirSectors);
189 DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector);
190
191 /* Allocate buffer for the cluster */
192 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
193 0,
194 32 * 1024);
195 if (Buffer == NULL)
196 return STATUS_INSUFFICIENT_RESOURCES;
197
198 /* Zero the buffer */
199 RtlZeroMemory(Buffer, 32 * 1024);
200
201 Sectors = 32 * 1024 / BootSector->BytesPerSector;
202 for (i = 0; i < RootDirSectors; i += Sectors)
203 {
204 /* Zero some sectors of the root directory */
205 FileOffset.QuadPart = (FirstRootDirSector + i) * BootSector->BytesPerSector;
206
207 if ((RootDirSectors - i) <= Sectors)
208 {
209 Sectors = RootDirSectors - i;
210 }
211
212 Status = NtWriteFile(FileHandle,
213 NULL,
214 NULL,
215 NULL,
216 &IoStatusBlock,
217 Buffer,
218 Sectors * BootSector->BytesPerSector,
219 &FileOffset,
220 NULL);
221 if (!NT_SUCCESS(Status))
222 {
223 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
224 goto done;
225 }
226
227 UpdateProgress(Context, Sectors);
228 }
229
230 done:
231 /* Free the buffer */
232 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
233 return Status;
234 }
235
236
237 NTSTATUS
Fat16Format(IN HANDLE FileHandle,IN PPARTITION_INFORMATION PartitionInfo,IN PDISK_GEOMETRY DiskGeometry,IN PUNICODE_STRING Label,IN BOOLEAN QuickFormat,IN ULONG ClusterSize,IN OUT PFORMAT_CONTEXT Context)238 Fat16Format(IN HANDLE FileHandle,
239 IN PPARTITION_INFORMATION PartitionInfo,
240 IN PDISK_GEOMETRY DiskGeometry,
241 IN PUNICODE_STRING Label,
242 IN BOOLEAN QuickFormat,
243 IN ULONG ClusterSize,
244 IN OUT PFORMAT_CONTEXT Context)
245 {
246 FAT16_BOOT_SECTOR BootSector;
247 OEM_STRING VolumeLabel;
248 ULONG SectorCount;
249 ULONG RootDirSectors;
250 ULONG TmpVal1;
251 ULONG TmpVal2;
252 ULONG TmpVal3;
253 NTSTATUS Status;
254
255 /* Calculate cluster size */
256 if (ClusterSize == 0)
257 {
258 if (PartitionInfo->PartitionLength.QuadPart < 16LL * 1024LL * 1024LL)
259 {
260 /* Partition < 16MB ==> 1KB Cluster */
261 ClusterSize = 1024;
262 }
263 else if (PartitionInfo->PartitionLength.QuadPart < 128LL * 1024LL * 1024LL)
264 {
265 /* Partition < 128MB ==> 2KB Cluster */
266 ClusterSize = 2048;
267 }
268 else if (PartitionInfo->PartitionLength.QuadPart < 256LL * 1024LL * 1024LL)
269 {
270 /* Partition < 256MB ==> 4KB Cluster */
271 ClusterSize = 4096;
272 }
273 else
274 {
275 /* Partition >= 256MB (< 512MB) ==> 8KB Cluster */
276 ClusterSize = 8192;
277 }
278 }
279
280 SectorCount = PartitionInfo->PartitionLength.QuadPart >>
281 GetShiftCount(DiskGeometry->BytesPerSector); /* Use shifting to avoid 64-bit division */
282
283 RtlZeroMemory(&BootSector, sizeof(FAT16_BOOT_SECTOR));
284 memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
285 /* FIXME: Add dummy bootloader for real */
286 BootSector.Jump[0] = 0xeb;
287 BootSector.Jump[1] = 0x3c;
288 BootSector.Jump[2] = 0x90;
289 BootSector.BytesPerSector = DiskGeometry->BytesPerSector;
290 BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector;
291 BootSector.ReservedSectors = 1;
292 BootSector.FATCount = 2;
293 BootSector.RootEntries = 512;
294 BootSector.Sectors = (SectorCount < 0x10000) ? (unsigned short)SectorCount : 0;
295 BootSector.Media = 0xf8;
296 BootSector.FATSectors = 0; /* Set later. See below. */
297 BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack;
298 BootSector.Heads = DiskGeometry->TracksPerCylinder;
299 BootSector.HiddenSectors = PartitionInfo->HiddenSectors;
300 BootSector.SectorsHuge = (SectorCount >= 0x10000) ? (unsigned long)SectorCount : 0;
301 BootSector.Drive = (DiskGeometry->MediaType == FixedMedia) ? 0x80 : 0x00;
302 BootSector.ExtBootSignature = 0x29;
303 BootSector.VolumeID = CalcVolumeSerialNumber();
304 if ((Label == NULL) || (Label->Buffer == NULL))
305 {
306 memcpy(&BootSector.VolumeLabel[0], "NO NAME ", 11);
307 }
308 else
309 {
310 RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE);
311 RtlFillMemory(&BootSector.VolumeLabel[0], 11, ' ');
312 memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
313 VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
314 RtlFreeOemString(&VolumeLabel);
315 }
316
317 memcpy(&BootSector.SysType[0], "FAT16 ", 8);
318
319 DPRINT("BootSector.SectorsHuge = %lx\n", BootSector.SectorsHuge);
320
321 RootDirSectors = ((BootSector.RootEntries * 32) +
322 (BootSector.BytesPerSector - 1)) / BootSector.BytesPerSector;
323
324 /* Calculate number of FAT sectors */
325 /* (BootSector.BytesPerSector / 2) FAT entries (16bit) fit into one sector */
326 TmpVal1 = SectorCount - (BootSector.ReservedSectors + RootDirSectors);
327 TmpVal2 = ((BootSector.BytesPerSector / 2) * BootSector.SectorsPerCluster) + BootSector.FATCount;
328 TmpVal3 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
329 BootSector.FATSectors = (unsigned short)(TmpVal3 & 0xffff);
330 DPRINT("BootSector.FATSectors = %hu\n", BootSector.FATSectors);
331
332 /* Init context data */
333 Context->TotalSectorCount =
334 1 + (BootSector.FATSectors * 2) + RootDirSectors;
335
336 if (!QuickFormat)
337 {
338 Context->TotalSectorCount += SectorCount;
339
340 Status = FatWipeSectors(FileHandle,
341 SectorCount,
342 (ULONG)BootSector.SectorsPerCluster,
343 (ULONG)BootSector.BytesPerSector,
344 Context);
345 if (!NT_SUCCESS(Status))
346 {
347 DPRINT("FatWipeSectors() failed with status 0x%.08x\n", Status);
348 return Status;
349 }
350 }
351
352 Status = Fat16WriteBootSector(FileHandle,
353 &BootSector,
354 Context);
355 if (!NT_SUCCESS(Status))
356 {
357 DPRINT("Fat16WriteBootSector() failed with status 0x%.08x\n", Status);
358 return Status;
359 }
360
361 /* Write first FAT copy */
362 Status = Fat16WriteFAT(FileHandle,
363 0,
364 &BootSector,
365 Context);
366 if (!NT_SUCCESS(Status))
367 {
368 DPRINT("Fat16WriteFAT() failed with status 0x%.08x\n", Status);
369 return Status;
370 }
371
372 /* Write second FAT copy */
373 Status = Fat16WriteFAT(FileHandle,
374 (ULONG)BootSector.FATSectors,
375 &BootSector,
376 Context);
377 if (!NT_SUCCESS(Status))
378 {
379 DPRINT("Fat16WriteFAT() failed with status 0x%.08x.\n", Status);
380 return Status;
381 }
382
383 Status = Fat16WriteRootDirectory(FileHandle,
384 &BootSector,
385 Context);
386 if (!NT_SUCCESS(Status))
387 {
388 DPRINT("Fat16WriteRootDirectory() failed with status 0x%.08x\n", Status);
389 }
390
391 return Status;
392 }
393