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