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