c35b8d38134399b8b73192c5d3e8a4fcc27b9be3
[reactos.git] / lib / fslib / vfatlib / vfatlib.c
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS VFAT filesystem library
4  * FILE:        vfatlib.c
5  * PURPOSE:     Main API
6  * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7  * REVISIONS:
8  *   CSH 05/04-2003 Created
9  */
10 #define NDEBUG
11 #include <debug.h>
12 #define NTOS_MODE_USER
13 #include <ntos.h>
14 #include <ddk/ntddscsi.h>
15 #include <fslib/vfatlib.h>
16 #include "vfatlib.h"
17
18
19 static ULONG
20 GetShiftCount(ULONG Value)
21 {
22   ULONG i = 1;
23   while (Value > 0)
24     {
25       i++;
26       Value /= 2;
27     }
28   return i - 2;
29 }
30
31
32 NTSTATUS
33 VfatInitialize()
34 {
35   DPRINT("VfatInitialize()\n");
36
37   return STATUS_SUCCESS;
38 }
39
40 static NTSTATUS
41 VfatWriteBootSector(IN HANDLE FileHandle,
42   IN PFAT32_BOOT_SECTOR BootSector)
43 {
44   OBJECT_ATTRIBUTES ObjectAttributes;
45   IO_STATUS_BLOCK IoStatusBlock;
46   UNICODE_STRING Name;
47   NTSTATUS Status;
48   PUCHAR NewBootSector;
49   LARGE_INTEGER FileOffset;
50
51   /* Allocate buffer for new bootsector */
52   NewBootSector = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
53     0,
54     SECTORSIZE);
55   if (NewBootSector == NULL)
56     return(STATUS_INSUFFICIENT_RESOURCES);
57
58   /* Zero the new bootsector */
59   memset(NewBootSector, 0, SECTORSIZE);
60
61   /* Copy FAT32 BPB to new bootsector */
62   memcpy((NewBootSector + 3),
63     &BootSector->OEMName[0],
64     87); /* FAT32 BPB length (up to (not including) Res2) */
65
66   /* Write sector 0 */
67   FileOffset.QuadPart = 0ULL;
68   Status = NtWriteFile(FileHandle,
69     NULL,
70     NULL,
71     NULL,
72     &IoStatusBlock,
73     NewBootSector,
74     SECTORSIZE,
75     &FileOffset,
76     NULL);
77   if (!NT_SUCCESS(Status))
78     {
79       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
80       RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
81       return(Status);
82     }
83
84   /* Write backup boot sector */
85   if (BootSector->BootBackup != 0x0000)
86     {
87       FileOffset.QuadPart = (ULONGLONG)((ULONG) BootSector->BootBackup * SECTORSIZE);
88       Status = NtWriteFile(FileHandle,
89         NULL,
90         NULL,
91         NULL,
92         &IoStatusBlock,
93         NewBootSector,
94         SECTORSIZE,
95         &FileOffset,
96         NULL);
97       if (!NT_SUCCESS(Status))
98         {
99           DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
100           RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
101           return(Status);
102         }
103     }
104
105   /* Free the new boot sector */
106   RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector);
107
108   return(Status);
109 }
110
111
112 static NTSTATUS
113 VfatWriteFsInfo(IN HANDLE FileHandle,
114   IN PFAT32_BOOT_SECTOR BootSector)
115 {
116   OBJECT_ATTRIBUTES ObjectAttributes;
117   IO_STATUS_BLOCK IoStatusBlock;
118   UNICODE_STRING Name;
119   NTSTATUS Status;
120   PFAT32_FSINFO FsInfo;
121   LARGE_INTEGER FileOffset;
122
123   /* Allocate buffer for new sector */
124   FsInfo = (PFAT32_FSINFO)RtlAllocateHeap(RtlGetProcessHeap(),
125     0,
126     BootSector->BytesPerSector);
127   if (FsInfo == NULL)
128     return(STATUS_INSUFFICIENT_RESOURCES);
129
130   /* Zero the new sector */
131   memset(FsInfo, 0, BootSector->BytesPerSector);
132
133   FsInfo->LeadSig = 0x41615252;
134   FsInfo->StrucSig = 0x61417272;
135   FsInfo->FreeCount = 0xffffffff;
136   FsInfo->NextFree = 0xffffffff;
137
138   /* Write sector */
139   FileOffset.QuadPart = BootSector->FSInfoSector * BootSector->BytesPerSector;
140   Status = NtWriteFile(FileHandle,
141     NULL,
142     NULL,
143     NULL,
144     &IoStatusBlock,
145     FsInfo,
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, FsInfo);
153       return(Status);
154     }
155
156   /* Free the new sector buffer */
157   RtlFreeHeap(RtlGetProcessHeap(), 0, FsInfo);
158
159   return(Status);
160 }
161
162
163 static NTSTATUS
164 VfatWriteFAT(IN HANDLE FileHandle,
165   ULONG SectorOffset,
166   IN PFAT32_BOOT_SECTOR BootSector)
167 {
168   OBJECT_ATTRIBUTES ObjectAttributes;
169   IO_STATUS_BLOCK IoStatusBlock;
170   UNICODE_STRING Name;
171   NTSTATUS Status;
172   PUCHAR Buffer;
173   LARGE_INTEGER FileOffset;
174   ULONG i;
175
176   /* Allocate buffer */
177   Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
178     0,
179     BootSector->BytesPerSector);
180   if (Buffer == NULL)
181     return(STATUS_INSUFFICIENT_RESOURCES);
182
183   /* Zero the buffer */
184   memset(Buffer, 0, BootSector->BytesPerSector);
185
186   /* FAT cluster 0 */
187   Buffer[0] = 0xf8; /* Media type */
188   Buffer[1] = 0xff;
189   Buffer[2] = 0xff;
190   Buffer[3] = 0xff;
191   /* FAT cluster 1 */
192   Buffer[4] = 0xcf; /* Clean shutdown, no disk read/write errors, end-of-cluster (EOC) mark */
193   Buffer[5] = 0xff;
194   Buffer[6] = 0xff;
195   Buffer[7] = 0xf7;
196   /* FAT cluster 2 */
197   Buffer[8] = 0xff; /* End of root directory */
198   Buffer[9] = 0xff;
199   Buffer[10] = 0xff;
200   Buffer[11] = 0x0f;
201
202   /* Write first sector of the FAT */
203   FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors) * BootSector->BytesPerSector;
204   Status = NtWriteFile(FileHandle,
205     NULL,
206     NULL,
207     NULL,
208     &IoStatusBlock,
209     Buffer,
210     BootSector->BytesPerSector,
211     &FileOffset,
212     NULL);
213   if (!NT_SUCCESS(Status))
214     {
215       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
216       RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
217       return(Status);
218     }
219
220   /* Zero the buffer */
221   memset(Buffer, 0, BootSector->BytesPerSector);
222
223   /* Zero the rest of the FAT */
224   for (i = 1; i < BootSector->FATSectors32 - 1; i++)
225     {
226       /* Zero one sector of the FAT */
227       FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector;
228       Status = NtWriteFile(FileHandle,
229         NULL,
230         NULL,
231         NULL,
232         &IoStatusBlock,
233         Buffer,
234         BootSector->BytesPerSector,
235         &FileOffset,
236         NULL);
237       if (!NT_SUCCESS(Status))
238         {
239           DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
240           RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
241           return(Status);
242         }
243     }
244
245   /* Free the buffer */
246   RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
247
248   return(Status);
249 }
250
251
252 static NTSTATUS
253 VfatWriteRootDirectory(IN HANDLE FileHandle,
254   IN PFAT32_BOOT_SECTOR BootSector)
255 {
256   OBJECT_ATTRIBUTES ObjectAttributes;
257   IO_STATUS_BLOCK IoStatusBlock;
258   NTSTATUS Status;
259   PUCHAR Buffer;
260   LARGE_INTEGER FileOffset;
261   ULONGLONG FirstDataSector;
262   ULONGLONG FirstRootDirSector;
263
264   /* Allocate buffer for the cluster */
265   Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
266     0,
267     BootSector->SectorsPerCluster * BootSector->BytesPerSector);
268   if (Buffer == NULL)
269     return(STATUS_INSUFFICIENT_RESOURCES);
270
271   /* Zero the buffer */
272   memset(Buffer, 0, BootSector->SectorsPerCluster * BootSector->BytesPerSector);
273
274   DPRINT("BootSector->ReservedSectors = %lu\n", BootSector->ReservedSectors);
275   DPRINT("BootSector->FATSectors32 = %lu\n", BootSector->FATSectors32);
276   DPRINT("BootSector->RootCluster = %lu\n", BootSector->RootCluster);
277   DPRINT("BootSector->SectorsPerCluster = %lu\n", BootSector->SectorsPerCluster);
278
279   /* Write cluster */
280   FirstDataSector = BootSector->ReservedSectors +
281     (BootSector->FATCount * BootSector->FATSectors32) + 0 /* RootDirSectors */;
282
283   DPRINT("FirstDataSector = %lu\n", FirstDataSector);
284
285   FirstRootDirSector = ((BootSector->RootCluster - 2) * BootSector->SectorsPerCluster) + FirstDataSector;
286   FileOffset.QuadPart = FirstRootDirSector * BootSector->BytesPerSector;
287
288   DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector);
289   DPRINT("FileOffset = %lu\n", FileOffset.QuadPart);
290
291   Status = NtWriteFile(FileHandle,
292     NULL,
293     NULL,
294     NULL,
295     &IoStatusBlock,
296     Buffer,
297     BootSector->SectorsPerCluster * BootSector->BytesPerSector,
298     &FileOffset,
299     NULL);
300   if (!NT_SUCCESS(Status))
301     {
302       DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
303       RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
304       return(Status);
305     }
306
307   /* Free the buffer */
308   RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
309
310   return(Status);
311 }
312
313
314 NTSTATUS
315 VfatFormat(
316         PUNICODE_STRING  DriveRoot,
317         DWORD  MediaFlag,
318         PUNICODE_STRING  Label,
319         BOOL  QuickFormat,
320         DWORD  ClusterSize,
321         PFMIFSCALLBACK  Callback)
322 {
323   OBJECT_ATTRIBUTES ObjectAttributes;
324   DISK_GEOMETRY DiskGeometry;
325   IO_STATUS_BLOCK Iosb;
326   HANDLE FileHandle;
327   SCSI_ADDRESS ScsiAddress;
328   PARTITION_INFORMATION PartitionInfo;
329   FAT32_BOOT_SECTOR BootSector;
330   ANSI_STRING VolumeLabel;
331   NTSTATUS Status;
332   ULONG RootDirSectors;
333   ULONG TmpVal1;
334   ULONG TmpVal2;
335
336   DPRINT("VfatFormat(DriveRoot '%S')\n", DriveRoot->Buffer);
337
338   InitializeObjectAttributes(&ObjectAttributes,
339     DriveRoot,
340     0,
341     NULL,
342     NULL);
343
344   Status = NtOpenFile(&FileHandle,
345     FILE_WRITE_ACCESS | FILE_WRITE_ATTRIBUTES,
346     &ObjectAttributes,
347     &Iosb,
348     1,
349     FILE_SYNCHRONOUS_IO_ALERT);
350   if (!NT_SUCCESS(Status))
351     {
352       DPRINT("NtOpenFile() failed with status 0x%.08x\n", Status);
353       return Status;
354     }
355
356   Status = NtDeviceIoControlFile(FileHandle,
357     NULL,
358     NULL,
359     NULL,
360     &Iosb,
361     IOCTL_DISK_GET_DRIVE_GEOMETRY,
362     NULL,
363     0,
364     &DiskGeometry,
365     sizeof(DISK_GEOMETRY));
366   if (!NT_SUCCESS(Status))
367     {
368       DPRINT("IOCTL_DISK_GET_DRIVE_GEOMETRY failed with status 0x%.08x\n", Status);
369       NtClose(FileHandle);
370       return Status;
371     }
372
373   if (DiskGeometry.MediaType == FixedMedia)
374     {
375       DPRINT("Cylinders %d\n", DiskGeometry.Cylinders.QuadPart);
376       DPRINT("TracksPerCylinder %d\n", DiskGeometry.TracksPerCylinder);
377       DPRINT("SectorsPerTrack %d\n", DiskGeometry.SectorsPerTrack);
378       DPRINT("BytesPerSector %d\n", DiskGeometry.BytesPerSector);
379       DPRINT("DiskSize %d\n",
380         DiskGeometry.Cylinders.QuadPart *
381         (ULONGLONG)DiskGeometry.TracksPerCylinder *
382         (ULONGLONG)DiskGeometry.SectorsPerTrack *
383         (ULONGLONG)DiskGeometry.BytesPerSector);
384
385       Status = NtDeviceIoControlFile(FileHandle,
386         NULL,
387         NULL,
388         NULL,
389         &Iosb,
390         IOCTL_DISK_GET_PARTITION_INFO,
391         NULL,
392         0,
393         &PartitionInfo,
394         sizeof(PARTITION_INFORMATION));
395       if (!NT_SUCCESS(Status))
396         {
397           DPRINT("IOCTL_DISK_GET_PARTITION_INFO failed with status 0x%.08x\n", Status);
398           NtClose(FileHandle);
399           return Status;
400         }
401
402             DPRINT("PartitionType 0x%x\n", PartitionInfo.PartitionType);
403             DPRINT("StartingOffset %d\n", PartitionInfo.StartingOffset);
404             DPRINT("PartitionLength %d\n", PartitionInfo.PartitionLength);
405             DPRINT("HiddenSectors %d\n", PartitionInfo.HiddenSectors);
406             DPRINT("PartitionNumber %d\n", PartitionInfo.PartitionNumber);
407             DPRINT("BootIndicator 0x%x\n", PartitionInfo.BootIndicator);
408             DPRINT("RewritePartition %d\n", PartitionInfo.RewritePartition);
409             DPRINT("RecognizedPartition %d\n", PartitionInfo.RecognizedPartition);
410     }
411   else
412     {
413       DPRINT1("FIXME: Removable media is not supported\n");
414       NtClose(FileHandle);
415       return Status;
416     }
417
418   memset(&BootSector, 0, sizeof(FAT32_BOOT_SECTOR));
419   memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8);
420   BootSector.BytesPerSector = DiskGeometry.BytesPerSector;
421   BootSector.SectorsPerCluster = 4096 / BootSector.BytesPerSector;  // 4KB clusters /* FIXME: */
422   BootSector.ReservedSectors = 32;
423   BootSector.FATCount = 2;
424   BootSector.RootEntries = 0;
425   BootSector.Sectors = 0;
426   BootSector.Media = 0xf8;
427   BootSector.FATSectors = 0;
428   BootSector.SectorsPerTrack = DiskGeometry.SectorsPerTrack;
429   BootSector.Heads = DiskGeometry.TracksPerCylinder;
430   BootSector.HiddenSectors = 63; /* FIXME: */
431   BootSector.SectorsHuge = PartitionInfo.PartitionLength.QuadPart >>
432     GetShiftCount(BootSector.BytesPerSector); /* Use shifting to avoid 64-bit division */
433   BootSector.FATSectors32 = 0; /* Set later */
434   BootSector.ExtFlag = 0; /* Mirror all FATs */
435   BootSector.FSVersion = 0x0000; /* 0:0 */
436   BootSector.RootCluster = 2;
437   BootSector.FSInfoSector = 1;
438   BootSector.BootBackup = 6;
439   BootSector.Drive = 0xff; /* No BIOS boot drive available */ //0x80; /* FIXME: */
440   BootSector.ExtBootSignature = 0x29;
441   BootSector.VolumeID = 0x45768798; /* FIXME: */
442   if ((Label == NULL) || (Label->Buffer == NULL))
443     {
444       memcpy(&BootSector.VolumeLabel[0], "NO NAME    ", 11);
445     }
446   else
447     {
448       RtlUnicodeStringToAnsiString(&VolumeLabel, Label, TRUE);
449       memset(&BootSector.VolumeLabel[0], ' ', 11);
450       memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer,
451         VolumeLabel.Length < 11 ? VolumeLabel.Length : 11);
452       RtlFreeAnsiString(&VolumeLabel);
453     }
454   memcpy(&BootSector.SysType[0], "FAT32   ", 8);
455
456   RootDirSectors = ((BootSector.RootEntries * 32) +
457     (BootSector.BytesPerSector - 1)) / BootSector.BytesPerSector;
458   TmpVal1 = BootSector.SectorsHuge - (BootSector.ReservedSectors + RootDirSectors);
459   TmpVal2 = (256 * BootSector.SectorsPerCluster) + BootSector.FATCount;
460   if (TRUE /* FAT32 */)
461     {
462       TmpVal2 /= 2;
463       BootSector.FATSectors = 0;
464       BootSector.FATSectors32 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
465     }
466
467   Status = VfatWriteBootSector(FileHandle,
468     &BootSector);
469   if (!NT_SUCCESS(Status))
470     {
471       DPRINT("VfatWriteBootSector() failed with status 0x%.08x\n", Status);
472       NtClose(FileHandle);
473       return Status;
474     }
475
476   Status = VfatWriteFsInfo(FileHandle,
477     &BootSector);
478   if (!NT_SUCCESS(Status))
479     {
480       DPRINT("VfatWriteFsInfo() failed with status 0x%.08x\n", Status);
481       NtClose(FileHandle);
482       return Status;
483     }
484
485   /* Write first FAT copy */
486   Status = VfatWriteFAT(FileHandle,
487     0,
488     &BootSector);
489   if (!NT_SUCCESS(Status))
490     {
491       DPRINT("VfatWriteFAT() failed with status 0x%.08x\n", Status);
492       NtClose(FileHandle);
493       return Status;
494     }
495
496   /* Write second FAT copy */
497   Status = VfatWriteFAT(FileHandle,
498     BootSector.FATSectors32,
499     &BootSector);
500   if (!NT_SUCCESS(Status))
501     {
502       DPRINT("VfatWriteFAT() failed with status 0x%.08x.\n", Status);
503       NtClose(FileHandle);
504       return Status;
505     }
506
507   Status = VfatWriteRootDirectory(FileHandle,
508     &BootSector);
509   if (!NT_SUCCESS(Status))
510     {
511       DPRINT("VfatWriteRootDirectory() failed with status 0x%.08x\n", Status);
512       NtClose(FileHandle);
513       return Status;
514     }
515
516   NtClose(FileHandle);
517
518   DPRINT("VfatFormat() done. Status 0x%.08x\n", Status);
519
520   return Status;
521 }
522
523
524 NTSTATUS
525 VfatCleanup()
526 {
527   DPRINT("VfatCleanup()\n");
528
529   return STATUS_SUCCESS;
530 }