Sync with trunk (r48545)
[reactos.git] / drivers / filesystems / fastfat_new / create.c
1 /*
2 * PROJECT: ReactOS FAT file system driver
3 * LICENSE: GNU GPLv3 as published by the Free Software Foundation
4 * FILE: drivers/filesystems/fastfat/create.c
5 * PURPOSE: Create routines
6 * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org)
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #define NDEBUG
12 #include "fastfat.h"
13
14 /* FUNCTIONS *****************************************************************/
15
16 IO_STATUS_BLOCK
17 NTAPI
18 FatiOpenRootDcb(IN PFAT_IRP_CONTEXT IrpContext,
19 IN PFILE_OBJECT FileObject,
20 IN PVCB Vcb,
21 IN PACCESS_MASK DesiredAccess,
22 IN USHORT ShareAccess,
23 IN ULONG CreateDisposition)
24 {
25 IO_STATUS_BLOCK Iosb;
26 PFCB Dcb;
27 NTSTATUS Status;
28 PCCB Ccb;
29
30 /* Reference our DCB */
31 Dcb = Vcb->RootDcb;
32
33 DPRINT("Opening root directory\n");
34
35 /* Exclusively lock this DCB */
36 (VOID)FatAcquireExclusiveFcb(IrpContext, Dcb);
37
38 do
39 {
40 /* Validate parameters */
41 if (CreateDisposition != FILE_OPEN &&
42 CreateDisposition != FILE_OPEN_IF)
43 {
44 Iosb.Status = STATUS_ACCESS_DENIED;
45 break;
46 }
47
48 // TODO: Check file access
49
50 /* Is it a first time open? */
51 if (Dcb->OpenCount == 0)
52 {
53 /* Set share access */
54 IoSetShareAccess(*DesiredAccess,
55 ShareAccess,
56 FileObject,
57 &Dcb->ShareAccess);
58 }
59 else
60 {
61 /* Check share access */
62 Status = IoCheckShareAccess(*DesiredAccess,
63 ShareAccess,
64 FileObject,
65 &Dcb->ShareAccess,
66 TRUE);
67 }
68
69 /* Set file object pointers */
70 Ccb = FatCreateCcb();
71 FatSetFileObject(FileObject, UserDirectoryOpen, Dcb, Ccb);
72
73 /* Increment counters */
74 Dcb->OpenCount++;
75 Dcb->UncleanCount++;
76 Vcb->OpenFileCount++;
77 if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++;
78
79 /* Set success statuses */
80 Iosb.Status = STATUS_SUCCESS;
81 Iosb.Information = FILE_OPENED;
82 } while (FALSE);
83
84 /* Release the DCB lock */
85 FatReleaseFcb(IrpContext, Dcb);
86
87 return Iosb;
88 }
89
90 FF_ERROR
91 NTAPI
92 FatiTryToOpen(IN PFILE_OBJECT FileObject,
93 IN PVCB Vcb)
94 {
95 OEM_STRING AnsiName;
96 CHAR AnsiNameBuf[512];
97 FF_ERROR Error;
98 NTSTATUS Status;
99 FF_FILE *FileHandle;
100
101 /* Convert the name to ANSI */
102 AnsiName.Buffer = AnsiNameBuf;
103 AnsiName.Length = 0;
104 AnsiName.MaximumLength = sizeof(AnsiNameBuf);
105 RtlZeroMemory(AnsiNameBuf, sizeof(AnsiNameBuf));
106 Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiName, &FileObject->FileName, FALSE);
107 if (!NT_SUCCESS(Status))
108 {
109 ASSERT(FALSE);
110 }
111
112 /* Open the file with FullFAT */
113 FileHandle = FF_Open(Vcb->Ioman, AnsiName.Buffer, FF_MODE_READ, &Error);
114
115 /* Close the handle */
116 if (FileHandle) FF_Close(FileHandle);
117
118 /* Return status */
119 return Error;
120 }
121
122 IO_STATUS_BLOCK
123 NTAPI
124 FatiOverwriteFile(PFAT_IRP_CONTEXT IrpContext,
125 PFILE_OBJECT FileObject,
126 PFCB Fcb,
127 ULONG AllocationSize,
128 PFILE_FULL_EA_INFORMATION EaBuffer,
129 ULONG EaLength,
130 UCHAR FileAttributes,
131 ULONG CreateDisposition,
132 BOOLEAN NoEaKnowledge)
133 {
134 IO_STATUS_BLOCK Iosb = {{0}};
135 PCCB Ccb;
136 LARGE_INTEGER Zero;
137 ULONG NotifyFilter;
138
139 Zero.QuadPart = 0;
140
141 /* Check Ea mismatch first */
142 if (NoEaKnowledge && EaLength > 0)
143 {
144 Iosb.Status = STATUS_ACCESS_DENIED;
145 return Iosb;
146 }
147
148 do
149 {
150 /* Check if it's not still mapped */
151 if (!MmCanFileBeTruncated(&Fcb->SectionObjectPointers,
152 &Zero))
153 {
154 /* Fail */
155 Iosb.Status = STATUS_USER_MAPPED_FILE;
156 break;
157 }
158
159 /* Set file object pointers */
160 Ccb = FatCreateCcb();
161 FatSetFileObject(FileObject,
162 UserFileOpen,
163 Fcb,
164 Ccb);
165
166 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
167
168 /* Indicate that create is in progress */
169 Fcb->Vcb->State |= VCB_STATE_CREATE_IN_PROGRESS;
170
171 /* Purge the cache section */
172 CcPurgeCacheSection(&Fcb->SectionObjectPointers, NULL, 0, FALSE);
173
174 /* Add Eas */
175 if (EaLength > 0)
176 {
177 ASSERT(FALSE);
178 }
179
180 /* Acquire the paging resource */
181 (VOID)ExAcquireResourceExclusiveLite(Fcb->Header.PagingIoResource, TRUE);
182
183 /* Initialize FCB header */
184 Fcb->Header.FileSize.QuadPart = 0;
185 Fcb->Header.ValidDataLength.QuadPart = 0;
186
187 /* Let CC know about changed file size */
188 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize);
189
190 // TODO: Actually truncate the file
191 DPRINT1("TODO: Actually truncate file '%wZ' with a fullfat handle %x\n", &Fcb->FullFileName, Fcb->FatHandle);
192
193 /* Release the paging resource */
194 ExReleaseResourceLite(Fcb->Header.PagingIoResource);
195
196 /* Specify truncate on close */
197 Fcb->State |= FCB_STATE_TRUNCATE_ON_CLOSE;
198
199 // TODO: Delete previous EA if needed
200
201 /* Send notification about changes */
202 NotifyFilter = FILE_NOTIFY_CHANGE_LAST_WRITE |
203 FILE_NOTIFY_CHANGE_ATTRIBUTES |
204 FILE_NOTIFY_CHANGE_SIZE;
205
206 FsRtlNotifyFullReportChange(Fcb->Vcb->NotifySync,
207 &Fcb->Vcb->NotifyList,
208 (PSTRING)&Fcb->FullFileName,
209 Fcb->FullFileName.Length - Fcb->FileNameLength,
210 NULL,
211 NULL,
212 NotifyFilter,
213 FILE_ACTION_MODIFIED,
214 NULL);
215
216 /* Set success status */
217 Iosb.Status = STATUS_SUCCESS;
218
219 /* Set correct information code */
220 Iosb.Information = (CreateDisposition == FILE_SUPERSEDE) ? FILE_SUPERSEDED : FILE_OVERWRITTEN;
221 } while (0);
222
223 /* Remove the create in progress flag */
224 ClearFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS);
225
226 return Iosb;
227 }
228
229 IO_STATUS_BLOCK
230 NTAPI
231 FatiOpenExistingDir(IN PFAT_IRP_CONTEXT IrpContext,
232 IN PFILE_OBJECT FileObject,
233 IN PVCB Vcb,
234 IN PFCB ParentDcb,
235 IN PACCESS_MASK DesiredAccess,
236 IN USHORT ShareAccess,
237 IN ULONG AllocationSize,
238 IN PFILE_FULL_EA_INFORMATION EaBuffer,
239 IN ULONG EaLength,
240 IN UCHAR FileAttributes,
241 IN ULONG CreateDisposition,
242 IN BOOLEAN DeleteOnClose)
243 {
244 IO_STATUS_BLOCK Iosb = {{0}};
245 OEM_STRING AnsiName;
246 CHAR AnsiNameBuf[512];
247 PFCB Fcb;
248 NTSTATUS Status;
249 FF_FILE *FileHandle;
250
251 /* Only open is permitted */
252 if (CreateDisposition != FILE_OPEN &&
253 CreateDisposition != FILE_OPEN_IF)
254 {
255 Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
256 return Iosb;
257 }
258
259 // TODO: Check dir access
260
261 /* Convert the name to ANSI */
262 AnsiName.Buffer = AnsiNameBuf;
263 AnsiName.Length = 0;
264 AnsiName.MaximumLength = sizeof(AnsiNameBuf);
265 RtlZeroMemory(AnsiNameBuf, sizeof(AnsiNameBuf));
266 Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiName, &FileObject->FileName, FALSE);
267 if (!NT_SUCCESS(Status))
268 {
269 ASSERT(FALSE);
270 }
271
272 /* Open the dir with FullFAT */
273 FileHandle = FF_Open(Vcb->Ioman, AnsiName.Buffer, FF_MODE_DIR, NULL);
274
275 if (!FileHandle)
276 {
277 Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND; // FIXME: A shortcut for now
278 return Iosb;
279 }
280
281 /* Create a new DCB for this directory */
282 Fcb = FatCreateDcb(IrpContext, Vcb, ParentDcb, FileHandle);
283
284 /* Set share access */
285 IoSetShareAccess(*DesiredAccess, ShareAccess, FileObject, &Fcb->ShareAccess);
286
287 /* Set context and section object pointers */
288 FatSetFileObject(FileObject,
289 UserDirectoryOpen,
290 Fcb,
291 FatCreateCcb());
292
293 /* Increase counters */
294 Fcb->UncleanCount++;
295 Fcb->OpenCount++;
296 Vcb->OpenFileCount++;
297 if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++;
298
299 Iosb.Status = STATUS_SUCCESS;
300 Iosb.Information = FILE_OPENED;
301
302 DPRINT1("Successfully opened dir %s\n", AnsiNameBuf);
303
304 return Iosb;
305 }
306
307 IO_STATUS_BLOCK
308 NTAPI
309 FatiOpenExistingFile(IN PFAT_IRP_CONTEXT IrpContext,
310 IN PFILE_OBJECT FileObject,
311 IN PVCB Vcb,
312 IN PFCB ParentDcb,
313 IN PACCESS_MASK DesiredAccess,
314 IN USHORT ShareAccess,
315 IN ULONG AllocationSize,
316 IN PFILE_FULL_EA_INFORMATION EaBuffer,
317 IN ULONG EaLength,
318 IN UCHAR FileAttributes,
319 IN ULONG CreateDisposition,
320 IN BOOLEAN IsPagingFile,
321 IN BOOLEAN DeleteOnClose,
322 IN BOOLEAN IsDosName)
323 {
324 IO_STATUS_BLOCK Iosb = {{0}};
325 OEM_STRING AnsiName;
326 CHAR AnsiNameBuf[512];
327 PFCB Fcb;
328 NTSTATUS Status;
329 FF_FILE *FileHandle;
330 FF_ERROR FfError;
331
332 /* Check for create file option and fail */
333 if (CreateDisposition == FILE_CREATE)
334 {
335 Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
336 return Iosb;
337 }
338
339 // TODO: Check more params
340
341 /* Convert the name to ANSI */
342 AnsiName.Buffer = AnsiNameBuf;
343 AnsiName.Length = 0;
344 AnsiName.MaximumLength = sizeof(AnsiNameBuf);
345 RtlZeroMemory(AnsiNameBuf, sizeof(AnsiNameBuf));
346 Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiName, &FileObject->FileName, FALSE);
347 if (!NT_SUCCESS(Status))
348 {
349 ASSERT(FALSE);
350 }
351
352 /* Open the file with FullFAT */
353 FileHandle = FF_Open(Vcb->Ioman, AnsiName.Buffer, FF_MODE_READ, &FfError);
354
355 if (!FileHandle)
356 {
357 DPRINT1("Failed to open file '%s', error %ld\n", AnsiName.Buffer, FfError);
358 Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND; // FIXME: A shortcut for now
359 return Iosb;
360 }
361 DPRINT1("Succeeded opening file '%s'\n", AnsiName.Buffer);
362
363 /* Create a new FCB for this file */
364 Fcb = FatCreateFcb(IrpContext, Vcb, ParentDcb, FileHandle);
365
366 // TODO: Check if overwrite is needed
367
368 // TODO: This is usual file open branch, without overwriting!
369 /* Set context and section object pointers */
370 FatSetFileObject(FileObject,
371 UserFileOpen,
372 Fcb,
373 FatCreateCcb());
374 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
375
376 Iosb.Status = STATUS_SUCCESS;
377 Iosb.Information = FILE_OPENED;
378
379
380 /* Increase counters */
381 Fcb->UncleanCount++;
382 Fcb->OpenCount++;
383 if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) Fcb->NonCachedUncleanCount++;
384 if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++;
385
386 return Iosb;
387 }
388
389 IO_STATUS_BLOCK
390 NTAPI
391 FatiOpenVolume(IN PFAT_IRP_CONTEXT IrpContext,
392 IN PFILE_OBJECT FileObject,
393 IN PVCB Vcb,
394 IN PACCESS_MASK DesiredAccess,
395 IN USHORT ShareAccess,
396 IN ULONG CreateDisposition)
397 {
398 PCCB Ccb;
399 IO_STATUS_BLOCK Iosb = {{0}};
400 BOOLEAN VolumeFlushed = FALSE;
401
402 /* Check parameters */
403 if (CreateDisposition != FILE_OPEN &&
404 CreateDisposition != FILE_OPEN_IF)
405 {
406 /* Deny access */
407 Iosb.Status = STATUS_ACCESS_DENIED;
408 }
409
410 /* Check if it's exclusive open */
411 if (!FlagOn(ShareAccess, FILE_SHARE_WRITE) &&
412 !FlagOn(ShareAccess, FILE_SHARE_DELETE))
413 {
414 // TODO: Check if exclusive read access requested
415 // and opened handles count is not 0
416 //if (!FlagOn(ShareAccess, FILE_SHARE_READ)
417
418 DPRINT1("Exclusive volume open\n");
419
420 // TODO: Flush the volume
421 VolumeFlushed = TRUE;
422 }
423 else if (FlagOn(*DesiredAccess, FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA))
424 {
425 DPRINT1("Shared open\n");
426
427 // TODO: Flush the volume
428 VolumeFlushed = TRUE;
429 }
430
431 if (VolumeFlushed &&
432 !FlagOn(Vcb->State, VCB_STATE_MOUNTED_DIRTY) &&
433 FlagOn(Vcb->State, VCB_STATE_FLAG_DIRTY) &&
434 CcIsThereDirtyData(Vcb->Vpb))
435 {
436 UNIMPLEMENTED;
437 }
438
439 /* Set share access */
440 if (Vcb->DirectOpenCount > 0)
441 {
442 /* This volume has already been opened */
443 Iosb.Status = IoCheckShareAccess(*DesiredAccess,
444 ShareAccess,
445 FileObject,
446 &Vcb->ShareAccess,
447 TRUE);
448
449 if (!NT_SUCCESS(Iosb.Status))
450 {
451 ASSERT(FALSE);
452 }
453 }
454 else
455 {
456 /* This is the first time open */
457 IoSetShareAccess(*DesiredAccess,
458 ShareAccess,
459 FileObject,
460 &Vcb->ShareAccess);
461 }
462
463 /* Set file object pointers */
464 Ccb = FatCreateCcb();
465 FatSetFileObject(FileObject, UserVolumeOpen, Vcb, Ccb);
466 FileObject->SectionObjectPointer = &Vcb->SectionObjectPointers;
467
468 /* Increase direct open count */
469 Vcb->DirectOpenCount++;
470 Vcb->OpenFileCount++;
471 if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++;
472
473 /* Set no buffering flag */
474 FileObject->Flags |= FO_NO_INTERMEDIATE_BUFFERING;
475
476 // TODO: User's access check
477
478 Iosb.Status = STATUS_SUCCESS;
479 Iosb.Information = FILE_OPENED;
480
481 return Iosb;
482 }
483
484 NTSTATUS
485 NTAPI
486 FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
487 IN PIRP Irp)
488 {
489 /* Boolean options */
490 BOOLEAN CreateDirectory;
491 BOOLEAN SequentialOnly;
492 BOOLEAN NoIntermediateBuffering;
493 BOOLEAN OpenDirectory;
494 BOOLEAN IsPagingFile;
495 BOOLEAN OpenTargetDirectory;
496 BOOLEAN DirectoryFile;
497 BOOLEAN NonDirectoryFile;
498 BOOLEAN NoEaKnowledge;
499 BOOLEAN DeleteOnClose;
500 BOOLEAN TemporaryFile;
501 ULONG CreateDisposition;
502
503 /* Control blocks */
504 PVCB Vcb, DecodedVcb, RelatedVcb;
505 PFCB Fcb, NextFcb, RelatedDcb;
506 PCCB Ccb, RelatedCcb;
507 PFCB ParentDcb;
508
509 /* IRP data */
510 PFILE_OBJECT FileObject;
511 PFILE_OBJECT RelatedFO;
512 UNICODE_STRING FileName;
513 ULONG AllocationSize;
514 PFILE_FULL_EA_INFORMATION EaBuffer;
515 PACCESS_MASK DesiredAccess;
516 ULONG Options;
517 UCHAR FileAttributes;
518 USHORT ShareAccess;
519 ULONG EaLength;
520
521 /* Misc */
522 NTSTATUS Status;
523 IO_STATUS_BLOCK Iosb;
524 PIO_STACK_LOCATION IrpSp;
525 BOOLEAN EndBackslash = FALSE, OpenedAsDos;
526 UNICODE_STRING RemainingPart, FirstName, NextName, FileNameUpcased;
527 OEM_STRING AnsiFirstName;
528 FF_ERROR FfError;
529 TYPE_OF_OPEN TypeOfOpen;
530 BOOLEAN OplockPostIrp = FALSE;
531
532 Iosb.Status = STATUS_SUCCESS;
533
534 /* Get current IRP stack location */
535 IrpSp = IoGetCurrentIrpStackLocation(Irp);
536
537 DPRINT("FatCommonCreate\n", 0 );
538 DPRINT("Irp = %08lx\n", Irp );
539 DPRINT("\t->Flags = %08lx\n", Irp->Flags );
540 DPRINT("\t->FileObject = %08lx\n", IrpSp->FileObject );
541 DPRINT("\t->RelatedFileObject = %08lx\n", IrpSp->FileObject->RelatedFileObject );
542 DPRINT("\t->FileName = %wZ\n", &IrpSp->FileObject->FileName );
543 DPRINT("\t->AllocationSize.LowPart = %08lx\n", Irp->Overlay.AllocationSize.LowPart );
544 DPRINT("\t->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart );
545 DPRINT("\t->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer );
546 DPRINT("\t->DesiredAccess = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
547 DPRINT("\t->Options = %08lx\n", IrpSp->Parameters.Create.Options );
548 DPRINT("\t->FileAttributes = %04x\n", IrpSp->Parameters.Create.FileAttributes );
549 DPRINT("\t->ShareAccess = %04x\n", IrpSp->Parameters.Create.ShareAccess );
550 DPRINT("\t->EaLength = %08lx\n", IrpSp->Parameters.Create.EaLength );
551
552 /* Apply a special hack for Win32, idea taken from FASTFAT reference driver from WDK */
553 if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) &&
554 (IrpSp->FileObject->FileName.Buffer[1] == L'\\') &&
555 (IrpSp->FileObject->FileName.Buffer[0] == L'\\'))
556 {
557 /* Remove a leading slash */
558 IrpSp->FileObject->FileName.Length -= sizeof(WCHAR);
559 RtlMoveMemory(&IrpSp->FileObject->FileName.Buffer[0],
560 &IrpSp->FileObject->FileName.Buffer[1],
561 IrpSp->FileObject->FileName.Length );
562
563 /* Check again: if there are still two leading slashes,
564 exit with an error */
565 if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) &&
566 (IrpSp->FileObject->FileName.Buffer[1] == L'\\') &&
567 (IrpSp->FileObject->FileName.Buffer[0] == L'\\'))
568 {
569 FatCompleteRequest( IrpContext, Irp, STATUS_OBJECT_NAME_INVALID );
570
571 DPRINT1("FatiCreate: STATUS_OBJECT_NAME_INVALID\n");
572 return STATUS_OBJECT_NAME_INVALID;
573 }
574 }
575
576 /* Make sure we have SecurityContext */
577 ASSERT(IrpSp->Parameters.Create.SecurityContext != NULL);
578
579 /* Get necessary data out of IRP */
580 FileObject = IrpSp->FileObject;
581 FileName = FileObject->FileName;
582 RelatedFO = FileObject->RelatedFileObject;
583 AllocationSize = Irp->Overlay.AllocationSize.LowPart;
584 EaBuffer = Irp->AssociatedIrp.SystemBuffer;
585 DesiredAccess = &IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
586 Options = IrpSp->Parameters.Create.Options;
587 FileAttributes = (UCHAR)(IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL);
588 ShareAccess = IrpSp->Parameters.Create.ShareAccess;
589 EaLength = IrpSp->Parameters.Create.EaLength;
590
591 /* Set VPB to related object's VPB if it exists */
592 if (RelatedFO)
593 FileObject->Vpb = RelatedFO->Vpb;
594
595 /* Prepare file attributes mask */
596 FileAttributes &= (FILE_ATTRIBUTE_READONLY |
597 FILE_ATTRIBUTE_HIDDEN |
598 FILE_ATTRIBUTE_SYSTEM |
599 FILE_ATTRIBUTE_ARCHIVE);
600
601 /* Get the volume control object */
602 Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;
603
604 /* Get options */
605 DirectoryFile = BooleanFlagOn(Options, FILE_DIRECTORY_FILE);
606 NonDirectoryFile = BooleanFlagOn(Options, FILE_NON_DIRECTORY_FILE);
607 SequentialOnly = BooleanFlagOn(Options, FILE_SEQUENTIAL_ONLY);
608 NoIntermediateBuffering = BooleanFlagOn(Options, FILE_NO_INTERMEDIATE_BUFFERING);
609 NoEaKnowledge = BooleanFlagOn(Options, FILE_NO_EA_KNOWLEDGE);
610 DeleteOnClose = BooleanFlagOn(Options, FILE_DELETE_ON_CLOSE);
611 TemporaryFile = BooleanFlagOn(IrpSp->Parameters.Create.FileAttributes,
612 FILE_ATTRIBUTE_TEMPORARY );
613 IsPagingFile = BooleanFlagOn(IrpSp->Flags, SL_OPEN_PAGING_FILE);
614 OpenTargetDirectory = BooleanFlagOn(IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY);
615
616 /* Calculate create disposition */
617 CreateDisposition = (Options >> 24) & 0x000000ff;
618
619 /* Get Create/Open directory flags based on it */
620 CreateDirectory = (BOOLEAN)(DirectoryFile &&
621 ((CreateDisposition == FILE_CREATE) ||
622 (CreateDisposition == FILE_OPEN_IF)));
623
624 OpenDirectory = (BOOLEAN)(DirectoryFile &&
625 ((CreateDisposition == FILE_OPEN) ||
626 (CreateDisposition == FILE_OPEN_IF)));
627
628 /* Validate parameters: directory/nondirectory mismatch and
629 AllocationSize being more than 4GB */
630 if ((DirectoryFile && NonDirectoryFile) ||
631 Irp->Overlay.AllocationSize.HighPart != 0)
632 {
633 FatCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
634
635 DPRINT1("FatiCreate: STATUS_INVALID_PARAMETER\n", 0);
636 return STATUS_INVALID_PARAMETER;
637 }
638
639 /* Acquire the VCB lock exclusively */
640 if (!FatAcquireExclusiveVcb(IrpContext, Vcb))
641 {
642 // TODO: Postpone the IRP for later processing
643 ASSERT(FALSE);
644 return STATUS_NOT_IMPLEMENTED;
645 }
646
647 // TODO: Verify the VCB
648
649 /* If VCB is locked, then no file openings are possible */
650 if (Vcb->State & VCB_STATE_FLAG_LOCKED)
651 {
652 DPRINT1("This volume is locked\n");
653 Status = STATUS_ACCESS_DENIED;
654
655 /* Cleanup and return */
656 FatReleaseVcb(IrpContext, Vcb);
657 return Status;
658 }
659
660 // TODO: Check if the volume is write protected and disallow DELETE_ON_CLOSE
661
662 // TODO: Make sure EAs aren't supported on FAT32
663
664 /* Check if it's a volume open request */
665 if (FileName.Length == 0)
666 {
667 /* Test related FO to be sure */
668 if (!RelatedFO ||
669 FatDecodeFileObject(RelatedFO, &DecodedVcb, &Fcb, &Ccb) == UserVolumeOpen)
670 {
671 /* Check parameters */
672 if (DirectoryFile || OpenTargetDirectory)
673 {
674 Status = DirectoryFile ? STATUS_NOT_A_DIRECTORY : STATUS_INVALID_PARAMETER;
675
676 /* Unlock VCB */
677 FatReleaseVcb(IrpContext, Vcb);
678
679 /* Complete the request and return */
680 FatCompleteRequest(IrpContext, Irp, Status);
681 return Status;
682 }
683
684 /* It is indeed a volume open request */
685 Iosb = FatiOpenVolume(IrpContext,
686 FileObject,
687 Vcb,
688 DesiredAccess,
689 ShareAccess,
690 CreateDisposition);
691
692 /* Set resulting information */
693 Irp->IoStatus.Information = Iosb.Information;
694
695 /* Unlock VCB */
696 FatReleaseVcb(IrpContext, Vcb);
697
698 /* Complete the request and return */
699 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
700 return Iosb.Status;
701 }
702 }
703
704 /* Check if this is a relative open */
705 if (RelatedFO)
706 {
707 /* Decode the file object */
708 TypeOfOpen = FatDecodeFileObject(RelatedFO,
709 &RelatedVcb,
710 &RelatedDcb,
711 &RelatedCcb);
712
713 /* Check open type */
714 if (TypeOfOpen != UserFileOpen &&
715 TypeOfOpen != UserDirectoryOpen)
716 {
717 DPRINT1("Invalid file object!\n");
718
719 /* Cleanup and return */
720 FatReleaseVcb(IrpContext, Vcb);
721 return STATUS_OBJECT_PATH_NOT_FOUND;
722 }
723
724 /* File path must be relative */
725 if (FileName.Length != 0 &&
726 FileName.Buffer[0] == L'\\')
727 {
728 /* The name is absolute, fail */
729 FatReleaseVcb(IrpContext, Vcb);
730 return STATUS_OBJECT_NAME_INVALID;
731 }
732
733 /* Make sure volume is the same */
734 ASSERT(RelatedVcb == Vcb);
735
736 /* Save VPB */
737 FileObject->Vpb = RelatedFO->Vpb;
738
739 /* Set parent DCB */
740 ParentDcb = RelatedDcb;
741
742 DPRINT("Opening file '%wZ' relatively to '%wZ'\n", &FileName, &ParentDcb->FullFileName);
743 }
744 else
745 {
746 /* Absolute open */
747 if ((FileName.Length == sizeof(WCHAR)) &&
748 (FileName.Buffer[0] == L'\\'))
749 {
750 /* Check if it's ok to open it */
751 if (NonDirectoryFile)
752 {
753 DPRINT1("Trying to open root dir as a file\n");
754
755 /* Cleanup and return */
756 FatReleaseVcb(IrpContext, Vcb);
757 return STATUS_FILE_IS_A_DIRECTORY;
758 }
759
760 /* Check delete on close on a root dir */
761 if (DeleteOnClose)
762 {
763 /* Cleanup and return */
764 FatReleaseVcb(IrpContext, Vcb);
765 return STATUS_CANNOT_DELETE;
766 }
767
768 /* Call root directory open routine */
769 Iosb = FatiOpenRootDcb(IrpContext,
770 FileObject,
771 Vcb,
772 DesiredAccess,
773 ShareAccess,
774 CreateDisposition);
775
776 Irp->IoStatus.Information = Iosb.Information;
777
778 /* Cleanup and return */
779 FatReleaseVcb(IrpContext, Vcb);
780 return Iosb.Status;
781 }
782 else
783 {
784 /* Not a root dir */
785 ParentDcb = Vcb->RootDcb;
786 DPRINT("ParentDcb %p\n", ParentDcb);
787 }
788 }
789
790 /* Check for backslash at the end */
791 if (FileName.Length &&
792 FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] == L'\\')
793 {
794 /* Cut it out */
795 FileName.Length -= sizeof(WCHAR);
796
797 /* Remember we cut it */
798 EndBackslash = TRUE;
799 }
800
801 /* Ensure the name is set */
802 if (!ParentDcb->FullFileName.Buffer)
803 {
804 /* Set it if it's missing */
805 FatSetFullFileNameInFcb(IrpContext, ParentDcb);
806 }
807
808 /* Check max path length */
809 if (ParentDcb->FullFileName.Length + FileName.Length + sizeof(WCHAR) <= FileName.Length)
810 {
811 DPRINT1("Max length is way off\n");
812 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
813 ASSERT(FALSE);
814 }
815
816 /* Loop through FCBs to find a good one */
817 while (TRUE)
818 {
819 Fcb = ParentDcb;
820
821 /* Dissect the name */
822 RemainingPart = FileName;
823 while (RemainingPart.Length)
824 {
825 FsRtlDissectName(RemainingPart, &FirstName, &NextName);
826
827 /* Check for validity */
828 if ((NextName.Length && NextName.Buffer[0] == L'\\') ||
829 (NextName.Length > 255 * sizeof(WCHAR)))
830 {
831 /* The name is invalid */
832 DPRINT1("Invalid name found\n");
833 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
834 ASSERT(FALSE);
835 }
836
837 /* Convert the name to ANSI */
838 AnsiFirstName.Buffer = ExAllocatePool(PagedPool, FirstName.Length);
839 AnsiFirstName.Length = 0;
840 AnsiFirstName.MaximumLength = FirstName.Length;
841 Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName, &FirstName, FALSE);
842
843 if (!NT_SUCCESS(Status))
844 {
845 DPRINT1("RtlUpcaseUnicodeStringToCountedOemString() failed with 0x%08x\n", Status);
846 ASSERT(FALSE);
847 NextFcb = NULL;
848 AnsiFirstName.Length = 0;
849 }
850 else
851 {
852 /* Find the coresponding FCB */
853 NextFcb = FatFindFcb(IrpContext,
854 &Fcb->Dcb.SplayLinksAnsi,
855 (PSTRING)&AnsiFirstName,
856 &OpenedAsDos);
857 }
858
859 /* If nothing found - try with unicode */
860 if (!NextFcb && Fcb->Dcb.SplayLinksUnicode)
861 {
862 FileNameUpcased.Buffer = FsRtlAllocatePool(PagedPool, FirstName.Length);
863 FileNameUpcased.Length = 0;
864 FileNameUpcased.MaximumLength = FirstName.Length;
865
866 /* Downcase and then upcase to normalize it */
867 Status = RtlDowncaseUnicodeString(&FileNameUpcased, &FirstName, FALSE);
868 Status = RtlUpcaseUnicodeString(&FileNameUpcased, &FileNameUpcased, FALSE);
869
870 /* Try to find FCB again using unicode name */
871 NextFcb = FatFindFcb(IrpContext,
872 &Fcb->Dcb.SplayLinksUnicode,
873 (PSTRING)&FileNameUpcased,
874 &OpenedAsDos);
875 }
876
877 /* Move to the next FCB */
878 if (NextFcb)
879 {
880 Fcb = NextFcb;
881 RemainingPart = NextName;
882 }
883
884 /* Break out of this loop if nothing can be found */
885 if (!NextFcb ||
886 NextName.Length == 0 ||
887 FatNodeType(NextFcb) == FAT_NTC_FCB)
888 {
889 break;
890 }
891 }
892
893 /* Ensure remaining name doesn't start from a backslash */
894 if (RemainingPart.Length &&
895 RemainingPart.Buffer[0] == L'\\')
896 {
897 /* Cut it */
898 RemainingPart.Buffer++;
899 RemainingPart.Length -= sizeof(WCHAR);
900 }
901
902 if (Fcb->Condition == FcbGood)
903 {
904 /* Good FCB, break out of the loop */
905 break;
906 }
907 else
908 {
909 ASSERT(FALSE);
910 }
911 }
912
913 /* We have a valid FCB now */
914 if (!RemainingPart.Length)
915 {
916 /* Check for target dir open */
917 if (OpenTargetDirectory)
918 {
919 DPRINT1("Opening target dir is missing\n");
920 ASSERT(FALSE);
921 }
922
923 /* Check this FCB's type */
924 if (FatNodeType(Fcb) == FAT_NTC_ROOT_DCB ||
925 FatNodeType(Fcb) == FAT_NTC_DCB)
926 {
927 /* Open a directory */
928 if (NonDirectoryFile)
929 {
930 /* Forbidden */
931 Iosb.Status = STATUS_FILE_IS_A_DIRECTORY;
932 ASSERT(FALSE);
933 return Iosb.Status;
934 }
935
936 /* Open existing DCB */
937 Iosb = FatiOpenExistingDcb(IrpContext,
938 FileObject,
939 Vcb,
940 Fcb,
941 DesiredAccess,
942 ShareAccess,
943 CreateDisposition,
944 NoEaKnowledge,
945 DeleteOnClose);
946
947 /* Save information */
948 Irp->IoStatus.Information = Iosb.Information;
949
950 /* Unlock VCB */
951 FatReleaseVcb(IrpContext, Vcb);
952
953 /* Complete the request */
954 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
955
956 return Iosb.Status;
957 }
958 else if (FatNodeType(Fcb) == FAT_NTC_FCB)
959 {
960 /* Open a file */
961 if (OpenDirectory)
962 {
963 /* Forbidden */
964 Iosb.Status = STATUS_NOT_A_DIRECTORY;
965 ASSERT(FALSE);
966 return Iosb.Status;
967 }
968
969 /* Check for trailing backslash */
970 if (EndBackslash)
971 {
972 /* Forbidden */
973 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
974 ASSERT(FALSE);
975 return Iosb.Status;
976 }
977
978 Iosb = FatiOpenExistingFcb(IrpContext,
979 FileObject,
980 Vcb,
981 Fcb,
982 DesiredAccess,
983 ShareAccess,
984 AllocationSize,
985 EaBuffer,
986 EaLength,
987 FileAttributes,
988 CreateDisposition,
989 NoEaKnowledge,
990 DeleteOnClose,
991 OpenedAsDos,
992 &OplockPostIrp);
993
994 /* Check if it's pending */
995 if (Iosb.Status != STATUS_PENDING)
996 {
997 /* In case of success set cache supported flag */
998 if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering)
999 {
1000 SetFlag(FileObject->Flags, FO_CACHE_SUPPORTED);
1001 }
1002
1003 /* Save information */
1004 Irp->IoStatus.Information = Iosb.Information;
1005
1006 /* Unlock VCB */
1007 FatReleaseVcb(IrpContext, Vcb);
1008
1009 /* Complete the request */
1010 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
1011
1012 return Iosb.Status;
1013 }
1014 else
1015 {
1016 /* Queue this IRP */
1017 UNIMPLEMENTED;
1018 ASSERT(FALSE);
1019 }
1020 }
1021 else
1022 {
1023 /* Unexpected FCB type */
1024 KeBugCheckEx(FAT_FILE_SYSTEM, __LINE__, (ULONG_PTR)Fcb, 0, 0);
1025 }
1026 }
1027
1028 /* During parsing we encountered a part which has no attached FCB/DCB.
1029 Check that the parent is really DCB and not FCB */
1030 if (FatNodeType(Fcb) != FAT_NTC_ROOT_DCB &&
1031 FatNodeType(Fcb) != FAT_NTC_DCB)
1032 {
1033 DPRINT1("Weird FCB node type %x, expected DCB or root DCB\n", FatNodeType(Fcb));
1034 ASSERT(FALSE);
1035 }
1036
1037 /* Create additional DCBs for all path items */
1038 ParentDcb = Fcb;
1039 while (TRUE)
1040 {
1041 FsRtlDissectName(RemainingPart, &FirstName, &RemainingPart);
1042
1043 /* Check for validity */
1044 if ((RemainingPart.Length && RemainingPart.Buffer[0] == L'\\') ||
1045 (NextName.Length > 255 * sizeof(WCHAR)))
1046 {
1047 /* The name is invalid */
1048 DPRINT1("Invalid name found\n");
1049 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
1050 ASSERT(FALSE);
1051 }
1052
1053 /* Convert the name to ANSI */
1054 AnsiFirstName.Buffer = ExAllocatePool(PagedPool, FirstName.Length);
1055 AnsiFirstName.Length = 0;
1056 AnsiFirstName.MaximumLength = FirstName.Length;
1057 Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName, &FirstName, FALSE);
1058
1059 if (!NT_SUCCESS(Status))
1060 {
1061 ASSERT(FALSE);
1062 }
1063
1064 DPRINT("FirstName %wZ, RemainingPart %wZ\n", &FirstName, &RemainingPart);
1065
1066 /* Break if came to the end */
1067 if (!RemainingPart.Length) break;
1068
1069 /* Create a DCB for this entry */
1070 ParentDcb = FatCreateDcb(IrpContext,
1071 Vcb,
1072 ParentDcb,
1073 NULL);
1074
1075 /* Set its name */
1076 FatSetFullNameInFcb(ParentDcb, &FirstName);
1077 }
1078
1079 /* Try to open it and get a result, saying if this is a dir or a file */
1080 FfError = FatiTryToOpen(FileObject, Vcb);
1081
1082 /* Check if we need to open target directory */
1083 if (OpenTargetDirectory)
1084 {
1085 // TODO: Open target directory
1086 UNIMPLEMENTED;
1087 }
1088
1089 /* Check, if path is a directory or a file */
1090 if (FfError == FF_ERR_FILE_OBJECT_IS_A_DIR)
1091 {
1092 if (NonDirectoryFile)
1093 {
1094 DPRINT1("Can't open dir as a file\n");
1095
1096 /* Unlock VCB */
1097 FatReleaseVcb(IrpContext, Vcb);
1098
1099 /* Complete the request */
1100 Iosb.Status = STATUS_FILE_IS_A_DIRECTORY;
1101 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
1102 return Iosb.Status;
1103 }
1104
1105 /* Open this directory */
1106 Iosb = FatiOpenExistingDir(IrpContext,
1107 FileObject,
1108 Vcb,
1109 ParentDcb,
1110 DesiredAccess,
1111 ShareAccess,
1112 AllocationSize,
1113 EaBuffer,
1114 EaLength,
1115 FileAttributes,
1116 CreateDisposition,
1117 DeleteOnClose);
1118
1119 Irp->IoStatus.Information = Iosb.Information;
1120
1121 /* Unlock VCB */
1122 FatReleaseVcb(IrpContext, Vcb);
1123
1124 /* Complete the request */
1125 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
1126
1127 return Iosb.Status;
1128 }
1129
1130 /* If end backslash here, then it's definately not permitted,
1131 since we're opening files here */
1132 if (EndBackslash)
1133 {
1134 /* Unlock VCB */
1135 FatReleaseVcb(IrpContext, Vcb);
1136
1137 /* Complete the request */
1138 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
1139 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
1140 return Iosb.Status;
1141 }
1142
1143 /* Try to open the file */
1144 Iosb = FatiOpenExistingFile(IrpContext,
1145 FileObject,
1146 Vcb,
1147 ParentDcb,
1148 DesiredAccess,
1149 ShareAccess,
1150 AllocationSize,
1151 EaBuffer,
1152 EaLength,
1153 FileAttributes,
1154 CreateDisposition,
1155 FALSE,
1156 DeleteOnClose,
1157 OpenedAsDos);
1158
1159 /* In case of success set cache supported flag */
1160 if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering)
1161 {
1162 SetFlag(FileObject->Flags, FO_CACHE_SUPPORTED);
1163 }
1164
1165 Irp->IoStatus.Information = Iosb.Information;
1166
1167 /* Unlock VCB */
1168 FatReleaseVcb(IrpContext, Vcb);
1169
1170 /* Complete the request */
1171 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
1172
1173 return Iosb.Status;
1174 }
1175
1176 NTSTATUS
1177 NTAPI
1178 FatCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
1179 {
1180 PFAT_IRP_CONTEXT IrpContext;
1181 NTSTATUS Status;
1182
1183 /* If it's called with our Disk FS device object - it's always open */
1184 // TODO: Add check for CDROM FS device object
1185 if (DeviceObject == FatGlobalData.DiskDeviceObject)
1186 {
1187 /* Complete the request and return success */
1188 Irp->IoStatus.Status = STATUS_SUCCESS;
1189 Irp->IoStatus.Information = FILE_OPENED;
1190
1191 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1192
1193 return STATUS_SUCCESS;
1194 }
1195
1196 /* Enter FsRtl critical region */
1197 FsRtlEnterFileSystem();
1198
1199 /* Build an irp context */
1200 IrpContext = FatBuildIrpContext(Irp, TRUE);
1201
1202 /* Call internal function */
1203 Status = FatiCreate(IrpContext, Irp);
1204
1205 /* Leave FsRtl critical region */
1206 FsRtlExitFileSystem();
1207
1208 return Status;
1209 }
1210
1211 /* EOF */