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_VFAT);
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             ExFreePool(PathNameBuffer);
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         ExFreePool(PathNameBuffer);
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             ExFreePool(PathNameBuffer);
255             return STATUS_SUCCESS;
256         }
257         DirContext->DirIndex++;
258     }
259 
260     if (Context)
261     {
262         CcUnpinData(Context);
263     }
264 
265     RtlFreeUnicodeString(&FileToFindUpcase);
266     ExFreePool(PathNameBuffer);
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) ||
364          (Fcb->LongNameU.Length == sizeof(WCHAR) && Fcb->LongNameU.Buffer[0] == L'.') ||
365          (Fcb->LongNameU.Length == 2 * sizeof(WCHAR) && Fcb->LongNameU.Buffer[0] == L'.' && Fcb->LongNameU.Buffer[1] == L'.')) &&
366         BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
367     {
368         // we cannot delete a '.', '..' or the root directory
369         vfatReleaseFCB(DeviceExt, Fcb);
370         return STATUS_CANNOT_DELETE;
371     }
372 
373     DPRINT("Attaching FCB to fileObject\n");
374     Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, FileObject);
375     if (!NT_SUCCESS(Status))
376     {
377         vfatReleaseFCB(DeviceExt, Fcb);
378     }
379     return  Status;
380 }
381 
382 /*
383  * FUNCTION: Create or open a file
384  */
385 static NTSTATUS
386 VfatCreateFile(
387     PDEVICE_OBJECT DeviceObject,
388     PIRP Irp)
389 {
390     PIO_STACK_LOCATION Stack;
391     PFILE_OBJECT FileObject;
392     NTSTATUS Status = STATUS_SUCCESS;
393     PDEVICE_EXTENSION DeviceExt;
394     ULONG RequestedDisposition, RequestedOptions;
395     PVFATFCB pFcb = NULL;
396     PVFATFCB ParentFcb = NULL;
397     PVFATCCB pCcb = NULL;
398     PWCHAR c, last;
399     BOOLEAN PagingFileCreate;
400     BOOLEAN Dots;
401     BOOLEAN OpenTargetDir;
402     BOOLEAN TrailingBackslash;
403     UNICODE_STRING FileNameU;
404     UNICODE_STRING PathNameU;
405     ULONG Attributes;
406 
407     /* Unpack the various parameters. */
408     Stack = IoGetCurrentIrpStackLocation(Irp);
409     RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
410     RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
411     PagingFileCreate = BooleanFlagOn(Stack->Flags, SL_OPEN_PAGING_FILE);
412     OpenTargetDir = BooleanFlagOn(Stack->Flags, SL_OPEN_TARGET_DIRECTORY);
413 
414     FileObject = Stack->FileObject;
415     DeviceExt = DeviceObject->DeviceExtension;
416 
417     if (BooleanFlagOn(Stack->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID))
418     {
419         return STATUS_NOT_IMPLEMENTED;
420     }
421 
422     /* Check their validity. */
423     if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
424         RequestedDisposition == FILE_SUPERSEDE)
425     {
426         return STATUS_INVALID_PARAMETER;
427     }
428 
429     if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
430         BooleanFlagOn(RequestedOptions, FILE_NON_DIRECTORY_FILE))
431     {
432         return STATUS_INVALID_PARAMETER;
433     }
434 
435     /* Deny create if the volume is locked */
436     if (BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED))
437     {
438         return STATUS_ACCESS_DENIED;
439     }
440 
441     /* This a open operation for the volume itself */
442     if (FileObject->FileName.Length == 0 &&
443         (FileObject->RelatedFileObject == NULL ||
444          FileObject->RelatedFileObject->FsContext2 != NULL ||
445          FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb))
446     {
447         DPRINT("Volume opening\n");
448 
449         if (RequestedDisposition != FILE_OPEN &&
450             RequestedDisposition != FILE_OPEN_IF)
451         {
452             return STATUS_ACCESS_DENIED;
453         }
454 
455         if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
456             (FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 == NULL || FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb))
457         {
458             return STATUS_NOT_A_DIRECTORY;
459         }
460 
461         if (OpenTargetDir)
462         {
463             return STATUS_INVALID_PARAMETER;
464         }
465 
466         if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
467         {
468             return STATUS_CANNOT_DELETE;
469         }
470 
471         vfatAddToStat(DeviceExt, Fat.CreateHits, 1);
472 
473         pFcb = DeviceExt->VolumeFcb;
474 
475         if (pFcb->OpenHandleCount == 0)
476         {
477             IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
478                              Stack->Parameters.Create.ShareAccess,
479                              FileObject,
480                              &pFcb->FCBShareAccess);
481         }
482         else
483         {
484             Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
485                                         Stack->Parameters.Create.ShareAccess,
486                                         FileObject,
487                                         &pFcb->FCBShareAccess,
488                                         FALSE);
489             if (!NT_SUCCESS(Status))
490             {
491                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
492                 return Status;
493             }
494         }
495 
496         vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject);
497         DeviceExt->OpenHandleCount++;
498         pFcb->OpenHandleCount++;
499         vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
500 
501         Irp->IoStatus.Information = FILE_OPENED;
502         return STATUS_SUCCESS;
503     }
504 
505     if (FileObject->RelatedFileObject != NULL &&
506         FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb)
507     {
508         ASSERT(FileObject->FileName.Length != 0);
509         return STATUS_OBJECT_PATH_NOT_FOUND;
510     }
511 
512     /* Check for illegal characters and illegal dot sequences in the file name */
513     PathNameU = FileObject->FileName;
514     c = PathNameU.Buffer + PathNameU.Length / sizeof(WCHAR);
515     last = c - 1;
516 
517     Dots = TRUE;
518     while (c-- > PathNameU.Buffer)
519     {
520         if (*c == L'\\' || c == PathNameU.Buffer)
521         {
522             if (Dots && last > c)
523             {
524                 return STATUS_OBJECT_NAME_INVALID;
525             }
526             if (*c == L'\\' && (c - 1) > PathNameU.Buffer &&
527                 *(c - 1) == L'\\')
528             {
529                 return STATUS_OBJECT_NAME_INVALID;
530             }
531 
532             last = c - 1;
533             Dots = TRUE;
534         }
535         else if (*c != L'.')
536         {
537             Dots = FALSE;
538         }
539 
540         if (*c != '\\' && vfatIsLongIllegal(*c))
541         {
542             return STATUS_OBJECT_NAME_INVALID;
543         }
544     }
545 
546     /* Check if we try to open target directory of root dir */
547     if (OpenTargetDir && FileObject->RelatedFileObject == NULL && PathNameU.Length == sizeof(WCHAR) &&
548         PathNameU.Buffer[0] == L'\\')
549     {
550         return STATUS_INVALID_PARAMETER;
551     }
552 
553     if (FileObject->RelatedFileObject && PathNameU.Length >= sizeof(WCHAR) && PathNameU.Buffer[0] == L'\\')
554     {
555         return STATUS_OBJECT_NAME_INVALID;
556     }
557 
558     TrailingBackslash = FALSE;
559     if (PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[PathNameU.Length/sizeof(WCHAR)-1] == L'\\')
560     {
561         PathNameU.Length -= sizeof(WCHAR);
562         TrailingBackslash = TRUE;
563     }
564 
565     if (PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[PathNameU.Length/sizeof(WCHAR)-1] == L'\\')
566     {
567         return STATUS_OBJECT_NAME_INVALID;
568     }
569 
570     /* Try opening the file. */
571     if (!OpenTargetDir)
572     {
573         vfatAddToStat(DeviceExt, Fat.CreateHits, 1);
574 
575         Status = VfatOpenFile(DeviceExt, &PathNameU, FileObject, RequestedDisposition, RequestedOptions, &ParentFcb);
576     }
577     else
578     {
579         PVFATFCB TargetFcb;
580         LONG idx, FileNameLen;
581 
582         vfatAddToStat(DeviceExt, Fat.CreateHits, 1);
583 
584         ParentFcb = (FileObject->RelatedFileObject != NULL) ? FileObject->RelatedFileObject->FsContext : NULL;
585         if (ParentFcb)
586         {
587             vfatGrabFCB(DeviceExt, ParentFcb);
588         }
589 
590         Status = vfatGetFCBForFile(DeviceExt, &ParentFcb, &TargetFcb, &PathNameU);
591         if (NT_SUCCESS(Status))
592         {
593             vfatReleaseFCB(DeviceExt, TargetFcb);
594             Irp->IoStatus.Information = FILE_EXISTS;
595         }
596         else
597         {
598             Irp->IoStatus.Information = FILE_DOES_NOT_EXIST;
599         }
600 
601         idx = FileObject->FileName.Length / sizeof(WCHAR) - 1;
602 
603         /* Skip trailing \ - if any */
604         if (PathNameU.Buffer[idx] == L'\\')
605         {
606             --idx;
607             PathNameU.Length -= sizeof(WCHAR);
608         }
609 
610         /* Get file name */
611         while (idx >= 0 && PathNameU.Buffer[idx] != L'\\')
612         {
613             --idx;
614         }
615 
616         if (idx > 0 || PathNameU.Buffer[0] == L'\\')
617         {
618             /* We don't want to include / in the name */
619             FileNameLen = PathNameU.Length - ((idx + 1) * sizeof(WCHAR));
620 
621             /* Update FO just to keep file name */
622             /* Skip first slash */
623             ++idx;
624             FileObject->FileName.Length = FileNameLen;
625             RtlMoveMemory(&PathNameU.Buffer[0], &PathNameU.Buffer[idx], FileObject->FileName.Length);
626 #if 0
627             /* Terminate the string at the last backslash */
628             PathNameU.Buffer[idx + 1] = UNICODE_NULL;
629             PathNameU.Length = (idx + 1) * sizeof(WCHAR);
630             PathNameU.MaximumLength = PathNameU.Length + sizeof(WCHAR);
631 
632             /* Update the file object as well */
633             FileObject->FileName.Length = PathNameU.Length;
634             FileObject->FileName.MaximumLength = PathNameU.MaximumLength;
635 #endif
636         }
637         else
638         {
639             /* This is a relative open and we have only the filename, so open the parent directory
640              * It is in RelatedFileObject
641              */
642             ASSERT(FileObject->RelatedFileObject != NULL);
643 
644             /* No need to modify the FO, it already has the name */
645         }
646 
647         /* We're done with opening! */
648         if (ParentFcb != NULL)
649         {
650             Status = vfatAttachFCBToFileObject(DeviceExt, ParentFcb, FileObject);
651         }
652 
653         if (NT_SUCCESS(Status))
654         {
655             pFcb = FileObject->FsContext;
656             ASSERT(pFcb == ParentFcb);
657 
658             if (pFcb->OpenHandleCount == 0)
659             {
660                 IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
661                                  Stack->Parameters.Create.ShareAccess,
662                                  FileObject,
663                                  &pFcb->FCBShareAccess);
664             }
665             else
666             {
667                 Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
668                                             Stack->Parameters.Create.ShareAccess,
669                                             FileObject,
670                                             &pFcb->FCBShareAccess,
671                                             FALSE);
672                 if (!NT_SUCCESS(Status))
673                 {
674                     VfatCloseFile(DeviceExt, FileObject);
675                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
676                     return Status;
677                 }
678             }
679 
680             pCcb = FileObject->FsContext2;
681             if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
682             {
683                 pCcb->Flags |= CCB_DELETE_ON_CLOSE;
684             }
685 
686             pFcb->OpenHandleCount++;
687             DeviceExt->OpenHandleCount++;
688         }
689         else if (ParentFcb != NULL)
690         {
691             vfatReleaseFCB(DeviceExt, ParentFcb);
692         }
693 
694         if (NT_SUCCESS(Status))
695         {
696             vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
697         }
698         else
699         {
700             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
701         }
702 
703         return Status;
704     }
705 
706     /*
707      * If the directory containing the file to open doesn't exist then
708      * fail immediately
709      */
710     if (Status == STATUS_OBJECT_PATH_NOT_FOUND ||
711         Status == STATUS_INVALID_PARAMETER ||
712         Status == STATUS_DELETE_PENDING ||
713         Status == STATUS_ACCESS_DENIED ||
714         Status == STATUS_OBJECT_NAME_COLLISION)
715     {
716         if (ParentFcb)
717         {
718             vfatReleaseFCB(DeviceExt, ParentFcb);
719         }
720         vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
721         return Status;
722     }
723 
724     if (!NT_SUCCESS(Status) && ParentFcb == NULL)
725     {
726         DPRINT1("VfatOpenFile failed for '%wZ', status %x\n", &PathNameU, Status);
727         vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
728         return Status;
729     }
730 
731     /* If the file open failed then create the required file */
732     if (!NT_SUCCESS (Status))
733     {
734         if (RequestedDisposition == FILE_CREATE ||
735             RequestedDisposition == FILE_OPEN_IF ||
736             RequestedDisposition == FILE_OVERWRITE_IF ||
737             RequestedDisposition == FILE_SUPERSEDE)
738         {
739             Attributes = Stack->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL;
740             if (!BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE))
741             {
742                 if (TrailingBackslash)
743                 {
744                     vfatReleaseFCB(DeviceExt, ParentFcb);
745                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
746                     return STATUS_OBJECT_NAME_INVALID;
747                 }
748                 Attributes |= FILE_ATTRIBUTE_ARCHIVE;
749             }
750             vfatSplitPathName(&PathNameU, NULL, &FileNameU);
751             Status = VfatAddEntry(DeviceExt, &FileNameU, &pFcb, ParentFcb, RequestedOptions,
752                                   (UCHAR)FlagOn(Attributes, FILE_ATTRIBUTE_VALID_FLAGS), NULL);
753             vfatReleaseFCB(DeviceExt, ParentFcb);
754             if (NT_SUCCESS(Status))
755             {
756                 Status = vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject);
757                 if (!NT_SUCCESS(Status))
758                 {
759                     vfatReleaseFCB(DeviceExt, pFcb);
760                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
761                     return Status;
762                 }
763 
764                 Irp->IoStatus.Information = FILE_CREATED;
765                 VfatSetAllocationSizeInformation(FileObject,
766                                                  pFcb,
767                                                  DeviceExt,
768                                                  &Irp->Overlay.AllocationSize);
769                 VfatSetExtendedAttributes(FileObject,
770                                           Irp->AssociatedIrp.SystemBuffer,
771                                           Stack->Parameters.Create.EaLength);
772 
773                 if (PagingFileCreate)
774                 {
775                     pFcb->Flags |= FCB_IS_PAGE_FILE;
776                 }
777             }
778             else
779             {
780                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
781                 return Status;
782             }
783         }
784         else
785         {
786             vfatReleaseFCB(DeviceExt, ParentFcb);
787             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
788             return Status;
789         }
790     }
791     else
792     {
793         if (ParentFcb)
794         {
795             vfatReleaseFCB(DeviceExt, ParentFcb);
796         }
797 
798         pFcb = FileObject->FsContext;
799 
800         /* Otherwise fail if the caller wanted to create a new file  */
801         if (RequestedDisposition == FILE_CREATE)
802         {
803             VfatCloseFile(DeviceExt, FileObject);
804             if (TrailingBackslash && !vfatFCBIsDirectory(pFcb))
805             {
806                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
807                 return STATUS_OBJECT_NAME_INVALID;
808             }
809             Irp->IoStatus.Information = FILE_EXISTS;
810             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
811             return STATUS_OBJECT_NAME_COLLISION;
812         }
813 
814         if (pFcb->OpenHandleCount != 0)
815         {
816             Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
817                                         Stack->Parameters.Create.ShareAccess,
818                                         FileObject,
819                                         &pFcb->FCBShareAccess,
820                                         FALSE);
821             if (!NT_SUCCESS(Status))
822             {
823                 VfatCloseFile(DeviceExt, FileObject);
824                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
825                 return Status;
826             }
827         }
828 
829         /*
830          * Check the file has the requested attributes
831          */
832         if (BooleanFlagOn(RequestedOptions, FILE_NON_DIRECTORY_FILE) &&
833             vfatFCBIsDirectory(pFcb))
834         {
835             VfatCloseFile (DeviceExt, FileObject);
836             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
837             return STATUS_FILE_IS_A_DIRECTORY;
838         }
839         if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
840             !vfatFCBIsDirectory(pFcb))
841         {
842             VfatCloseFile (DeviceExt, FileObject);
843             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
844             return STATUS_NOT_A_DIRECTORY;
845         }
846         if (TrailingBackslash && !vfatFCBIsDirectory(pFcb))
847         {
848             VfatCloseFile (DeviceExt, FileObject);
849             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
850             return STATUS_OBJECT_NAME_INVALID;
851         }
852 #ifndef USE_ROS_CC_AND_FS
853         if (!vfatFCBIsDirectory(pFcb))
854         {
855             if (BooleanFlagOn(Stack->Parameters.Create.SecurityContext->DesiredAccess, FILE_WRITE_DATA) ||
856                 RequestedDisposition == FILE_OVERWRITE ||
857                 RequestedDisposition == FILE_OVERWRITE_IF ||
858                 (RequestedOptions & FILE_DELETE_ON_CLOSE))
859             {
860                 if (!MmFlushImageSection(&pFcb->SectionObjectPointers, MmFlushForWrite))
861                 {
862                     DPRINT1("%wZ\n", &pFcb->PathNameU);
863                     DPRINT1("%d %d %d\n", Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA,
864                             RequestedDisposition == FILE_OVERWRITE, RequestedDisposition == FILE_OVERWRITE_IF);
865                     VfatCloseFile (DeviceExt, FileObject);
866                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
867                     return (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE)) ? STATUS_CANNOT_DELETE
868                                                                                    : STATUS_SHARING_VIOLATION;
869                 }
870             }
871         }
872 #endif
873         if (PagingFileCreate)
874         {
875             /* FIXME:
876              *   Do more checking for page files. It is possible,
877              *   that the file was opened and closed previously
878              *   as a normal cached file. In this case, the cache
879              *   manager has referenced the fileobject and the fcb
880              *   is held in memory. Try to remove the fileobject
881              *   from cache manager and use the fcb.
882              */
883             if (pFcb->RefCount > 1)
884             {
885                 if(!BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE))
886                 {
887                     VfatCloseFile(DeviceExt, FileObject);
888                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
889                     return STATUS_INVALID_PARAMETER;
890                 }
891             }
892             else
893             {
894                 pFcb->Flags |= FCB_IS_PAGE_FILE;
895             }
896         }
897         else
898         {
899             if (BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE))
900             {
901                 VfatCloseFile(DeviceExt, FileObject);
902                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
903                 return STATUS_INVALID_PARAMETER;
904             }
905         }
906 
907         if (RequestedDisposition == FILE_OVERWRITE ||
908             RequestedDisposition == FILE_OVERWRITE_IF ||
909             RequestedDisposition == FILE_SUPERSEDE)
910         {
911             if (!vfatFCBIsDirectory(pFcb))
912             {
913                 *pFcb->Attributes = Stack->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL;
914                 *pFcb->Attributes |= FILE_ATTRIBUTE_ARCHIVE;
915                 VfatUpdateEntry(pFcb, vfatVolumeIsFatX(DeviceExt));
916             }
917 
918             ExAcquireResourceExclusiveLite(&(pFcb->MainResource), TRUE);
919             Status = VfatSetAllocationSizeInformation(FileObject,
920                                                       pFcb,
921                                                       DeviceExt,
922                                                       &Irp->Overlay.AllocationSize);
923             ExReleaseResourceLite(&(pFcb->MainResource));
924             if (!NT_SUCCESS (Status))
925             {
926                 VfatCloseFile(DeviceExt, FileObject);
927                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
928                 return Status;
929             }
930         }
931 
932         if (RequestedDisposition == FILE_SUPERSEDE)
933         {
934             Irp->IoStatus.Information = FILE_SUPERSEDED;
935         }
936         else if (RequestedDisposition == FILE_OVERWRITE ||
937                  RequestedDisposition == FILE_OVERWRITE_IF)
938         {
939             Irp->IoStatus.Information = FILE_OVERWRITTEN;
940         }
941         else
942         {
943             Irp->IoStatus.Information = FILE_OPENED;
944         }
945     }
946 
947     if (pFcb->OpenHandleCount == 0)
948     {
949         IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
950                          Stack->Parameters.Create.ShareAccess,
951                          FileObject,
952                          &pFcb->FCBShareAccess);
953     }
954     else
955     {
956         IoUpdateShareAccess(FileObject,
957                             &pFcb->FCBShareAccess);
958     }
959 
960     pCcb = FileObject->FsContext2;
961     if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
962     {
963         pCcb->Flags |= CCB_DELETE_ON_CLOSE;
964     }
965 
966     if (Irp->IoStatus.Information == FILE_CREATED)
967     {
968         FsRtlNotifyFullReportChange(DeviceExt->NotifySync,
969                                     &(DeviceExt->NotifyList),
970                                     (PSTRING)&pFcb->PathNameU,
971                                     pFcb->PathNameU.Length - pFcb->LongNameU.Length,
972                                     NULL,
973                                     NULL,
974                                     (vfatFCBIsDirectory(pFcb) ?
975                                     FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
976                                     FILE_ACTION_ADDED,
977                                     NULL);
978     }
979 
980     pFcb->OpenHandleCount++;
981     DeviceExt->OpenHandleCount++;
982 
983     /* FIXME : test write access if requested */
984 
985     /* FIXME: That is broken, we cannot reach this code path with failure */
986     ASSERT(NT_SUCCESS(Status));
987     if (NT_SUCCESS(Status))
988     {
989         vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
990     }
991     else
992     {
993         vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
994     }
995 
996     return Status;
997 }
998 
999 /*
1000  * FUNCTION: Create or open a file
1001  */
1002 NTSTATUS
1003 VfatCreate(
1004     PVFAT_IRP_CONTEXT IrpContext)
1005 {
1006     NTSTATUS Status;
1007 
1008     ASSERT(IrpContext);
1009 
1010     if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
1011     {
1012         /* DeviceObject represents FileSystem instead of logical volume */
1013         DPRINT ("FsdCreate called with file system\n");
1014         IrpContext->Irp->IoStatus.Information = FILE_OPENED;
1015         IrpContext->PriorityBoost = IO_DISK_INCREMENT;
1016 
1017         return STATUS_SUCCESS;
1018     }
1019 
1020     if (!BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT))
1021     {
1022         return VfatMarkIrpContextForQueue(IrpContext);
1023     }
1024 
1025     IrpContext->Irp->IoStatus.Information = 0;
1026     ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, TRUE);
1027     Status = VfatCreateFile(IrpContext->DeviceObject, IrpContext->Irp);
1028     ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
1029 
1030     if (NT_SUCCESS(Status))
1031         IrpContext->PriorityBoost = IO_DISK_INCREMENT;
1032 
1033     return Status;
1034 }
1035 
1036 /* EOF */
1037