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