1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * PROJECT:          ReactOS kernel
21  * FILE:             drivers/filesystems/fastfat/create.c
22  * PURPOSE:          VFAT Filesystem
23  * PROGRAMMER:       Jason Filby (jasonfilby@yahoo.com)
24  *                   Pierre Schweitzer (pierre@reactos.org)
25  */
26 
27 /* INCLUDES *****************************************************************/
28 
29 #include "vfat.h"
30 
31 #define NDEBUG
32 #include <debug.h>
33 
34 /* FUNCTIONS *****************************************************************/
35 
36 VOID
37 vfat8Dot3ToString(
38     PFAT_DIR_ENTRY pEntry,
39     PUNICODE_STRING NameU)
40 {
41     OEM_STRING StringA;
42     USHORT Length;
43     CHAR  cString[12];
44 
45     RtlCopyMemory(cString, pEntry->ShortName, 11);
46     cString[11] = 0;
47     if (cString[0] == 0x05)
48     {
49         cString[0] = 0xe5;
50     }
51 
52     StringA.Buffer = cString;
53     for (StringA.Length = 0;
54          StringA.Length < 8 && StringA.Buffer[StringA.Length] != ' ';
55          StringA.Length++);
56     StringA.MaximumLength = StringA.Length;
57 
58     RtlOemStringToUnicodeString(NameU, &StringA, FALSE);
59 
60     if (BooleanFlagOn(pEntry->lCase, VFAT_CASE_LOWER_BASE))
61     {
62         RtlDowncaseUnicodeString(NameU, NameU, FALSE);
63     }
64 
65     if (cString[8] != ' ')
66     {
67         Length = NameU->Length;
68         NameU->Buffer += Length / sizeof(WCHAR);
69         if (!FAT_ENTRY_VOLUME(pEntry))
70         {
71             Length += sizeof(WCHAR);
72             NameU->Buffer[0] = L'.';
73             NameU->Buffer++;
74         }
75         NameU->Length = 0;
76         NameU->MaximumLength -= Length;
77 
78         StringA.Buffer = &cString[8];
79         for (StringA.Length = 0;
80         StringA.Length < 3 && StringA.Buffer[StringA.Length] != ' ';
81         StringA.Length++);
82         StringA.MaximumLength = StringA.Length;
83         RtlOemStringToUnicodeString(NameU, &StringA, FALSE);
84         if (BooleanFlagOn(pEntry->lCase, VFAT_CASE_LOWER_EXT))
85         {
86             RtlDowncaseUnicodeString(NameU, NameU, FALSE);
87         }
88         NameU->Buffer -= Length / sizeof(WCHAR);
89         NameU->Length += Length;
90         NameU->MaximumLength += Length;
91     }
92 
93     NameU->Buffer[NameU->Length / sizeof(WCHAR)] = 0;
94     DPRINT("'%wZ'\n", NameU);
95 }
96 
97 /*
98  * FUNCTION: Find a file
99  */
100 NTSTATUS
101 FindFile(
102     PDEVICE_EXTENSION DeviceExt,
103     PVFATFCB Parent,
104     PUNICODE_STRING FileToFindU,
105     PVFAT_DIRENTRY_CONTEXT DirContext,
106     BOOLEAN First)
107 {
108     PWCHAR PathNameBuffer;
109     USHORT PathNameBufferLength;
110     NTSTATUS Status;
111     PVOID Context = NULL;
112     PVOID Page;
113     PVFATFCB rcFcb;
114     BOOLEAN Found;
115     UNICODE_STRING PathNameU;
116     UNICODE_STRING FileToFindUpcase;
117     BOOLEAN WildCard;
118     BOOLEAN IsFatX = vfatVolumeIsFatX(DeviceExt);
119 
120     DPRINT("FindFile(Parent %p, FileToFind '%wZ', DirIndex: %u)\n",
121            Parent, FileToFindU, DirContext->DirIndex);
122     DPRINT("FindFile: Path %wZ\n",&Parent->PathNameU);
123 
124     PathNameBufferLength = LONGNAME_MAX_LENGTH * sizeof(WCHAR);
125     PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameBufferLength + sizeof(WCHAR), TAG_NAME);
126     if (!PathNameBuffer)
127     {
128         return STATUS_INSUFFICIENT_RESOURCES;
129     }
130 
131     PathNameU.Buffer = PathNameBuffer;
132     PathNameU.Length = 0;
133     PathNameU.MaximumLength = PathNameBufferLength;
134 
135     DirContext->LongNameU.Length = 0;
136     DirContext->ShortNameU.Length = 0;
137 
138     WildCard = FsRtlDoesNameContainWildCards(FileToFindU);
139 
140     if (WildCard == FALSE)
141     {
142         /* if there is no '*?' in the search name, than look first for an existing fcb */
143         RtlCopyUnicodeString(&PathNameU, &Parent->PathNameU);
144         if (!vfatFCBIsRoot(Parent))
145         {
146             PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = L'\\';
147             PathNameU.Length += sizeof(WCHAR);
148         }
149         RtlAppendUnicodeStringToString(&PathNameU, FileToFindU);
150         PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = 0;
151         rcFcb = vfatGrabFCBFromTable(DeviceExt, &PathNameU);
152         if (rcFcb)
153         {
154             ULONG startIndex = rcFcb->startIndex;
155             if (IsFatX && !vfatFCBIsRoot(Parent))
156             {
157                 startIndex += 2;
158             }
159             if(startIndex >= DirContext->DirIndex)
160             {
161                 RtlCopyUnicodeString(&DirContext->LongNameU, &rcFcb->LongNameU);
162                 RtlCopyUnicodeString(&DirContext->ShortNameU, &rcFcb->ShortNameU);
163                 RtlCopyMemory(&DirContext->DirEntry, &rcFcb->entry, sizeof(DIR_ENTRY));
164                 DirContext->StartIndex = rcFcb->startIndex;
165                 DirContext->DirIndex = rcFcb->dirIndex;
166                 DPRINT("FindFile: new Name %wZ, DirIndex %u (%u)\n",
167                     &DirContext->LongNameU, DirContext->DirIndex, DirContext->StartIndex);
168                 Status = STATUS_SUCCESS;
169             }
170             else
171             {
172                 DPRINT("FCB not found for %wZ\n", &PathNameU);
173                 Status = STATUS_UNSUCCESSFUL;
174             }
175             vfatReleaseFCB(DeviceExt, rcFcb);
176             ExFreePoolWithTag(PathNameBuffer, TAG_NAME);
177             return Status;
178         }
179     }
180 
181     /* FsRtlIsNameInExpression need the searched string to be upcase,
182     * even if IgnoreCase is specified */
183     Status = RtlUpcaseUnicodeString(&FileToFindUpcase, FileToFindU, TRUE);
184     if (!NT_SUCCESS(Status))
185     {
186         ExFreePoolWithTag(PathNameBuffer, TAG_NAME);
187         return Status;
188     }
189 
190     while (TRUE)
191     {
192         Status = VfatGetNextDirEntry(DeviceExt, &Context, &Page, Parent, DirContext, First);
193         First = FALSE;
194         if (Status == STATUS_NO_MORE_ENTRIES)
195         {
196             break;
197         }
198         if (ENTRY_VOLUME(IsFatX, &DirContext->DirEntry))
199         {
200             DirContext->DirIndex++;
201             continue;
202         }
203         if (DirContext->LongNameU.Length == 0 ||
204             DirContext->ShortNameU.Length == 0)
205         {
206             DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
207             if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
208             {
209                 ASSERT(DirContext->LongNameU.Length != 0 &&
210                        DirContext->ShortNameU.Length != 0);
211             }
212             DirContext->DirIndex++;
213             continue;
214         }
215         if (WildCard)
216         {
217             Found = FsRtlIsNameInExpression(&FileToFindUpcase, &DirContext->LongNameU, TRUE, NULL) ||
218                 FsRtlIsNameInExpression(&FileToFindUpcase, &DirContext->ShortNameU, TRUE, NULL);
219         }
220         else
221         {
222             Found = FsRtlAreNamesEqual(&DirContext->LongNameU, FileToFindU, TRUE, NULL) ||
223                 FsRtlAreNamesEqual(&DirContext->ShortNameU, FileToFindU, TRUE, NULL);
224         }
225 
226         if (Found)
227         {
228             if (WildCard)
229             {
230                 RtlCopyUnicodeString(&PathNameU, &Parent->PathNameU);
231                 if (!vfatFCBIsRoot(Parent))
232                 {
233                     PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = L'\\';
234                     PathNameU.Length += sizeof(WCHAR);
235                 }
236                 RtlAppendUnicodeStringToString(&PathNameU, &DirContext->LongNameU);
237                 PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = 0;
238                 rcFcb = vfatGrabFCBFromTable(DeviceExt, &PathNameU);
239                 if (rcFcb != NULL)
240                 {
241                     RtlCopyMemory(&DirContext->DirEntry, &rcFcb->entry, sizeof(DIR_ENTRY));
242                     vfatReleaseFCB(DeviceExt, rcFcb);
243                 }
244             }
245             DPRINT("%u\n", DirContext->LongNameU.Length);
246             DPRINT("FindFile: new Name %wZ, DirIndex %u\n",
247                 &DirContext->LongNameU, DirContext->DirIndex);
248 
249             if (Context)
250             {
251                 CcUnpinData(Context);
252             }
253             RtlFreeUnicodeString(&FileToFindUpcase);
254             ExFreePoolWithTag(PathNameBuffer, TAG_NAME);
255             return STATUS_SUCCESS;
256         }
257         DirContext->DirIndex++;
258     }
259 
260     if (Context)
261     {
262         CcUnpinData(Context);
263     }
264 
265     RtlFreeUnicodeString(&FileToFindUpcase);
266     ExFreePoolWithTag(PathNameBuffer, TAG_NAME);
267     return Status;
268 }
269 
270 /*
271  * FUNCTION: Opens a file
272  */
273 static
274 NTSTATUS
275 VfatOpenFile(
276     PDEVICE_EXTENSION DeviceExt,
277     PUNICODE_STRING PathNameU,
278     PFILE_OBJECT FileObject,
279     ULONG RequestedDisposition,
280     ULONG RequestedOptions,
281     PVFATFCB *ParentFcb)
282 {
283     PVFATFCB Fcb;
284     NTSTATUS Status;
285 
286     DPRINT("VfatOpenFile(%p, '%wZ', %p, %p)\n", DeviceExt, PathNameU, FileObject, ParentFcb);
287 
288     if (FileObject->RelatedFileObject)
289     {
290         DPRINT("'%wZ'\n", &FileObject->RelatedFileObject->FileName);
291 
292         *ParentFcb = FileObject->RelatedFileObject->FsContext;
293     }
294     else
295     {
296         *ParentFcb = NULL;
297     }
298 
299     if (!DeviceExt->FatInfo.FixedMedia)
300     {
301         Status = VfatBlockDeviceIoControl(DeviceExt->StorageDevice,
302                                           IOCTL_DISK_CHECK_VERIFY,
303                                           NULL,
304                                           0,
305                                           NULL,
306                                           0,
307                                           FALSE);
308         if (!NT_SUCCESS(Status))
309         {
310             DPRINT("Status %lx\n", Status);
311             *ParentFcb = NULL;
312             return Status;
313         }
314     }
315 
316     if (*ParentFcb)
317     {
318         vfatGrabFCB(DeviceExt, *ParentFcb);
319     }
320 
321     /*  try first to find an existing FCB in memory  */
322     DPRINT("Checking for existing FCB in memory\n");
323 
324     Status = vfatGetFCBForFile(DeviceExt, ParentFcb, &Fcb, PathNameU);
325     if (!NT_SUCCESS(Status))
326     {
327         DPRINT ("Could not make a new FCB, status: %x\n", Status);
328         return  Status;
329     }
330 
331     /* Fail, if we try to overwrite an existing directory */
332     if ((!BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) && vfatFCBIsDirectory(Fcb)) &&
333         (RequestedDisposition == FILE_OVERWRITE ||
334          RequestedDisposition == FILE_OVERWRITE_IF ||
335          RequestedDisposition == FILE_SUPERSEDE))
336     {
337         vfatReleaseFCB(DeviceExt, Fcb);
338         return STATUS_OBJECT_NAME_COLLISION;
339     }
340 
341     if (BooleanFlagOn(Fcb->Flags, FCB_DELETE_PENDING))
342     {
343         vfatReleaseFCB(DeviceExt, Fcb);
344         return STATUS_DELETE_PENDING;
345     }
346 
347     /* Fail, if we try to overwrite a read-only file */
348     if (vfatFCBIsReadOnly(Fcb) &&
349         (RequestedDisposition == FILE_OVERWRITE ||
350          RequestedDisposition == FILE_OVERWRITE_IF))
351     {
352         vfatReleaseFCB(DeviceExt, Fcb);
353         return STATUS_ACCESS_DENIED;
354     }
355 
356     if (vfatFCBIsReadOnly(Fcb) &&
357         (RequestedOptions & FILE_DELETE_ON_CLOSE))
358     {
359         vfatReleaseFCB(DeviceExt, Fcb);
360         return STATUS_CANNOT_DELETE;
361     }
362 
363     if ((vfatFCBIsRoot(Fcb) || IsDotOrDotDot(&Fcb->LongNameU)) &&
364         BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
365     {
366         // we cannot delete a '.', '..' or the root directory
367         vfatReleaseFCB(DeviceExt, Fcb);
368         return STATUS_CANNOT_DELETE;
369     }
370 
371     /* If that one was marked for closing, remove it */
372     if (BooleanFlagOn(Fcb->Flags, FCB_DELAYED_CLOSE))
373     {
374         BOOLEAN ConcurrentDeletion;
375         PVFAT_CLOSE_CONTEXT CloseContext;
376 
377         /* Get the context */
378         CloseContext = Fcb->CloseContext;
379         /* Is someone already taking over? */
380         if (CloseContext != NULL)
381         {
382             ConcurrentDeletion = FALSE;
383             /* Lock list */
384             ExAcquireFastMutex(&VfatGlobalData->CloseMutex);
385             /* Check whether it was already removed, if not, do it */
386             if (!IsListEmpty(&CloseContext->CloseListEntry))
387             {
388                 RemoveEntryList(&CloseContext->CloseListEntry);
389                 --VfatGlobalData->CloseCount;
390                 ConcurrentDeletion = TRUE;
391             }
392             ExReleaseFastMutex(&VfatGlobalData->CloseMutex);
393 
394             /* It's not delayed anymore! */
395             ClearFlag(Fcb->Flags, FCB_DELAYED_CLOSE);
396             /* Release the extra reference (would have been removed by IRP_MJ_CLOSE) */
397             vfatReleaseFCB(DeviceExt, Fcb);
398             Fcb->CloseContext = NULL;
399             /* If no concurrent deletion, free work item */
400             if (!ConcurrentDeletion)
401             {
402                 ExFreeToPagedLookasideList(&VfatGlobalData->CloseContextLookasideList, CloseContext);
403             }
404         }
405 
406         DPRINT("Reusing delayed close FCB for %wZ\n", &Fcb->PathNameU);
407     }
408 
409     DPRINT("Attaching FCB to fileObject\n");
410     Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, FileObject);
411     if (!NT_SUCCESS(Status))
412     {
413         vfatReleaseFCB(DeviceExt, Fcb);
414     }
415     return  Status;
416 }
417 
418 /*
419  * FUNCTION: Create or open a file
420  */
421 static NTSTATUS
422 VfatCreateFile(
423     PDEVICE_OBJECT DeviceObject,
424     PIRP Irp)
425 {
426     PIO_STACK_LOCATION Stack;
427     PFILE_OBJECT FileObject;
428     NTSTATUS Status = STATUS_SUCCESS;
429     PDEVICE_EXTENSION DeviceExt;
430     ULONG RequestedDisposition, RequestedOptions;
431     PVFATFCB pFcb = NULL;
432     PVFATFCB ParentFcb = NULL;
433     PVFATCCB pCcb = NULL;
434     PWCHAR c, last;
435     BOOLEAN PagingFileCreate;
436     BOOLEAN Dots;
437     BOOLEAN OpenTargetDir;
438     BOOLEAN TrailingBackslash;
439     UNICODE_STRING FileNameU;
440     UNICODE_STRING PathNameU;
441     ULONG Attributes;
442 
443     /* Unpack the various parameters. */
444     Stack = IoGetCurrentIrpStackLocation(Irp);
445     RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
446     RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
447     PagingFileCreate = BooleanFlagOn(Stack->Flags, SL_OPEN_PAGING_FILE);
448     OpenTargetDir = BooleanFlagOn(Stack->Flags, SL_OPEN_TARGET_DIRECTORY);
449 
450     FileObject = Stack->FileObject;
451     DeviceExt = DeviceObject->DeviceExtension;
452 
453     if (BooleanFlagOn(Stack->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID))
454     {
455         return STATUS_NOT_IMPLEMENTED;
456     }
457 
458     /* Check their validity. */
459     if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
460         RequestedDisposition == FILE_SUPERSEDE)
461     {
462         return STATUS_INVALID_PARAMETER;
463     }
464 
465     if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
466         BooleanFlagOn(RequestedOptions, FILE_NON_DIRECTORY_FILE))
467     {
468         return STATUS_INVALID_PARAMETER;
469     }
470 
471     /* Deny create if the volume is locked */
472     if (BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED))
473     {
474         return STATUS_ACCESS_DENIED;
475     }
476 
477     /* This a open operation for the volume itself */
478     if (FileObject->FileName.Length == 0 &&
479         (FileObject->RelatedFileObject == NULL ||
480          FileObject->RelatedFileObject->FsContext2 != NULL ||
481          FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb))
482     {
483         DPRINT("Volume opening\n");
484 
485         if (RequestedDisposition != FILE_OPEN &&
486             RequestedDisposition != FILE_OPEN_IF)
487         {
488             return STATUS_ACCESS_DENIED;
489         }
490 
491         if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
492             (FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 == NULL || FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb))
493         {
494             return STATUS_NOT_A_DIRECTORY;
495         }
496 
497         if (OpenTargetDir)
498         {
499             return STATUS_INVALID_PARAMETER;
500         }
501 
502         if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
503         {
504             return STATUS_CANNOT_DELETE;
505         }
506 
507         vfatAddToStat(DeviceExt, Fat.CreateHits, 1);
508 
509         pFcb = DeviceExt->VolumeFcb;
510 
511         if (pFcb->OpenHandleCount == 0)
512         {
513             IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
514                              Stack->Parameters.Create.ShareAccess,
515                              FileObject,
516                              &pFcb->FCBShareAccess);
517         }
518         else
519         {
520             Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
521                                         Stack->Parameters.Create.ShareAccess,
522                                         FileObject,
523                                         &pFcb->FCBShareAccess,
524                                         TRUE);
525             if (!NT_SUCCESS(Status))
526             {
527                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
528                 return Status;
529             }
530         }
531 
532         vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject);
533         DeviceExt->OpenHandleCount++;
534         pFcb->OpenHandleCount++;
535         vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
536 
537         Irp->IoStatus.Information = FILE_OPENED;
538         return STATUS_SUCCESS;
539     }
540 
541     if (FileObject->RelatedFileObject != NULL &&
542         FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb)
543     {
544         ASSERT(FileObject->FileName.Length != 0);
545         return STATUS_OBJECT_PATH_NOT_FOUND;
546     }
547 
548     /* Check for illegal characters and illegal dot sequences in the file name */
549     PathNameU = FileObject->FileName;
550     c = PathNameU.Buffer + PathNameU.Length / sizeof(WCHAR);
551     last = c - 1;
552 
553     Dots = TRUE;
554     while (c-- > PathNameU.Buffer)
555     {
556         if (*c == L'\\' || c == PathNameU.Buffer)
557         {
558             if (Dots && last > c)
559             {
560                 return STATUS_OBJECT_NAME_INVALID;
561             }
562             if (*c == L'\\' && (c - 1) > PathNameU.Buffer &&
563                 *(c - 1) == L'\\')
564             {
565                 return STATUS_OBJECT_NAME_INVALID;
566             }
567 
568             last = c - 1;
569             Dots = TRUE;
570         }
571         else if (*c != L'.')
572         {
573             Dots = FALSE;
574         }
575 
576         if (*c != '\\' && vfatIsLongIllegal(*c))
577         {
578             return STATUS_OBJECT_NAME_INVALID;
579         }
580     }
581 
582     /* Check if we try to open target directory of root dir */
583     if (OpenTargetDir && FileObject->RelatedFileObject == NULL && PathNameU.Length == sizeof(WCHAR) &&
584         PathNameU.Buffer[0] == L'\\')
585     {
586         return STATUS_INVALID_PARAMETER;
587     }
588 
589     if (FileObject->RelatedFileObject && PathNameU.Length >= sizeof(WCHAR) && PathNameU.Buffer[0] == L'\\')
590     {
591         return STATUS_OBJECT_NAME_INVALID;
592     }
593 
594     TrailingBackslash = FALSE;
595     if (PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[PathNameU.Length/sizeof(WCHAR)-1] == L'\\')
596     {
597         PathNameU.Length -= sizeof(WCHAR);
598         TrailingBackslash = TRUE;
599     }
600 
601     if (PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[PathNameU.Length/sizeof(WCHAR)-1] == L'\\')
602     {
603         return STATUS_OBJECT_NAME_INVALID;
604     }
605 
606     /* Try opening the file. */
607     if (!OpenTargetDir)
608     {
609         vfatAddToStat(DeviceExt, Fat.CreateHits, 1);
610 
611         Status = VfatOpenFile(DeviceExt, &PathNameU, FileObject, RequestedDisposition, RequestedOptions, &ParentFcb);
612     }
613     else
614     {
615         PVFATFCB TargetFcb;
616         LONG idx, FileNameLen;
617 
618         vfatAddToStat(DeviceExt, Fat.CreateHits, 1);
619 
620         ParentFcb = (FileObject->RelatedFileObject != NULL) ? FileObject->RelatedFileObject->FsContext : NULL;
621         if (ParentFcb)
622         {
623             vfatGrabFCB(DeviceExt, ParentFcb);
624         }
625 
626         Status = vfatGetFCBForFile(DeviceExt, &ParentFcb, &TargetFcb, &PathNameU);
627         if (NT_SUCCESS(Status))
628         {
629             vfatReleaseFCB(DeviceExt, TargetFcb);
630             Irp->IoStatus.Information = FILE_EXISTS;
631         }
632         else
633         {
634             Irp->IoStatus.Information = FILE_DOES_NOT_EXIST;
635         }
636 
637         idx = FileObject->FileName.Length / sizeof(WCHAR) - 1;
638 
639         /* Skip trailing \ - if any */
640         if (PathNameU.Buffer[idx] == L'\\')
641         {
642             --idx;
643             PathNameU.Length -= sizeof(WCHAR);
644         }
645 
646         /* Get file name */
647         while (idx >= 0 && PathNameU.Buffer[idx] != L'\\')
648         {
649             --idx;
650         }
651 
652         if (idx > 0 || PathNameU.Buffer[0] == L'\\')
653         {
654             /* We don't want to include / in the name */
655             FileNameLen = PathNameU.Length - ((idx + 1) * sizeof(WCHAR));
656 
657             /* Update FO just to keep file name */
658             /* Skip first slash */
659             ++idx;
660             FileObject->FileName.Length = FileNameLen;
661             RtlMoveMemory(&PathNameU.Buffer[0], &PathNameU.Buffer[idx], FileObject->FileName.Length);
662 #if 0
663             /* Terminate the string at the last backslash */
664             PathNameU.Buffer[idx + 1] = UNICODE_NULL;
665             PathNameU.Length = (idx + 1) * sizeof(WCHAR);
666             PathNameU.MaximumLength = PathNameU.Length + sizeof(WCHAR);
667 
668             /* Update the file object as well */
669             FileObject->FileName.Length = PathNameU.Length;
670             FileObject->FileName.MaximumLength = PathNameU.MaximumLength;
671 #endif
672         }
673         else
674         {
675             /* This is a relative open and we have only the filename, so open the parent directory
676              * It is in RelatedFileObject
677              */
678             ASSERT(FileObject->RelatedFileObject != NULL);
679 
680             /* No need to modify the FO, it already has the name */
681         }
682 
683         /* We're done with opening! */
684         if (ParentFcb != NULL)
685         {
686             Status = vfatAttachFCBToFileObject(DeviceExt, ParentFcb, FileObject);
687         }
688 
689         if (NT_SUCCESS(Status))
690         {
691             pFcb = FileObject->FsContext;
692             ASSERT(pFcb == ParentFcb);
693 
694             if (pFcb->OpenHandleCount == 0)
695             {
696                 IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
697                                  Stack->Parameters.Create.ShareAccess,
698                                  FileObject,
699                                  &pFcb->FCBShareAccess);
700             }
701             else
702             {
703                 Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
704                                             Stack->Parameters.Create.ShareAccess,
705                                             FileObject,
706                                             &pFcb->FCBShareAccess,
707                                             FALSE);
708                 if (!NT_SUCCESS(Status))
709                 {
710                     VfatCloseFile(DeviceExt, FileObject);
711                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
712                     return Status;
713                 }
714             }
715 
716             pCcb = FileObject->FsContext2;
717             if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
718             {
719                 pCcb->Flags |= CCB_DELETE_ON_CLOSE;
720             }
721 
722             pFcb->OpenHandleCount++;
723             DeviceExt->OpenHandleCount++;
724         }
725         else if (ParentFcb != NULL)
726         {
727             vfatReleaseFCB(DeviceExt, ParentFcb);
728         }
729 
730         if (NT_SUCCESS(Status))
731         {
732             vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
733         }
734         else
735         {
736             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
737         }
738 
739         return Status;
740     }
741 
742     /*
743      * If the directory containing the file to open doesn't exist then
744      * fail immediately
745      */
746     if (Status == STATUS_OBJECT_PATH_NOT_FOUND ||
747         Status == STATUS_INVALID_PARAMETER ||
748         Status == STATUS_DELETE_PENDING ||
749         Status == STATUS_ACCESS_DENIED ||
750         Status == STATUS_OBJECT_NAME_COLLISION)
751     {
752         if (ParentFcb)
753         {
754             vfatReleaseFCB(DeviceExt, ParentFcb);
755         }
756         vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
757         return Status;
758     }
759 
760     if (!NT_SUCCESS(Status) && ParentFcb == NULL)
761     {
762         DPRINT1("VfatOpenFile failed for '%wZ', status %x\n", &PathNameU, Status);
763         vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
764         return Status;
765     }
766 
767     Attributes = (Stack->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_ARCHIVE |
768                                                              FILE_ATTRIBUTE_SYSTEM |
769                                                              FILE_ATTRIBUTE_HIDDEN |
770                                                              FILE_ATTRIBUTE_DIRECTORY |
771                                                              FILE_ATTRIBUTE_READONLY));
772 
773     /* If the file open failed then create the required file */
774     if (!NT_SUCCESS (Status))
775     {
776         if (RequestedDisposition == FILE_CREATE ||
777             RequestedDisposition == FILE_OPEN_IF ||
778             RequestedDisposition == FILE_OVERWRITE_IF ||
779             RequestedDisposition == FILE_SUPERSEDE)
780         {
781             if (!BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE))
782             {
783                 if (TrailingBackslash)
784                 {
785                     vfatReleaseFCB(DeviceExt, ParentFcb);
786                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
787                     return STATUS_OBJECT_NAME_INVALID;
788                 }
789                 Attributes |= FILE_ATTRIBUTE_ARCHIVE;
790             }
791             vfatSplitPathName(&PathNameU, NULL, &FileNameU);
792 
793             if (IsDotOrDotDot(&FileNameU))
794             {
795                 vfatReleaseFCB(DeviceExt, ParentFcb);
796                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
797                 return STATUS_OBJECT_NAME_INVALID;
798             }
799             Status = VfatAddEntry(DeviceExt, &FileNameU, &pFcb, ParentFcb, RequestedOptions,
800                                   Attributes, NULL);
801             vfatReleaseFCB(DeviceExt, ParentFcb);
802             if (NT_SUCCESS(Status))
803             {
804                 Status = vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject);
805                 if (!NT_SUCCESS(Status))
806                 {
807                     vfatReleaseFCB(DeviceExt, pFcb);
808                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
809                     return Status;
810                 }
811 
812                 Irp->IoStatus.Information = FILE_CREATED;
813                 VfatSetAllocationSizeInformation(FileObject,
814                                                  pFcb,
815                                                  DeviceExt,
816                                                  &Irp->Overlay.AllocationSize);
817                 VfatSetExtendedAttributes(FileObject,
818                                           Irp->AssociatedIrp.SystemBuffer,
819                                           Stack->Parameters.Create.EaLength);
820 
821                 if (PagingFileCreate)
822                 {
823                     pFcb->Flags |= FCB_IS_PAGE_FILE;
824                     SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE);
825                 }
826             }
827             else
828             {
829                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
830                 return Status;
831             }
832         }
833         else
834         {
835             vfatReleaseFCB(DeviceExt, ParentFcb);
836             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
837             return Status;
838         }
839     }
840     else
841     {
842         if (ParentFcb)
843         {
844             vfatReleaseFCB(DeviceExt, ParentFcb);
845         }
846 
847         pFcb = FileObject->FsContext;
848 
849         /* Otherwise fail if the caller wanted to create a new file  */
850         if (RequestedDisposition == FILE_CREATE)
851         {
852             VfatCloseFile(DeviceExt, FileObject);
853             if (TrailingBackslash && !vfatFCBIsDirectory(pFcb))
854             {
855                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
856                 return STATUS_OBJECT_NAME_INVALID;
857             }
858             Irp->IoStatus.Information = FILE_EXISTS;
859             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
860             return STATUS_OBJECT_NAME_COLLISION;
861         }
862 
863         if (pFcb->OpenHandleCount != 0)
864         {
865             Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
866                                         Stack->Parameters.Create.ShareAccess,
867                                         FileObject,
868                                         &pFcb->FCBShareAccess,
869                                         FALSE);
870             if (!NT_SUCCESS(Status))
871             {
872                 VfatCloseFile(DeviceExt, FileObject);
873                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
874                 return Status;
875             }
876         }
877 
878         /*
879          * Check the file has the requested attributes
880          */
881         if (BooleanFlagOn(RequestedOptions, FILE_NON_DIRECTORY_FILE) &&
882             vfatFCBIsDirectory(pFcb))
883         {
884             VfatCloseFile (DeviceExt, FileObject);
885             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
886             return STATUS_FILE_IS_A_DIRECTORY;
887         }
888         if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
889             !vfatFCBIsDirectory(pFcb))
890         {
891             VfatCloseFile (DeviceExt, FileObject);
892             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
893             return STATUS_NOT_A_DIRECTORY;
894         }
895         if (TrailingBackslash && !vfatFCBIsDirectory(pFcb))
896         {
897             VfatCloseFile (DeviceExt, FileObject);
898             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
899             return STATUS_OBJECT_NAME_INVALID;
900         }
901 #ifndef USE_ROS_CC_AND_FS
902         if (!vfatFCBIsDirectory(pFcb))
903         {
904             if (BooleanFlagOn(Stack->Parameters.Create.SecurityContext->DesiredAccess, FILE_WRITE_DATA) ||
905                 RequestedDisposition == FILE_OVERWRITE ||
906                 RequestedDisposition == FILE_OVERWRITE_IF ||
907                 (RequestedOptions & FILE_DELETE_ON_CLOSE))
908             {
909                 if (!MmFlushImageSection(&pFcb->SectionObjectPointers, MmFlushForWrite))
910                 {
911                     DPRINT1("%wZ\n", &pFcb->PathNameU);
912                     DPRINT1("%d %d %d\n", Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA,
913                             RequestedDisposition == FILE_OVERWRITE, RequestedDisposition == FILE_OVERWRITE_IF);
914                     VfatCloseFile (DeviceExt, FileObject);
915                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
916                     return (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE)) ? STATUS_CANNOT_DELETE
917                                                                                    : STATUS_SHARING_VIOLATION;
918                 }
919             }
920         }
921 #endif
922         if (PagingFileCreate)
923         {
924             /* FIXME:
925              *   Do more checking for page files. It is possible,
926              *   that the file was opened and closed previously
927              *   as a normal cached file. In this case, the cache
928              *   manager has referenced the fileobject and the fcb
929              *   is held in memory. Try to remove the fileobject
930              *   from cache manager and use the fcb.
931              */
932             if (pFcb->RefCount > 1)
933             {
934                 if(!BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE))
935                 {
936                     VfatCloseFile(DeviceExt, FileObject);
937                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
938                     return STATUS_INVALID_PARAMETER;
939                 }
940             }
941             else
942             {
943                 pFcb->Flags |= FCB_IS_PAGE_FILE;
944                 SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE);
945             }
946         }
947         else
948         {
949             if (BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE))
950             {
951                 VfatCloseFile(DeviceExt, FileObject);
952                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
953                 return STATUS_INVALID_PARAMETER;
954             }
955         }
956 
957         if (RequestedDisposition == FILE_OVERWRITE ||
958             RequestedDisposition == FILE_OVERWRITE_IF ||
959             RequestedDisposition == FILE_SUPERSEDE)
960         {
961             if ((BooleanFlagOn(*pFcb->Attributes, FILE_ATTRIBUTE_HIDDEN) && !BooleanFlagOn(Attributes, FILE_ATTRIBUTE_HIDDEN)) ||
962                 (BooleanFlagOn(*pFcb->Attributes, FILE_ATTRIBUTE_SYSTEM) && !BooleanFlagOn(Attributes, FILE_ATTRIBUTE_SYSTEM)))
963             {
964                 VfatCloseFile(DeviceExt, FileObject);
965                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
966                 return STATUS_ACCESS_DENIED;
967             }
968 
969             if (!vfatFCBIsDirectory(pFcb))
970             {
971                 LARGE_INTEGER SystemTime;
972 
973                 if (RequestedDisposition == FILE_SUPERSEDE)
974                 {
975                     *pFcb->Attributes = Attributes;
976                 }
977                 else
978                 {
979                     *pFcb->Attributes |= Attributes;
980                 }
981                 *pFcb->Attributes |= FILE_ATTRIBUTE_ARCHIVE;
982 
983                 KeQuerySystemTime(&SystemTime);
984                 if (vfatVolumeIsFatX(DeviceExt))
985                 {
986                     FsdSystemTimeToDosDateTime(DeviceExt,
987                                                &SystemTime, &pFcb->entry.FatX.UpdateDate,
988                                                &pFcb->entry.FatX.UpdateTime);
989                 }
990                 else
991                 {
992                     FsdSystemTimeToDosDateTime(DeviceExt,
993                                                &SystemTime, &pFcb->entry.Fat.UpdateDate,
994                                                &pFcb->entry.Fat.UpdateTime);
995                 }
996 
997                 VfatUpdateEntry(DeviceExt, pFcb);
998             }
999 
1000             ExAcquireResourceExclusiveLite(&(pFcb->MainResource), TRUE);
1001             Status = VfatSetAllocationSizeInformation(FileObject,
1002                                                       pFcb,
1003                                                       DeviceExt,
1004                                                       &Irp->Overlay.AllocationSize);
1005             ExReleaseResourceLite(&(pFcb->MainResource));
1006             if (!NT_SUCCESS (Status))
1007             {
1008                 VfatCloseFile(DeviceExt, FileObject);
1009                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
1010                 return Status;
1011             }
1012         }
1013 
1014         if (RequestedDisposition == FILE_SUPERSEDE)
1015         {
1016             Irp->IoStatus.Information = FILE_SUPERSEDED;
1017         }
1018         else if (RequestedDisposition == FILE_OVERWRITE ||
1019                  RequestedDisposition == FILE_OVERWRITE_IF)
1020         {
1021             Irp->IoStatus.Information = FILE_OVERWRITTEN;
1022         }
1023         else
1024         {
1025             Irp->IoStatus.Information = FILE_OPENED;
1026         }
1027     }
1028 
1029     if (pFcb->OpenHandleCount == 0)
1030     {
1031         IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
1032                          Stack->Parameters.Create.ShareAccess,
1033                          FileObject,
1034                          &pFcb->FCBShareAccess);
1035     }
1036     else
1037     {
1038         IoUpdateShareAccess(FileObject,
1039                             &pFcb->FCBShareAccess);
1040     }
1041 
1042     pCcb = FileObject->FsContext2;
1043     if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
1044     {
1045         pCcb->Flags |= CCB_DELETE_ON_CLOSE;
1046     }
1047 
1048     if (Irp->IoStatus.Information == FILE_CREATED)
1049     {
1050         vfatReportChange(DeviceExt,
1051                          pFcb,
1052                          (vfatFCBIsDirectory(pFcb) ?
1053                           FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
1054                          FILE_ACTION_ADDED);
1055     }
1056     else if (Irp->IoStatus.Information == FILE_OVERWRITTEN ||
1057              Irp->IoStatus.Information == FILE_SUPERSEDED)
1058     {
1059         vfatReportChange(DeviceExt,
1060                          pFcb,
1061                          FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE,
1062                          FILE_ACTION_MODIFIED);
1063     }
1064 
1065     pFcb->OpenHandleCount++;
1066     DeviceExt->OpenHandleCount++;
1067 
1068     /* FIXME : test write access if requested */
1069 
1070     /* FIXME: That is broken, we cannot reach this code path with failure */
1071     ASSERT(NT_SUCCESS(Status));
1072     if (NT_SUCCESS(Status))
1073     {
1074         vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
1075     }
1076     else
1077     {
1078         vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
1079     }
1080 
1081     return Status;
1082 }
1083 
1084 /*
1085  * FUNCTION: Create or open a file
1086  */
1087 NTSTATUS
1088 VfatCreate(
1089     PVFAT_IRP_CONTEXT IrpContext)
1090 {
1091     NTSTATUS Status;
1092 
1093     ASSERT(IrpContext);
1094 
1095     if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
1096     {
1097         /* DeviceObject represents FileSystem instead of logical volume */
1098         DPRINT ("FsdCreate called with file system\n");
1099         IrpContext->Irp->IoStatus.Information = FILE_OPENED;
1100         IrpContext->PriorityBoost = IO_DISK_INCREMENT;
1101 
1102         return STATUS_SUCCESS;
1103     }
1104 
1105     IrpContext->Irp->IoStatus.Information = 0;
1106     ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, TRUE);
1107     Status = VfatCreateFile(IrpContext->DeviceObject, IrpContext->Irp);
1108     ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
1109 
1110     if (NT_SUCCESS(Status))
1111         IrpContext->PriorityBoost = IO_DISK_INCREMENT;
1112 
1113     return Status;
1114 }
1115 
1116 /* EOF */
1117