[DDK]: Merge 46183 from header-branch.
[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 = 0x08;
220 }
221 else
222 {
223 RtlCopyMemory(VolumeLabelDirEntry.Fat.Filename, cString, LabelLen);
224 memset(&VolumeLabelDirEntry.Fat.Filename[LabelLen], ' ', 11 - LabelLen);
225 VolumeLabelDirEntry.Fat.Attrib = 0x08;
226 }
227
228 pRootFcb = vfatOpenRootFCB(DeviceExt);
229
230 /* Search existing volume entry on disk */
231 FileOffset.QuadPart = 0;
232 if (CcPinRead(pRootFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, &Context, (PVOID*)&Entry))
233 {
234 while (TRUE)
235 {
236 if (ENTRY_VOLUME(DeviceExt, Entry))
237 {
238 /* Update entry */
239 LabelFound = TRUE;
240 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry);
241 CcSetDirtyPinnedData(Context, NULL);
242 Status = STATUS_SUCCESS;
243 break;
244 }
245 if (ENTRY_END(DeviceExt, Entry))
246 {
247 break;
248 }
249 DirIndex++;
250 Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry);
251 if ((DirIndex % EntriesPerPage) == 0)
252 {
253 CcUnpinData(Context);
254 FileOffset.u.LowPart += PAGE_SIZE;
255 if (!CcPinRead(pRootFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, &Context, (PVOID*)&Entry))
256 {
257 Context = NULL;
258 break;
259 }
260 }
261 }
262 if (Context)
263 {
264 CcUnpinData(Context);
265 }
266 }
267 if (!LabelFound)
268 {
269 /* Add new entry for label */
270 if (!vfatFindDirSpace(DeviceExt, pRootFcb, 1, &VolumeLabelDirIndex))
271 Status = STATUS_DISK_FULL;
272 else
273 {
274 FileOffset.u.HighPart = 0;
275 FileOffset.u.LowPart = VolumeLabelDirIndex * SizeDirEntry;
276 CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry,
277 TRUE, &Context, (PVOID*)&Entry);
278 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry);
279 CcSetDirtyPinnedData(Context, NULL);
280 CcUnpinData(Context);
281 Status = STATUS_SUCCESS;
282 }
283 }
284
285 vfatReleaseFCB(DeviceExt, pRootFcb);
286 if (!NT_SUCCESS(Status))
287 {
288 return Status;
289 }
290
291 /* Update volume label in memory */
292 DeviceObject->Vpb->VolumeLabelLength = (USHORT)FsLabelInfo->VolumeLabelLength;
293 RtlCopyMemory(DeviceObject->Vpb->VolumeLabel, FsLabelInfo->VolumeLabel, DeviceObject->Vpb->VolumeLabelLength);
294
295 return Status;
296 }
297
298
299 NTSTATUS VfatQueryVolumeInformation(PVFAT_IRP_CONTEXT IrpContext)
300 /*
301 * FUNCTION: Retrieve the specified volume information
302 */
303 {
304 FS_INFORMATION_CLASS FsInformationClass;
305 NTSTATUS RC = STATUS_SUCCESS;
306 PVOID SystemBuffer;
307 ULONG BufferLength;
308
309 /* PRECONDITION */
310 ASSERT(IrpContext);
311
312 DPRINT("VfatQueryVolumeInformation(IrpContext %p)\n", IrpContext);
313
314 if (!ExAcquireResourceSharedLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
315 (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
316 {
317 return VfatQueueRequest (IrpContext);
318 }
319
320 /* INITIALIZATION */
321 FsInformationClass = IrpContext->Stack->Parameters.QueryVolume.FsInformationClass;
322 BufferLength = IrpContext->Stack->Parameters.QueryVolume.Length;
323 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
324
325
326 DPRINT ("FsInformationClass %d\n", FsInformationClass);
327 DPRINT ("SystemBuffer %p\n", SystemBuffer);
328
329 switch (FsInformationClass)
330 {
331 case FileFsVolumeInformation:
332 RC = FsdGetFsVolumeInformation(IrpContext->DeviceObject,
333 SystemBuffer,
334 &BufferLength);
335 break;
336
337 case FileFsAttributeInformation:
338 RC = FsdGetFsAttributeInformation(IrpContext->DeviceObject->DeviceExtension,
339 SystemBuffer,
340 &BufferLength);
341 break;
342
343 case FileFsSizeInformation:
344 RC = FsdGetFsSizeInformation(IrpContext->DeviceObject,
345 SystemBuffer,
346 &BufferLength);
347 break;
348
349 case FileFsDeviceInformation:
350 RC = FsdGetFsDeviceInformation(IrpContext->DeviceObject,
351 SystemBuffer,
352 &BufferLength);
353 break;
354
355 default:
356 RC = STATUS_NOT_SUPPORTED;
357 }
358
359 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
360 IrpContext->Irp->IoStatus.Status = RC;
361 if (NT_SUCCESS(RC))
362 IrpContext->Irp->IoStatus.Information =
363 IrpContext->Stack->Parameters.QueryVolume.Length - BufferLength;
364 else
365 IrpContext->Irp->IoStatus.Information = 0;
366 IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
367 VfatFreeIrpContext(IrpContext);
368
369 return RC;
370 }
371
372
373 NTSTATUS VfatSetVolumeInformation(PVFAT_IRP_CONTEXT IrpContext)
374 /*
375 * FUNCTION: Set the specified volume information
376 */
377 {
378 FS_INFORMATION_CLASS FsInformationClass;
379 NTSTATUS Status = STATUS_SUCCESS;
380 PVOID SystemBuffer;
381 ULONG BufferLength;
382 PIO_STACK_LOCATION Stack = IrpContext->Stack;
383
384 /* PRECONDITION */
385 ASSERT(IrpContext);
386
387 DPRINT ("VfatSetVolumeInformation(IrpContext %p)\n", IrpContext);
388
389 if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
390 (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
391 {
392 return VfatQueueRequest (IrpContext);
393 }
394
395 FsInformationClass = Stack->Parameters.SetVolume.FsInformationClass;
396 BufferLength = Stack->Parameters.SetVolume.Length;
397 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
398
399 DPRINT ("FsInformationClass %d\n", FsInformationClass);
400 DPRINT ("BufferLength %d\n", BufferLength);
401 DPRINT ("SystemBuffer %p\n", SystemBuffer);
402
403 switch(FsInformationClass)
404 {
405 case FileFsLabelInformation:
406 Status = FsdSetFsLabelInformation(IrpContext->DeviceObject,
407 SystemBuffer);
408 break;
409
410 default:
411 Status = STATUS_NOT_SUPPORTED;
412 }
413
414 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
415 IrpContext->Irp->IoStatus.Status = Status;
416 IrpContext->Irp->IoStatus.Information = 0;
417 IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
418 VfatFreeIrpContext(IrpContext);
419
420 return(Status);
421 }
422
423 /* EOF */