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