[VFATLIB]
[reactos.git] / reactos / 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 PUCHAR NewBootSector;
60 LARGE_INTEGER FileOffset;
61
62 /* Allocate buffer for new bootsector */
63 NewBootSector = (PUCHAR)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 + 3),
74 &BootSector->OEMName[0],
75 59); /* 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 BootSector->BytesPerSector,
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 UpdateProgress(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 Fat16WriteFAT(IN HANDLE FileHandle,
106 IN ULONG SectorOffset,
107 IN PFAT16_BOOT_SECTOR BootSector,
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 + BootSector->ReservedSectors) * BootSector->BytesPerSector;
137 Status = NtWriteFile(FileHandle,
138 NULL,
139 NULL,
140 NULL,
141 &IoStatusBlock,
142 Buffer,
143 BootSector->BytesPerSector,
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 UpdateProgress(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 / BootSector->BytesPerSector;
160 for (i = 1; i < (ULONG)BootSector->FATSectors; i += Sectors)
161 {
162 /* Zero some sectors of the FAT */
163 FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector;
164
165 if (((ULONG)BootSector->FATSectors - i) <= Sectors)
166 {
167 Sectors = (ULONG)BootSector->FATSectors - i;
168 }
169
170 Status = NtWriteFile(FileHandle,
171 NULL,
172 NULL,
173 NULL,
174 &IoStatusBlock,
175 Buffer,
176 Sectors * BootSector->BytesPerSector,
177 &FileOffset,
178 NULL);
179 if (!NT_SUCCESS(Status))
180 {
181 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
182 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
183 return Status;
184 }
185
186 UpdateProgress(Context, Sectors);
187 }
188
189 /* Free the buffer */
190 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
191
192 return Status;
193 }
194
195
196 static NTSTATUS
197 Fat16WriteRootDirectory(IN HANDLE FileHandle,
198 IN PFAT16_BOOT_SECTOR BootSector,
199 IN OUT PFORMAT_CONTEXT Context)
200 {
201 IO_STATUS_BLOCK IoStatusBlock;
202 NTSTATUS Status = STATUS_SUCCESS;
203 PUCHAR Buffer;
204 LARGE_INTEGER FileOffset;
205 ULONG FirstRootDirSector;
206 ULONG RootDirSectors;
207 ULONG Sectors;
208 ULONG i;
209
210 DPRINT("BootSector->ReservedSectors = %hu\n", BootSector->ReservedSectors);
211 DPRINT("BootSector->FATSectors = %hu\n", BootSector->FATSectors);
212 DPRINT("BootSector->SectorsPerCluster = %u\n", BootSector->SectorsPerCluster);
213
214 /* Write cluster */
215 RootDirSectors = ((BootSector->RootEntries * 32) +
216 (BootSector->BytesPerSector - 1)) / BootSector->BytesPerSector;
217 FirstRootDirSector =
218 BootSector->ReservedSectors + (BootSector->FATCount * BootSector->FATSectors);
219
220 DPRINT("RootDirSectors = %lu\n", RootDirSectors);
221 DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector);
222
223 /* Allocate buffer for the cluster */
224 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
225 0,
226 32 * 1024);
227 if (Buffer == NULL)
228 return STATUS_INSUFFICIENT_RESOURCES;
229
230 /* Zero the buffer */
231 memset(Buffer, 0, 32 * 1024);
232
233 Sectors = 32 * 1024 / BootSector->BytesPerSector;
234 for (i = 0; i < RootDirSectors; i += Sectors)
235 {
236 /* Zero some sectors of the root directory */
237 FileOffset.QuadPart = (FirstRootDirSector + i) * BootSector->BytesPerSector;
238
239 if ((RootDirSectors - i) <= Sectors)
240 {
241 Sectors = RootDirSectors - i;
242 }
243
244 Status = NtWriteFile(FileHandle,
245 NULL,
246 NULL,
247 NULL,
248 &IoStatusBlock,
249 Buffer,
250 Sectors * BootSector->BytesPerSector,
251 &FileOffset,
252 NULL);
253 if (!NT_SUCCESS(Status))
254 {
255 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
256 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
257 return Status;
258 }
259
260 UpdateProgress(Context, Sectors);
261 }
262
263 /* Free the buffer */
264 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
265
266 return Status;
267 }
268
269
270 NTSTATUS
271 Fat16Format(IN HANDLE FileHandle,
272 IN PPARTITION_INFORMATION PartitionInfo,
273 IN PDISK_GEOMETRY DiskGeometry,
274 IN PUNICODE_STRING Label,
275 IN BOOLEAN QuickFormat,
276 IN ULONG ClusterSize,
277 IN OUT PFORMAT_CONTEXT Context)
278 {
279 FAT16_BOOT_SECTOR BootSector;
280 OEM_STRING VolumeLabel;
281 ULONG SectorCount;
282 ULONG RootDirSectors;
283 ULONG TmpVal1;
284 ULONG TmpVal2;
285 ULONG TmpVal3;
286 NTSTATUS Status;
287
288 /* Calculate cluster size */
289 if (ClusterSize == 0)
290 {
291 if (PartitionInfo->PartitionLength.QuadPart < 16LL * 1024LL * 1024LL)
292 {
293 /* Partition < 16MB ==> 1KB Cluster */
294 ClusterSize = 1024;
295 }
296 else if (PartitionInfo->PartitionLength.QuadPart < 128LL * 1024LL * 1024LL)
297 {
298 /* Partition < 128MB ==> 2KB Cluster */
299 ClusterSize = 2048;
300 }
301 else if (PartitionInfo->PartitionLength.QuadPart < 256LL * 1024LL * 1024LL)
302 {
303 /* Partition < 256MB ==> 4KB Cluster */
304 ClusterSize = 4096;
305 }
306 else
307 {
308 /* Partition >= 256MB (< 512MB) ==> 8KB Cluster */
309 ClusterSize = 8192;
310 }
311 }
312
313 SectorCount = PartitionInfo->PartitionLength.QuadPart >>
314 GetShiftCount(DiskGeometry->BytesPerSector); /* Use shifting to avoid 64-bit division */
315
316 memset(&BootSector, 0, sizeof(FAT16_BOOT_SECTOR));
317 memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
318 BootSector.BytesPerSector = DiskGeometry->BytesPerSector;
319 BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector;
320 BootSector.ReservedSectors = 1;
321 BootSector.FATCount = 2;
322 BootSector.RootEntries = 512;
323 BootSector.Sectors = (SectorCount < 0x10000) ? (unsigned short)SectorCount : 0;
324 BootSector.Media = 0xf8;
325 BootSector.FATSectors = 0; /* Set later. See below. */
326 BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack;
327 BootSector.Heads = DiskGeometry->TracksPerCylinder;
328 BootSector.HiddenSectors = PartitionInfo->HiddenSectors;
329 BootSector.SectorsHuge = (SectorCount >= 0x10000) ? (unsigned long)SectorCount : 0;
330 BootSector.Drive = 0xff; /* No BIOS boot drive available */
331 BootSector.ExtBootSignature = 0x29;
332 BootSector.VolumeID = CalcVolumeSerialNumber();
333 if ((Label == NULL) || (Label->Buffer == NULL))
334 {
335 memcpy(&BootSector.VolumeLabel[0], "NO NAME ", 11);
336 }
337 else
338 {
339 RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE);
340 memset(&BootSector.VolumeLabel[0], ' ', 11);
341 memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
342 VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
343 RtlFreeOemString(&VolumeLabel);
344 }
345
346 memcpy(&BootSector.SysType[0], "FAT16 ", 8);
347
348 DPRINT("BootSector.SectorsHuge = %lx\n", BootSector.SectorsHuge);
349
350 RootDirSectors = ((BootSector.RootEntries * 32) +
351 (BootSector.BytesPerSector - 1)) / BootSector.BytesPerSector;
352
353 /* Calculate number of FAT sectors */
354 /* (BootSector.BytesPerSector / 2) FAT entries (16bit) fit into one sector */
355 TmpVal1 = SectorCount - (BootSector.ReservedSectors + RootDirSectors);
356 TmpVal2 = ((BootSector.BytesPerSector / 2) * BootSector.SectorsPerCluster) + BootSector.FATCount;
357 TmpVal3 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
358 BootSector.FATSectors = (unsigned short)(TmpVal3 & 0xffff);
359 DPRINT("BootSector.FATSectors = %hu\n", BootSector.FATSectors);
360
361 /* Init context data */
362 Context->TotalSectorCount =
363 1 + (BootSector.FATSectors * 2) + RootDirSectors;
364
365 Status = Fat16WriteBootSector(FileHandle,
366 &BootSector,
367 Context);
368 if (!NT_SUCCESS(Status))
369 {
370 DPRINT("Fat16WriteBootSector() failed with status 0x%.08x\n", Status);
371 return Status;
372 }
373
374 /* Write first FAT copy */
375 Status = Fat16WriteFAT(FileHandle,
376 0,
377 &BootSector,
378 Context);
379 if (!NT_SUCCESS(Status))
380 {
381 DPRINT("Fat16WriteFAT() failed with status 0x%.08x\n", Status);
382 return Status;
383 }
384
385 /* Write second FAT copy */
386 Status = Fat16WriteFAT(FileHandle,
387 (ULONG)BootSector.FATSectors,
388 &BootSector,
389 Context);
390 if (!NT_SUCCESS(Status))
391 {
392 DPRINT("Fat16WriteFAT() failed with status 0x%.08x.\n", Status);
393 return Status;
394 }
395
396 Status = Fat16WriteRootDirectory(FileHandle,
397 &BootSector,
398 Context);
399 if (!NT_SUCCESS(Status))
400 {
401 DPRINT("Fat16WriteRootDirectory() failed with status 0x%.08x\n", Status);
402 }
403
404 if (!QuickFormat)
405 {
406 /* FIXME: Fill remaining sectors */
407 }
408
409 return Status;
410 }