[FASTFAT] Fix size checking in VfatGetFileNameInformation()
[reactos.git] / 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 (vfatVolumeIsFatX(DeviceExt))
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 BOOLEAN IsFatX;
254
255 DPRINT("FsdSetFsLabelInformation()\n");
256
257 DeviceExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
258 IsFatX = vfatVolumeIsFatX(DeviceExt);
259
260 if (sizeof(DeviceObject->Vpb->VolumeLabel) < FsLabelInfo->VolumeLabelLength)
261 {
262 return STATUS_NAME_TOO_LONG;
263 }
264
265 if (IsFatX)
266 {
267 if (FsLabelInfo->VolumeLabelLength / sizeof(WCHAR) > 42)
268 return STATUS_NAME_TOO_LONG;
269
270 SizeDirEntry = sizeof(FATX_DIR_ENTRY);
271 EntriesPerPage = FATX_ENTRIES_PER_PAGE;
272 }
273 else
274 {
275 if (FsLabelInfo->VolumeLabelLength / sizeof(WCHAR) > 11)
276 return STATUS_NAME_TOO_LONG;
277
278 SizeDirEntry = sizeof(FAT_DIR_ENTRY);
279 EntriesPerPage = FAT_ENTRIES_PER_PAGE;
280 }
281
282 /* Create Volume label dir entry */
283 LabelLen = FsLabelInfo->VolumeLabelLength / sizeof(WCHAR);
284 RtlZeroMemory(&VolumeLabelDirEntry, SizeDirEntry);
285 StringW.Buffer = FsLabelInfo->VolumeLabel;
286 StringW.Length = StringW.MaximumLength = (USHORT)FsLabelInfo->VolumeLabelLength;
287 StringO.Buffer = cString;
288 StringO.Length = 0;
289 StringO.MaximumLength = 42;
290 Status = RtlUnicodeStringToOemString(&StringO, &StringW, FALSE);
291 if (!NT_SUCCESS(Status))
292 return Status;
293
294 if (IsFatX)
295 {
296 RtlCopyMemory(VolumeLabelDirEntry.FatX.Filename, cString, LabelLen);
297 memset(&VolumeLabelDirEntry.FatX.Filename[LabelLen], ' ', 42 - LabelLen);
298 VolumeLabelDirEntry.FatX.Attrib = _A_VOLID;
299 }
300 else
301 {
302 RtlCopyMemory(VolumeLabelDirEntry.Fat.Filename, cString, max(sizeof(VolumeLabelDirEntry.Fat.Filename), LabelLen));
303 if (LabelLen > sizeof(VolumeLabelDirEntry.Fat.Filename))
304 {
305 memset(VolumeLabelDirEntry.Fat.Ext, ' ', sizeof(VolumeLabelDirEntry.Fat.Ext));
306 RtlCopyMemory(VolumeLabelDirEntry.Fat.Ext, cString + sizeof(VolumeLabelDirEntry.Fat.Filename), LabelLen - sizeof(VolumeLabelDirEntry.Fat.Filename));
307 }
308 else
309 {
310 memset(&VolumeLabelDirEntry.Fat.Filename[LabelLen], ' ', sizeof(VolumeLabelDirEntry.Fat.Filename) - LabelLen);
311 }
312 VolumeLabelDirEntry.Fat.Attrib = _A_VOLID;
313 }
314
315 pRootFcb = vfatOpenRootFCB(DeviceExt);
316
317 /* Search existing volume entry on disk */
318 FileOffset.QuadPart = 0;
319 _SEH2_TRY
320 {
321 CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&Entry);
322 }
323 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
324 {
325 Status = _SEH2_GetExceptionCode();
326 }
327 _SEH2_END;
328
329 if (NT_SUCCESS(Status))
330 {
331 while (TRUE)
332 {
333 if (ENTRY_VOLUME(IsFatX, Entry))
334 {
335 /* Update entry */
336 LabelFound = TRUE;
337 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry);
338 CcSetDirtyPinnedData(Context, NULL);
339 Status = STATUS_SUCCESS;
340 break;
341 }
342
343 if (ENTRY_END(IsFatX, Entry))
344 {
345 break;
346 }
347
348 DirIndex++;
349 Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry);
350 if ((DirIndex % EntriesPerPage) == 0)
351 {
352 CcUnpinData(Context);
353 FileOffset.u.LowPart += PAGE_SIZE;
354 _SEH2_TRY
355 {
356 CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&Entry);
357 }
358 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
359 {
360 Status = _SEH2_GetExceptionCode();
361 }
362 _SEH2_END;
363
364 if (!NT_SUCCESS(Status))
365 {
366 Context = NULL;
367 break;
368 }
369 }
370 }
371
372 if (Context)
373 {
374 CcUnpinData(Context);
375 }
376 }
377
378 if (!LabelFound)
379 {
380 /* Add new entry for label */
381 if (!vfatFindDirSpace(DeviceExt, pRootFcb, 1, &VolumeLabelDirIndex))
382 Status = STATUS_DISK_FULL;
383 else
384 {
385 FileOffset.u.HighPart = 0;
386 FileOffset.u.LowPart = VolumeLabelDirIndex * SizeDirEntry;
387
388 Status = STATUS_SUCCESS;
389 _SEH2_TRY
390 {
391 CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&Entry);
392 }
393 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
394 {
395 Status = _SEH2_GetExceptionCode();
396 }
397 _SEH2_END;
398
399 if (NT_SUCCESS(Status))
400 {
401 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry);
402 CcSetDirtyPinnedData(Context, NULL);
403 CcUnpinData(Context);
404 Status = STATUS_SUCCESS;
405 }
406 }
407 }
408
409 vfatReleaseFCB(DeviceExt, pRootFcb);
410 if (!NT_SUCCESS(Status))
411 {
412 return Status;
413 }
414
415 /* Update volume label in memory */
416 DeviceObject->Vpb->VolumeLabelLength = (USHORT)FsLabelInfo->VolumeLabelLength;
417 RtlCopyMemory(DeviceObject->Vpb->VolumeLabel, FsLabelInfo->VolumeLabel, DeviceObject->Vpb->VolumeLabelLength);
418
419 return Status;
420 }
421
422
423 /*
424 * FUNCTION: Retrieve the specified volume information
425 */
426 NTSTATUS
427 VfatQueryVolumeInformation(
428 PVFAT_IRP_CONTEXT IrpContext)
429 {
430 FS_INFORMATION_CLASS FsInformationClass;
431 NTSTATUS RC = STATUS_SUCCESS;
432 PVOID SystemBuffer;
433 ULONG BufferLength;
434
435 /* PRECONDITION */
436 ASSERT(IrpContext);
437
438 DPRINT("VfatQueryVolumeInformation(IrpContext %p)\n", IrpContext);
439
440 if (!ExAcquireResourceSharedLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
441 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
442 {
443 DPRINT1("DirResource failed!\n");
444 return VfatMarkIrpContextForQueue(IrpContext);
445 }
446
447 /* INITIALIZATION */
448 FsInformationClass = IrpContext->Stack->Parameters.QueryVolume.FsInformationClass;
449 BufferLength = IrpContext->Stack->Parameters.QueryVolume.Length;
450 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
451
452 DPRINT("FsInformationClass %d\n", FsInformationClass);
453 DPRINT("SystemBuffer %p\n", SystemBuffer);
454
455 switch (FsInformationClass)
456 {
457 case FileFsVolumeInformation:
458 RC = FsdGetFsVolumeInformation(IrpContext->DeviceObject,
459 SystemBuffer,
460 &BufferLength);
461 break;
462
463 case FileFsAttributeInformation:
464 RC = FsdGetFsAttributeInformation(IrpContext->DeviceObject->DeviceExtension,
465 SystemBuffer,
466 &BufferLength);
467 break;
468
469 case FileFsSizeInformation:
470 RC = FsdGetFsSizeInformation(IrpContext->DeviceObject,
471 SystemBuffer,
472 &BufferLength);
473 break;
474
475 case FileFsDeviceInformation:
476 RC = FsdGetFsDeviceInformation(IrpContext->DeviceObject,
477 SystemBuffer,
478 &BufferLength);
479 break;
480
481 case FileFsFullSizeInformation:
482 RC = FsdGetFsFullSizeInformation(IrpContext->DeviceObject,
483 SystemBuffer,
484 &BufferLength);
485 break;
486
487 default:
488 RC = STATUS_NOT_SUPPORTED;
489 }
490
491 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
492
493 IrpContext->Irp->IoStatus.Information =
494 IrpContext->Stack->Parameters.QueryVolume.Length - BufferLength;
495
496 return RC;
497 }
498
499
500 /*
501 * FUNCTION: Set the specified volume information
502 */
503 NTSTATUS
504 VfatSetVolumeInformation(
505 PVFAT_IRP_CONTEXT IrpContext)
506 {
507 FS_INFORMATION_CLASS FsInformationClass;
508 NTSTATUS Status = STATUS_SUCCESS;
509 PVOID SystemBuffer;
510 ULONG BufferLength;
511 PIO_STACK_LOCATION Stack = IrpContext->Stack;
512
513 /* PRECONDITION */
514 ASSERT(IrpContext);
515
516 DPRINT("VfatSetVolumeInformation(IrpContext %p)\n", IrpContext);
517
518 if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
519 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
520 {
521 return VfatMarkIrpContextForQueue(IrpContext);
522 }
523
524 FsInformationClass = Stack->Parameters.SetVolume.FsInformationClass;
525 BufferLength = Stack->Parameters.SetVolume.Length;
526 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
527
528 DPRINT("FsInformationClass %d\n", FsInformationClass);
529 DPRINT("BufferLength %u\n", BufferLength);
530 DPRINT("SystemBuffer %p\n", SystemBuffer);
531
532 switch (FsInformationClass)
533 {
534 case FileFsLabelInformation:
535 Status = FsdSetFsLabelInformation(IrpContext->DeviceObject,
536 SystemBuffer);
537 break;
538
539 default:
540 Status = STATUS_NOT_SUPPORTED;
541 }
542
543 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
544 IrpContext->Irp->IoStatus.Information = 0;
545
546 return Status;
547 }
548
549 /* EOF */