6fd04800e5fa81b94368864a75c286776e831f31
[reactos.git] / reactos / drivers / filesystems / fastfat / volume.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/fs/vfat/volume.c
5 * PURPOSE: VFAT Filesystem
6 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
7 * Herve Poussineau (reactos@poussine.freesurf.fr)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #define NDEBUG
13 #include "vfat.h"
14
15 /* FUNCTIONS ****************************************************************/
16
17 static NTSTATUS
18 FsdGetFsVolumeInformation(PDEVICE_OBJECT DeviceObject,
19 PFILE_FS_VOLUME_INFORMATION FsVolumeInfo,
20 PULONG BufferLength)
21 {
22 DPRINT("FsdGetFsVolumeInformation()\n");
23 DPRINT("FsVolumeInfo = %p\n", FsVolumeInfo);
24 DPRINT("BufferLength %lu\n", *BufferLength);
25
26 DPRINT("Required length %lu\n", (sizeof(FILE_FS_VOLUME_INFORMATION) + DeviceObject->Vpb->VolumeLabelLength));
27 DPRINT("LabelLength %hu\n", DeviceObject->Vpb->VolumeLabelLength);
28 DPRINT("Label %*.S\n", DeviceObject->Vpb->VolumeLabelLength / sizeof(WCHAR), DeviceObject->Vpb->VolumeLabel);
29
30 if (*BufferLength < sizeof(FILE_FS_VOLUME_INFORMATION))
31 return STATUS_INFO_LENGTH_MISMATCH;
32
33 if (*BufferLength < (sizeof(FILE_FS_VOLUME_INFORMATION) + DeviceObject->Vpb->VolumeLabelLength))
34 return STATUS_BUFFER_OVERFLOW;
35
36 /* valid entries */
37 FsVolumeInfo->VolumeSerialNumber = DeviceObject->Vpb->SerialNumber;
38 FsVolumeInfo->VolumeLabelLength = DeviceObject->Vpb->VolumeLabelLength;
39 RtlCopyMemory(FsVolumeInfo->VolumeLabel, DeviceObject->Vpb->VolumeLabel, FsVolumeInfo->VolumeLabelLength);
40
41 /* dummy entries */
42 FsVolumeInfo->VolumeCreationTime.QuadPart = 0;
43 FsVolumeInfo->SupportsObjects = FALSE;
44
45 DPRINT("Finished FsdGetFsVolumeInformation()\n");
46
47 *BufferLength -= (sizeof(FILE_FS_VOLUME_INFORMATION) + DeviceObject->Vpb->VolumeLabelLength);
48
49 DPRINT("BufferLength %lu\n", *BufferLength);
50
51 return(STATUS_SUCCESS);
52 }
53
54
55 static NTSTATUS
56 FsdGetFsAttributeInformation(PDEVICE_EXTENSION DeviceExt,
57 PFILE_FS_ATTRIBUTE_INFORMATION FsAttributeInfo,
58 PULONG BufferLength)
59 {
60 PCWSTR pName; ULONG Length;
61 DPRINT("FsdGetFsAttributeInformation()\n");
62 DPRINT("FsAttributeInfo = %p\n", FsAttributeInfo);
63 DPRINT("BufferLength %lu\n", *BufferLength);
64
65 if (*BufferLength < sizeof (FILE_FS_ATTRIBUTE_INFORMATION))
66 return STATUS_INFO_LENGTH_MISMATCH;
67
68 if (DeviceExt->FatInfo.FatType == FAT32)
69 {
70 Length = 10;
71 pName = L"FAT32";
72 }
73 else
74 {
75 Length = 6;
76 pName = L"FAT";
77 }
78
79 DPRINT("Required length %lu\n", (sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + Length));
80
81 if (*BufferLength < (sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + Length))
82 return STATUS_BUFFER_OVERFLOW;
83
84 FsAttributeInfo->FileSystemAttributes =
85 FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK;
86
87 FsAttributeInfo->MaximumComponentNameLength = 255;
88
89 FsAttributeInfo->FileSystemNameLength = Length;
90
91 RtlCopyMemory(FsAttributeInfo->FileSystemName, pName, Length );
92
93 DPRINT("Finished FsdGetFsAttributeInformation()\n");
94
95 *BufferLength -= (sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + Length);
96 DPRINT("BufferLength %lu\n", *BufferLength);
97
98 return(STATUS_SUCCESS);
99 }
100
101
102 static NTSTATUS
103 FsdGetFsSizeInformation(PDEVICE_OBJECT DeviceObject,
104 PFILE_FS_SIZE_INFORMATION FsSizeInfo,
105 PULONG BufferLength)
106 {
107 PDEVICE_EXTENSION DeviceExt;
108 NTSTATUS Status;
109
110 DPRINT("FsdGetFsSizeInformation()\n");
111 DPRINT("FsSizeInfo = %p\n", FsSizeInfo);
112
113 if (*BufferLength < sizeof(FILE_FS_SIZE_INFORMATION))
114 return(STATUS_BUFFER_OVERFLOW);
115
116 DeviceExt = DeviceObject->DeviceExtension;
117 Status = CountAvailableClusters(DeviceExt, &FsSizeInfo->AvailableAllocationUnits);
118
119 FsSizeInfo->TotalAllocationUnits.QuadPart = DeviceExt->FatInfo.NumberOfClusters;
120 FsSizeInfo->SectorsPerAllocationUnit = DeviceExt->FatInfo.SectorsPerCluster;
121 FsSizeInfo->BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
122
123 DPRINT("Finished FsdGetFsSizeInformation()\n");
124 if (NT_SUCCESS(Status))
125 *BufferLength -= sizeof(FILE_FS_SIZE_INFORMATION);
126
127 return(Status);
128 }
129
130
131 static NTSTATUS
132 FsdGetFsDeviceInformation
133 (
134 PDEVICE_OBJECT DeviceObject,
135 PFILE_FS_DEVICE_INFORMATION FsDeviceInfo,
136 PULONG BufferLength
137 )
138 {
139 DPRINT("FsdGetFsDeviceInformation()\n");
140 DPRINT("FsDeviceInfo = %p\n", FsDeviceInfo);
141 DPRINT("BufferLength %lu\n", *BufferLength);
142 DPRINT("Required length %lu\n", sizeof(FILE_FS_DEVICE_INFORMATION));
143
144 if (*BufferLength < sizeof(FILE_FS_DEVICE_INFORMATION))
145 return(STATUS_BUFFER_OVERFLOW);
146
147 FsDeviceInfo->DeviceType = FILE_DEVICE_DISK;
148 FsDeviceInfo->Characteristics = DeviceObject->Characteristics;
149
150 DPRINT("FsdGetFsDeviceInformation() finished.\n");
151
152 *BufferLength -= sizeof(FILE_FS_DEVICE_INFORMATION);
153 DPRINT("BufferLength %lu\n", *BufferLength);
154
155 return(STATUS_SUCCESS);
156 }
157
158
159 static NTSTATUS
160 FsdSetFsLabelInformation(PDEVICE_OBJECT DeviceObject,
161 PFILE_FS_LABEL_INFORMATION FsLabelInfo)
162 {
163 PDEVICE_EXTENSION DeviceExt;
164 PVOID Context = NULL;
165 ULONG DirIndex = 0;
166 PDIR_ENTRY Entry;
167 PVFATFCB pRootFcb;
168 LARGE_INTEGER FileOffset;
169 BOOLEAN LabelFound = FALSE;
170 DIR_ENTRY VolumeLabelDirEntry;
171 ULONG VolumeLabelDirIndex;
172 ULONG LabelLen;
173 NTSTATUS Status = STATUS_UNSUCCESSFUL;
174 OEM_STRING StringO;
175 UNICODE_STRING StringW;
176 CHAR cString[43];
177 ULONG SizeDirEntry;
178 ULONG EntriesPerPage;
179
180 DPRINT("FsdSetFsLabelInformation()\n");
181
182 DeviceExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
183
184 if (sizeof(DeviceObject->Vpb->VolumeLabel) < FsLabelInfo->VolumeLabelLength)
185 {
186 return STATUS_NAME_TOO_LONG;
187 }
188
189 if (DeviceExt->Flags & VCB_IS_FATX)
190 {
191 if (FsLabelInfo->VolumeLabelLength / sizeof(WCHAR) > 42)
192 return STATUS_NAME_TOO_LONG;
193 SizeDirEntry = sizeof(FATX_DIR_ENTRY);
194 EntriesPerPage = FATX_ENTRIES_PER_PAGE;
195 }
196 else
197 {
198 if (FsLabelInfo->VolumeLabelLength / sizeof(WCHAR) > 11)
199 return STATUS_NAME_TOO_LONG;
200 SizeDirEntry = sizeof(FAT_DIR_ENTRY);
201 EntriesPerPage = FAT_ENTRIES_PER_PAGE;
202 }
203
204 /* Create Volume label dir entry */
205 LabelLen = FsLabelInfo->VolumeLabelLength / sizeof(WCHAR);
206 RtlZeroMemory(&VolumeLabelDirEntry, SizeDirEntry);
207 StringW.Buffer = FsLabelInfo->VolumeLabel;
208 StringW.Length = StringW.MaximumLength = (USHORT)FsLabelInfo->VolumeLabelLength;
209 StringO.Buffer = cString;
210 StringO.Length = 0;
211 StringO.MaximumLength = 42;
212 Status = RtlUnicodeStringToOemString(&StringO, &StringW, FALSE);
213 if (!NT_SUCCESS(Status))
214 return Status;
215 if (DeviceExt->Flags & VCB_IS_FATX)
216 {
217 RtlCopyMemory(VolumeLabelDirEntry.FatX.Filename, cString, LabelLen);
218 memset(&VolumeLabelDirEntry.FatX.Filename[LabelLen], ' ', 42 - LabelLen);
219 VolumeLabelDirEntry.FatX.Attrib = 0x08;
220 }
221 else
222 {
223 RtlCopyMemory(VolumeLabelDirEntry.Fat.Filename, cString, max(sizeof(VolumeLabelDirEntry.Fat.Filename), LabelLen));
224 if (LabelLen > sizeof(VolumeLabelDirEntry.Fat.Filename))
225 {
226 memset(VolumeLabelDirEntry.Fat.Ext, ' ', sizeof(VolumeLabelDirEntry.Fat.Ext));
227 RtlCopyMemory(VolumeLabelDirEntry.Fat.Ext, cString + sizeof(VolumeLabelDirEntry.Fat.Filename), LabelLen - sizeof(VolumeLabelDirEntry.Fat.Filename));
228 }
229 else
230 {
231 memset(&VolumeLabelDirEntry.Fat.Filename[LabelLen], ' ', sizeof(VolumeLabelDirEntry.Fat.Filename) - LabelLen);
232 }
233 VolumeLabelDirEntry.Fat.Attrib = 0x08;
234 }
235
236 pRootFcb = vfatOpenRootFCB(DeviceExt);
237
238 /* Search existing volume entry on disk */
239 FileOffset.QuadPart = 0;
240 if (CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, TRUE, &Context, (PVOID*)&Entry))
241 {
242 while (TRUE)
243 {
244 if (ENTRY_VOLUME(DeviceExt, Entry))
245 {
246 /* Update entry */
247 LabelFound = TRUE;
248 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry);
249 CcSetDirtyPinnedData(Context, NULL);
250 Status = STATUS_SUCCESS;
251 break;
252 }
253 if (ENTRY_END(DeviceExt, Entry))
254 {
255 break;
256 }
257 DirIndex++;
258 Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry);
259 if ((DirIndex % EntriesPerPage) == 0)
260 {
261 CcUnpinData(Context);
262 FileOffset.u.LowPart += PAGE_SIZE;
263 if (!CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, TRUE, &Context, (PVOID*)&Entry))
264 {
265 Context = NULL;
266 break;
267 }
268 }
269 }
270 if (Context)
271 {
272 CcUnpinData(Context);
273 }
274 }
275 if (!LabelFound)
276 {
277 /* Add new entry for label */
278 if (!vfatFindDirSpace(DeviceExt, pRootFcb, 1, &VolumeLabelDirIndex))
279 Status = STATUS_DISK_FULL;
280 else
281 {
282 FileOffset.u.HighPart = 0;
283 FileOffset.u.LowPart = VolumeLabelDirIndex * SizeDirEntry;
284 CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry,
285 TRUE, &Context, (PVOID*)&Entry);
286 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry);
287 CcSetDirtyPinnedData(Context, NULL);
288 CcUnpinData(Context);
289 Status = STATUS_SUCCESS;
290 }
291 }
292
293 vfatReleaseFCB(DeviceExt, pRootFcb);
294 if (!NT_SUCCESS(Status))
295 {
296 return Status;
297 }
298
299 /* Update volume label in memory */
300 DeviceObject->Vpb->VolumeLabelLength = (USHORT)FsLabelInfo->VolumeLabelLength;
301 RtlCopyMemory(DeviceObject->Vpb->VolumeLabel, FsLabelInfo->VolumeLabel, DeviceObject->Vpb->VolumeLabelLength);
302
303 return Status;
304 }
305
306
307 NTSTATUS VfatQueryVolumeInformation(PVFAT_IRP_CONTEXT IrpContext)
308 /*
309 * FUNCTION: Retrieve the specified volume information
310 */
311 {
312 FS_INFORMATION_CLASS FsInformationClass;
313 NTSTATUS RC = STATUS_SUCCESS;
314 PVOID SystemBuffer;
315 ULONG BufferLength;
316
317 /* PRECONDITION */
318 ASSERT(IrpContext);
319
320 DPRINT("VfatQueryVolumeInformation(IrpContext %p)\n", IrpContext);
321
322 if (!ExAcquireResourceSharedLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
323 (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
324 {
325 return VfatQueueRequest (IrpContext);
326 }
327
328 /* INITIALIZATION */
329 FsInformationClass = IrpContext->Stack->Parameters.QueryVolume.FsInformationClass;
330 BufferLength = IrpContext->Stack->Parameters.QueryVolume.Length;
331 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
332
333
334 DPRINT ("FsInformationClass %d\n", FsInformationClass);
335 DPRINT ("SystemBuffer %p\n", SystemBuffer);
336
337 switch (FsInformationClass)
338 {
339 case FileFsVolumeInformation:
340 RC = FsdGetFsVolumeInformation(IrpContext->DeviceObject,
341 SystemBuffer,
342 &BufferLength);
343 break;
344
345 case FileFsAttributeInformation:
346 RC = FsdGetFsAttributeInformation(IrpContext->DeviceObject->DeviceExtension,
347 SystemBuffer,
348 &BufferLength);
349 break;
350
351 case FileFsSizeInformation:
352 RC = FsdGetFsSizeInformation(IrpContext->DeviceObject,
353 SystemBuffer,
354 &BufferLength);
355 break;
356
357 case FileFsDeviceInformation:
358 RC = FsdGetFsDeviceInformation(IrpContext->DeviceObject,
359 SystemBuffer,
360 &BufferLength);
361 break;
362
363 default:
364 RC = STATUS_NOT_SUPPORTED;
365 }
366
367 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
368 IrpContext->Irp->IoStatus.Status = RC;
369 if (NT_SUCCESS(RC))
370 IrpContext->Irp->IoStatus.Information =
371 IrpContext->Stack->Parameters.QueryVolume.Length - BufferLength;
372 else
373 IrpContext->Irp->IoStatus.Information = 0;
374 IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
375 VfatFreeIrpContext(IrpContext);
376
377 return RC;
378 }
379
380
381 NTSTATUS VfatSetVolumeInformation(PVFAT_IRP_CONTEXT IrpContext)
382 /*
383 * FUNCTION: Set the specified volume information
384 */
385 {
386 FS_INFORMATION_CLASS FsInformationClass;
387 NTSTATUS Status = STATUS_SUCCESS;
388 PVOID SystemBuffer;
389 ULONG BufferLength;
390 PIO_STACK_LOCATION Stack = IrpContext->Stack;
391
392 /* PRECONDITION */
393 ASSERT(IrpContext);
394
395 DPRINT ("VfatSetVolumeInformation(IrpContext %p)\n", IrpContext);
396
397 if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
398 (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
399 {
400 return VfatQueueRequest (IrpContext);
401 }
402
403 FsInformationClass = Stack->Parameters.SetVolume.FsInformationClass;
404 BufferLength = Stack->Parameters.SetVolume.Length;
405 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
406
407 DPRINT ("FsInformationClass %d\n", FsInformationClass);
408 DPRINT ("BufferLength %d\n", BufferLength);
409 DPRINT ("SystemBuffer %p\n", SystemBuffer);
410
411 switch(FsInformationClass)
412 {
413 case FileFsLabelInformation:
414 Status = FsdSetFsLabelInformation(IrpContext->DeviceObject,
415 SystemBuffer);
416 break;
417
418 default:
419 Status = STATUS_NOT_SUPPORTED;
420 }
421
422 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
423 IrpContext->Irp->IoStatus.Status = Status;
424 IrpContext->Irp->IoStatus.Information = 0;
425 IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
426 VfatFreeIrpContext(IrpContext);
427
428 return(Status);
429 }
430
431 /* EOF */