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