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