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