xref: /reactos/drivers/filesystems/vfatfs/volume.c (revision 1de09c47)
1 /*
2  * PROJECT:     VFAT Filesystem
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Volume routines
5  * COPYRIGHT:   Copyright 1998 Jason Filby <jasonfilby@yahoo.com>
6  *              Copyright 2004-2022 Hervé Poussineau <hpoussin@reactos.org>
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include "vfat.h"
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* FUNCTIONS ****************************************************************/
17 
18 static
19 NTSTATUS
20 FsdGetFsVolumeInformation(
21     PDEVICE_OBJECT DeviceObject,
22     PFILE_FS_VOLUME_INFORMATION FsVolumeInfo,
23     PULONG BufferLength)
24 {
25     NTSTATUS Status;
26     PDEVICE_EXTENSION DeviceExt;
27 
28     DPRINT("FsdGetFsVolumeInformation()\n");
29     DPRINT("FsVolumeInfo = %p\n", FsVolumeInfo);
30     DPRINT("BufferLength %lu\n", *BufferLength);
31 
32     DPRINT("Required length %lu\n", FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + DeviceObject->Vpb->VolumeLabelLength);
33     DPRINT("LabelLength %hu\n", DeviceObject->Vpb->VolumeLabelLength);
34     DPRINT("Label %.*S\n", DeviceObject->Vpb->VolumeLabelLength / sizeof(WCHAR), DeviceObject->Vpb->VolumeLabel);
35 
36     ASSERT(*BufferLength >= sizeof(FILE_FS_VOLUME_INFORMATION));
37     *BufferLength -= FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel);
38 
39     DeviceExt = DeviceObject->DeviceExtension;
40 
41     /* valid entries */
42     FsVolumeInfo->VolumeSerialNumber = DeviceObject->Vpb->SerialNumber;
43     FsVolumeInfo->VolumeLabelLength = DeviceObject->Vpb->VolumeLabelLength;
44     if (*BufferLength < DeviceObject->Vpb->VolumeLabelLength)
45     {
46         Status =  STATUS_BUFFER_OVERFLOW;
47         RtlCopyMemory(FsVolumeInfo->VolumeLabel,
48                       DeviceObject->Vpb->VolumeLabel,
49                       *BufferLength);
50     }
51     else
52     {
53         Status =  STATUS_SUCCESS;
54         RtlCopyMemory(FsVolumeInfo->VolumeLabel,
55                       DeviceObject->Vpb->VolumeLabel,
56                       FsVolumeInfo->VolumeLabelLength);
57         *BufferLength -= DeviceObject->Vpb->VolumeLabelLength;
58     }
59 
60     if (vfatVolumeIsFatX(DeviceExt))
61     {
62         FsdDosDateTimeToSystemTime(DeviceExt,
63                                    DeviceExt->VolumeFcb->entry.FatX.CreationDate,
64                                    DeviceExt->VolumeFcb->entry.FatX.CreationTime,
65                                    &FsVolumeInfo->VolumeCreationTime);
66     }
67     else
68     {
69         FsdDosDateTimeToSystemTime(DeviceExt,
70                                    DeviceExt->VolumeFcb->entry.Fat.CreationDate,
71                                    DeviceExt->VolumeFcb->entry.Fat.CreationTime,
72                                    &FsVolumeInfo->VolumeCreationTime);
73     }
74 
75     FsVolumeInfo->SupportsObjects = FALSE;
76 
77     DPRINT("Finished FsdGetFsVolumeInformation()\n");
78     DPRINT("BufferLength %lu\n", *BufferLength);
79 
80     return Status;
81 }
82 
83 
84 static
85 NTSTATUS
86 FsdGetFsAttributeInformation(
87     PDEVICE_EXTENSION DeviceExt,
88     PFILE_FS_ATTRIBUTE_INFORMATION FsAttributeInfo,
89     PULONG BufferLength)
90 {
91     NTSTATUS Status;
92     PCWSTR pName;
93     ULONG Length;
94 
95     DPRINT("FsdGetFsAttributeInformation()\n");
96     DPRINT("FsAttributeInfo = %p\n", FsAttributeInfo);
97     DPRINT("BufferLength %lu\n", *BufferLength);
98 
99     ASSERT(*BufferLength >= sizeof(FILE_FS_ATTRIBUTE_INFORMATION));
100     *BufferLength -= FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName);
101 
102     switch (DeviceExt->FatInfo.FatType)
103     {
104         case FAT12: pName = L"FAT"; break;
105         case FAT16: pName = L"FAT"; break;
106         case FAT32: pName = L"FAT32"; break;
107         case FATX16: pName = L"FATX"; break;
108         case FATX32: pName = L"FATX"; break;
109         default: return STATUS_NOT_SUPPORTED;
110     }
111 
112     Length = wcslen(pName) * sizeof(WCHAR);
113     DPRINT("Required length %lu\n", (FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName) + Length));
114 
115     if (*BufferLength < Length)
116     {
117         Status = STATUS_BUFFER_OVERFLOW;
118         Length = *BufferLength;
119     }
120     else
121     {
122         Status = STATUS_SUCCESS;
123     }
124 
125     FsAttributeInfo->FileSystemAttributes =
126         FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK;
127 
128     FsAttributeInfo->MaximumComponentNameLength = 255;
129 
130     FsAttributeInfo->FileSystemNameLength = Length;
131 
132     RtlCopyMemory(FsAttributeInfo->FileSystemName, pName, Length);
133 
134     DPRINT("Finished FsdGetFsAttributeInformation()\n");
135 
136     *BufferLength -= Length;
137     DPRINT("BufferLength %lu\n", *BufferLength);
138 
139     return Status;
140 }
141 
142 
143 static
144 NTSTATUS
145 FsdGetFsSizeInformation(
146     PDEVICE_OBJECT DeviceObject,
147     PFILE_FS_SIZE_INFORMATION FsSizeInfo,
148     PULONG BufferLength)
149 {
150     PDEVICE_EXTENSION DeviceExt;
151     NTSTATUS Status;
152 
153     DPRINT("FsdGetFsSizeInformation()\n");
154     DPRINT("FsSizeInfo = %p\n", FsSizeInfo);
155 
156     ASSERT(*BufferLength >= sizeof(FILE_FS_SIZE_INFORMATION));
157 
158     DeviceExt = DeviceObject->DeviceExtension;
159     Status = CountAvailableClusters(DeviceExt, &FsSizeInfo->AvailableAllocationUnits);
160 
161     FsSizeInfo->TotalAllocationUnits.QuadPart = DeviceExt->FatInfo.NumberOfClusters;
162     FsSizeInfo->SectorsPerAllocationUnit = DeviceExt->FatInfo.SectorsPerCluster;
163     FsSizeInfo->BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
164 
165     DPRINT("Finished FsdGetFsSizeInformation()\n");
166     if (NT_SUCCESS(Status))
167         *BufferLength -= sizeof(FILE_FS_SIZE_INFORMATION);
168 
169     return Status;
170 }
171 
172 
173 static
174 NTSTATUS
175 FsdGetFsDeviceInformation(
176     PDEVICE_OBJECT DeviceObject,
177     PFILE_FS_DEVICE_INFORMATION FsDeviceInfo,
178     PULONG BufferLength)
179 {
180     DPRINT("FsdGetFsDeviceInformation()\n");
181     DPRINT("FsDeviceInfo = %p\n", FsDeviceInfo);
182     DPRINT("BufferLength %lu\n", *BufferLength);
183     DPRINT("Required length %lu\n", sizeof(FILE_FS_DEVICE_INFORMATION));
184 
185     ASSERT(*BufferLength >= sizeof(FILE_FS_DEVICE_INFORMATION));
186 
187     FsDeviceInfo->DeviceType = FILE_DEVICE_DISK;
188     FsDeviceInfo->Characteristics = DeviceObject->Characteristics;
189 
190     DPRINT("FsdGetFsDeviceInformation() finished.\n");
191 
192     *BufferLength -= sizeof(FILE_FS_DEVICE_INFORMATION);
193     DPRINT("BufferLength %lu\n", *BufferLength);
194 
195     return STATUS_SUCCESS;
196 }
197 
198 
199 static
200 NTSTATUS
201 FsdGetFsFullSizeInformation(
202     PDEVICE_OBJECT DeviceObject,
203     PFILE_FS_FULL_SIZE_INFORMATION FsSizeInfo,
204     PULONG BufferLength)
205 {
206     PDEVICE_EXTENSION DeviceExt;
207     NTSTATUS Status;
208 
209     DPRINT("FsdGetFsFullSizeInformation()\n");
210     DPRINT("FsSizeInfo = %p\n", FsSizeInfo);
211 
212     ASSERT(*BufferLength >= sizeof(FILE_FS_FULL_SIZE_INFORMATION));
213 
214     DeviceExt = DeviceObject->DeviceExtension;
215     Status = CountAvailableClusters(DeviceExt, &FsSizeInfo->CallerAvailableAllocationUnits);
216 
217     FsSizeInfo->TotalAllocationUnits.QuadPart = DeviceExt->FatInfo.NumberOfClusters;
218     FsSizeInfo->ActualAvailableAllocationUnits.QuadPart = FsSizeInfo->CallerAvailableAllocationUnits.QuadPart;
219     FsSizeInfo->SectorsPerAllocationUnit = DeviceExt->FatInfo.SectorsPerCluster;
220     FsSizeInfo->BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
221 
222     DPRINT("Finished FsdGetFsFullSizeInformation()\n");
223     if (NT_SUCCESS(Status))
224         *BufferLength -= sizeof(FILE_FS_FULL_SIZE_INFORMATION);
225 
226     return Status;
227 }
228 
229 
230 static
231 NTSTATUS
232 FsdSetFsLabelInformation(
233     PDEVICE_OBJECT DeviceObject,
234     PFILE_FS_LABEL_INFORMATION FsLabelInfo)
235 {
236     PDEVICE_EXTENSION DeviceExt;
237     PVOID Context = NULL;
238     ULONG DirIndex = 0;
239     PDIR_ENTRY Entry;
240     PVFATFCB pRootFcb;
241     LARGE_INTEGER FileOffset;
242     BOOLEAN LabelFound = FALSE;
243     DIR_ENTRY VolumeLabelDirEntry;
244     ULONG VolumeLabelDirIndex;
245     ULONG LabelLen;
246     NTSTATUS Status = STATUS_UNSUCCESSFUL;
247     OEM_STRING StringO;
248     UNICODE_STRING StringW;
249     CHAR cString[43];
250     ULONG SizeDirEntry;
251     ULONG EntriesPerPage;
252     BOOLEAN IsFatX;
253 
254     DPRINT("FsdSetFsLabelInformation()\n");
255 
256     DeviceExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
257     IsFatX = vfatVolumeIsFatX(DeviceExt);
258 
259     if (sizeof(DeviceObject->Vpb->VolumeLabel) < FsLabelInfo->VolumeLabelLength)
260     {
261         return STATUS_NAME_TOO_LONG;
262     }
263 
264     if (IsFatX)
265     {
266         if (FsLabelInfo->VolumeLabelLength / sizeof(WCHAR) > 42)
267             return STATUS_NAME_TOO_LONG;
268 
269         SizeDirEntry = sizeof(FATX_DIR_ENTRY);
270         EntriesPerPage = FATX_ENTRIES_PER_PAGE;
271     }
272     else
273     {
274         if (FsLabelInfo->VolumeLabelLength / sizeof(WCHAR) > 11)
275             return STATUS_NAME_TOO_LONG;
276 
277         SizeDirEntry = sizeof(FAT_DIR_ENTRY);
278         EntriesPerPage = FAT_ENTRIES_PER_PAGE;
279     }
280 
281     /* Create Volume label dir entry */
282     LabelLen = FsLabelInfo->VolumeLabelLength / sizeof(WCHAR);
283     RtlZeroMemory(&VolumeLabelDirEntry, SizeDirEntry);
284     StringW.Buffer = FsLabelInfo->VolumeLabel;
285     StringW.Length = StringW.MaximumLength = (USHORT)FsLabelInfo->VolumeLabelLength;
286     StringO.Buffer = cString;
287     StringO.Length = 0;
288     StringO.MaximumLength = 42;
289     Status = RtlUnicodeStringToOemString(&StringO, &StringW, FALSE);
290     if (!NT_SUCCESS(Status))
291         return Status;
292 
293     if (IsFatX)
294     {
295         RtlCopyMemory(VolumeLabelDirEntry.FatX.Filename, cString, LabelLen);
296         memset(&VolumeLabelDirEntry.FatX.Filename[LabelLen], ' ', 42 - LabelLen);
297         VolumeLabelDirEntry.FatX.Attrib = _A_VOLID;
298     }
299     else
300     {
301         RtlCopyMemory(VolumeLabelDirEntry.Fat.Filename, cString, max(sizeof(VolumeLabelDirEntry.Fat.Filename), LabelLen));
302         if (LabelLen > sizeof(VolumeLabelDirEntry.Fat.Filename))
303         {
304             memset(VolumeLabelDirEntry.Fat.Ext, ' ', sizeof(VolumeLabelDirEntry.Fat.Ext));
305             RtlCopyMemory(VolumeLabelDirEntry.Fat.Ext, cString + sizeof(VolumeLabelDirEntry.Fat.Filename), LabelLen - sizeof(VolumeLabelDirEntry.Fat.Filename));
306         }
307         else
308         {
309             memset(&VolumeLabelDirEntry.Fat.Filename[LabelLen], ' ', sizeof(VolumeLabelDirEntry.Fat.Filename) - LabelLen);
310         }
311         VolumeLabelDirEntry.Fat.Attrib = _A_VOLID;
312     }
313 
314     pRootFcb = vfatOpenRootFCB(DeviceExt);
315     Status = vfatFCBInitializeCacheFromVolume(DeviceExt, pRootFcb);
316     if (!NT_SUCCESS(Status))
317     {
318         return Status;
319     }
320 
321     /* Search existing volume entry on disk */
322     FileOffset.QuadPart = 0;
323     _SEH2_TRY
324     {
325         CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&Entry);
326     }
327     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
328     {
329         Status = _SEH2_GetExceptionCode();
330     }
331     _SEH2_END;
332 
333     if (NT_SUCCESS(Status))
334     {
335         while (TRUE)
336         {
337             if (ENTRY_VOLUME(IsFatX, Entry))
338             {
339                 /* Update entry */
340                 LabelFound = TRUE;
341                 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry);
342                 CcSetDirtyPinnedData(Context, NULL);
343                 Status = STATUS_SUCCESS;
344                 break;
345             }
346 
347             if (ENTRY_END(IsFatX, Entry))
348             {
349                 break;
350             }
351 
352             DirIndex++;
353             Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry);
354             if ((DirIndex % EntriesPerPage) == 0)
355             {
356                 CcUnpinData(Context);
357                 FileOffset.u.LowPart += PAGE_SIZE;
358                 _SEH2_TRY
359                 {
360                     CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&Entry);
361                 }
362                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
363                 {
364                     Status = _SEH2_GetExceptionCode();
365                 }
366                 _SEH2_END;
367 
368                 if (!NT_SUCCESS(Status))
369                 {
370                     Context = NULL;
371                     break;
372                 }
373             }
374         }
375 
376         if (Context)
377         {
378             CcUnpinData(Context);
379         }
380     }
381 
382     if (!LabelFound)
383     {
384         /* Add new entry for label */
385         if (!vfatFindDirSpace(DeviceExt, pRootFcb, 1, &VolumeLabelDirIndex))
386             Status = STATUS_DISK_FULL;
387         else
388         {
389             FileOffset.u.HighPart = 0;
390             FileOffset.u.LowPart = VolumeLabelDirIndex * SizeDirEntry;
391 
392             Status = STATUS_SUCCESS;
393             _SEH2_TRY
394             {
395                 CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&Entry);
396             }
397             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
398             {
399                 Status = _SEH2_GetExceptionCode();
400             }
401             _SEH2_END;
402 
403             if (NT_SUCCESS(Status))
404             {
405                 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry);
406                 CcSetDirtyPinnedData(Context, NULL);
407                 CcUnpinData(Context);
408                 Status = STATUS_SUCCESS;
409             }
410         }
411     }
412 
413     vfatReleaseFCB(DeviceExt, pRootFcb);
414     if (!NT_SUCCESS(Status))
415     {
416         return Status;
417     }
418 
419     /* Update volume label in memory */
420     DeviceObject->Vpb->VolumeLabelLength = (USHORT)FsLabelInfo->VolumeLabelLength;
421     RtlCopyMemory(DeviceObject->Vpb->VolumeLabel, FsLabelInfo->VolumeLabel, DeviceObject->Vpb->VolumeLabelLength);
422 
423     return Status;
424 }
425 
426 
427 /*
428  * FUNCTION: Retrieve the specified volume information
429  */
430 NTSTATUS
431 VfatQueryVolumeInformation(
432     PVFAT_IRP_CONTEXT IrpContext)
433 {
434     FS_INFORMATION_CLASS FsInformationClass;
435     NTSTATUS RC = STATUS_SUCCESS;
436     PVOID SystemBuffer;
437     ULONG BufferLength;
438 
439     /* PRECONDITION */
440     ASSERT(IrpContext);
441 
442     DPRINT("VfatQueryVolumeInformation(IrpContext %p)\n", IrpContext);
443 
444     if (!ExAcquireResourceSharedLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
445                                      BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
446     {
447         DPRINT1("DirResource failed!\n");
448         return VfatMarkIrpContextForQueue(IrpContext);
449     }
450 
451     /* INITIALIZATION */
452     FsInformationClass = IrpContext->Stack->Parameters.QueryVolume.FsInformationClass;
453     BufferLength = IrpContext->Stack->Parameters.QueryVolume.Length;
454     SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
455 
456     DPRINT("FsInformationClass %d\n", FsInformationClass);
457     DPRINT("SystemBuffer %p\n", SystemBuffer);
458 
459     RtlZeroMemory(SystemBuffer, BufferLength);
460 
461     switch (FsInformationClass)
462     {
463         case FileFsVolumeInformation:
464             RC = FsdGetFsVolumeInformation(IrpContext->DeviceObject,
465                                            SystemBuffer,
466                                            &BufferLength);
467             break;
468 
469         case FileFsAttributeInformation:
470             RC = FsdGetFsAttributeInformation(IrpContext->DeviceObject->DeviceExtension,
471                                               SystemBuffer,
472                                               &BufferLength);
473             break;
474 
475         case FileFsSizeInformation:
476             RC = FsdGetFsSizeInformation(IrpContext->DeviceObject,
477                                          SystemBuffer,
478                                          &BufferLength);
479             break;
480 
481         case FileFsDeviceInformation:
482             RC = FsdGetFsDeviceInformation(IrpContext->DeviceObject,
483                                            SystemBuffer,
484                                            &BufferLength);
485             break;
486 
487         case FileFsFullSizeInformation:
488             RC = FsdGetFsFullSizeInformation(IrpContext->DeviceObject,
489                                              SystemBuffer,
490                                              &BufferLength);
491             break;
492 
493         default:
494             RC = STATUS_NOT_SUPPORTED;
495     }
496 
497     ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
498 
499     IrpContext->Irp->IoStatus.Information =
500         IrpContext->Stack->Parameters.QueryVolume.Length - BufferLength;
501 
502     return RC;
503 }
504 
505 
506 /*
507  * FUNCTION: Set the specified volume information
508  */
509 NTSTATUS
510 VfatSetVolumeInformation(
511     PVFAT_IRP_CONTEXT IrpContext)
512 {
513     FS_INFORMATION_CLASS FsInformationClass;
514     NTSTATUS Status = STATUS_SUCCESS;
515     PVOID SystemBuffer;
516     ULONG BufferLength;
517     PIO_STACK_LOCATION Stack = IrpContext->Stack;
518 
519     /* PRECONDITION */
520     ASSERT(IrpContext);
521 
522     DPRINT("VfatSetVolumeInformation(IrpContext %p)\n", IrpContext);
523 
524     if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
525                                         BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
526     {
527         return VfatMarkIrpContextForQueue(IrpContext);
528     }
529 
530     FsInformationClass = Stack->Parameters.SetVolume.FsInformationClass;
531     BufferLength = Stack->Parameters.SetVolume.Length;
532     SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
533 
534     DPRINT("FsInformationClass %d\n", FsInformationClass);
535     DPRINT("BufferLength %u\n", BufferLength);
536     DPRINT("SystemBuffer %p\n", SystemBuffer);
537 
538     switch (FsInformationClass)
539     {
540         case FileFsLabelInformation:
541             Status = FsdSetFsLabelInformation(IrpContext->DeviceObject,
542                                               SystemBuffer);
543             break;
544 
545         default:
546             Status = STATUS_NOT_SUPPORTED;
547     }
548 
549     ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
550     IrpContext->Irp->IoStatus.Information = 0;
551 
552     return Status;
553 }
554 
555 /* EOF */
556