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     Attributes = (Stack->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_ARCHIVE |
732                                                              FILE_ATTRIBUTE_SYSTEM |
733                                                              FILE_ATTRIBUTE_HIDDEN |
734                                                              FILE_ATTRIBUTE_DIRECTORY |
735                                                              FILE_ATTRIBUTE_READONLY));
736 
737     /* If the file open failed then create the required file */
738     if (!NT_SUCCESS (Status))
739     {
740         if (RequestedDisposition == FILE_CREATE ||
741             RequestedDisposition == FILE_OPEN_IF ||
742             RequestedDisposition == FILE_OVERWRITE_IF ||
743             RequestedDisposition == FILE_SUPERSEDE)
744         {
745             if (!BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE))
746             {
747                 if (TrailingBackslash)
748                 {
749                     vfatReleaseFCB(DeviceExt, ParentFcb);
750                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
751                     return STATUS_OBJECT_NAME_INVALID;
752                 }
753                 Attributes |= FILE_ATTRIBUTE_ARCHIVE;
754             }
755             vfatSplitPathName(&PathNameU, NULL, &FileNameU);
756             Status = VfatAddEntry(DeviceExt, &FileNameU, &pFcb, ParentFcb, RequestedOptions,
757                                   Attributes, NULL);
758             vfatReleaseFCB(DeviceExt, ParentFcb);
759             if (NT_SUCCESS(Status))
760             {
761                 Status = vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject);
762                 if (!NT_SUCCESS(Status))
763                 {
764                     vfatReleaseFCB(DeviceExt, pFcb);
765                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
766                     return Status;
767                 }
768 
769                 Irp->IoStatus.Information = FILE_CREATED;
770                 VfatSetAllocationSizeInformation(FileObject,
771                                                  pFcb,
772                                                  DeviceExt,
773                                                  &Irp->Overlay.AllocationSize);
774                 VfatSetExtendedAttributes(FileObject,
775                                           Irp->AssociatedIrp.SystemBuffer,
776                                           Stack->Parameters.Create.EaLength);
777 
778                 if (PagingFileCreate)
779                 {
780                     pFcb->Flags |= FCB_IS_PAGE_FILE;
781                     SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE);
782                 }
783             }
784             else
785             {
786                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
787                 return Status;
788             }
789         }
790         else
791         {
792             vfatReleaseFCB(DeviceExt, ParentFcb);
793             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
794             return Status;
795         }
796     }
797     else
798     {
799         if (ParentFcb)
800         {
801             vfatReleaseFCB(DeviceExt, ParentFcb);
802         }
803 
804         pFcb = FileObject->FsContext;
805 
806         /* Otherwise fail if the caller wanted to create a new file  */
807         if (RequestedDisposition == FILE_CREATE)
808         {
809             VfatCloseFile(DeviceExt, FileObject);
810             if (TrailingBackslash && !vfatFCBIsDirectory(pFcb))
811             {
812                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
813                 return STATUS_OBJECT_NAME_INVALID;
814             }
815             Irp->IoStatus.Information = FILE_EXISTS;
816             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
817             return STATUS_OBJECT_NAME_COLLISION;
818         }
819 
820         if (pFcb->OpenHandleCount != 0)
821         {
822             Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
823                                         Stack->Parameters.Create.ShareAccess,
824                                         FileObject,
825                                         &pFcb->FCBShareAccess,
826                                         FALSE);
827             if (!NT_SUCCESS(Status))
828             {
829                 VfatCloseFile(DeviceExt, FileObject);
830                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
831                 return Status;
832             }
833         }
834 
835         /*
836          * Check the file has the requested attributes
837          */
838         if (BooleanFlagOn(RequestedOptions, FILE_NON_DIRECTORY_FILE) &&
839             vfatFCBIsDirectory(pFcb))
840         {
841             VfatCloseFile (DeviceExt, FileObject);
842             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
843             return STATUS_FILE_IS_A_DIRECTORY;
844         }
845         if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) &&
846             !vfatFCBIsDirectory(pFcb))
847         {
848             VfatCloseFile (DeviceExt, FileObject);
849             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
850             return STATUS_NOT_A_DIRECTORY;
851         }
852         if (TrailingBackslash && !vfatFCBIsDirectory(pFcb))
853         {
854             VfatCloseFile (DeviceExt, FileObject);
855             vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
856             return STATUS_OBJECT_NAME_INVALID;
857         }
858 #ifndef USE_ROS_CC_AND_FS
859         if (!vfatFCBIsDirectory(pFcb))
860         {
861             if (BooleanFlagOn(Stack->Parameters.Create.SecurityContext->DesiredAccess, FILE_WRITE_DATA) ||
862                 RequestedDisposition == FILE_OVERWRITE ||
863                 RequestedDisposition == FILE_OVERWRITE_IF ||
864                 (RequestedOptions & FILE_DELETE_ON_CLOSE))
865             {
866                 if (!MmFlushImageSection(&pFcb->SectionObjectPointers, MmFlushForWrite))
867                 {
868                     DPRINT1("%wZ\n", &pFcb->PathNameU);
869                     DPRINT1("%d %d %d\n", Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA,
870                             RequestedDisposition == FILE_OVERWRITE, RequestedDisposition == FILE_OVERWRITE_IF);
871                     VfatCloseFile (DeviceExt, FileObject);
872                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
873                     return (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE)) ? STATUS_CANNOT_DELETE
874                                                                                    : STATUS_SHARING_VIOLATION;
875                 }
876             }
877         }
878 #endif
879         if (PagingFileCreate)
880         {
881             /* FIXME:
882              *   Do more checking for page files. It is possible,
883              *   that the file was opened and closed previously
884              *   as a normal cached file. In this case, the cache
885              *   manager has referenced the fileobject and the fcb
886              *   is held in memory. Try to remove the fileobject
887              *   from cache manager and use the fcb.
888              */
889             if (pFcb->RefCount > 1)
890             {
891                 if(!BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE))
892                 {
893                     VfatCloseFile(DeviceExt, FileObject);
894                     vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
895                     return STATUS_INVALID_PARAMETER;
896                 }
897             }
898             else
899             {
900                 pFcb->Flags |= FCB_IS_PAGE_FILE;
901                 SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE);
902             }
903         }
904         else
905         {
906             if (BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE))
907             {
908                 VfatCloseFile(DeviceExt, FileObject);
909                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
910                 return STATUS_INVALID_PARAMETER;
911             }
912         }
913 
914         if (RequestedDisposition == FILE_OVERWRITE ||
915             RequestedDisposition == FILE_OVERWRITE_IF ||
916             RequestedDisposition == FILE_SUPERSEDE)
917         {
918             if ((BooleanFlagOn(*pFcb->Attributes, FILE_ATTRIBUTE_HIDDEN) && !BooleanFlagOn(Attributes, FILE_ATTRIBUTE_HIDDEN)) ||
919                 (BooleanFlagOn(*pFcb->Attributes, FILE_ATTRIBUTE_SYSTEM) && !BooleanFlagOn(Attributes, FILE_ATTRIBUTE_SYSTEM)))
920             {
921                 VfatCloseFile(DeviceExt, FileObject);
922                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
923                 return STATUS_ACCESS_DENIED;
924             }
925 
926             if (!vfatFCBIsDirectory(pFcb))
927             {
928                 LARGE_INTEGER SystemTime;
929 
930                 if (RequestedDisposition == FILE_SUPERSEDE)
931                 {
932                     *pFcb->Attributes = Attributes;
933                 }
934                 else
935                 {
936                     *pFcb->Attributes |= Attributes;
937                 }
938                 *pFcb->Attributes |= FILE_ATTRIBUTE_ARCHIVE;
939 
940                 KeQuerySystemTime(&SystemTime);
941                 if (vfatVolumeIsFatX(DeviceExt))
942                 {
943                     FsdSystemTimeToDosDateTime(DeviceExt,
944                                                &SystemTime, &pFcb->entry.FatX.UpdateDate,
945                                                &pFcb->entry.FatX.UpdateTime);
946                 }
947                 else
948                 {
949                     FsdSystemTimeToDosDateTime(DeviceExt,
950                                                &SystemTime, &pFcb->entry.Fat.UpdateDate,
951                                                &pFcb->entry.Fat.UpdateTime);
952                 }
953 
954                 VfatUpdateEntry(DeviceExt, pFcb);
955             }
956 
957             ExAcquireResourceExclusiveLite(&(pFcb->MainResource), TRUE);
958             Status = VfatSetAllocationSizeInformation(FileObject,
959                                                       pFcb,
960                                                       DeviceExt,
961                                                       &Irp->Overlay.AllocationSize);
962             ExReleaseResourceLite(&(pFcb->MainResource));
963             if (!NT_SUCCESS (Status))
964             {
965                 VfatCloseFile(DeviceExt, FileObject);
966                 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
967                 return Status;
968             }
969         }
970 
971         if (RequestedDisposition == FILE_SUPERSEDE)
972         {
973             Irp->IoStatus.Information = FILE_SUPERSEDED;
974         }
975         else if (RequestedDisposition == FILE_OVERWRITE ||
976                  RequestedDisposition == FILE_OVERWRITE_IF)
977         {
978             Irp->IoStatus.Information = FILE_OVERWRITTEN;
979         }
980         else
981         {
982             Irp->IoStatus.Information = FILE_OPENED;
983         }
984     }
985 
986     if (pFcb->OpenHandleCount == 0)
987     {
988         IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
989                          Stack->Parameters.Create.ShareAccess,
990                          FileObject,
991                          &pFcb->FCBShareAccess);
992     }
993     else
994     {
995         IoUpdateShareAccess(FileObject,
996                             &pFcb->FCBShareAccess);
997     }
998 
999     pCcb = FileObject->FsContext2;
1000     if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE))
1001     {
1002         pCcb->Flags |= CCB_DELETE_ON_CLOSE;
1003     }
1004 
1005     if (Irp->IoStatus.Information == FILE_CREATED)
1006     {
1007         vfatReportChange(DeviceExt,
1008                          pFcb,
1009                          (vfatFCBIsDirectory(pFcb) ?
1010                           FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
1011                          FILE_ACTION_ADDED);
1012     }
1013     else if (Irp->IoStatus.Information == FILE_OVERWRITTEN ||
1014              Irp->IoStatus.Information == FILE_SUPERSEDED)
1015     {
1016         vfatReportChange(DeviceExt,
1017                          pFcb,
1018                          FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE,
1019                          FILE_ACTION_MODIFIED);
1020     }
1021 
1022     pFcb->OpenHandleCount++;
1023     DeviceExt->OpenHandleCount++;
1024 
1025     /* FIXME : test write access if requested */
1026 
1027     /* FIXME: That is broken, we cannot reach this code path with failure */
1028     ASSERT(NT_SUCCESS(Status));
1029     if (NT_SUCCESS(Status))
1030     {
1031         vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1);
1032     }
1033     else
1034     {
1035         vfatAddToStat(DeviceExt, Fat.FailedCreates, 1);
1036     }
1037 
1038     return Status;
1039 }
1040 
1041 /*
1042  * FUNCTION: Create or open a file
1043  */
1044 NTSTATUS
1045 VfatCreate(
1046     PVFAT_IRP_CONTEXT IrpContext)
1047 {
1048     NTSTATUS Status;
1049 
1050     ASSERT(IrpContext);
1051 
1052     if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
1053     {
1054         /* DeviceObject represents FileSystem instead of logical volume */
1055         DPRINT ("FsdCreate called with file system\n");
1056         IrpContext->Irp->IoStatus.Information = FILE_OPENED;
1057         IrpContext->PriorityBoost = IO_DISK_INCREMENT;
1058 
1059         return STATUS_SUCCESS;
1060     }
1061 
1062     IrpContext->Irp->IoStatus.Information = 0;
1063     ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, TRUE);
1064     Status = VfatCreateFile(IrpContext->DeviceObject, IrpContext->Irp);
1065     ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
1066 
1067     if (NT_SUCCESS(Status))
1068         IrpContext->PriorityBoost = IO_DISK_INCREMENT;
1069 
1070     return Status;
1071 }
1072 
1073 /* EOF */
1074