4bf4ffeea40df2fe622be3287bd1badcdcbd1c44
[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(PFILE_FS_DEVICE_INFORMATION FsDeviceInfo,
133 PULONG BufferLength)
134 {
135 DPRINT("FsdGetFsDeviceInformation()\n");
136 DPRINT("FsDeviceInfo = %p\n", FsDeviceInfo);
137 DPRINT("BufferLength %lu\n", *BufferLength);
138 DPRINT("Required length %lu\n", sizeof(FILE_FS_DEVICE_INFORMATION));
139
140 if (*BufferLength < sizeof(FILE_FS_DEVICE_INFORMATION))
141 return(STATUS_BUFFER_OVERFLOW);
142
143 FsDeviceInfo->DeviceType = FILE_DEVICE_DISK;
144 FsDeviceInfo->Characteristics = 0; /* FIXME: fix this !! */
145
146 DPRINT("FsdGetFsDeviceInformation() finished.\n");
147
148 *BufferLength -= sizeof(FILE_FS_DEVICE_INFORMATION);
149 DPRINT("BufferLength %lu\n", *BufferLength);
150
151 return(STATUS_SUCCESS);
152 }
153
154
155 static NTSTATUS
156 FsdSetFsLabelInformation(PDEVICE_OBJECT DeviceObject,
157 PFILE_FS_LABEL_INFORMATION FsLabelInfo)
158 {
159 PDEVICE_EXTENSION DeviceExt;
160 PVOID Context = NULL;
161 ULONG DirIndex = 0;
162 PDIR_ENTRY Entry;
163 PVFATFCB pRootFcb;
164 LARGE_INTEGER FileOffset;
165 BOOLEAN LabelFound = FALSE;
166 DIR_ENTRY VolumeLabelDirEntry;
167 ULONG VolumeLabelDirIndex;
168 ULONG LabelLen;
169 NTSTATUS Status = STATUS_UNSUCCESSFUL;
170 OEM_STRING StringO;
171 UNICODE_STRING StringW;
172 CHAR cString[43];
173 ULONG SizeDirEntry;
174 ULONG EntriesPerPage;
175
176 DPRINT("FsdSetFsLabelInformation()\n");
177
178 DeviceExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
179
180 if (sizeof(DeviceObject->Vpb->VolumeLabel) < FsLabelInfo->VolumeLabelLength)
181 {
182 return STATUS_NAME_TOO_LONG;
183 }
184
185 if (DeviceExt->Flags & VCB_IS_FATX)
186 {
187 if (FsLabelInfo->VolumeLabelLength / sizeof(WCHAR) > 42)
188 return STATUS_NAME_TOO_LONG;
189 SizeDirEntry = sizeof(FATX_DIR_ENTRY);
190 EntriesPerPage = FATX_ENTRIES_PER_PAGE;
191 }
192 else
193 {
194 if (FsLabelInfo->VolumeLabelLength / sizeof(WCHAR) > 11)
195 return STATUS_NAME_TOO_LONG;
196 SizeDirEntry = sizeof(FAT_DIR_ENTRY);
197 EntriesPerPage = FAT_ENTRIES_PER_PAGE;
198 }
199
200 /* Create Volume label dir entry */
201 LabelLen = FsLabelInfo->VolumeLabelLength / sizeof(WCHAR);
202 RtlZeroMemory(&VolumeLabelDirEntry, SizeDirEntry);
203 StringW.Buffer = FsLabelInfo->VolumeLabel;
204 StringW.Length = StringW.MaximumLength = (USHORT)FsLabelInfo->VolumeLabelLength;
205 StringO.Buffer = cString;
206 StringO.Length = 0;
207 StringO.MaximumLength = 42;
208 Status = RtlUnicodeStringToOemString(&StringO, &StringW, FALSE);
209 if (!NT_SUCCESS(Status))
210 return Status;
211 if (DeviceExt->Flags & VCB_IS_FATX)
212 {
213 RtlCopyMemory(VolumeLabelDirEntry.FatX.Filename, cString, LabelLen);
214 memset(&VolumeLabelDirEntry.FatX.Filename[LabelLen], ' ', 42 - LabelLen);
215 VolumeLabelDirEntry.FatX.Attrib = 0x08;
216 }
217 else
218 {
219 RtlCopyMemory(VolumeLabelDirEntry.Fat.Filename, cString, LabelLen);
220 memset(&VolumeLabelDirEntry.Fat.Filename[LabelLen], ' ', 11 - LabelLen);
221 VolumeLabelDirEntry.Fat.Attrib = 0x08;
222 }
223
224 pRootFcb = vfatOpenRootFCB(DeviceExt);
225
226 /* Search existing volume entry on disk */
227 FileOffset.QuadPart = 0;
228 if (CcPinRead(pRootFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, &Context, (PVOID*)&Entry))
229 {
230 while (TRUE)
231 {
232 if (ENTRY_VOLUME(DeviceExt, Entry))
233 {
234 /* Update entry */
235 LabelFound = TRUE;
236 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry);
237 CcSetDirtyPinnedData(Context, NULL);
238 Status = STATUS_SUCCESS;
239 break;
240 }
241 if (ENTRY_END(DeviceExt, Entry))
242 {
243 break;
244 }
245 DirIndex++;
246 Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry);
247 if ((DirIndex % EntriesPerPage) == 0)
248 {
249 CcUnpinData(Context);
250 FileOffset.u.LowPart += PAGE_SIZE;
251 if (!CcPinRead(pRootFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, &Context, (PVOID*)&Entry))
252 {
253 Context = NULL;
254 break;
255 }
256 }
257 }
258 if (Context)
259 {
260 CcUnpinData(Context);
261 }
262 }
263 if (!LabelFound)
264 {
265 /* Add new entry for label */
266 if (!vfatFindDirSpace(DeviceExt, pRootFcb, 1, &VolumeLabelDirIndex))
267 Status = STATUS_DISK_FULL;
268 else
269 {
270 FileOffset.u.HighPart = 0;
271 FileOffset.u.LowPart = VolumeLabelDirIndex * SizeDirEntry;
272 CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry,
273 TRUE, &Context, (PVOID*)&Entry);
274 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry);
275 CcSetDirtyPinnedData(Context, NULL);
276 CcUnpinData(Context);
277 Status = STATUS_SUCCESS;
278 }
279 }
280
281 vfatReleaseFCB(DeviceExt, pRootFcb);
282 if (!NT_SUCCESS(Status))
283 {
284 return Status;
285 }
286
287 /* Update volume label in memory */
288 DeviceObject->Vpb->VolumeLabelLength = (USHORT)FsLabelInfo->VolumeLabelLength;
289 RtlCopyMemory(DeviceObject->Vpb->VolumeLabel, FsLabelInfo->VolumeLabel, DeviceObject->Vpb->VolumeLabelLength);
290
291 return Status;
292 }
293
294
295 NTSTATUS VfatQueryVolumeInformation(PVFAT_IRP_CONTEXT IrpContext)
296 /*
297 * FUNCTION: Retrieve the specified volume information
298 */
299 {
300 FS_INFORMATION_CLASS FsInformationClass;
301 NTSTATUS RC = STATUS_SUCCESS;
302 PVOID SystemBuffer;
303 ULONG BufferLength;
304
305 /* PRECONDITION */
306 ASSERT(IrpContext);
307
308 DPRINT("VfatQueryVolumeInformation(IrpContext %p)\n", IrpContext);
309
310 if (!ExAcquireResourceSharedLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
311 (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
312 {
313 return VfatQueueRequest (IrpContext);
314 }
315
316 /* INITIALIZATION */
317 FsInformationClass = IrpContext->Stack->Parameters.QueryVolume.FsInformationClass;
318 BufferLength = IrpContext->Stack->Parameters.QueryVolume.Length;
319 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
320
321
322 DPRINT ("FsInformationClass %d\n", FsInformationClass);
323 DPRINT ("SystemBuffer %p\n", SystemBuffer);
324
325 switch (FsInformationClass)
326 {
327 case FileFsVolumeInformation:
328 RC = FsdGetFsVolumeInformation(IrpContext->DeviceObject,
329 SystemBuffer,
330 &BufferLength);
331 break;
332
333 case FileFsAttributeInformation:
334 RC = FsdGetFsAttributeInformation(IrpContext->DeviceObject->DeviceExtension,
335 SystemBuffer,
336 &BufferLength);
337 break;
338
339 case FileFsSizeInformation:
340 RC = FsdGetFsSizeInformation(IrpContext->DeviceObject,
341 SystemBuffer,
342 &BufferLength);
343 break;
344
345 case FileFsDeviceInformation:
346 RC = FsdGetFsDeviceInformation(SystemBuffer,
347 &BufferLength);
348 break;
349
350 default:
351 RC = STATUS_NOT_SUPPORTED;
352 }
353
354 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
355 IrpContext->Irp->IoStatus.Status = RC;
356 if (NT_SUCCESS(RC))
357 IrpContext->Irp->IoStatus.Information =
358 IrpContext->Stack->Parameters.QueryVolume.Length - BufferLength;
359 else
360 IrpContext->Irp->IoStatus.Information = 0;
361 IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
362 VfatFreeIrpContext(IrpContext);
363
364 return RC;
365 }
366
367
368 NTSTATUS VfatSetVolumeInformation(PVFAT_IRP_CONTEXT IrpContext)
369 /*
370 * FUNCTION: Set the specified volume information
371 */
372 {
373 FS_INFORMATION_CLASS FsInformationClass;
374 NTSTATUS Status = STATUS_SUCCESS;
375 PVOID SystemBuffer;
376 ULONG BufferLength;
377 PIO_STACK_LOCATION Stack = IrpContext->Stack;
378
379 /* PRECONDITION */
380 ASSERT(IrpContext);
381
382 DPRINT ("VfatSetVolumeInformation(IrpContext %p)\n", IrpContext);
383
384 if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
385 (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
386 {
387 return VfatQueueRequest (IrpContext);
388 }
389
390 FsInformationClass = Stack->Parameters.SetVolume.FsInformationClass;
391 BufferLength = Stack->Parameters.SetVolume.Length;
392 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
393
394 DPRINT ("FsInformationClass %d\n", FsInformationClass);
395 DPRINT ("BufferLength %d\n", BufferLength);
396 DPRINT ("SystemBuffer %p\n", SystemBuffer);
397
398 switch(FsInformationClass)
399 {
400 case FileFsLabelInformation:
401 Status = FsdSetFsLabelInformation(IrpContext->DeviceObject,
402 SystemBuffer);
403 break;
404
405 default:
406 Status = STATUS_NOT_SUPPORTED;
407 }
408
409 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
410 IrpContext->Irp->IoStatus.Status = Status;
411 IrpContext->Irp->IoStatus.Information = 0;
412 IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
413 VfatFreeIrpContext(IrpContext);
414
415 return(Status);
416 }
417
418 /* EOF */