xref: /reactos/drivers/filesystems/vfatfs/finfo.c (revision 3c5a56ed)
1 /*
2  * PROJECT:     VFAT Filesystem
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     File information routines
5  * COPYRIGHT:   Copyright 1998 Jason Filby <jasonfilby@yahoo.com>
6  *              Copyright 2005 Hervé Poussineau <hpoussin@reactos.org>
7  *              Copyright 2008-2018 Pierre Schweitzer <pierre@reactos.org>
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include "vfat.h"
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 #define NASSERTS_RENAME
18 
19 /* GLOBALS ******************************************************************/
20 
21 const char* FileInformationClassNames[] =
22 {
23     "??????",
24     "FileDirectoryInformation",
25     "FileFullDirectoryInformation",
26     "FileBothDirectoryInformation",
27     "FileBasicInformation",
28     "FileStandardInformation",
29     "FileInternalInformation",
30     "FileEaInformation",
31     "FileAccessInformation",
32     "FileNameInformation",
33     "FileRenameInformation",
34     "FileLinkInformation",
35     "FileNamesInformation",
36     "FileDispositionInformation",
37     "FilePositionInformation",
38     "FileFullEaInformation",
39     "FileModeInformation",
40     "FileAlignmentInformation",
41     "FileAllInformation",
42     "FileAllocationInformation",
43     "FileEndOfFileInformation",
44     "FileAlternateNameInformation",
45     "FileStreamInformation",
46     "FilePipeInformation",
47     "FilePipeLocalInformation",
48     "FilePipeRemoteInformation",
49     "FileMailslotQueryInformation",
50     "FileMailslotSetInformation",
51     "FileCompressionInformation",
52     "FileObjectIdInformation",
53     "FileCompletionInformation",
54     "FileMoveClusterInformation",
55     "FileQuotaInformation",
56     "FileReparsePointInformation",
57     "FileNetworkOpenInformation",
58     "FileAttributeTagInformation",
59     "FileTrackingInformation",
60     "FileIdBothDirectoryInformation",
61     "FileIdFullDirectoryInformation",
62     "FileValidDataLengthInformation",
63     "FileShortNameInformation",
64     "FileMaximumInformation"
65 };
66 
67 /* FUNCTIONS ****************************************************************/
68 
69 /*
70  * FUNCTION: Retrieve the standard file information
71  */
72 NTSTATUS
73 VfatGetStandardInformation(
74     PVFATFCB FCB,
75     PFILE_STANDARD_INFORMATION StandardInfo,
76     PULONG BufferLength)
77 {
78     if (*BufferLength < sizeof(FILE_STANDARD_INFORMATION))
79         return STATUS_BUFFER_OVERFLOW;
80 
81     /* PRECONDITION */
82     ASSERT(StandardInfo != NULL);
83     ASSERT(FCB != NULL);
84 
85     if (vfatFCBIsDirectory(FCB))
86     {
87         StandardInfo->AllocationSize.QuadPart = 0;
88         StandardInfo->EndOfFile.QuadPart = 0;
89         StandardInfo->Directory = TRUE;
90     }
91     else
92     {
93         StandardInfo->AllocationSize = FCB->RFCB.AllocationSize;
94         StandardInfo->EndOfFile = FCB->RFCB.FileSize;
95         StandardInfo->Directory = FALSE;
96     }
97     StandardInfo->NumberOfLinks = 1;
98     StandardInfo->DeletePending = BooleanFlagOn(FCB->Flags, FCB_DELETE_PENDING);
99 
100     *BufferLength -= sizeof(FILE_STANDARD_INFORMATION);
101     return STATUS_SUCCESS;
102 }
103 
104 static
105 NTSTATUS
106 VfatSetPositionInformation(
107     PFILE_OBJECT FileObject,
108     PFILE_POSITION_INFORMATION PositionInfo)
109 {
110     DPRINT("FsdSetPositionInformation()\n");
111 
112     DPRINT("PositionInfo %p\n", PositionInfo);
113     DPRINT("Setting position %u\n", PositionInfo->CurrentByteOffset.u.LowPart);
114 
115     FileObject->CurrentByteOffset.QuadPart =
116         PositionInfo->CurrentByteOffset.QuadPart;
117 
118     return STATUS_SUCCESS;
119 }
120 
121 static
122 NTSTATUS
123 VfatGetPositionInformation(
124     PFILE_OBJECT FileObject,
125     PVFATFCB FCB,
126     PDEVICE_EXTENSION DeviceExt,
127     PFILE_POSITION_INFORMATION PositionInfo,
128     PULONG BufferLength)
129 {
130     UNREFERENCED_PARAMETER(FileObject);
131     UNREFERENCED_PARAMETER(FCB);
132     UNREFERENCED_PARAMETER(DeviceExt);
133 
134     DPRINT("VfatGetPositionInformation()\n");
135 
136     if (*BufferLength < sizeof(FILE_POSITION_INFORMATION))
137         return STATUS_BUFFER_OVERFLOW;
138 
139     PositionInfo->CurrentByteOffset.QuadPart =
140         FileObject->CurrentByteOffset.QuadPart;
141 
142     DPRINT("Getting position %I64x\n",
143            PositionInfo->CurrentByteOffset.QuadPart);
144 
145     *BufferLength -= sizeof(FILE_POSITION_INFORMATION);
146     return STATUS_SUCCESS;
147 }
148 
149 static
150 NTSTATUS
151 VfatSetBasicInformation(
152     PFILE_OBJECT FileObject,
153     PVFATFCB FCB,
154     PDEVICE_EXTENSION DeviceExt,
155     PFILE_BASIC_INFORMATION BasicInfo)
156 {
157     ULONG NotifyFilter;
158 
159     DPRINT("VfatSetBasicInformation()\n");
160 
161     ASSERT(NULL != FileObject);
162     ASSERT(NULL != FCB);
163     ASSERT(NULL != DeviceExt);
164     ASSERT(NULL != BasicInfo);
165     /* Check volume label bit */
166     ASSERT(0 == (*FCB->Attributes & _A_VOLID));
167 
168     NotifyFilter = 0;
169 
170     if (BasicInfo->FileAttributes != 0)
171     {
172         UCHAR Attributes;
173 
174         Attributes = (BasicInfo->FileAttributes & (FILE_ATTRIBUTE_ARCHIVE |
175                                                    FILE_ATTRIBUTE_SYSTEM |
176                                                    FILE_ATTRIBUTE_HIDDEN |
177                                                    FILE_ATTRIBUTE_DIRECTORY |
178                                                    FILE_ATTRIBUTE_READONLY));
179 
180         if (vfatFCBIsDirectory(FCB))
181         {
182             if (BooleanFlagOn(BasicInfo->FileAttributes, FILE_ATTRIBUTE_TEMPORARY))
183             {
184                 DPRINT("Setting temporary attribute on a directory!\n");
185                 return STATUS_INVALID_PARAMETER;
186             }
187 
188             Attributes |= FILE_ATTRIBUTE_DIRECTORY;
189         }
190         else
191         {
192             if (BooleanFlagOn(BasicInfo->FileAttributes, FILE_ATTRIBUTE_DIRECTORY))
193             {
194                 DPRINT("Setting directory attribute on a file!\n");
195                 return STATUS_INVALID_PARAMETER;
196             }
197         }
198 
199         if (Attributes != *FCB->Attributes)
200         {
201             *FCB->Attributes = Attributes;
202             DPRINT("Setting attributes 0x%02x\n", *FCB->Attributes);
203             NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
204         }
205     }
206 
207     if (vfatVolumeIsFatX(DeviceExt))
208     {
209         if (BasicInfo->CreationTime.QuadPart != 0 && BasicInfo->CreationTime.QuadPart != -1)
210         {
211             FsdSystemTimeToDosDateTime(DeviceExt,
212                                        &BasicInfo->CreationTime,
213                                        &FCB->entry.FatX.CreationDate,
214                                        &FCB->entry.FatX.CreationTime);
215             NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION;
216         }
217 
218         if (BasicInfo->LastAccessTime.QuadPart != 0 && BasicInfo->LastAccessTime.QuadPart != -1)
219         {
220             FsdSystemTimeToDosDateTime(DeviceExt,
221                                        &BasicInfo->LastAccessTime,
222                                        &FCB->entry.FatX.AccessDate,
223                                        &FCB->entry.FatX.AccessTime);
224             NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
225         }
226 
227         if (BasicInfo->LastWriteTime.QuadPart != 0 && BasicInfo->LastWriteTime.QuadPart != -1)
228         {
229             FsdSystemTimeToDosDateTime(DeviceExt,
230                                        &BasicInfo->LastWriteTime,
231                                        &FCB->entry.FatX.UpdateDate,
232                                        &FCB->entry.FatX.UpdateTime);
233             NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
234         }
235     }
236     else
237     {
238         if (BasicInfo->CreationTime.QuadPart != 0 && BasicInfo->CreationTime.QuadPart != -1)
239         {
240             FsdSystemTimeToDosDateTime(DeviceExt,
241                                        &BasicInfo->CreationTime,
242                                        &FCB->entry.Fat.CreationDate,
243                                        &FCB->entry.Fat.CreationTime);
244             NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION;
245         }
246 
247         if (BasicInfo->LastAccessTime.QuadPart != 0 && BasicInfo->LastAccessTime.QuadPart != -1)
248         {
249             FsdSystemTimeToDosDateTime(DeviceExt,
250                                        &BasicInfo->LastAccessTime,
251                                        &FCB->entry.Fat.AccessDate,
252                                        NULL);
253             NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
254         }
255 
256         if (BasicInfo->LastWriteTime.QuadPart != 0 && BasicInfo->LastWriteTime.QuadPart != -1)
257         {
258             FsdSystemTimeToDosDateTime(DeviceExt,
259                                        &BasicInfo->LastWriteTime,
260                                        &FCB->entry.Fat.UpdateDate,
261                                        &FCB->entry.Fat.UpdateTime);
262             NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
263         }
264     }
265 
266     VfatUpdateEntry(DeviceExt, FCB);
267 
268     if (NotifyFilter != 0)
269     {
270         vfatReportChange(DeviceExt,
271                          FCB,
272                          NotifyFilter,
273                          FILE_ACTION_MODIFIED);
274     }
275 
276     return STATUS_SUCCESS;
277 }
278 
279 NTSTATUS
280 VfatGetBasicInformation(
281     PFILE_OBJECT FileObject,
282     PVFATFCB FCB,
283     PDEVICE_EXTENSION DeviceExt,
284     PFILE_BASIC_INFORMATION BasicInfo,
285     PULONG BufferLength)
286 {
287     UNREFERENCED_PARAMETER(FileObject);
288 
289     DPRINT("VfatGetBasicInformation()\n");
290 
291     if (*BufferLength < sizeof(FILE_BASIC_INFORMATION))
292         return STATUS_BUFFER_OVERFLOW;
293 
294     RtlZeroMemory(BasicInfo, sizeof(FILE_BASIC_INFORMATION));
295 
296     if (vfatVolumeIsFatX(DeviceExt))
297     {
298         FsdDosDateTimeToSystemTime(DeviceExt,
299                                    FCB->entry.FatX.CreationDate,
300                                    FCB->entry.FatX.CreationTime,
301                                    &BasicInfo->CreationTime);
302         FsdDosDateTimeToSystemTime(DeviceExt,
303                                    FCB->entry.FatX.AccessDate,
304                                    FCB->entry.FatX.AccessTime,
305                                    &BasicInfo->LastAccessTime);
306         FsdDosDateTimeToSystemTime(DeviceExt,
307                                    FCB->entry.FatX.UpdateDate,
308                                    FCB->entry.FatX.UpdateTime,
309                                    &BasicInfo->LastWriteTime);
310         BasicInfo->ChangeTime = BasicInfo->LastWriteTime;
311     }
312     else
313     {
314         FsdDosDateTimeToSystemTime(DeviceExt,
315                                    FCB->entry.Fat.CreationDate,
316                                    FCB->entry.Fat.CreationTime,
317                                    &BasicInfo->CreationTime);
318         FsdDosDateTimeToSystemTime(DeviceExt,
319                                    FCB->entry.Fat.AccessDate,
320                                    0,
321                                    &BasicInfo->LastAccessTime);
322         FsdDosDateTimeToSystemTime(DeviceExt,
323                                    FCB->entry.Fat.UpdateDate,
324                                    FCB->entry.Fat.UpdateTime,
325                                    &BasicInfo->LastWriteTime);
326         BasicInfo->ChangeTime = BasicInfo->LastWriteTime;
327     }
328 
329     BasicInfo->FileAttributes = *FCB->Attributes & 0x3f;
330     /* Synthesize FILE_ATTRIBUTE_NORMAL */
331     if (0 == (BasicInfo->FileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
332                                            FILE_ATTRIBUTE_ARCHIVE |
333                                            FILE_ATTRIBUTE_SYSTEM |
334                                            FILE_ATTRIBUTE_HIDDEN |
335                                            FILE_ATTRIBUTE_READONLY)))
336     {
337         DPRINT("Synthesizing FILE_ATTRIBUTE_NORMAL\n");
338         BasicInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL;
339     }
340     DPRINT("Getting attributes 0x%02x\n", BasicInfo->FileAttributes);
341 
342     *BufferLength -= sizeof(FILE_BASIC_INFORMATION);
343     return STATUS_SUCCESS;
344 }
345 
346 
347 static
348 NTSTATUS
349 VfatSetDispositionInformation(
350     PFILE_OBJECT FileObject,
351     PVFATFCB FCB,
352     PDEVICE_EXTENSION DeviceExt,
353     PFILE_DISPOSITION_INFORMATION DispositionInfo)
354 {
355     DPRINT("FsdSetDispositionInformation(<%wZ>, Delete %u)\n", &FCB->PathNameU, DispositionInfo->DeleteFile);
356 
357     ASSERT(DeviceExt != NULL);
358     ASSERT(DeviceExt->FatInfo.BytesPerCluster != 0);
359     ASSERT(FCB != NULL);
360 
361     if (!DispositionInfo->DeleteFile)
362     {
363         /* undelete the file */
364         FCB->Flags &= ~FCB_DELETE_PENDING;
365         FileObject->DeletePending = FALSE;
366         return STATUS_SUCCESS;
367     }
368 
369     if (BooleanFlagOn(FCB->Flags, FCB_DELETE_PENDING))
370     {
371         /* stream already marked for deletion. just update the file object */
372         FileObject->DeletePending = TRUE;
373         return STATUS_SUCCESS;
374     }
375 
376     if (vfatFCBIsReadOnly(FCB))
377     {
378         return STATUS_CANNOT_DELETE;
379     }
380 
381     if (vfatFCBIsRoot(FCB) || IsDotOrDotDot(&FCB->LongNameU))
382     {
383         /* we cannot delete a '.', '..' or the root directory */
384         return STATUS_ACCESS_DENIED;
385     }
386 
387     if (!MmFlushImageSection (FileObject->SectionObjectPointer, MmFlushForDelete))
388     {
389         /* can't delete a file if its mapped into a process */
390 
391         DPRINT("MmFlushImageSection returned FALSE\n");
392         return STATUS_CANNOT_DELETE;
393     }
394 
395     if (vfatFCBIsDirectory(FCB) && !VfatIsDirectoryEmpty(DeviceExt, FCB))
396     {
397         /* can't delete a non-empty directory */
398 
399         return STATUS_DIRECTORY_NOT_EMPTY;
400     }
401 
402     /* all good */
403     FCB->Flags |= FCB_DELETE_PENDING;
404     FileObject->DeletePending = TRUE;
405 
406     return STATUS_SUCCESS;
407 }
408 
409 static NTSTATUS
410 vfatPrepareTargetForRename(
411     IN PDEVICE_EXTENSION DeviceExt,
412     IN PVFATFCB * ParentFCB,
413     IN PUNICODE_STRING NewName,
414     IN BOOLEAN ReplaceIfExists,
415     IN PUNICODE_STRING ParentName,
416     OUT PBOOLEAN Deleted)
417 {
418     NTSTATUS Status;
419     PVFATFCB TargetFcb;
420 
421     DPRINT("vfatPrepareTargetForRename(%p, %p, %wZ, %d, %wZ, %p)\n", DeviceExt, ParentFCB, NewName, ReplaceIfExists, ParentName);
422 
423     *Deleted = FALSE;
424     /* Try to open target */
425     Status = vfatGetFCBForFile(DeviceExt, ParentFCB, &TargetFcb, NewName);
426     /* If it exists */
427     if (NT_SUCCESS(Status))
428     {
429         DPRINT("Target file %wZ exists. FCB Flags %08x\n", NewName, TargetFcb->Flags);
430         /* Check whether we are allowed to replace */
431         if (ReplaceIfExists)
432         {
433             /* If that's a directory or a read-only file, we're not allowed */
434             if (vfatFCBIsDirectory(TargetFcb) || vfatFCBIsReadOnly(TargetFcb))
435             {
436                 DPRINT("And this is a readonly file!\n");
437                 vfatReleaseFCB(DeviceExt, *ParentFCB);
438                 *ParentFCB = NULL;
439                 vfatReleaseFCB(DeviceExt, TargetFcb);
440                 return STATUS_OBJECT_NAME_COLLISION;
441             }
442 
443 
444             /* If we still have a file object, close it. */
445             if (TargetFcb->FileObject)
446             {
447                 if (!MmFlushImageSection(TargetFcb->FileObject->SectionObjectPointer, MmFlushForDelete))
448                 {
449                     DPRINT("MmFlushImageSection failed.\n");
450                     vfatReleaseFCB(DeviceExt, *ParentFCB);
451                     *ParentFCB = NULL;
452                     vfatReleaseFCB(DeviceExt, TargetFcb);
453                     return STATUS_ACCESS_DENIED;
454                 }
455 
456                 TargetFcb->FileObject->DeletePending = TRUE;
457                 VfatCloseFile(DeviceExt, TargetFcb->FileObject);
458             }
459 
460             /* If we are here, ensure the file isn't open by anyone! */
461             if (TargetFcb->OpenHandleCount != 0)
462             {
463                 DPRINT("There are still open handles for this file.\n");
464                 vfatReleaseFCB(DeviceExt, *ParentFCB);
465                 *ParentFCB = NULL;
466                 vfatReleaseFCB(DeviceExt, TargetFcb);
467                 return STATUS_ACCESS_DENIED;
468             }
469 
470             /* Effectively delete old file to allow renaming */
471             DPRINT("Effectively deleting the file.\n");
472             VfatDelEntry(DeviceExt, TargetFcb, NULL);
473             vfatReleaseFCB(DeviceExt, TargetFcb);
474             *Deleted = TRUE;
475             return STATUS_SUCCESS;
476         }
477         else
478         {
479             vfatReleaseFCB(DeviceExt, *ParentFCB);
480             *ParentFCB = NULL;
481             vfatReleaseFCB(DeviceExt, TargetFcb);
482             return STATUS_OBJECT_NAME_COLLISION;
483         }
484     }
485     else if (*ParentFCB != NULL)
486     {
487         return STATUS_SUCCESS;
488     }
489 
490     /* Failure */
491     return Status;
492 }
493 
494 static
495 BOOLEAN
496 IsThereAChildOpened(PVFATFCB FCB)
497 {
498     PLIST_ENTRY Entry;
499     PVFATFCB VolFCB;
500 
501     for (Entry = FCB->ParentListHead.Flink; Entry != &FCB->ParentListHead; Entry = Entry->Flink)
502     {
503         VolFCB = CONTAINING_RECORD(Entry, VFATFCB, ParentListEntry);
504         if (VolFCB->OpenHandleCount != 0)
505         {
506             ASSERT(VolFCB->parentFcb == FCB);
507             DPRINT1("At least one children file opened! %wZ (%u, %u)\n", &VolFCB->PathNameU, VolFCB->RefCount, VolFCB->OpenHandleCount);
508             return TRUE;
509         }
510 
511         if (vfatFCBIsDirectory(VolFCB) && !IsListEmpty(&VolFCB->ParentListHead))
512         {
513             if (IsThereAChildOpened(VolFCB))
514             {
515                 return TRUE;
516             }
517         }
518     }
519 
520     return FALSE;
521 }
522 
523 static
524 VOID
525 VfatRenameChildFCB(
526     PDEVICE_EXTENSION DeviceExt,
527     PVFATFCB FCB)
528 {
529     PLIST_ENTRY Entry;
530     PVFATFCB Child;
531 
532     if (IsListEmpty(&FCB->ParentListHead))
533         return;
534 
535     for (Entry = FCB->ParentListHead.Flink; Entry != &FCB->ParentListHead; Entry = Entry->Flink)
536     {
537         NTSTATUS Status;
538 
539         Child = CONTAINING_RECORD(Entry, VFATFCB, ParentListEntry);
540         DPRINT("Found %wZ with still %lu references (parent: %lu)!\n", &Child->PathNameU, Child->RefCount, FCB->RefCount);
541 
542         Status = vfatSetFCBNewDirName(DeviceExt, Child, FCB);
543         if (!NT_SUCCESS(Status))
544             continue;
545 
546         if (vfatFCBIsDirectory(Child))
547         {
548             VfatRenameChildFCB(DeviceExt, Child);
549         }
550     }
551 }
552 
553 /*
554  * FUNCTION: Set the file name information
555  */
556 static
557 NTSTATUS
558 VfatSetRenameInformation(
559     PFILE_OBJECT FileObject,
560     PVFATFCB FCB,
561     PDEVICE_EXTENSION DeviceExt,
562     PFILE_RENAME_INFORMATION RenameInfo,
563     PFILE_OBJECT TargetFileObject)
564 {
565 #ifdef NASSERTS_RENAME
566 #pragma push_macro("ASSERT")
567 #undef ASSERT
568 #define ASSERT(x) ((VOID) 0)
569 #endif
570     NTSTATUS Status;
571     UNICODE_STRING NewName;
572     UNICODE_STRING SourcePath;
573     UNICODE_STRING SourceFile;
574     UNICODE_STRING NewPath;
575     UNICODE_STRING NewFile;
576     PFILE_OBJECT RootFileObject;
577     PVFATFCB RootFCB;
578     UNICODE_STRING RenameInfoString;
579     PVFATFCB ParentFCB;
580     IO_STATUS_BLOCK IoStatusBlock;
581     OBJECT_ATTRIBUTES ObjectAttributes;
582     HANDLE TargetHandle;
583     BOOLEAN DeletedTarget;
584     ULONG OldReferences, NewReferences;
585     PVFATFCB OldParent;
586 
587     DPRINT("VfatSetRenameInfo(%p, %p, %p, %p, %p)\n", FileObject, FCB, DeviceExt, RenameInfo, TargetFileObject);
588 
589     /* Disallow renaming root */
590     if (vfatFCBIsRoot(FCB))
591     {
592         return STATUS_INVALID_PARAMETER;
593     }
594 
595     OldReferences = FCB->parentFcb->RefCount;
596 #ifdef NASSERTS_RENAME
597     UNREFERENCED_PARAMETER(OldReferences);
598 #endif
599 
600     /* If we are performing relative opening for rename, get FO for getting FCB and path name */
601     if (RenameInfo->RootDirectory != NULL)
602     {
603         /* We cannot tolerate relative opening with a full path */
604         if (RenameInfo->FileName[0] == L'\\')
605         {
606             return STATUS_OBJECT_NAME_INVALID;
607         }
608 
609         Status = ObReferenceObjectByHandle(RenameInfo->RootDirectory,
610                                            FILE_READ_DATA,
611                                            *IoFileObjectType,
612                                            ExGetPreviousMode(),
613                                            (PVOID *)&RootFileObject,
614                                            NULL);
615         if (!NT_SUCCESS(Status))
616         {
617             return Status;
618         }
619 
620         RootFCB = RootFileObject->FsContext;
621     }
622 
623     RtlInitEmptyUnicodeString(&NewName, NULL, 0);
624     ParentFCB = NULL;
625 
626     if (TargetFileObject == NULL)
627     {
628         /* If we don't have target file object, construct paths thanks to relative FCB, if any, and with
629          * information supplied by the user
630          */
631 
632         /* First, setup a string we'll work on */
633         RenameInfoString.Length = RenameInfo->FileNameLength;
634         RenameInfoString.MaximumLength = RenameInfo->FileNameLength;
635         RenameInfoString.Buffer = RenameInfo->FileName;
636 
637         /* Check whether we have FQN */
638         if (RenameInfoString.Length > 6 * sizeof(WCHAR))
639         {
640             if (RenameInfoString.Buffer[0] == L'\\' && RenameInfoString.Buffer[1] == L'?' &&
641                 RenameInfoString.Buffer[2] == L'?' && RenameInfoString.Buffer[3] == L'\\' &&
642                 RenameInfoString.Buffer[5] == L':' && (RenameInfoString.Buffer[4] >= L'A' &&
643                 RenameInfoString.Buffer[4] <= L'Z'))
644             {
645                 /* If so, open its target directory */
646                 InitializeObjectAttributes(&ObjectAttributes,
647                                            &RenameInfoString,
648                                            OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
649                                            NULL, NULL);
650 
651                 Status = IoCreateFile(&TargetHandle,
652                                       FILE_WRITE_DATA | SYNCHRONIZE,
653                                       &ObjectAttributes,
654                                       &IoStatusBlock,
655                                       NULL, 0,
656                                       FILE_SHARE_READ | FILE_SHARE_WRITE,
657                                       FILE_OPEN,
658                                       FILE_OPEN_FOR_BACKUP_INTENT,
659                                       NULL, 0,
660                                       CreateFileTypeNone,
661                                       NULL,
662                                       IO_FORCE_ACCESS_CHECK | IO_OPEN_TARGET_DIRECTORY);
663                 if (!NT_SUCCESS(Status))
664                 {
665                     goto Cleanup;
666                 }
667 
668                 /* Get its FO to get the FCB */
669                 Status = ObReferenceObjectByHandle(TargetHandle,
670                                                    FILE_WRITE_DATA,
671                                                    *IoFileObjectType,
672                                                    KernelMode,
673                                                    (PVOID *)&TargetFileObject,
674                                                    NULL);
675                 if (!NT_SUCCESS(Status))
676                 {
677                     ZwClose(TargetHandle);
678                     goto Cleanup;
679                 }
680 
681                 /* Are we working on the same volume? */
682                 if (IoGetRelatedDeviceObject(TargetFileObject) != IoGetRelatedDeviceObject(FileObject))
683                 {
684                     ObDereferenceObject(TargetFileObject);
685                     ZwClose(TargetHandle);
686                     TargetFileObject = NULL;
687                     Status = STATUS_NOT_SAME_DEVICE;
688                     goto Cleanup;
689                 }
690             }
691         }
692 
693         NewName.Length = 0;
694         NewName.MaximumLength = RenameInfo->FileNameLength;
695         if (RenameInfo->RootDirectory != NULL)
696         {
697             NewName.MaximumLength += sizeof(WCHAR) + RootFCB->PathNameU.Length;
698         }
699         else if (RenameInfo->FileName[0] != L'\\')
700         {
701             /* We don't have full path, and we don't have root directory:
702              * => we move inside the same directory
703              */
704             NewName.MaximumLength += sizeof(WCHAR) + FCB->DirNameU.Length;
705         }
706         else if (TargetFileObject != NULL)
707         {
708             /* We had a FQN:
709              * => we need to use its correct path
710              */
711             NewName.MaximumLength += sizeof(WCHAR) + ((PVFATFCB)TargetFileObject->FsContext)->PathNameU.Length;
712         }
713 
714         NewName.Buffer = ExAllocatePoolWithTag(NonPagedPool, NewName.MaximumLength, TAG_NAME);
715         if (NewName.Buffer == NULL)
716         {
717             if (TargetFileObject != NULL)
718             {
719                 ObDereferenceObject(TargetFileObject);
720                 ZwClose(TargetHandle);
721                 TargetFileObject = NULL;
722             }
723             Status = STATUS_INSUFFICIENT_RESOURCES;
724             goto Cleanup;
725         }
726 
727         if (RenameInfo->RootDirectory != NULL)
728         {
729             /* Here, copy first absolute and then append relative */
730             RtlCopyUnicodeString(&NewName, &RootFCB->PathNameU);
731             NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\';
732             NewName.Length += sizeof(WCHAR);
733             RtlAppendUnicodeStringToString(&NewName, &RenameInfoString);
734         }
735         else if (RenameInfo->FileName[0] != L'\\')
736         {
737             /* Here, copy first work directory and then append filename */
738             RtlCopyUnicodeString(&NewName, &FCB->DirNameU);
739             NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\';
740             NewName.Length += sizeof(WCHAR);
741             RtlAppendUnicodeStringToString(&NewName, &RenameInfoString);
742         }
743         else if (TargetFileObject != NULL)
744         {
745             /* Here, copy first path name and then append filename */
746             RtlCopyUnicodeString(&NewName, &((PVFATFCB)TargetFileObject->FsContext)->PathNameU);
747             NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\';
748             NewName.Length += sizeof(WCHAR);
749             RtlAppendUnicodeStringToString(&NewName, &RenameInfoString);
750         }
751         else
752         {
753             /* Here we should have full path, so simply copy it */
754             RtlCopyUnicodeString(&NewName, &RenameInfoString);
755         }
756 
757         /* Do we have to cleanup some stuff? */
758         if (TargetFileObject != NULL)
759         {
760             ObDereferenceObject(TargetFileObject);
761             ZwClose(TargetHandle);
762             TargetFileObject = NULL;
763         }
764     }
765     else
766     {
767         /* At that point, we shouldn't care about whether we are relative opening
768          * Target FO FCB should already have full path
769          */
770 
771         /* Before constructing string, just make a sanity check (just to be sure!) */
772         if (IoGetRelatedDeviceObject(TargetFileObject) != IoGetRelatedDeviceObject(FileObject))
773         {
774             Status = STATUS_NOT_SAME_DEVICE;
775             goto Cleanup;
776         }
777 
778         NewName.Length = 0;
779         NewName.MaximumLength = TargetFileObject->FileName.Length + ((PVFATFCB)TargetFileObject->FsContext)->PathNameU.Length + sizeof(WCHAR);
780         NewName.Buffer = ExAllocatePoolWithTag(NonPagedPool, NewName.MaximumLength, TAG_NAME);
781         if (NewName.Buffer == NULL)
782         {
783             Status = STATUS_INSUFFICIENT_RESOURCES;
784             goto Cleanup;
785         }
786 
787         RtlCopyUnicodeString(&NewName, &((PVFATFCB)TargetFileObject->FsContext)->PathNameU);
788         /* If \, it's already backslash terminated, don't add it */
789         if (!vfatFCBIsRoot(TargetFileObject->FsContext))
790         {
791             NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\';
792             NewName.Length += sizeof(WCHAR);
793         }
794         RtlAppendUnicodeStringToString(&NewName, &TargetFileObject->FileName);
795     }
796 
797     /* Explode our paths to get path & filename */
798     vfatSplitPathName(&FCB->PathNameU, &SourcePath, &SourceFile);
799     DPRINT("Old dir: %wZ, Old file: %wZ\n", &SourcePath, &SourceFile);
800     vfatSplitPathName(&NewName, &NewPath, &NewFile);
801     DPRINT("New dir: %wZ, New file: %wZ\n", &NewPath, &NewFile);
802 
803     if (IsDotOrDotDot(&NewFile))
804     {
805         Status = STATUS_OBJECT_NAME_INVALID;
806         goto Cleanup;
807     }
808 
809     if (vfatFCBIsDirectory(FCB) && !IsListEmpty(&FCB->ParentListHead))
810     {
811         if (IsThereAChildOpened(FCB))
812         {
813             Status = STATUS_ACCESS_DENIED;
814             ASSERT(OldReferences == FCB->parentFcb->RefCount);
815             goto Cleanup;
816         }
817     }
818 
819     /* Are we working in place? */
820     if (FsRtlAreNamesEqual(&SourcePath, &NewPath, TRUE, NULL))
821     {
822         if (FsRtlAreNamesEqual(&SourceFile, &NewFile, FALSE, NULL))
823         {
824             Status = STATUS_SUCCESS;
825             ASSERT(OldReferences == FCB->parentFcb->RefCount);
826             goto Cleanup;
827         }
828 
829         if (FsRtlAreNamesEqual(&SourceFile, &NewFile, TRUE, NULL))
830         {
831             vfatReportChange(DeviceExt,
832                              FCB,
833                              (vfatFCBIsDirectory(FCB) ?
834                               FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
835                              FILE_ACTION_RENAMED_OLD_NAME);
836             Status = vfatRenameEntry(DeviceExt, FCB, &NewFile, TRUE);
837             if (NT_SUCCESS(Status))
838             {
839                 vfatReportChange(DeviceExt,
840                                  FCB,
841                                  (vfatFCBIsDirectory(FCB) ?
842                                   FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
843                                  FILE_ACTION_RENAMED_NEW_NAME);
844             }
845         }
846         else
847         {
848             /* Try to find target */
849             ParentFCB = FCB->parentFcb;
850             vfatGrabFCB(DeviceExt, ParentFCB);
851             Status = vfatPrepareTargetForRename(DeviceExt,
852                                                 &ParentFCB,
853                                                 &NewFile,
854                                                 RenameInfo->ReplaceIfExists,
855                                                 &NewPath,
856                                                 &DeletedTarget);
857             if (!NT_SUCCESS(Status))
858             {
859                 ASSERT(OldReferences == FCB->parentFcb->RefCount - 1);
860                 ASSERT(OldReferences == ParentFCB->RefCount - 1);
861                 goto Cleanup;
862             }
863 
864             vfatReportChange(DeviceExt,
865                              FCB,
866                              (vfatFCBIsDirectory(FCB) ?
867                               FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
868                              (DeletedTarget ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME));
869             Status = vfatRenameEntry(DeviceExt, FCB, &NewFile, FALSE);
870             if (NT_SUCCESS(Status))
871             {
872                 if (DeletedTarget)
873                 {
874                     vfatReportChange(DeviceExt,
875                                      FCB,
876                                      FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE
877                                      | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA,
878                                      FILE_ACTION_MODIFIED);
879                 }
880                 else
881                 {
882                     vfatReportChange(DeviceExt,
883                                      FCB,
884                                      (vfatFCBIsDirectory(FCB) ?
885                                       FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
886                                      FILE_ACTION_RENAMED_NEW_NAME);
887                 }
888             }
889         }
890 
891         ASSERT(OldReferences == FCB->parentFcb->RefCount - 1); // extra grab
892         ASSERT(OldReferences == ParentFCB->RefCount - 1); // extra grab
893     }
894     else
895     {
896 
897         /* Try to find target */
898         ParentFCB = NULL;
899         OldParent = FCB->parentFcb;
900 #ifdef NASSERTS_RENAME
901         UNREFERENCED_PARAMETER(OldParent);
902 #endif
903         Status = vfatPrepareTargetForRename(DeviceExt,
904                                             &ParentFCB,
905                                             &NewName,
906                                             RenameInfo->ReplaceIfExists,
907                                             &NewPath,
908                                             &DeletedTarget);
909         if (!NT_SUCCESS(Status))
910         {
911             ASSERT(OldReferences == FCB->parentFcb->RefCount);
912             goto Cleanup;
913         }
914 
915         NewReferences = ParentFCB->RefCount;
916 #ifdef NASSERTS_RENAME
917         UNREFERENCED_PARAMETER(NewReferences);
918 #endif
919 
920         vfatReportChange(DeviceExt,
921                          FCB,
922                          (vfatFCBIsDirectory(FCB) ?
923                           FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
924                          FILE_ACTION_REMOVED);
925         Status = VfatMoveEntry(DeviceExt, FCB, &NewFile, ParentFCB);
926         if (NT_SUCCESS(Status))
927         {
928             if (DeletedTarget)
929             {
930                 vfatReportChange(DeviceExt,
931                                  FCB,
932                                  FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE
933                                  | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA,
934                                  FILE_ACTION_MODIFIED);
935             }
936             else
937             {
938                 vfatReportChange(DeviceExt,
939                                  FCB,
940                                  (vfatFCBIsDirectory(FCB) ?
941                                   FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
942                                  FILE_ACTION_ADDED);
943             }
944         }
945     }
946 
947     if (NT_SUCCESS(Status) && vfatFCBIsDirectory(FCB))
948     {
949         VfatRenameChildFCB(DeviceExt, FCB);
950     }
951 
952     ASSERT(OldReferences == OldParent->RefCount + 1); // removed file
953     ASSERT(NewReferences == ParentFCB->RefCount - 1); // new file
954 Cleanup:
955     if (ParentFCB != NULL) vfatReleaseFCB(DeviceExt, ParentFCB);
956     if (NewName.Buffer != NULL) ExFreePoolWithTag(NewName.Buffer, TAG_NAME);
957     if (RenameInfo->RootDirectory != NULL) ObDereferenceObject(RootFileObject);
958 
959     return Status;
960 #ifdef NASSERTS_RENAME
961 #pragma pop_macro("ASSERT")
962 #endif
963 }
964 
965 /*
966  * FUNCTION: Retrieve the file name information
967  */
968 static
969 NTSTATUS
970 VfatGetNameInformation(
971     PFILE_OBJECT FileObject,
972     PVFATFCB FCB,
973     PDEVICE_EXTENSION DeviceExt,
974     PFILE_NAME_INFORMATION NameInfo,
975     PULONG BufferLength)
976 {
977     ULONG BytesToCopy;
978 
979     UNREFERENCED_PARAMETER(FileObject);
980     UNREFERENCED_PARAMETER(DeviceExt);
981 
982     ASSERT(NameInfo != NULL);
983     ASSERT(FCB != NULL);
984 
985     /* If buffer can't hold at least the file name length, bail out */
986     if (*BufferLength < (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]))
987         return STATUS_BUFFER_OVERFLOW;
988 
989     /* Save file name length, and as much file len, as buffer length allows */
990     NameInfo->FileNameLength = FCB->PathNameU.Length;
991 
992     /* Calculate amount of bytes to copy not to overflow the buffer */
993     BytesToCopy = min(FCB->PathNameU.Length,
994                       *BufferLength - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]));
995 
996     /* Fill in the bytes */
997     RtlCopyMemory(NameInfo->FileName, FCB->PathNameU.Buffer, BytesToCopy);
998 
999     /* Check if we could write more but are not able to */
1000     if (*BufferLength < FCB->PathNameU.Length + (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]))
1001     {
1002         /* Return number of bytes written */
1003         *BufferLength -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + BytesToCopy;
1004         return STATUS_BUFFER_OVERFLOW;
1005     }
1006 
1007     /* We filled up as many bytes, as needed */
1008     *BufferLength -= (FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + FCB->PathNameU.Length);
1009 
1010     return STATUS_SUCCESS;
1011 }
1012 
1013 static
1014 NTSTATUS
1015 VfatGetInternalInformation(
1016     PVFATFCB Fcb,
1017     PDEVICE_EXTENSION DeviceExt,
1018     PFILE_INTERNAL_INFORMATION InternalInfo,
1019     PULONG BufferLength)
1020 {
1021     ASSERT(InternalInfo);
1022     ASSERT(Fcb);
1023 
1024     if (*BufferLength < sizeof(FILE_INTERNAL_INFORMATION))
1025         return STATUS_BUFFER_OVERFLOW;
1026 
1027     InternalInfo->IndexNumber.QuadPart = (LONGLONG)vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry) * DeviceExt->FatInfo.BytesPerCluster;
1028 
1029     *BufferLength -= sizeof(FILE_INTERNAL_INFORMATION);
1030     return STATUS_SUCCESS;
1031 }
1032 
1033 
1034 /*
1035  * FUNCTION: Retrieve the file network open information
1036  */
1037 static
1038 NTSTATUS
1039 VfatGetNetworkOpenInformation(
1040     PVFATFCB Fcb,
1041     PDEVICE_EXTENSION DeviceExt,
1042     PFILE_NETWORK_OPEN_INFORMATION NetworkInfo,
1043     PULONG BufferLength)
1044 {
1045     ASSERT(NetworkInfo);
1046     ASSERT(Fcb);
1047 
1048     if (*BufferLength < sizeof(FILE_NETWORK_OPEN_INFORMATION))
1049         return(STATUS_BUFFER_OVERFLOW);
1050 
1051     if (vfatVolumeIsFatX(DeviceExt))
1052     {
1053         FsdDosDateTimeToSystemTime(DeviceExt,
1054                                    Fcb->entry.FatX.CreationDate,
1055                                    Fcb->entry.FatX.CreationTime,
1056                                    &NetworkInfo->CreationTime);
1057         FsdDosDateTimeToSystemTime(DeviceExt,
1058                                    Fcb->entry.FatX.AccessDate,
1059                                    Fcb->entry.FatX.AccessTime,
1060                                    &NetworkInfo->LastAccessTime);
1061         FsdDosDateTimeToSystemTime(DeviceExt,
1062                                    Fcb->entry.FatX.UpdateDate,
1063                                    Fcb->entry.FatX.UpdateTime,
1064                                    &NetworkInfo->LastWriteTime);
1065         NetworkInfo->ChangeTime.QuadPart = NetworkInfo->LastWriteTime.QuadPart;
1066     }
1067     else
1068     {
1069         FsdDosDateTimeToSystemTime(DeviceExt,
1070                                    Fcb->entry.Fat.CreationDate,
1071                                    Fcb->entry.Fat.CreationTime,
1072                                    &NetworkInfo->CreationTime);
1073         FsdDosDateTimeToSystemTime(DeviceExt,
1074                                    Fcb->entry.Fat.AccessDate,
1075                                    0,
1076                                    &NetworkInfo->LastAccessTime);
1077         FsdDosDateTimeToSystemTime(DeviceExt,
1078                                    Fcb->entry.Fat.UpdateDate,
1079                                    Fcb->entry.Fat.UpdateTime,
1080                                    &NetworkInfo->LastWriteTime);
1081         NetworkInfo->ChangeTime.QuadPart = NetworkInfo->LastWriteTime.QuadPart;
1082     }
1083 
1084     if (vfatFCBIsDirectory(Fcb))
1085     {
1086         NetworkInfo->EndOfFile.QuadPart = 0L;
1087         NetworkInfo->AllocationSize.QuadPart = 0L;
1088     }
1089     else
1090     {
1091         NetworkInfo->AllocationSize = Fcb->RFCB.AllocationSize;
1092         NetworkInfo->EndOfFile = Fcb->RFCB.FileSize;
1093     }
1094 
1095     NetworkInfo->FileAttributes = *Fcb->Attributes & 0x3f;
1096     /* Synthesize FILE_ATTRIBUTE_NORMAL */
1097     if (0 == (NetworkInfo->FileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
1098                                              FILE_ATTRIBUTE_ARCHIVE |
1099                                              FILE_ATTRIBUTE_SYSTEM |
1100                                              FILE_ATTRIBUTE_HIDDEN |
1101                                              FILE_ATTRIBUTE_READONLY)))
1102     {
1103         DPRINT("Synthesizing FILE_ATTRIBUTE_NORMAL\n");
1104         NetworkInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL;
1105     }
1106 
1107     *BufferLength -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
1108     return STATUS_SUCCESS;
1109 }
1110 
1111 
1112 static
1113 NTSTATUS
1114 VfatGetEaInformation(
1115     PFILE_OBJECT FileObject,
1116     PVFATFCB Fcb,
1117     PDEVICE_EXTENSION DeviceExt,
1118     PFILE_EA_INFORMATION Info,
1119     PULONG BufferLength)
1120 {
1121     UNREFERENCED_PARAMETER(FileObject);
1122     UNREFERENCED_PARAMETER(Fcb);
1123 
1124     /* FIXME - use SEH to access the buffer! */
1125     Info->EaSize = 0;
1126     *BufferLength -= sizeof(*Info);
1127     if (DeviceExt->FatInfo.FatType == FAT12 ||
1128         DeviceExt->FatInfo.FatType == FAT16)
1129     {
1130         /* FIXME */
1131         DPRINT1("VFAT: FileEaInformation not implemented!\n");
1132     }
1133     return STATUS_SUCCESS;
1134 }
1135 
1136 
1137 /*
1138  * FUNCTION: Retrieve the all file information
1139  */
1140 static
1141 NTSTATUS
1142 VfatGetAllInformation(
1143     PFILE_OBJECT FileObject,
1144     PVFATFCB Fcb,
1145     PDEVICE_EXTENSION DeviceExt,
1146     PFILE_ALL_INFORMATION Info,
1147     PULONG BufferLength)
1148 {
1149     NTSTATUS Status;
1150 
1151     ASSERT(Info);
1152     ASSERT(Fcb);
1153 
1154     if (*BufferLength < FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName))
1155         return STATUS_BUFFER_OVERFLOW;
1156 
1157     *BufferLength -= (sizeof(FILE_ACCESS_INFORMATION) + sizeof(FILE_MODE_INFORMATION) + sizeof(FILE_ALIGNMENT_INFORMATION));
1158 
1159     /* Basic Information */
1160     Status = VfatGetBasicInformation(FileObject, Fcb, DeviceExt, &Info->BasicInformation, BufferLength);
1161     if (!NT_SUCCESS(Status)) return Status;
1162     /* Standard Information */
1163     Status = VfatGetStandardInformation(Fcb, &Info->StandardInformation, BufferLength);
1164     if (!NT_SUCCESS(Status)) return Status;
1165     /* Internal Information */
1166     Status = VfatGetInternalInformation(Fcb, DeviceExt, &Info->InternalInformation, BufferLength);
1167     if (!NT_SUCCESS(Status)) return Status;
1168     /* EA Information */
1169     Status = VfatGetEaInformation(FileObject, Fcb, DeviceExt, &Info->EaInformation, BufferLength);
1170     if (!NT_SUCCESS(Status)) return Status;
1171     /* Position Information */
1172     Status = VfatGetPositionInformation(FileObject, Fcb, DeviceExt, &Info->PositionInformation, BufferLength);
1173     if (!NT_SUCCESS(Status)) return Status;
1174     /* Name Information */
1175     Status = VfatGetNameInformation(FileObject, Fcb, DeviceExt, &Info->NameInformation, BufferLength);
1176 
1177     return Status;
1178 }
1179 
1180 static
1181 VOID
1182 UpdateFileSize(
1183     PFILE_OBJECT FileObject,
1184     PVFATFCB Fcb,
1185     ULONG Size,
1186     ULONG ClusterSize,
1187     BOOLEAN IsFatX)
1188 {
1189     if (Size > 0)
1190     {
1191         Fcb->RFCB.AllocationSize.QuadPart = ROUND_UP_64(Size, ClusterSize);
1192     }
1193     else
1194     {
1195         Fcb->RFCB.AllocationSize.QuadPart = (LONGLONG)0;
1196     }
1197     if (!vfatFCBIsDirectory(Fcb))
1198     {
1199         if (IsFatX)
1200             Fcb->entry.FatX.FileSize = Size;
1201         else
1202             Fcb->entry.Fat.FileSize = Size;
1203     }
1204     Fcb->RFCB.FileSize.QuadPart = Size;
1205     Fcb->RFCB.ValidDataLength.QuadPart = Size;
1206 
1207     CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
1208 }
1209 
1210 NTSTATUS
1211 VfatSetAllocationSizeInformation(
1212     PFILE_OBJECT FileObject,
1213     PVFATFCB Fcb,
1214     PDEVICE_EXTENSION DeviceExt,
1215     PLARGE_INTEGER AllocationSize)
1216 {
1217     ULONG OldSize;
1218     ULONG Cluster, FirstCluster;
1219     NTSTATUS Status;
1220 
1221     ULONG ClusterSize = DeviceExt->FatInfo.BytesPerCluster;
1222     ULONG NewSize = AllocationSize->u.LowPart;
1223     ULONG NCluster;
1224     BOOLEAN AllocSizeChanged = FALSE, IsFatX = vfatVolumeIsFatX(DeviceExt);
1225 
1226     DPRINT("VfatSetAllocationSizeInformation(File <%wZ>, AllocationSize %d %u)\n",
1227            &Fcb->PathNameU, AllocationSize->HighPart, AllocationSize->LowPart);
1228 
1229     if (IsFatX)
1230         OldSize = Fcb->entry.FatX.FileSize;
1231     else
1232         OldSize = Fcb->entry.Fat.FileSize;
1233 
1234     if (AllocationSize->u.HighPart > 0)
1235     {
1236         return STATUS_INVALID_PARAMETER;
1237     }
1238 
1239     if (OldSize == NewSize)
1240     {
1241         return STATUS_SUCCESS;
1242     }
1243 
1244     FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry);
1245 
1246     if (NewSize > Fcb->RFCB.AllocationSize.u.LowPart)
1247     {
1248         AllocSizeChanged = TRUE;
1249         if (FirstCluster == 0)
1250         {
1251             Fcb->LastCluster = Fcb->LastOffset = 0;
1252             Status = NextCluster(DeviceExt, FirstCluster, &FirstCluster, TRUE);
1253             if (!NT_SUCCESS(Status))
1254             {
1255                 DPRINT1("NextCluster failed. Status = %x\n", Status);
1256                 return Status;
1257             }
1258 
1259             if (FirstCluster == 0xffffffff)
1260             {
1261                 return STATUS_DISK_FULL;
1262             }
1263 
1264             Status = OffsetToCluster(DeviceExt, FirstCluster,
1265                                      ROUND_DOWN(NewSize - 1, ClusterSize),
1266                                      &NCluster, TRUE);
1267             if (NCluster == 0xffffffff || !NT_SUCCESS(Status))
1268             {
1269                 /* disk is full */
1270                 NCluster = Cluster = FirstCluster;
1271                 Status = STATUS_SUCCESS;
1272                 while (NT_SUCCESS(Status) && Cluster != 0xffffffff && Cluster > 1)
1273                 {
1274                     Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1275                     WriteCluster(DeviceExt, Cluster, 0);
1276                     Cluster = NCluster;
1277                 }
1278                 return STATUS_DISK_FULL;
1279             }
1280 
1281             if (IsFatX)
1282             {
1283                 Fcb->entry.FatX.FirstCluster = FirstCluster;
1284             }
1285             else
1286             {
1287                 if (DeviceExt->FatInfo.FatType == FAT32)
1288                 {
1289                     Fcb->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0x0000FFFF);
1290                     Fcb->entry.Fat.FirstClusterHigh = FirstCluster >> 16;
1291                 }
1292                 else
1293                 {
1294                     ASSERT((FirstCluster >> 16) == 0);
1295                     Fcb->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0x0000FFFF);
1296                 }
1297             }
1298         }
1299         else
1300         {
1301             if (Fcb->LastCluster > 0)
1302             {
1303                 if (Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize == Fcb->LastOffset)
1304                 {
1305                     Cluster = Fcb->LastCluster;
1306                     Status = STATUS_SUCCESS;
1307                 }
1308                 else
1309                 {
1310                     Status = OffsetToCluster(DeviceExt, Fcb->LastCluster,
1311                                              Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize - Fcb->LastOffset,
1312                                              &Cluster, FALSE);
1313                 }
1314             }
1315             else
1316             {
1317                 Status = OffsetToCluster(DeviceExt, FirstCluster,
1318                                          Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize,
1319                                          &Cluster, FALSE);
1320             }
1321 
1322             if (!NT_SUCCESS(Status))
1323             {
1324                 return Status;
1325             }
1326 
1327             Fcb->LastCluster = Cluster;
1328             Fcb->LastOffset = Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize;
1329 
1330             /* FIXME: Check status */
1331             /* Cluster points now to the last cluster within the chain */
1332             Status = OffsetToCluster(DeviceExt, Cluster,
1333                                      ROUND_DOWN(NewSize - 1, ClusterSize) - Fcb->LastOffset,
1334                                      &NCluster, TRUE);
1335             if (NCluster == 0xffffffff || !NT_SUCCESS(Status))
1336             {
1337                 /* disk is full */
1338                 NCluster = Cluster;
1339                 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1340                 WriteCluster(DeviceExt, Cluster, 0xffffffff);
1341                 Cluster = NCluster;
1342                 while (NT_SUCCESS(Status) && Cluster != 0xffffffff && Cluster > 1)
1343                 {
1344                     Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1345                     WriteCluster(DeviceExt, Cluster, 0);
1346                     Cluster = NCluster;
1347                 }
1348                 return STATUS_DISK_FULL;
1349             }
1350         }
1351         UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize, vfatVolumeIsFatX(DeviceExt));
1352     }
1353     else if (NewSize + ClusterSize <= Fcb->RFCB.AllocationSize.u.LowPart)
1354     {
1355         DPRINT("Check for the ability to set file size\n");
1356         if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer,
1357                                   (PLARGE_INTEGER)AllocationSize))
1358         {
1359             DPRINT("Couldn't set file size!\n");
1360             return STATUS_USER_MAPPED_FILE;
1361         }
1362         DPRINT("Can set file size\n");
1363 
1364         AllocSizeChanged = TRUE;
1365         /* FIXME: Use the cached cluster/offset better way. */
1366         Fcb->LastCluster = Fcb->LastOffset = 0;
1367         UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize, vfatVolumeIsFatX(DeviceExt));
1368         if (NewSize > 0)
1369         {
1370             Status = OffsetToCluster(DeviceExt, FirstCluster,
1371                                      ROUND_DOWN(NewSize - 1, ClusterSize),
1372                                      &Cluster, FALSE);
1373 
1374             NCluster = Cluster;
1375             Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1376             WriteCluster(DeviceExt, Cluster, 0xffffffff);
1377             Cluster = NCluster;
1378         }
1379         else
1380         {
1381             if (IsFatX)
1382             {
1383                 Fcb->entry.FatX.FirstCluster = 0;
1384             }
1385             else
1386             {
1387                 if (DeviceExt->FatInfo.FatType == FAT32)
1388                 {
1389                     Fcb->entry.Fat.FirstCluster = 0;
1390                     Fcb->entry.Fat.FirstClusterHigh = 0;
1391                 }
1392                 else
1393                 {
1394                     Fcb->entry.Fat.FirstCluster = 0;
1395                 }
1396             }
1397 
1398             NCluster = Cluster = FirstCluster;
1399             Status = STATUS_SUCCESS;
1400         }
1401 
1402         while (NT_SUCCESS(Status) && 0xffffffff != Cluster && Cluster > 1)
1403         {
1404             Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1405             WriteCluster(DeviceExt, Cluster, 0);
1406             Cluster = NCluster;
1407         }
1408 
1409         if (DeviceExt->FatInfo.FatType == FAT32)
1410         {
1411             FAT32UpdateFreeClustersCount(DeviceExt);
1412         }
1413     }
1414     else
1415     {
1416         UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize, vfatVolumeIsFatX(DeviceExt));
1417     }
1418 
1419     /* Update the on-disk directory entry */
1420     Fcb->Flags |= FCB_IS_DIRTY;
1421     if (AllocSizeChanged)
1422     {
1423         VfatUpdateEntry(DeviceExt, Fcb);
1424 
1425         vfatReportChange(DeviceExt, Fcb, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED);
1426     }
1427     return STATUS_SUCCESS;
1428 }
1429 
1430 /*
1431  * FUNCTION: Retrieve the specified file information
1432  */
1433 NTSTATUS
1434 VfatQueryInformation(
1435     PVFAT_IRP_CONTEXT IrpContext)
1436 {
1437     FILE_INFORMATION_CLASS FileInformationClass;
1438     PVFATFCB FCB;
1439 
1440     NTSTATUS Status = STATUS_SUCCESS;
1441     PVOID SystemBuffer;
1442     ULONG BufferLength;
1443 
1444     /* PRECONDITION */
1445     ASSERT(IrpContext);
1446 
1447     /* INITIALIZATION */
1448     FileInformationClass = IrpContext->Stack->Parameters.QueryFile.FileInformationClass;
1449     FCB = (PVFATFCB) IrpContext->FileObject->FsContext;
1450 
1451     DPRINT("VfatQueryInformation is called for '%s'\n",
1452            FileInformationClass >= FileMaximumInformation - 1 ? "????" : FileInformationClassNames[FileInformationClass]);
1453 
1454     if (FCB == NULL)
1455     {
1456         DPRINT1("IRP_MJ_QUERY_INFORMATION without FCB!\n");
1457         IrpContext->Irp->IoStatus.Information = 0;
1458         return STATUS_INVALID_PARAMETER;
1459     }
1460 
1461     SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
1462     BufferLength = IrpContext->Stack->Parameters.QueryFile.Length;
1463 
1464     if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE))
1465     {
1466         if (!ExAcquireResourceSharedLite(&FCB->MainResource,
1467                                          BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1468         {
1469             return VfatMarkIrpContextForQueue(IrpContext);
1470         }
1471     }
1472 
1473     switch (FileInformationClass)
1474     {
1475         case FileStandardInformation:
1476             Status = VfatGetStandardInformation(FCB,
1477                                                 SystemBuffer,
1478                                                 &BufferLength);
1479             break;
1480 
1481         case FilePositionInformation:
1482             Status = VfatGetPositionInformation(IrpContext->FileObject,
1483                                                 FCB,
1484                                                 IrpContext->DeviceExt,
1485                                                 SystemBuffer,
1486                                                 &BufferLength);
1487             break;
1488 
1489         case FileBasicInformation:
1490             Status = VfatGetBasicInformation(IrpContext->FileObject,
1491                                              FCB,
1492                                              IrpContext->DeviceExt,
1493                                              SystemBuffer,
1494                                              &BufferLength);
1495             break;
1496 
1497         case FileNameInformation:
1498             Status = VfatGetNameInformation(IrpContext->FileObject,
1499                                             FCB,
1500                                             IrpContext->DeviceExt,
1501                                             SystemBuffer,
1502                                             &BufferLength);
1503             break;
1504 
1505         case FileInternalInformation:
1506             Status = VfatGetInternalInformation(FCB,
1507                                                 IrpContext->DeviceExt,
1508                                                 SystemBuffer,
1509                                                 &BufferLength);
1510             break;
1511 
1512         case FileNetworkOpenInformation:
1513             Status = VfatGetNetworkOpenInformation(FCB,
1514                                                    IrpContext->DeviceExt,
1515                                                    SystemBuffer,
1516                                                    &BufferLength);
1517             break;
1518 
1519         case FileAllInformation:
1520             Status = VfatGetAllInformation(IrpContext->FileObject,
1521                                            FCB,
1522                                            IrpContext->DeviceExt,
1523                                            SystemBuffer,
1524                                            &BufferLength);
1525             break;
1526 
1527         case FileEaInformation:
1528             Status = VfatGetEaInformation(IrpContext->FileObject,
1529                                           FCB,
1530                                           IrpContext->DeviceExt,
1531                                           SystemBuffer,
1532                                           &BufferLength);
1533             break;
1534 
1535         case FileAlternateNameInformation:
1536             Status = STATUS_NOT_IMPLEMENTED;
1537             break;
1538 
1539         default:
1540             Status = STATUS_INVALID_PARAMETER;
1541     }
1542 
1543     if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE))
1544     {
1545         ExReleaseResourceLite(&FCB->MainResource);
1546     }
1547 
1548     if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW)
1549         IrpContext->Irp->IoStatus.Information =
1550             IrpContext->Stack->Parameters.QueryFile.Length - BufferLength;
1551     else
1552         IrpContext->Irp->IoStatus.Information = 0;
1553 
1554     return Status;
1555 }
1556 
1557 /*
1558  * FUNCTION: Retrieve the specified file information
1559  */
1560 NTSTATUS
1561 VfatSetInformation(
1562     PVFAT_IRP_CONTEXT IrpContext)
1563 {
1564     FILE_INFORMATION_CLASS FileInformationClass;
1565     PVFATFCB FCB;
1566     NTSTATUS Status = STATUS_SUCCESS;
1567     PVOID SystemBuffer;
1568     BOOLEAN LockDir;
1569 
1570     /* PRECONDITION */
1571     ASSERT(IrpContext);
1572 
1573     DPRINT("VfatSetInformation(IrpContext %p)\n", IrpContext);
1574 
1575     /* INITIALIZATION */
1576     FileInformationClass =
1577         IrpContext->Stack->Parameters.SetFile.FileInformationClass;
1578     FCB = (PVFATFCB) IrpContext->FileObject->FsContext;
1579     SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
1580 
1581     DPRINT("VfatSetInformation is called for '%s'\n",
1582            FileInformationClass >= FileMaximumInformation - 1 ? "????" : FileInformationClassNames[ FileInformationClass]);
1583 
1584     DPRINT("FileInformationClass %d\n", FileInformationClass);
1585     DPRINT("SystemBuffer %p\n", SystemBuffer);
1586 
1587     if (FCB == NULL)
1588     {
1589         DPRINT1("IRP_MJ_SET_INFORMATION without FCB!\n");
1590         IrpContext->Irp->IoStatus.Information = 0;
1591         return STATUS_INVALID_PARAMETER;
1592     }
1593 
1594     /* Special: We should call MmCanFileBeTruncated here to determine if changing
1595        the file size would be allowed.  If not, we bail with the right error.
1596        We must do this before acquiring the lock. */
1597     if (FileInformationClass == FileEndOfFileInformation)
1598     {
1599         DPRINT("Check for the ability to set file size\n");
1600         if (!MmCanFileBeTruncated(IrpContext->FileObject->SectionObjectPointer,
1601                                   (PLARGE_INTEGER)SystemBuffer))
1602         {
1603             DPRINT("Couldn't set file size!\n");
1604             IrpContext->Irp->IoStatus.Information = 0;
1605             return STATUS_USER_MAPPED_FILE;
1606         }
1607         DPRINT("Can set file size\n");
1608     }
1609 
1610     LockDir = FALSE;
1611     if (FileInformationClass == FileRenameInformation || FileInformationClass == FileAllocationInformation ||
1612         FileInformationClass == FileEndOfFileInformation || FileInformationClass == FileBasicInformation)
1613     {
1614         LockDir = TRUE;
1615     }
1616 
1617     if (LockDir)
1618     {
1619         if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
1620                                             BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1621         {
1622             return VfatMarkIrpContextForQueue(IrpContext);
1623         }
1624     }
1625 
1626     if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE))
1627     {
1628         if (!ExAcquireResourceExclusiveLite(&FCB->MainResource,
1629                                             BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1630         {
1631             if (LockDir)
1632             {
1633                 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
1634             }
1635 
1636             return VfatMarkIrpContextForQueue(IrpContext);
1637         }
1638     }
1639 
1640     switch (FileInformationClass)
1641     {
1642         case FilePositionInformation:
1643             Status = VfatSetPositionInformation(IrpContext->FileObject,
1644                                                 SystemBuffer);
1645             break;
1646 
1647         case FileDispositionInformation:
1648             Status = VfatSetDispositionInformation(IrpContext->FileObject,
1649                                                    FCB,
1650                                                    IrpContext->DeviceExt,
1651                                                    SystemBuffer);
1652             break;
1653 
1654         case FileAllocationInformation:
1655         case FileEndOfFileInformation:
1656             Status = VfatSetAllocationSizeInformation(IrpContext->FileObject,
1657                                                       FCB,
1658                                                       IrpContext->DeviceExt,
1659                                                       (PLARGE_INTEGER)SystemBuffer);
1660             break;
1661 
1662         case FileBasicInformation:
1663             Status = VfatSetBasicInformation(IrpContext->FileObject,
1664                                              FCB,
1665                                              IrpContext->DeviceExt,
1666                                              SystemBuffer);
1667             break;
1668 
1669         case FileRenameInformation:
1670             Status = VfatSetRenameInformation(IrpContext->FileObject,
1671                                               FCB,
1672                                               IrpContext->DeviceExt,
1673                                               SystemBuffer,
1674                                               IrpContext->Stack->Parameters.SetFile.FileObject);
1675             break;
1676 
1677         default:
1678             Status = STATUS_NOT_SUPPORTED;
1679     }
1680 
1681     if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE))
1682     {
1683         ExReleaseResourceLite(&FCB->MainResource);
1684     }
1685 
1686     if (LockDir)
1687     {
1688         ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
1689     }
1690 
1691     IrpContext->Irp->IoStatus.Information = 0;
1692     return Status;
1693 }
1694 
1695 /* EOF */
1696