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