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