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