update for HEAD-2003091401
[reactos.git] / drivers / fs / ntfs / 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/ntfs/fsctl.c
24  * PURPOSE:          NTFS filesystem driver
25  * PROGRAMMER:       Eric Kohl
26  */
27
28 /* INCLUDES *****************************************************************/
29
30 #include <ddk/ntddk.h>
31
32 //#define NDEBUG
33 #include <debug.h>
34
35 #include "ntfs.h"
36
37 /* FUNCTIONS ****************************************************************/
38
39 static NTSTATUS
40 NtfsHasFileSystem(PDEVICE_OBJECT DeviceToMount)
41 /*
42  * FUNCTION: Tests if the device contains a filesystem that can be mounted 
43  * by this fsd
44  */
45 {
46   PARTITION_INFORMATION PartitionInfo;
47   DISK_GEOMETRY DiskGeometry;
48   ULONG Size;
49   PBOOT_SECTOR BootSector;
50   NTSTATUS Status;
51
52   DPRINT("NtfsHasFileSystem() called\n");
53
54   Size = sizeof(DISK_GEOMETRY);
55   Status = NtfsDeviceIoControl(DeviceToMount,
56                                IOCTL_DISK_GET_DRIVE_GEOMETRY,
57                                NULL,
58                                0,
59                                &DiskGeometry,
60                                &Size);
61   if (!NT_SUCCESS(Status))
62     {
63       DPRINT("NtfsDeviceIoControl() failed (Status %lx)\n", Status);
64       return(Status);
65     }
66
67   if (DiskGeometry.MediaType == FixedMedia)
68     {
69       /* We have found a hard disk */
70       Size = sizeof(PARTITION_INFORMATION);
71       Status = NtfsDeviceIoControl(DeviceToMount,
72                                    IOCTL_DISK_GET_PARTITION_INFO,
73                                    NULL,
74                                    0,
75                                    &PartitionInfo,
76                                    &Size);
77       if (!NT_SUCCESS(Status))
78         {
79           DPRINT("NtfsDeviceIoControl() failed (Status %lx)\n", Status);
80           return(Status);
81         }
82
83       if (PartitionInfo.PartitionType != PARTITION_IFS)
84         {
85           return(STATUS_UNRECOGNIZED_VOLUME);
86         }
87     }
88
89   DPRINT("BytesPerSector: %lu\n", DiskGeometry.BytesPerSector);
90   BootSector = ExAllocatePool(NonPagedPool,
91                               DiskGeometry.BytesPerSector);
92   if (BootSector == NULL)
93     {
94       return(STATUS_INSUFFICIENT_RESOURCES);
95     }
96
97   Status = NtfsReadRawSectors(DeviceToMount,
98                               0,
99                               1,
100                               DiskGeometry.BytesPerSector,
101                               (PVOID)BootSector);
102   if (NT_SUCCESS(Status))
103     {
104       DPRINT("NTFS-identifier: [%.8s]\n", BootSector->OemName);
105       if (strncmp(BootSector->OemName, "NTFS    ", 8) != 0)
106         {
107           Status = STATUS_UNRECOGNIZED_VOLUME;
108         }
109     }
110
111   ExFreePool(BootSector);
112
113   return(Status);
114 }
115
116
117 static NTSTATUS
118 NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
119                   PDEVICE_EXTENSION Vcb)
120 {
121   DISK_GEOMETRY DiskGeometry;
122 //  PUCHAR Buffer;
123   ULONG Size;
124   NTSTATUS Status;
125   PBOOT_SECTOR BootSector;
126
127   DPRINT("NtfsGetVolumeData() called\n");
128
129   Size = sizeof(DISK_GEOMETRY);
130   Status = NtfsDeviceIoControl(DeviceObject,
131                                IOCTL_DISK_GET_DRIVE_GEOMETRY,
132                                NULL,
133                                0,
134                                &DiskGeometry,
135                                &Size);
136   if (!NT_SUCCESS(Status))
137     {
138       DPRINT("NtfsDeviceIoControl() failed (Status %lx)\n", Status);
139       return(Status);
140     }
141
142   DPRINT("BytesPerSector: %lu\n", DiskGeometry.BytesPerSector);
143   BootSector = ExAllocatePool(NonPagedPool,
144                               DiskGeometry.BytesPerSector);
145   if (BootSector == NULL)
146     {
147       return(STATUS_INSUFFICIENT_RESOURCES);
148     }
149
150   Status = NtfsReadRawSectors(DeviceObject,
151                               0, /* Partition boot sector */
152                               1,
153                               DiskGeometry.BytesPerSector,
154                               (PVOID)BootSector);
155   if (NT_SUCCESS(Status))
156     {
157       /* Read data from the bootsector */
158       Vcb->NtfsInfo.BytesPerSector = BootSector->BytesPerSector;
159       Vcb->NtfsInfo.SectorsPerCluster = BootSector->SectorsPerCluster;
160       Vcb->NtfsInfo.BytesPerCluster = BootSector->BytesPerSector * BootSector->SectorsPerCluster;
161       Vcb->NtfsInfo.SectorCount = BootSector->SectorCount;
162
163       Vcb->NtfsInfo.MftStart.QuadPart = BootSector->MftLocation;
164       Vcb->NtfsInfo.MftMirrStart.QuadPart = BootSector->MftMirrLocation;
165       Vcb->NtfsInfo.SerialNumber = BootSector->SerialNumber;
166
167 //#indef NDEBUG
168       DbgPrint("Boot sector information:\n");
169       DbgPrint("  BytesPerSector:         %hu\n", BootSector->BytesPerSector);
170       DbgPrint("  SectorsPerCluster:      %hu\n", BootSector->SectorsPerCluster);
171
172       DbgPrint("  SectorCount:            %I64u\n", BootSector->SectorCount);
173
174       DbgPrint("  MftStart:               %I64u\n", BootSector->MftLocation);
175       DbgPrint("  MftMirrStart:           %I64u\n", BootSector->MftMirrLocation);
176
177       DbgPrint("  ClustersPerMftRecord:   %lx\n", BootSector->ClustersPerMftRecord);
178       DbgPrint("  ClustersPerIndexRecord: %lx\n", BootSector->ClustersPerIndexRecord);
179
180       DbgPrint("  SerialNumber:           %I64x\n", BootSector->SerialNumber);
181 //#endif
182
183       NtfsOpenMft(DeviceObject, Vcb);
184
185     }
186
187   ExFreePool(BootSector);
188
189   return(Status);
190 }
191
192
193
194 static NTSTATUS
195 NtfsMountVolume(PDEVICE_OBJECT DeviceObject,
196                 PIRP Irp)
197 {
198   PDEVICE_EXTENSION DeviceExt = NULL;
199   PDEVICE_OBJECT NewDeviceObject = NULL;
200   PDEVICE_OBJECT DeviceToMount;
201   PIO_STACK_LOCATION Stack;
202   PFCB Fcb = NULL;
203   PCCB Ccb = NULL;
204   PVPB Vpb;
205   NTSTATUS Status;
206
207   DPRINT("NtfsMountVolume() called\n");
208
209   if (DeviceObject != NtfsGlobalData->DeviceObject)
210     {
211       Status = STATUS_INVALID_DEVICE_REQUEST;
212       goto ByeBye;
213     }
214
215   Stack = IoGetCurrentIrpStackLocation(Irp);
216   DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
217   Vpb = Stack->Parameters.MountVolume.Vpb;
218
219   Status = NtfsHasFileSystem(DeviceToMount);
220   if (!NT_SUCCESS(Status))
221     {
222       goto ByeBye;
223     }
224
225   Status = IoCreateDevice(NtfsGlobalData->DriverObject,
226                           sizeof(DEVICE_EXTENSION),
227                           NULL,
228                           FILE_DEVICE_FILE_SYSTEM,
229 //                        FILE_DEVICE_DISK_FILE_SYSTEM,
230                           0,
231                           FALSE,
232                           &NewDeviceObject);
233   if (!NT_SUCCESS(Status))
234     goto ByeBye;
235
236   NewDeviceObject->Flags = NewDeviceObject->Flags | DO_DIRECT_IO;
237   DeviceExt = (PVOID)NewDeviceObject->DeviceExtension;
238   RtlZeroMemory(DeviceExt,
239                 sizeof(DEVICE_EXTENSION));
240
241   Status = NtfsGetVolumeData(DeviceToMount,
242                              DeviceExt);
243   if (!NT_SUCCESS(Status))
244     goto ByeBye;
245
246   NewDeviceObject->Vpb = DeviceToMount->Vpb;
247
248   DeviceExt->StorageDevice = DeviceToMount;
249   DeviceExt->StorageDevice->Vpb->DeviceObject = NewDeviceObject;
250   DeviceExt->StorageDevice->Vpb->RealDevice = DeviceExt->StorageDevice;
251   DeviceExt->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
252   NewDeviceObject->StackSize = DeviceExt->StorageDevice->StackSize + 1;
253   NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
254
255   DeviceExt->StreamFileObject = IoCreateStreamFileObject(NULL,
256                                                          DeviceExt->StorageDevice);
257
258
259   Fcb = NtfsCreateFCB(NULL);
260   if (Fcb == NULL)
261     {
262       Status = STATUS_INSUFFICIENT_RESOURCES;
263       goto ByeBye;
264     }
265
266   Ccb = ExAllocatePoolWithTag(NonPagedPool,
267                               sizeof(CCB),
268                               TAG_CCB);
269   if (Ccb == NULL)
270     {
271       Status =  STATUS_INSUFFICIENT_RESOURCES;
272       goto ByeBye;
273     }
274   RtlZeroMemory(Ccb,
275                 sizeof(CCB));
276
277   DeviceExt->StreamFileObject->Flags = DeviceExt->StreamFileObject->Flags | FO_FCB_IS_VALID | FO_DIRECT_CACHE_PAGING_READ;
278   DeviceExt->StreamFileObject->FsContext = Fcb;
279   DeviceExt->StreamFileObject->FsContext2 = Ccb;
280   DeviceExt->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
281   DeviceExt->StreamFileObject->PrivateCacheMap = NULL;
282   DeviceExt->StreamFileObject->Vpb = DeviceExt->Vpb;
283   Ccb->PtrFileObject = DeviceExt->StreamFileObject;
284   Fcb->FileObject = DeviceExt->StreamFileObject;
285   Fcb->DevExt = (PDEVICE_EXTENSION)DeviceExt->StorageDevice;
286
287   Fcb->Flags = FCB_IS_VOLUME_STREAM;
288
289   Fcb->RFCB.FileSize.QuadPart = DeviceExt->NtfsInfo.SectorCount * DeviceExt->NtfsInfo.BytesPerSector;
290   Fcb->RFCB.ValidDataLength.QuadPart = DeviceExt->NtfsInfo.SectorCount * DeviceExt->NtfsInfo.BytesPerSector;
291   Fcb->RFCB.AllocationSize.QuadPart = DeviceExt->NtfsInfo.SectorCount * DeviceExt->NtfsInfo.BytesPerSector; /* Correct? */
292
293 //  Fcb->Entry.ExtentLocationL = 0;
294 //  Fcb->Entry.DataLengthL = DeviceExt->CdInfo.VolumeSpaceSize * BLOCKSIZE;
295
296   Status = CcRosInitializeFileCache(DeviceExt->StreamFileObject,
297                                     CACHEPAGESIZE(DeviceExt));
298   if (!NT_SUCCESS (Status))
299     {
300       DbgPrint("CcRosInitializeFileCache() failed (Status %lx)\n", Status);
301       goto ByeBye;
302     }
303
304   ExInitializeResourceLite(&DeviceExt->DirResource);
305 //  ExInitializeResourceLite(&DeviceExt->FatResource);
306
307   KeInitializeSpinLock(&DeviceExt->FcbListLock);
308   InitializeListHead(&DeviceExt->FcbListHead);
309
310   /* Read serial number */
311   NewDeviceObject->Vpb->SerialNumber = DeviceExt->NtfsInfo.SerialNumber;
312
313   /* Read volume label */
314 //  NtfsReadVolumeLabel(DeviceExt,
315 //                    NewDeviceObject->Vpb);
316
317   Status = STATUS_SUCCESS;
318
319 ByeBye:
320   if (!NT_SUCCESS(Status))
321     {
322       /* Cleanup */
323       if (DeviceExt && DeviceExt->StreamFileObject)
324         ObDereferenceObject(DeviceExt->StreamFileObject);
325       if (Fcb)
326         ExFreePool(Fcb);
327       if (Ccb)
328         ExFreePool(Ccb);
329       if (NewDeviceObject)
330         IoDeleteDevice(NewDeviceObject);
331     }
332
333   DPRINT("NtfsMountVolume() done (Status: %lx)\n", Status);
334
335   return(Status);
336 }
337
338
339 static NTSTATUS
340 NtfsVerifyVolume(PDEVICE_OBJECT DeviceObject,
341                  PIRP Irp)
342 {
343 #if 0
344   PDEVICE_OBJECT DeviceToVerify;
345   PIO_STACK_LOCATION Stack;
346   PUCHAR Buffer;
347   ULONG Sector;
348   ULONG i;
349   NTSTATUS Status;
350
351   union
352     {
353       ULONG Value;
354       UCHAR Part[4];
355     } Serial;
356 #endif
357
358   DPRINT1("NtfsVerifyVolume() called\n");
359
360 #if 0
361   Stack = IoGetCurrentIrpStackLocation(Irp);
362   DeviceToVerify = Stack->Parameters.VerifyVolume.DeviceObject;
363
364   DPRINT("Device object %p  Device to verify %p\n", DeviceObject, DeviceToVerify);
365
366   Sector = CDFS_PRIMARY_DESCRIPTOR_LOCATION;
367
368   Buffer = ExAllocatePool(NonPagedPool,
369                           CDFS_BASIC_SECTOR);
370   if (Buffer == NULL)
371     {
372       return(STATUS_INSUFFICIENT_RESOURCES);
373     }
374
375   do
376     {
377       /* Read the Primary Volume Descriptor (PVD) */
378       Status = CdfsReadRawSectors(DeviceToVerify,
379                                   Sector,
380                                   1,
381                                   Buffer);
382       DPRINT("CdfsReadRawSectors() status %lx\n", Status);
383       if (!NT_SUCCESS(Status))
384         {
385           goto ByeBye;
386         }
387
388       if (Buffer[0] == 1 &&
389           Buffer[1] == 'C' &&
390           Buffer[2] == 'D' &&
391           Buffer[3] == '0' &&
392           Buffer[4] == '0' &&
393           Buffer[5] == '1')
394         {
395           break;
396         }
397
398       Sector++;
399     }
400   while (Buffer[0] != 255);
401
402   if (Buffer[0] == 255)
403     goto ByeBye;
404
405   Status = STATUS_WRONG_VOLUME;
406
407   /* Calculate the volume serial number */
408   Serial.Value = 0;
409   for (i = 0; i < 2048; i += 4)
410     {
411       /* DON'T optimize this to ULONG!!! (breaks overflow) */
412       Serial.Part[0] += Buffer[i+3];
413       Serial.Part[1] += Buffer[i+2];
414       Serial.Part[2] += Buffer[i+1];
415       Serial.Part[3] += Buffer[i+0];
416     }
417
418   DPRINT("Current serial number %08lx  Vpb serial number %08lx\n",
419           Serial.Value, DeviceToVerify->Vpb->SerialNumber);
420
421   if (Serial.Value == DeviceToVerify->Vpb->SerialNumber)
422     Status = STATUS_SUCCESS;
423
424 ByeBye:
425   ExFreePool(Buffer);
426
427   DPRINT("CdfsVerifyVolume() done (Status: %lx)\n", Status);
428
429   return(Status);
430 #endif
431   return(STATUS_UNSUCCESSFUL);
432 }
433
434
435 NTSTATUS STDCALL
436 NtfsFileSystemControl(PDEVICE_OBJECT DeviceObject,
437                       PIRP Irp)
438 {
439   PIO_STACK_LOCATION Stack;
440   NTSTATUS Status;
441
442   DPRINT("NtfsFileSystemControl() called\n");
443
444   Stack = IoGetCurrentIrpStackLocation(Irp);
445
446   switch (Stack->MinorFunction)
447     {
448       case IRP_MN_USER_FS_REQUEST:
449         DPRINT("NTFS: IRP_MN_USER_FS_REQUEST\n");
450         Status = STATUS_INVALID_DEVICE_REQUEST;
451         break;
452
453       case IRP_MN_MOUNT_VOLUME:
454         DPRINT("NTFS: IRP_MN_MOUNT_VOLUME\n");
455         Status = NtfsMountVolume(DeviceObject, Irp);
456         break;
457
458       case IRP_MN_VERIFY_VOLUME:
459         DPRINT1("NTFS: IRP_MN_VERIFY_VOLUME\n");
460         Status = NtfsVerifyVolume(DeviceObject, Irp);
461         break;
462
463       default:
464         DPRINT("NTFS FSC: MinorFunction %d\n", Stack->MinorFunction);
465         Status = STATUS_INVALID_DEVICE_REQUEST;
466         break;
467     }
468
469   Irp->IoStatus.Status = Status;
470   Irp->IoStatus.Information = 0;
471
472   IoCompleteRequest(Irp, IO_NO_INCREMENT);
473
474   return(Status);
475 }
476
477 /* EOF */