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