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