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