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