Sync to trunk revision 63922.
[reactos.git] / drivers / filesystems / fastfat / create.c
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/fs/vfat/create.c
22 * PURPOSE: VFAT Filesystem
23 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
24 */
25
26 /* INCLUDES *****************************************************************/
27
28 #include "vfat.h"
29
30 #define NDEBUG
31 #include <debug.h>
32
33 /* FUNCTIONS *****************************************************************/
34
35 VOID
36 vfat8Dot3ToString(
37 PFAT_DIR_ENTRY pEntry,
38 PUNICODE_STRING NameU)
39 {
40 OEM_STRING StringA;
41 USHORT Length;
42 CHAR cString[12];
43
44 RtlCopyMemory(cString, pEntry->ShortName, 11);
45 cString[11] = 0;
46 if (cString[0] == 0x05)
47 {
48 cString[0] = 0xe5;
49 }
50
51 StringA.Buffer = cString;
52 for (StringA.Length = 0;
53 StringA.Length < 8 && StringA.Buffer[StringA.Length] != ' ';
54 StringA.Length++);
55 StringA.MaximumLength = StringA.Length;
56
57 RtlOemStringToUnicodeString(NameU, &StringA, FALSE);
58
59 if (pEntry->lCase & VFAT_CASE_LOWER_BASE)
60 {
61 RtlDowncaseUnicodeString(NameU, NameU, FALSE);
62 }
63
64 if (cString[8] != ' ')
65 {
66 Length = NameU->Length;
67 NameU->Buffer += Length / sizeof(WCHAR);
68 if (!FAT_ENTRY_VOLUME(pEntry))
69 {
70 Length += sizeof(WCHAR);
71 NameU->Buffer[0] = L'.';
72 NameU->Buffer++;
73 }
74 NameU->Length = 0;
75 NameU->MaximumLength -= Length;
76
77 StringA.Buffer = &cString[8];
78 for (StringA.Length = 0;
79 StringA.Length < 3 && StringA.Buffer[StringA.Length] != ' ';
80 StringA.Length++);
81 StringA.MaximumLength = StringA.Length;
82 RtlOemStringToUnicodeString(NameU, &StringA, FALSE);
83 if (pEntry->lCase & VFAT_CASE_LOWER_EXT)
84 {
85 RtlDowncaseUnicodeString(NameU, NameU, FALSE);
86 }
87 NameU->Buffer -= Length / sizeof(WCHAR);
88 NameU->Length += Length;
89 NameU->MaximumLength += Length;
90 }
91
92 NameU->Buffer[NameU->Length / sizeof(WCHAR)] = 0;
93 DPRINT("'%wZ'\n", NameU);
94 }
95
96 /*
97 * FUNCTION: Read the volume label
98 */
99 NTSTATUS
100 ReadVolumeLabel(
101 PDEVICE_EXTENSION DeviceExt,
102 PVPB Vpb)
103 {
104 PVOID Context = NULL;
105 ULONG DirIndex = 0;
106 PDIR_ENTRY Entry;
107 PVFATFCB pFcb;
108 LARGE_INTEGER FileOffset;
109 UNICODE_STRING NameU;
110 ULONG SizeDirEntry;
111 ULONG EntriesPerPage;
112 OEM_STRING StringO;
113
114 NameU.Buffer = Vpb->VolumeLabel;
115 NameU.Length = 0;
116 NameU.MaximumLength = sizeof(Vpb->VolumeLabel);
117 *(Vpb->VolumeLabel) = 0;
118 Vpb->VolumeLabelLength = 0;
119
120 if (DeviceExt->Flags & VCB_IS_FATX)
121 {
122 SizeDirEntry = sizeof(FATX_DIR_ENTRY);
123 EntriesPerPage = FATX_ENTRIES_PER_PAGE;
124 }
125 else
126 {
127 SizeDirEntry = sizeof(FAT_DIR_ENTRY);
128 EntriesPerPage = FAT_ENTRIES_PER_PAGE;
129 }
130
131 ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE);
132 pFcb = vfatOpenRootFCB(DeviceExt);
133 ExReleaseResourceLite(&DeviceExt->DirResource);
134
135 FileOffset.QuadPart = 0;
136 if (CcMapData(pFcb->FileObject, &FileOffset, SizeDirEntry, TRUE, &Context, (PVOID*)&Entry))
137 {
138 while (TRUE)
139 {
140 if (ENTRY_VOLUME(DeviceExt, Entry))
141 {
142 /* copy volume label */
143 if (DeviceExt->Flags & VCB_IS_FATX)
144 {
145 StringO.Buffer = (PCHAR)Entry->FatX.Filename;
146 StringO.MaximumLength = StringO.Length = Entry->FatX.FilenameLength;
147 RtlOemStringToUnicodeString(&NameU, &StringO, FALSE);
148 }
149 else
150 {
151 vfat8Dot3ToString(&Entry->Fat, &NameU);
152 }
153 Vpb->VolumeLabelLength = NameU.Length;
154 break;
155 }
156 if (ENTRY_END(DeviceExt, Entry))
157 {
158 break;
159 }
160 DirIndex++;
161 Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry);
162 if ((DirIndex % EntriesPerPage) == 0)
163 {
164 CcUnpinData(Context);
165 FileOffset.u.LowPart += PAGE_SIZE;
166 if (!CcMapData(pFcb->FileObject, &FileOffset, SizeDirEntry, TRUE, &Context, (PVOID*)&Entry))
167 {
168 Context = NULL;
169 break;
170 }
171 }
172 }
173 if (Context)
174 {
175 CcUnpinData(Context);
176 }
177 }
178 ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE);
179 vfatReleaseFCB(DeviceExt, pFcb);
180 ExReleaseResourceLite(&DeviceExt->DirResource);
181
182 return STATUS_SUCCESS;
183 }
184
185 /*
186 * FUNCTION: Find a file
187 */
188 NTSTATUS
189 FindFile(
190 PDEVICE_EXTENSION DeviceExt,
191 PVFATFCB Parent,
192 PUNICODE_STRING FileToFindU,
193 PVFAT_DIRENTRY_CONTEXT DirContext,
194 BOOLEAN First)
195 {
196 PWCHAR PathNameBuffer;
197 USHORT PathNameBufferLength;
198 NTSTATUS Status;
199 PVOID Context = NULL;
200 PVOID Page;
201 PVFATFCB rcFcb;
202 BOOLEAN Found;
203 UNICODE_STRING PathNameU;
204 UNICODE_STRING FileToFindUpcase;
205 BOOLEAN WildCard;
206
207 DPRINT("FindFile(Parent %p, FileToFind '%wZ', DirIndex: %u)\n",
208 Parent, FileToFindU, DirContext->DirIndex);
209 DPRINT("FindFile: Path %wZ\n",&Parent->PathNameU);
210
211 PathNameBufferLength = LONGNAME_MAX_LENGTH * sizeof(WCHAR);
212 PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameBufferLength + sizeof(WCHAR), TAG_VFAT);
213 if (!PathNameBuffer)
214 {
215 return STATUS_INSUFFICIENT_RESOURCES;
216 }
217
218 PathNameU.Buffer = PathNameBuffer;
219 PathNameU.Length = 0;
220 PathNameU.MaximumLength = PathNameBufferLength;
221
222 DirContext->LongNameU.Length = 0;
223 DirContext->ShortNameU.Length = 0;
224
225 WildCard = FsRtlDoesNameContainWildCards(FileToFindU);
226
227 if (WildCard == FALSE)
228 {
229 /* if there is no '*?' in the search name, than look first for an existing fcb */
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, FileToFindU);
237 PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = 0;
238 rcFcb = vfatGrabFCBFromTable(DeviceExt, &PathNameU);
239 if (rcFcb)
240 {
241 ULONG startIndex = rcFcb->startIndex;
242 if ((rcFcb->Flags & FCB_IS_FATX_ENTRY) && !vfatFCBIsRoot(Parent))
243 {
244 startIndex += 2;
245 }
246 if(startIndex >= DirContext->DirIndex)
247 {
248 RtlCopyUnicodeString(&DirContext->LongNameU, &rcFcb->LongNameU);
249 RtlCopyUnicodeString(&DirContext->ShortNameU, &rcFcb->ShortNameU);
250 RtlCopyMemory(&DirContext->DirEntry, &rcFcb->entry, sizeof(DIR_ENTRY));
251 DirContext->StartIndex = rcFcb->startIndex;
252 DirContext->DirIndex = rcFcb->dirIndex;
253 DPRINT("FindFile: new Name %wZ, DirIndex %u (%u)\n",
254 &DirContext->LongNameU, DirContext->DirIndex, DirContext->StartIndex);
255 Status = STATUS_SUCCESS;
256 }
257 else
258 {
259 DPRINT("FCB not found for %wZ\n", &PathNameU);
260 Status = STATUS_UNSUCCESSFUL;
261 }
262 vfatReleaseFCB(DeviceExt, rcFcb);
263 ExFreePool(PathNameBuffer);
264 return Status;
265 }
266 }
267
268 /* FsRtlIsNameInExpression need the searched string to be upcase,
269 * even if IgnoreCase is specified */
270 Status = RtlUpcaseUnicodeString(&FileToFindUpcase, FileToFindU, TRUE);
271 if (!NT_SUCCESS(Status))
272 {
273 ExFreePool(PathNameBuffer);
274 return Status;
275 }
276
277 while (TRUE)
278 {
279 Status = DeviceExt->GetNextDirEntry(&Context, &Page, Parent, DirContext, First);
280 First = FALSE;
281 if (Status == STATUS_NO_MORE_ENTRIES)
282 {
283 break;
284 }
285 if (ENTRY_VOLUME(DeviceExt, &DirContext->DirEntry))
286 {
287 DirContext->DirIndex++;
288 continue;
289 }
290 if (WildCard)
291 {
292 Found = FsRtlIsNameInExpression(&FileToFindUpcase, &DirContext->LongNameU, TRUE, NULL) ||
293 FsRtlIsNameInExpression(&FileToFindUpcase, &DirContext->ShortNameU, TRUE, NULL);
294 }
295 else
296 {
297 Found = FsRtlAreNamesEqual(&DirContext->LongNameU, FileToFindU, TRUE, NULL) ||
298 FsRtlAreNamesEqual(&DirContext->ShortNameU, FileToFindU, TRUE, NULL);
299 }
300
301 if (Found)
302 {
303 if (WildCard)
304 {
305 RtlCopyUnicodeString(&PathNameU, &Parent->PathNameU);
306 if (!vfatFCBIsRoot(Parent))
307 {
308 PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = L'\\';
309 PathNameU.Length += sizeof(WCHAR);
310 }
311 RtlAppendUnicodeStringToString(&PathNameU, &DirContext->LongNameU);
312 PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = 0;
313 rcFcb = vfatGrabFCBFromTable(DeviceExt, &PathNameU);
314 if (rcFcb != NULL)
315 {
316 RtlCopyMemory(&DirContext->DirEntry, &rcFcb->entry, sizeof(DIR_ENTRY));
317 vfatReleaseFCB(DeviceExt, rcFcb);
318 }
319 }
320 DPRINT("%u\n", DirContext->LongNameU.Length);
321 DPRINT("FindFile: new Name %wZ, DirIndex %u\n",
322 &DirContext->LongNameU, DirContext->DirIndex);
323
324 if (Context)
325 {
326 CcUnpinData(Context);
327 }
328 RtlFreeUnicodeString(&FileToFindUpcase);
329 ExFreePool(PathNameBuffer);
330 return STATUS_SUCCESS;
331 }
332 DirContext->DirIndex++;
333 }
334
335 if (Context)
336 {
337 CcUnpinData(Context);
338 }
339
340 RtlFreeUnicodeString(&FileToFindUpcase);
341 ExFreePool(PathNameBuffer);
342 return Status;
343 }
344
345 /*
346 * FUNCTION: Opens a file
347 */
348 static
349 NTSTATUS
350 VfatOpenFile(
351 PDEVICE_EXTENSION DeviceExt,
352 PUNICODE_STRING PathNameU,
353 PFILE_OBJECT FileObject,
354 ULONG RequestedDisposition,
355 BOOLEAN OpenTargetDir,
356 PVFATFCB *ParentFcb)
357 {
358 PVFATFCB Fcb;
359 NTSTATUS Status;
360
361 DPRINT("VfatOpenFile(%p, '%wZ', %p, %p)\n", DeviceExt, PathNameU, FileObject, ParentFcb);
362
363 if (FileObject->RelatedFileObject)
364 {
365 DPRINT("'%wZ'\n", &FileObject->RelatedFileObject->FileName);
366
367 *ParentFcb = FileObject->RelatedFileObject->FsContext;
368 (*ParentFcb)->RefCount++;
369 }
370 else
371 {
372 *ParentFcb = NULL;
373 }
374
375 if (!DeviceExt->FatInfo.FixedMedia)
376 {
377 Status = VfatBlockDeviceIoControl(DeviceExt->StorageDevice,
378 IOCTL_DISK_CHECK_VERIFY,
379 NULL,
380 0,
381 NULL,
382 0,
383 FALSE);
384 if (!NT_SUCCESS(Status))
385 {
386 DPRINT("Status %lx\n", Status);
387 *ParentFcb = NULL;
388 return Status;
389 }
390 }
391
392 if (*ParentFcb)
393 {
394 (*ParentFcb)->RefCount++;
395 }
396
397 /* try first to find an existing FCB in memory */
398 DPRINT("Checking for existing FCB in memory\n");
399
400 Status = vfatGetFCBForFile(DeviceExt, ParentFcb, &Fcb, PathNameU);
401 if (!NT_SUCCESS(Status))
402 {
403 DPRINT ("Could not make a new FCB, status: %x\n", Status);
404 return Status;
405 }
406
407 /* In case we're to open target, just check whether file exist, but don't open it */
408 if (OpenTargetDir)
409 {
410 vfatReleaseFCB(DeviceExt, Fcb);
411 return STATUS_OBJECT_NAME_COLLISION;
412 }
413
414 if (Fcb->Flags & FCB_DELETE_PENDING)
415 {
416 vfatReleaseFCB(DeviceExt, Fcb);
417 return STATUS_DELETE_PENDING;
418 }
419
420 /* Fail, if we try to overwrite a read-only file */
421 if ((*Fcb->Attributes & FILE_ATTRIBUTE_READONLY) &&
422 (RequestedDisposition == FILE_OVERWRITE))
423 {
424 vfatReleaseFCB(DeviceExt, Fcb);
425 return STATUS_ACCESS_DENIED;
426 }
427
428 DPRINT("Attaching FCB to fileObject\n");
429 Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, FileObject);
430 if (!NT_SUCCESS(Status))
431 {
432 vfatReleaseFCB(DeviceExt, Fcb);
433 }
434 return Status;
435 }
436
437 /*
438 * FUNCTION: Create or open a file
439 */
440 static NTSTATUS
441 VfatCreateFile(
442 PDEVICE_OBJECT DeviceObject,
443 PIRP Irp)
444 {
445 PIO_STACK_LOCATION Stack;
446 PFILE_OBJECT FileObject;
447 NTSTATUS Status = STATUS_SUCCESS;
448 PDEVICE_EXTENSION DeviceExt;
449 ULONG RequestedDisposition, RequestedOptions;
450 PVFATFCB pFcb = NULL;
451 PVFATFCB ParentFcb = NULL;
452 PWCHAR c, last;
453 BOOLEAN PagingFileCreate = FALSE;
454 BOOLEAN Dots;
455 BOOLEAN OpenTargetDir = FALSE;
456 UNICODE_STRING FileNameU;
457 UNICODE_STRING PathNameU;
458 ULONG Attributes;
459
460 /* Unpack the various parameters. */
461 Stack = IoGetCurrentIrpStackLocation(Irp);
462 RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
463 RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
464 PagingFileCreate = (Stack->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE;
465 #if 0
466 OpenTargetDir = (Stack->Flags & SL_OPEN_TARGET_DIRECTORY) ? TRUE : FALSE;
467 #else
468 OpenTargetDir = FALSE;
469 #endif
470 FileObject = Stack->FileObject;
471 DeviceExt = DeviceObject->DeviceExtension;
472
473 /* Check their validity. */
474 if (RequestedOptions & FILE_DIRECTORY_FILE &&
475 RequestedDisposition == FILE_SUPERSEDE)
476 {
477 return STATUS_INVALID_PARAMETER;
478 }
479
480 if (RequestedOptions & FILE_DIRECTORY_FILE &&
481 RequestedOptions & FILE_NON_DIRECTORY_FILE)
482 {
483 return STATUS_INVALID_PARAMETER;
484 }
485
486 /* This a open operation for the volume itself */
487 if (FileObject->FileName.Length == 0 &&
488 (FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 != NULL))
489 {
490 if (RequestedDisposition != FILE_OPEN &&
491 RequestedDisposition != FILE_OPEN_IF)
492 {
493 return STATUS_ACCESS_DENIED;
494 }
495 #if 0
496 /* In spite of what is shown in WDK, it seems that Windows FAT driver doesn't perform that test */
497 if (RequestedOptions & FILE_DIRECTORY_FILE)
498 {
499 return STATUS_NOT_A_DIRECTORY;
500 }
501 #endif
502
503 if (OpenTargetDir)
504 {
505 return STATUS_INVALID_PARAMETER;
506 }
507
508 pFcb = DeviceExt->VolumeFcb;
509 vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject);
510 pFcb->RefCount++;
511
512 Irp->IoStatus.Information = FILE_OPENED;
513 return STATUS_SUCCESS;
514 }
515
516 /* Check for illegal characters and illegale dot sequences in the file name */
517 PathNameU = FileObject->FileName;
518 c = PathNameU.Buffer + PathNameU.Length / sizeof(WCHAR);
519 last = c - 1;
520 Dots = TRUE;
521 while (c-- > PathNameU.Buffer)
522 {
523 if (*c == L'\\' || c == PathNameU.Buffer)
524 {
525 if (Dots && last > c)
526 {
527 return STATUS_OBJECT_NAME_INVALID;
528 }
529 last = c - 1;
530 Dots = TRUE;
531 }
532 else if (*c != L'.')
533 {
534 Dots = FALSE;
535 }
536
537 if (*c != '\\' && vfatIsLongIllegal(*c))
538 {
539 return STATUS_OBJECT_NAME_INVALID;
540 }
541 }
542
543 /* Check if we try to open target directory of root dir */
544 if (OpenTargetDir && FileObject->RelatedFileObject == NULL && PathNameU.Length == sizeof(WCHAR) &&
545 PathNameU.Buffer[0] == L'\\')
546 {
547 return STATUS_INVALID_PARAMETER;
548 }
549
550 if (FileObject->RelatedFileObject && PathNameU.Length >= sizeof(WCHAR) && PathNameU.Buffer[0] == L'\\')
551 {
552 return STATUS_OBJECT_NAME_INVALID;
553 }
554
555 if (PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[PathNameU.Length/sizeof(WCHAR)-1] == L'\\')
556 {
557 PathNameU.Length -= sizeof(WCHAR);
558 }
559
560 /* Try opening the file. */
561 Status = VfatOpenFile(DeviceExt, &PathNameU, FileObject, RequestedDisposition, OpenTargetDir, &ParentFcb);
562
563 if (OpenTargetDir)
564 {
565 LONG idx, FileNameLen;
566
567 if (Status == STATUS_OBJECT_NAME_COLLISION)
568 {
569 Irp->IoStatus.Information = FILE_EXISTS;
570 }
571 else
572 {
573 Irp->IoStatus.Information = FILE_DOES_NOT_EXIST;
574 }
575
576 idx = FileObject->FileName.Length / sizeof(WCHAR) - 1;
577
578 /* Skip tailing \ - if any */
579 if (PathNameU.Buffer[idx] == L'\\')
580 {
581 --idx;
582 PathNameU.Length -= sizeof(WCHAR);
583 }
584
585 /* Get file name */
586 while (idx >= 0 && PathNameU.Buffer[idx] != L'\\')
587 {
588 --idx;
589 }
590
591 if (idx > 0 || PathNameU.Buffer[0] == L'\\')
592 {
593 /* We don't want to include / in the name */
594 FileNameLen = PathNameU.Length - ((idx + 1) * sizeof(WCHAR));
595
596 /* Try to open parent */
597 PathNameU.Length -= (PathNameU.Length - idx * sizeof(WCHAR));
598 Status = VfatOpenFile(DeviceExt, &PathNameU, FileObject, RequestedDisposition, FALSE, &ParentFcb);
599
600 /* Update FO just to keep file name */
601 /* Skip first slash */
602 ++idx;
603 FileObject->FileName.Length = FileNameLen;
604 RtlMoveMemory(&PathNameU.Buffer[0], &PathNameU.Buffer[idx], FileObject->FileName.Length);
605 }
606 else
607 {
608 /* This is a relative open and we have only the filename, so open the parent directory
609 * It is in RelatedFileObject
610 */
611 BOOLEAN Chomp = FALSE;
612 PFILE_OBJECT RelatedFileObject = FileObject->RelatedFileObject;
613
614 DPRINT("%wZ\n", &PathNameU);
615
616 ASSERT(RelatedFileObject != NULL);
617
618 DPRINT("Relative opening\n");
619 DPRINT("FileObject->RelatedFileObject->FileName: %wZ\n", &RelatedFileObject->FileName);
620
621 /* VfatOpenFile() doesn't like our name ends with \, so chomp it if there's one */
622 if (RelatedFileObject->FileName.Buffer[RelatedFileObject->FileName.Length / sizeof(WCHAR) - 1] == L'\\')
623 {
624 Chomp = TRUE;
625 RelatedFileObject->FileName.Length -= sizeof(WCHAR);
626 }
627
628 /* Tricky part - fake our FO. It's NOT relative, we want to open the complete file path */
629 FileObject->RelatedFileObject = NULL;
630 Status = VfatOpenFile(DeviceExt, &RelatedFileObject->FileName, FileObject, RequestedDisposition, FALSE, &ParentFcb);
631
632 /* We're done opening, restore what we broke */
633 FileObject->RelatedFileObject = RelatedFileObject;
634 if (Chomp) RelatedFileObject->FileName.Length += sizeof(WCHAR);
635
636 /* No need to modify the FO, it already has the name */
637 }
638
639 return Status;
640 }
641
642 /*
643 * If the directory containing the file to open doesn't exist then
644 * fail immediately
645 */
646 if (Status == STATUS_OBJECT_PATH_NOT_FOUND ||
647 Status == STATUS_INVALID_PARAMETER ||
648 Status == STATUS_DELETE_PENDING)
649 {
650 if (ParentFcb)
651 {
652 vfatReleaseFCB(DeviceExt, ParentFcb);
653 }
654 return Status;
655 }
656
657 if (!NT_SUCCESS(Status) && ParentFcb == NULL)
658 {
659 DPRINT1("VfatOpenFile failed for '%wZ', status %x\n", &PathNameU, Status);
660 return Status;
661 }
662
663 /* If the file open failed then create the required file */
664 if (!NT_SUCCESS (Status))
665 {
666 if (RequestedDisposition == FILE_CREATE ||
667 RequestedDisposition == FILE_OPEN_IF ||
668 RequestedDisposition == FILE_OVERWRITE_IF ||
669 RequestedDisposition == FILE_SUPERSEDE)
670 {
671 Attributes = Stack->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL;
672 if (!(RequestedOptions & FILE_DIRECTORY_FILE))
673 Attributes |= FILE_ATTRIBUTE_ARCHIVE;
674 vfatSplitPathName(&PathNameU, NULL, &FileNameU);
675 Status = VfatAddEntry(DeviceExt, &FileNameU, &pFcb, ParentFcb, RequestedOptions,
676 (UCHAR)(Attributes & FILE_ATTRIBUTE_VALID_FLAGS));
677 vfatReleaseFCB(DeviceExt, ParentFcb);
678 if (NT_SUCCESS(Status))
679 {
680 Status = vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject);
681 if (!NT_SUCCESS(Status))
682 {
683 vfatReleaseFCB(DeviceExt, pFcb);
684 return Status;
685 }
686
687 Irp->IoStatus.Information = FILE_CREATED;
688 VfatSetAllocationSizeInformation(FileObject,
689 pFcb,
690 DeviceExt,
691 &Irp->Overlay.AllocationSize);
692 VfatSetExtendedAttributes(FileObject,
693 Irp->AssociatedIrp.SystemBuffer,
694 Stack->Parameters.Create.EaLength);
695
696 if (PagingFileCreate)
697 {
698 pFcb->Flags |= FCB_IS_PAGE_FILE;
699 }
700 }
701 else
702 {
703 return Status;
704 }
705 }
706 else
707 {
708 if (ParentFcb)
709 {
710 vfatReleaseFCB(DeviceExt, ParentFcb);
711 }
712 return Status;
713 }
714 }
715 else
716 {
717 if (ParentFcb)
718 {
719 vfatReleaseFCB(DeviceExt, ParentFcb);
720 }
721
722 /* Otherwise fail if the caller wanted to create a new file */
723 if (RequestedDisposition == FILE_CREATE)
724 {
725 Irp->IoStatus.Information = FILE_EXISTS;
726 VfatCloseFile(DeviceExt, FileObject);
727 return STATUS_OBJECT_NAME_COLLISION;
728 }
729
730 pFcb = FileObject->FsContext;
731
732 if (pFcb->OpenHandleCount != 0)
733 {
734 Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
735 Stack->Parameters.Create.ShareAccess,
736 FileObject,
737 &pFcb->FCBShareAccess,
738 FALSE);
739 if (!NT_SUCCESS(Status))
740 {
741 VfatCloseFile(DeviceExt, FileObject);
742 return Status;
743 }
744 }
745
746 /*
747 * Check the file has the requested attributes
748 */
749 if (RequestedOptions & FILE_NON_DIRECTORY_FILE &&
750 *pFcb->Attributes & FILE_ATTRIBUTE_DIRECTORY)
751 {
752 VfatCloseFile (DeviceExt, FileObject);
753 return STATUS_FILE_IS_A_DIRECTORY;
754 }
755 if (RequestedOptions & FILE_DIRECTORY_FILE &&
756 !(*pFcb->Attributes & FILE_ATTRIBUTE_DIRECTORY))
757 {
758 VfatCloseFile (DeviceExt, FileObject);
759 return STATUS_NOT_A_DIRECTORY;
760 }
761 #ifndef USE_ROS_CC_AND_FS
762 if (!(*pFcb->Attributes & FILE_ATTRIBUTE_DIRECTORY))
763 {
764 if (Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA ||
765 RequestedDisposition == FILE_OVERWRITE ||
766 RequestedDisposition == FILE_OVERWRITE_IF)
767 {
768 if (!MmFlushImageSection(&pFcb->SectionObjectPointers, MmFlushForWrite))
769 {
770 DPRINT1("%wZ\n", &pFcb->PathNameU);
771 DPRINT1("%d %d %d\n", Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA,
772 RequestedDisposition == FILE_OVERWRITE, RequestedDisposition == FILE_OVERWRITE_IF);
773 VfatCloseFile (DeviceExt, FileObject);
774 return STATUS_SHARING_VIOLATION;
775 }
776 }
777 }
778 #endif
779 if (PagingFileCreate)
780 {
781 /* FIXME:
782 * Do more checking for page files. It is possible,
783 * that the file was opened and closed previously
784 * as a normal cached file. In this case, the cache
785 * manager has referenced the fileobject and the fcb
786 * is held in memory. Try to remove the fileobject
787 * from cache manager and use the fcb.
788 */
789 if (pFcb->RefCount > 1)
790 {
791 if(!(pFcb->Flags & FCB_IS_PAGE_FILE))
792 {
793 VfatCloseFile(DeviceExt, FileObject);
794 return STATUS_INVALID_PARAMETER;
795 }
796 }
797 else
798 {
799 pFcb->Flags |= FCB_IS_PAGE_FILE;
800 }
801 }
802 else
803 {
804 if (pFcb->Flags & FCB_IS_PAGE_FILE)
805 {
806 VfatCloseFile(DeviceExt, FileObject);
807 return STATUS_INVALID_PARAMETER;
808 }
809 }
810
811 if (RequestedDisposition == FILE_OVERWRITE ||
812 RequestedDisposition == FILE_OVERWRITE_IF ||
813 RequestedDisposition == FILE_SUPERSEDE)
814 {
815 if (!(*pFcb->Attributes & FILE_ATTRIBUTE_DIRECTORY))
816 {
817 *pFcb->Attributes = Stack->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL;
818 *pFcb->Attributes |= FILE_ATTRIBUTE_ARCHIVE;
819 VfatUpdateEntry(pFcb);
820 }
821
822 ExAcquireResourceExclusiveLite(&(pFcb->MainResource), TRUE);
823 Status = VfatSetAllocationSizeInformation(FileObject,
824 pFcb,
825 DeviceExt,
826 &Irp->Overlay.AllocationSize);
827 ExReleaseResourceLite(&(pFcb->MainResource));
828 if (!NT_SUCCESS (Status))
829 {
830 VfatCloseFile(DeviceExt, FileObject);
831 return Status;
832 }
833 }
834
835 if (RequestedDisposition == FILE_SUPERSEDE)
836 {
837 Irp->IoStatus.Information = FILE_SUPERSEDED;
838 }
839 else if (RequestedDisposition == FILE_OVERWRITE ||
840 RequestedDisposition == FILE_OVERWRITE_IF)
841 {
842 Irp->IoStatus.Information = FILE_OVERWRITTEN;
843 }
844 else
845 {
846 Irp->IoStatus.Information = FILE_OPENED;
847 }
848 }
849
850 if (pFcb->OpenHandleCount == 0)
851 {
852 IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess,
853 Stack->Parameters.Create.ShareAccess,
854 FileObject,
855 &pFcb->FCBShareAccess);
856 }
857 else
858 {
859 IoUpdateShareAccess(FileObject,
860 &pFcb->FCBShareAccess);
861 }
862
863 if (Irp->IoStatus.Information == FILE_CREATED)
864 {
865 FsRtlNotifyFullReportChange(DeviceExt->NotifySync,
866 &(DeviceExt->NotifyList),
867 (PSTRING)&pFcb->PathNameU,
868 pFcb->PathNameU.Length - pFcb->LongNameU.Length,
869 NULL,
870 NULL,
871 ((*pFcb->Attributes & FILE_ATTRIBUTE_DIRECTORY) ?
872 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
873 FILE_ACTION_ADDED,
874 NULL);
875 }
876
877 pFcb->OpenHandleCount++;
878
879 /* FIXME : test write access if requested */
880
881 return Status;
882 }
883
884 /*
885 * FUNCTION: Create or open a file
886 */
887 NTSTATUS
888 VfatCreate(
889 PVFAT_IRP_CONTEXT IrpContext)
890 {
891 NTSTATUS Status;
892
893 ASSERT(IrpContext);
894
895 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
896 {
897 /* DeviceObject represents FileSystem instead of logical volume */
898 DPRINT ("FsdCreate called with file system\n");
899 IrpContext->Irp->IoStatus.Information = FILE_OPENED;
900 IrpContext->Irp->IoStatus.Status = STATUS_SUCCESS;
901 IoCompleteRequest(IrpContext->Irp, IO_DISK_INCREMENT);
902 VfatFreeIrpContext(IrpContext);
903 return STATUS_SUCCESS;
904 }
905
906 if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT))
907 {
908 return(VfatQueueRequest(IrpContext));
909 }
910
911 IrpContext->Irp->IoStatus.Information = 0;
912 ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, TRUE);
913 Status = VfatCreateFile(IrpContext->DeviceObject, IrpContext->Irp);
914 ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
915
916 IrpContext->Irp->IoStatus.Status = Status;
917 IoCompleteRequest(IrpContext->Irp,
918 (CCHAR)(NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT));
919 VfatFreeIrpContext(IrpContext);
920 return Status;
921 }
922
923 /* EOF */