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