branch update for HEAD-2003021201
[reactos.git] / drivers / fs / vfat / fsctl.c
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /* $Id$
20  *
21  * COPYRIGHT:        See COPYING in the top level directory
22  * PROJECT:          ReactOS kernel
23  * FILE:             services/fs/vfat/fsctl.c
24  * PURPOSE:          VFAT Filesystem
25  */
26
27 /* INCLUDES *****************************************************************/
28
29 #include <ddk/ntddk.h>
30 #include <wchar.h>
31
32 #define NDEBUG
33 #include <debug.h>
34
35 #include "vfat.h"
36
37 /* FUNCTIONS ****************************************************************/
38
39 #define  CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
40                    (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
41
42
43 static NTSTATUS
44 VfatHasFileSystem(PDEVICE_OBJECT DeviceToMount,
45                   PBOOLEAN RecognizedFS,
46                   PFATINFO pFatInfo)
47 {
48    NTSTATUS Status;
49    PARTITION_INFORMATION PartitionInfo;
50    DISK_GEOMETRY DiskGeometry;
51    FATINFO FatInfo;
52    ULONG Size;
53    ULONG Sectors;
54    LARGE_INTEGER Offset;
55    struct _BootSector* Boot;
56
57    *RecognizedFS = FALSE;
58
59    Size = sizeof(DISK_GEOMETRY);
60    Status = VfatBlockDeviceIoControl(DeviceToMount,
61                                      IOCTL_DISK_GET_DRIVE_GEOMETRY,
62                                      NULL,
63                                      0,
64                                      &DiskGeometry,
65                                      &Size);
66    if (!NT_SUCCESS(Status))
67    {
68       DPRINT("VfatBlockDeviceIoControl faild (%x)\n", Status);
69       return Status;
70    }
71    if (DiskGeometry.MediaType == FixedMedia)
72    {
73       // We have found a hard disk
74       Size = sizeof(PARTITION_INFORMATION);
75       Status = VfatBlockDeviceIoControl(DeviceToMount,
76                                         IOCTL_DISK_GET_PARTITION_INFO,
77                                         NULL,
78                                         0,
79                                         &PartitionInfo,
80                                         &Size);
81       if (!NT_SUCCESS(Status))
82       {
83          DPRINT("VfatBlockDeviceIoControl faild (%x)\n", Status);
84          return Status;
85       }
86 #ifndef NDEBUG
87       DbgPrint("Partition Information:\n");
88       DbgPrint("StartingOffset      %u\n", PartitionInfo.StartingOffset.QuadPart  / 512);
89       DbgPrint("PartitionLength     %u\n", PartitionInfo.PartitionLength.QuadPart / 512);
90       DbgPrint("HiddenSectors       %u\n", PartitionInfo.HiddenSectors);
91       DbgPrint("PartitionNumber     %u\n", PartitionInfo.PartitionNumber);
92       DbgPrint("PartitionType       %u\n", PartitionInfo.PartitionType);
93       DbgPrint("BootIndicator       %u\n", PartitionInfo.BootIndicator);
94       DbgPrint("RecognizedPartition %u\n", PartitionInfo.RecognizedPartition);
95       DbgPrint("RewritePartition    %u\n", PartitionInfo.RewritePartition);
96 #endif
97       if (PartitionInfo.PartitionType == PARTITION_FAT_12       ||
98           PartitionInfo.PartitionType == PARTITION_FAT_16       ||
99           PartitionInfo.PartitionType == PARTITION_HUGE         ||
100           PartitionInfo.PartitionType == PARTITION_FAT32        ||
101           PartitionInfo.PartitionType == PARTITION_FAT32_XINT13 ||
102           PartitionInfo.PartitionType == PARTITION_XINT13)
103       {
104          *RecognizedFS = TRUE;
105       }
106    }
107    else if (DiskGeometry.MediaType > Unknown && DiskGeometry.MediaType <= RemovableMedia)
108    {
109       *RecognizedFS = TRUE;
110    }
111    if (*RecognizedFS == FALSE)
112    {
113       return STATUS_SUCCESS;
114    }
115
116    Boot = ExAllocatePool(NonPagedPool, DiskGeometry.BytesPerSector);
117    if (Boot == NULL)
118    {
119       *RecognizedFS=FALSE;
120       return STATUS_INSUFFICIENT_RESOURCES;
121    }
122    Offset.QuadPart = 0;
123    Status = VfatReadDisk(DeviceToMount, &Offset, DiskGeometry.BytesPerSector, (PUCHAR) Boot);
124    if (NT_SUCCESS(Status))
125    {
126       FatInfo.VolumeID = Boot->VolumeID;
127       FatInfo.FATStart = Boot->ReservedSectors;
128       FatInfo.FATCount = Boot->FATCount;
129       FatInfo.FATSectors = Boot->FATSectors ? Boot->FATSectors : ((struct _BootSector32*) Boot)->FATSectors32;
130       FatInfo.BytesPerSector = Boot->BytesPerSector;
131       FatInfo.SectorsPerCluster = Boot->SectorsPerCluster;
132       FatInfo.BytesPerCluster = FatInfo.BytesPerSector * FatInfo.SectorsPerCluster;
133       FatInfo.rootDirectorySectors = ((Boot->RootEntries * 32) + Boot->BytesPerSector - 1) / Boot->BytesPerSector;
134       FatInfo.rootStart = FatInfo.FATStart + FatInfo.FATCount * FatInfo.FATSectors;
135       FatInfo.dataStart = FatInfo.rootStart + FatInfo.rootDirectorySectors;
136       FatInfo.Sectors = Sectors = Boot->Sectors ? Boot->Sectors : Boot->SectorsHuge;
137       Sectors -= Boot->ReservedSectors + FatInfo.FATCount * FatInfo.FATSectors + FatInfo.rootDirectorySectors;
138       FatInfo.NumberOfClusters = Sectors / Boot->SectorsPerCluster;
139       if (FatInfo.NumberOfClusters < 4085)
140       {
141          DPRINT("FAT12\n");
142          FatInfo.FatType = FAT12;
143       }
144       else if (FatInfo.NumberOfClusters >= 65525)
145       {
146          DPRINT("FAT32\n");
147          FatInfo.FatType = FAT32;
148          FatInfo.RootCluster = ((struct _BootSector32*) Boot)->RootCluster;
149          FatInfo.rootStart = FatInfo.dataStart + ((FatInfo.RootCluster - 2) * FatInfo.SectorsPerCluster);
150          FatInfo.VolumeID = ((struct _BootSector32*) Boot)->VolumeID;
151       }
152       else
153       {
154          DPRINT("FAT16\n");
155          FatInfo.FatType = FAT16;
156       }
157       if (pFatInfo)
158       {
159          *pFatInfo = FatInfo;
160       }
161    }
162    ExFreePool(Boot);
163    return Status;
164 }
165
166 static NTSTATUS
167 VfatMountDevice(PDEVICE_EXTENSION DeviceExt,
168                 PDEVICE_OBJECT DeviceToMount)
169 /*
170  * FUNCTION: Mounts the device
171  */
172 {
173    NTSTATUS Status;
174    BOOLEAN RecognizedFS;
175
176    DPRINT("Mounting VFAT device...\n");
177
178    Status = VfatHasFileSystem(DeviceToMount, &RecognizedFS, &DeviceExt->FatInfo);
179    if (!NT_SUCCESS(Status))
180    {
181       return(Status);
182    }
183
184    if (DeviceExt->FatInfo.BytesPerCluster >= PAGE_SIZE &&
185       (DeviceExt->FatInfo.BytesPerCluster % PAGE_SIZE) != 0)
186    {
187       DbgPrint("(%s:%d) Invalid cluster size\n", __FILE__, __LINE__);
188       KeBugCheck(0);
189    }
190    else if (DeviceExt->FatInfo.BytesPerCluster < PAGE_SIZE &&
191       (PAGE_SIZE % DeviceExt->FatInfo.BytesPerCluster) != 0)
192    {
193       DbgPrint("(%s:%d) Invalid cluster size2\n", __FILE__, __LINE__);
194       KeBugCheck(0);
195    }
196
197    return(STATUS_SUCCESS);
198 }
199
200
201 static NTSTATUS
202 VfatMount (PVFAT_IRP_CONTEXT IrpContext)
203 /*
204  * FUNCTION: Mount the filesystem
205  */
206 {
207    PDEVICE_OBJECT DeviceObject = NULL;
208    PDEVICE_EXTENSION DeviceExt = NULL;
209    BOOLEAN RecognizedFS;
210    NTSTATUS Status;
211    PVFATFCB Fcb = NULL;
212    PVFATFCB VolumeFcb = NULL;
213    PVFATCCB Ccb = NULL;
214    LARGE_INTEGER timeout;
215
216    DPRINT("VfatMount(IrpContext %x)\n", IrpContext);
217
218    assert (IrpContext);
219
220    if (IrpContext->DeviceObject != VfatGlobalData->DeviceObject)
221    {
222       Status = STATUS_INVALID_DEVICE_REQUEST;
223       goto ByeBye;
224    }
225
226    Status = VfatHasFileSystem (IrpContext->Stack->Parameters.MountVolume.DeviceObject, &RecognizedFS, NULL);
227    if (!NT_SUCCESS(Status))
228    {
229       goto ByeBye;
230    }
231
232    if (RecognizedFS == FALSE)
233    {
234       DPRINT("VFAT: Unrecognized Volume\n");
235       Status = STATUS_UNRECOGNIZED_VOLUME;
236       goto ByeBye;
237    }
238
239    DPRINT("VFAT: Recognized volume\n");
240    Status = IoCreateDevice(VfatGlobalData->DriverObject,
241                            sizeof (DEVICE_EXTENSION),
242                            NULL,
243                            FILE_DEVICE_FILE_SYSTEM,
244                            0,
245                            FALSE,
246                            &DeviceObject);
247    if (!NT_SUCCESS(Status))
248    {
249       goto ByeBye;
250    }
251
252    DeviceObject->Flags = DeviceObject->Flags | DO_DIRECT_IO;
253    DeviceExt = (PVOID) DeviceObject->DeviceExtension;
254    RtlZeroMemory(DeviceExt, sizeof(DEVICE_EXTENSION));
255
256    /* use same vpb as device disk */
257    DeviceObject->Vpb = IrpContext->Stack->Parameters.MountVolume.DeviceObject->Vpb;
258    Status = VfatMountDevice(DeviceExt, IrpContext->Stack->Parameters.MountVolume.DeviceObject);
259    if (!NT_SUCCESS(Status))
260    {
261       /* FIXME: delete device object */
262       goto ByeBye;
263    }
264
265 #ifndef NDEBUG
266    DbgPrint("BytesPerSector:     %d\n", DeviceExt->FatInfo.BytesPerSector);
267    DbgPrint("SectorsPerCluster:  %d\n", DeviceExt->FatInfo.SectorsPerCluster);
268    DbgPrint("FATCount:           %d\n", DeviceExt->FatInfo.FATCount);
269    DbgPrint("FATSectors:         %d\n", DeviceExt->FatInfo.FATSectors);
270    DbgPrint("RootStart:          %d\n", DeviceExt->FatInfo.rootStart);
271    DbgPrint("DataStart:          %d\n", DeviceExt->FatInfo.dataStart);
272    if (DeviceExt->FatInfo.FatType == FAT32)
273    {
274       DbgPrint("RootCluster:        %d\n", DeviceExt->FatInfo.RootCluster);
275    }
276 #endif
277
278   DeviceExt->StorageDevice = IrpContext->Stack->Parameters.MountVolume.DeviceObject;
279   DeviceExt->StorageDevice->Vpb->DeviceObject = DeviceObject;
280   DeviceExt->StorageDevice->Vpb->RealDevice = DeviceExt->StorageDevice;
281   DeviceExt->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
282   DeviceObject->StackSize = DeviceExt->StorageDevice->StackSize + 1;
283   DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
284
285   DPRINT("FsDeviceObject %lx\n", DeviceObject);
286
287    DeviceExt->FATFileObject = IoCreateStreamFileObject(NULL, DeviceExt->StorageDevice);
288    Fcb = vfatNewFCB(NULL);
289    if (Fcb == NULL)
290    {
291       Status = STATUS_INSUFFICIENT_RESOURCES;
292       goto ByeBye;
293    }
294    Ccb = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
295    if (Ccb == NULL)
296    {
297       Status =  STATUS_INSUFFICIENT_RESOURCES;
298       goto ByeBye;
299    }
300    memset(Ccb, 0, sizeof (VFATCCB));
301    wcscpy(Fcb->PathName, L"$$Fat$$");
302    Fcb->ObjectName = Fcb->PathName;
303    DeviceExt->FATFileObject->Flags = DeviceExt->FATFileObject->Flags | FO_FCB_IS_VALID | FO_DIRECT_CACHE_PAGING_READ;
304    DeviceExt->FATFileObject->FsContext = (PVOID) &Fcb->RFCB;
305    DeviceExt->FATFileObject->FsContext2 = Ccb;
306    DeviceExt->FATFileObject->SectionObjectPointers = &Fcb->SectionObjectPointers;
307    DeviceExt->FATFileObject->PrivateCacheMap = NULL;
308    DeviceExt->FATFileObject->Vpb = DeviceObject->Vpb;
309    Ccb->pFcb = Fcb;
310    Ccb->PtrFileObject = DeviceExt->FATFileObject;
311    Fcb->FileObject = DeviceExt->FATFileObject;
312    Fcb->pDevExt = (PDEVICE_EXTENSION)DeviceExt->StorageDevice;
313
314    Fcb->Flags = FCB_IS_FAT;
315
316    Fcb->RFCB.FileSize.QuadPart = DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector;
317    Fcb->RFCB.ValidDataLength = Fcb->RFCB.FileSize;
318    Fcb->RFCB.AllocationSize = Fcb->RFCB.FileSize;
319
320    if (DeviceExt->FatInfo.FatType != FAT12)
321    {
322       Status = CcRosInitializeFileCache(DeviceExt->FATFileObject, &Fcb->RFCB.Bcb, CACHEPAGESIZE(DeviceExt));
323    }
324    else
325    {
326       Status = CcRosInitializeFileCache(DeviceExt->FATFileObject, &Fcb->RFCB.Bcb, 2 * PAGE_SIZE);
327    }
328    if (!NT_SUCCESS (Status))
329    {
330       DbgPrint ("CcRosInitializeFileCache failed\n");
331       goto ByeBye;
332    }
333    DeviceExt->LastAvailableCluster = 2;
334    ExInitializeResourceLite(&DeviceExt->DirResource);
335    ExInitializeResourceLite(&DeviceExt->FatResource);
336
337    KeInitializeSpinLock(&DeviceExt->FcbListLock);
338    InitializeListHead(&DeviceExt->FcbListHead);
339
340    VolumeFcb = vfatNewFCB(NULL);
341    if (VolumeFcb == NULL)
342    {
343       Status = STATUS_INSUFFICIENT_RESOURCES;
344       goto ByeBye;
345    }
346    wcscpy(VolumeFcb->PathName, L"$$Volume$$");
347    VolumeFcb->ObjectName = VolumeFcb->PathName;
348    VolumeFcb->Flags = FCB_IS_VOLUME;
349    VolumeFcb->RFCB.FileSize.QuadPart = DeviceExt->FatInfo.Sectors * DeviceExt->FatInfo.BytesPerSector;
350    VolumeFcb->RFCB.ValidDataLength = VolumeFcb->RFCB.FileSize;
351    VolumeFcb->RFCB.AllocationSize = VolumeFcb->RFCB.FileSize;
352    VolumeFcb->pDevExt = (PDEVICE_EXTENSION)DeviceExt->StorageDevice;
353    DeviceExt->VolumeFcb = VolumeFcb;
354
355    ExAcquireResourceExclusiveLite(&VfatGlobalData->VolumeListLock, TRUE);
356    InsertHeadList(&VfatGlobalData->VolumeListHead, &DeviceExt->VolumeListEntry);
357    ExReleaseResourceLite(&VfatGlobalData->VolumeListLock);
358
359    /* read serial number */
360    DeviceObject->Vpb->SerialNumber = DeviceExt->FatInfo.VolumeID;
361
362    /* read volume label */
363    ReadVolumeLabel(DeviceExt,  DeviceObject->Vpb);
364
365    Status = STATUS_SUCCESS;
366 ByeBye:
367
368   if (!NT_SUCCESS(Status))
369   {
370      // cleanup
371      if (DeviceExt && DeviceExt->FATFileObject)
372         ObDereferenceObject (DeviceExt->FATFileObject);
373      if (Fcb)
374         vfatDestroyFCB(Fcb);
375      if (Ccb)
376         vfatDestroyCCB(Ccb);
377      if (DeviceObject)
378        IoDeleteDevice(DeviceObject);
379      if (VolumeFcb)
380         vfatDestroyFCB(VolumeFcb);
381   }
382   return Status;
383 }
384
385
386 static NTSTATUS
387 VfatVerify (PVFAT_IRP_CONTEXT IrpContext)
388 /*
389  * FUNCTION: Mount the filesystem
390  */
391 {
392   DPRINT("VfatVerify(IrpContext %x)\n", IrpContext);
393
394   assert(IrpContext);
395
396   return(STATUS_INVALID_DEVICE_REQUEST);
397 }
398
399
400 NTSTATUS VfatFileSystemControl(PVFAT_IRP_CONTEXT IrpContext)
401 /*
402  * FUNCTION: File system control
403  */
404 {
405
406    NTSTATUS Status;
407
408    DPRINT("VfatFileSystemControl(IrpContext %x)\n", IrpContext);
409
410    assert (IrpContext);
411
412    switch (IrpContext->MinorFunction)
413    {
414       case IRP_MN_USER_FS_REQUEST:
415          DPRINT("VFAT FSC: IRP_MN_USER_FS_REQUEST\n");
416          Status = STATUS_INVALID_DEVICE_REQUEST;
417          break;
418
419       case IRP_MN_MOUNT_VOLUME:
420          Status = VfatMount(IrpContext);
421          break;
422
423       case IRP_MN_VERIFY_VOLUME:
424          Status = VfatVerify(IrpContext);
425          break;
426
427       default:
428            DPRINT("VFAT FSC: MinorFunction %d\n", IrpContext->MinorFunction);
429            Status = STATUS_INVALID_DEVICE_REQUEST;
430            break;
431    }
432
433    IrpContext->Irp->IoStatus.Status = Status;
434    IrpContext->Irp->IoStatus.Information = 0;
435
436    IoCompleteRequest (IrpContext->Irp, IO_NO_INCREMENT);
437    VfatFreeIrpContext(IrpContext);
438    return (Status);
439 }
440