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