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