3538f55bc1eeec14fdae4a4b46e7bc85d5192f98
[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 if (CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, TRUE, &Context, (PVOID*)&Entry))
318 {
319 while (TRUE)
320 {
321 if (ENTRY_VOLUME(DeviceExt, Entry))
322 {
323 /* Update entry */
324 LabelFound = TRUE;
325 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry);
326 CcSetDirtyPinnedData(Context, NULL);
327 Status = STATUS_SUCCESS;
328 break;
329 }
330
331 if (ENTRY_END(DeviceExt, Entry))
332 {
333 break;
334 }
335
336 DirIndex++;
337 Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry);
338 if ((DirIndex % EntriesPerPage) == 0)
339 {
340 CcUnpinData(Context);
341 FileOffset.u.LowPart += PAGE_SIZE;
342 if (!CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, TRUE, &Context, (PVOID*)&Entry))
343 {
344 Context = NULL;
345 break;
346 }
347 }
348 }
349
350 if (Context)
351 {
352 CcUnpinData(Context);
353 }
354 }
355
356 if (!LabelFound)
357 {
358 /* Add new entry for label */
359 if (!vfatFindDirSpace(DeviceExt, pRootFcb, 1, &VolumeLabelDirIndex))
360 Status = STATUS_DISK_FULL;
361 else
362 {
363 FileOffset.u.HighPart = 0;
364 FileOffset.u.LowPart = VolumeLabelDirIndex * SizeDirEntry;
365 if (!CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry,
366 TRUE, &Context, (PVOID*)&Entry))
367 {
368 Status = STATUS_UNSUCCESSFUL;
369 }
370 else
371 {
372 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry);
373 CcSetDirtyPinnedData(Context, NULL);
374 CcUnpinData(Context);
375 Status = STATUS_SUCCESS;
376 }
377 }
378 }
379
380 vfatReleaseFCB(DeviceExt, pRootFcb);
381 if (!NT_SUCCESS(Status))
382 {
383 return Status;
384 }
385
386 /* Update volume label in memory */
387 DeviceObject->Vpb->VolumeLabelLength = (USHORT)FsLabelInfo->VolumeLabelLength;
388 RtlCopyMemory(DeviceObject->Vpb->VolumeLabel, FsLabelInfo->VolumeLabel, DeviceObject->Vpb->VolumeLabelLength);
389
390 return Status;
391 }
392
393
394 /*
395 * FUNCTION: Retrieve the specified volume information
396 */
397 NTSTATUS
398 VfatQueryVolumeInformation(
399 PVFAT_IRP_CONTEXT IrpContext)
400 {
401 FS_INFORMATION_CLASS FsInformationClass;
402 NTSTATUS RC = STATUS_SUCCESS;
403 PVOID SystemBuffer;
404 ULONG BufferLength;
405
406 /* PRECONDITION */
407 ASSERT(IrpContext);
408
409 DPRINT("VfatQueryVolumeInformation(IrpContext %p)\n", IrpContext);
410
411 if (!ExAcquireResourceSharedLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
412 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
413 {
414 DPRINT1("DirResource failed!\n");
415 return VfatMarkIrpContextForQueue(IrpContext);
416 }
417
418 /* INITIALIZATION */
419 FsInformationClass = IrpContext->Stack->Parameters.QueryVolume.FsInformationClass;
420 BufferLength = IrpContext->Stack->Parameters.QueryVolume.Length;
421 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
422
423 DPRINT("FsInformationClass %d\n", FsInformationClass);
424 DPRINT("SystemBuffer %p\n", SystemBuffer);
425
426 switch (FsInformationClass)
427 {
428 case FileFsVolumeInformation:
429 RC = FsdGetFsVolumeInformation(IrpContext->DeviceObject,
430 SystemBuffer,
431 &BufferLength);
432 break;
433
434 case FileFsAttributeInformation:
435 RC = FsdGetFsAttributeInformation(IrpContext->DeviceObject->DeviceExtension,
436 SystemBuffer,
437 &BufferLength);
438 break;
439
440 case FileFsSizeInformation:
441 RC = FsdGetFsSizeInformation(IrpContext->DeviceObject,
442 SystemBuffer,
443 &BufferLength);
444 break;
445
446 case FileFsDeviceInformation:
447 RC = FsdGetFsDeviceInformation(IrpContext->DeviceObject,
448 SystemBuffer,
449 &BufferLength);
450 break;
451
452 case FileFsFullSizeInformation:
453 RC = FsdGetFsFullSizeInformation(IrpContext->DeviceObject,
454 SystemBuffer,
455 &BufferLength);
456 break;
457
458 default:
459 RC = STATUS_NOT_SUPPORTED;
460 }
461
462 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
463
464 IrpContext->Irp->IoStatus.Information =
465 IrpContext->Stack->Parameters.QueryVolume.Length - BufferLength;
466
467 return RC;
468 }
469
470
471 /*
472 * FUNCTION: Set the specified volume information
473 */
474 NTSTATUS
475 VfatSetVolumeInformation(
476 PVFAT_IRP_CONTEXT IrpContext)
477 {
478 FS_INFORMATION_CLASS FsInformationClass;
479 NTSTATUS Status = STATUS_SUCCESS;
480 PVOID SystemBuffer;
481 ULONG BufferLength;
482 PIO_STACK_LOCATION Stack = IrpContext->Stack;
483
484 /* PRECONDITION */
485 ASSERT(IrpContext);
486
487 DPRINT("VfatSetVolumeInformation(IrpContext %p)\n", IrpContext);
488
489 if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
490 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
491 {
492 return VfatMarkIrpContextForQueue(IrpContext);
493 }
494
495 FsInformationClass = Stack->Parameters.SetVolume.FsInformationClass;
496 BufferLength = Stack->Parameters.SetVolume.Length;
497 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
498
499 DPRINT("FsInformationClass %d\n", FsInformationClass);
500 DPRINT("BufferLength %u\n", BufferLength);
501 DPRINT("SystemBuffer %p\n", SystemBuffer);
502
503 switch (FsInformationClass)
504 {
505 case FileFsLabelInformation:
506 Status = FsdSetFsLabelInformation(IrpContext->DeviceObject,
507 SystemBuffer);
508 break;
509
510 default:
511 Status = STATUS_NOT_SUPPORTED;
512 }
513
514 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
515 IrpContext->Irp->IoStatus.Information = 0;
516
517 return Status;
518 }
519
520 /* EOF */