[FASTFAT_NEW]
[reactos.git] / reactos / 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, FirstRun = TRUE;
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 /* Reject open by id */
596 if (Options & FILE_OPEN_BY_FILE_ID)
597 {
598 FatCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
599 return STATUS_INVALID_PARAMETER;
600 }
601
602 /* Prepare file attributes mask */
603 FileAttributes &= (FILE_ATTRIBUTE_READONLY |
604 FILE_ATTRIBUTE_HIDDEN |
605 FILE_ATTRIBUTE_SYSTEM |
606 FILE_ATTRIBUTE_ARCHIVE);
607
608 /* Get the volume control object */
609 Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;
610
611 /* Get options */
612 DirectoryFile = BooleanFlagOn(Options, FILE_DIRECTORY_FILE);
613 NonDirectoryFile = BooleanFlagOn(Options, FILE_NON_DIRECTORY_FILE);
614 SequentialOnly = BooleanFlagOn(Options, FILE_SEQUENTIAL_ONLY);
615 NoIntermediateBuffering = BooleanFlagOn(Options, FILE_NO_INTERMEDIATE_BUFFERING);
616 NoEaKnowledge = BooleanFlagOn(Options, FILE_NO_EA_KNOWLEDGE);
617 DeleteOnClose = BooleanFlagOn(Options, FILE_DELETE_ON_CLOSE);
618 TemporaryFile = BooleanFlagOn(IrpSp->Parameters.Create.FileAttributes,
619 FILE_ATTRIBUTE_TEMPORARY );
620 IsPagingFile = BooleanFlagOn(IrpSp->Flags, SL_OPEN_PAGING_FILE);
621 OpenTargetDirectory = BooleanFlagOn(IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY);
622
623 /* Calculate create disposition */
624 CreateDisposition = (Options >> 24) & 0x000000ff;
625
626 /* Get Create/Open directory flags based on it */
627 CreateDirectory = (BOOLEAN)(DirectoryFile &&
628 ((CreateDisposition == FILE_CREATE) ||
629 (CreateDisposition == FILE_OPEN_IF)));
630
631 OpenDirectory = (BOOLEAN)(DirectoryFile &&
632 ((CreateDisposition == FILE_OPEN) ||
633 (CreateDisposition == FILE_OPEN_IF)));
634
635 /* Validate parameters: directory/nondirectory mismatch and
636 AllocationSize being more than 4GB */
637 if ((DirectoryFile && NonDirectoryFile) ||
638 Irp->Overlay.AllocationSize.HighPart != 0)
639 {
640 FatCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
641
642 DPRINT1("FatiCreate: STATUS_INVALID_PARAMETER\n", 0);
643 return STATUS_INVALID_PARAMETER;
644 }
645
646 /* Acquire the VCB lock exclusively */
647 if (!FatAcquireExclusiveVcb(IrpContext, Vcb))
648 {
649 // TODO: Postpone the IRP for later processing
650 ASSERT(FALSE);
651 return STATUS_NOT_IMPLEMENTED;
652 }
653
654 // TODO: Verify the VCB
655
656 /* If VCB is locked, then no file openings are possible */
657 if (Vcb->State & VCB_STATE_FLAG_LOCKED)
658 {
659 DPRINT1("This volume is locked\n");
660 Status = STATUS_ACCESS_DENIED;
661
662 /* Set volume dismount status */
663 if (Vcb->Condition != VcbGood)
664 Status = STATUS_VOLUME_DISMOUNTED;
665
666 /* Cleanup and return */
667 FatReleaseVcb(IrpContext, Vcb);
668 FatCompleteRequest(IrpContext, Irp, Status);
669 return Status;
670 }
671
672 /* Check if the volume is write protected and disallow DELETE_ON_CLOSE */
673 if (DeleteOnClose & FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED))
674 {
675 ASSERT(FALSE);
676 return STATUS_NOT_IMPLEMENTED;
677 }
678
679 // TODO: Make sure EAs aren't supported on FAT32
680
681 /* Check if it's a volume open request */
682 if (FileName.Length == 0)
683 {
684 /* Test related FO to be sure */
685 if (!RelatedFO ||
686 FatDecodeFileObject(RelatedFO, &DecodedVcb, &Fcb, &Ccb) == UserVolumeOpen)
687 {
688 /* Check parameters */
689 if (DirectoryFile || OpenTargetDirectory)
690 {
691 Status = DirectoryFile ? STATUS_NOT_A_DIRECTORY : STATUS_INVALID_PARAMETER;
692
693 /* Unlock VCB */
694 FatReleaseVcb(IrpContext, Vcb);
695
696 /* Complete the request and return */
697 FatCompleteRequest(IrpContext, Irp, Status);
698 return Status;
699 }
700
701 /* It is indeed a volume open request */
702 Iosb = FatiOpenVolume(IrpContext,
703 FileObject,
704 Vcb,
705 DesiredAccess,
706 ShareAccess,
707 CreateDisposition);
708
709 /* Set resulting information */
710 Irp->IoStatus.Information = Iosb.Information;
711
712 /* Unlock VCB */
713 FatReleaseVcb(IrpContext, Vcb);
714
715 /* Complete the request and return */
716 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
717 return Iosb.Status;
718 }
719 }
720
721 /* Check if this is a relative open */
722 if (RelatedFO)
723 {
724 /* Decode the file object */
725 TypeOfOpen = FatDecodeFileObject(RelatedFO,
726 &RelatedVcb,
727 &RelatedDcb,
728 &RelatedCcb);
729
730 /* Check open type */
731 if (TypeOfOpen != UserFileOpen &&
732 TypeOfOpen != UserDirectoryOpen)
733 {
734 DPRINT1("Invalid file object!\n");
735
736 Status = STATUS_OBJECT_PATH_NOT_FOUND;
737
738 /* Cleanup and return */
739 FatReleaseVcb(IrpContext, Vcb);
740 FatCompleteRequest(IrpContext, Irp, Status);
741 return Status;
742 }
743
744 /* File path must be relative */
745 if (FileName.Length != 0 &&
746 FileName.Buffer[0] == L'\\')
747 {
748 Status = STATUS_OBJECT_NAME_INVALID;
749
750 /* The name is absolute, fail */
751 FatReleaseVcb(IrpContext, Vcb);
752 FatCompleteRequest(IrpContext, Irp, Status);
753 return Status;
754 }
755
756 /* Make sure volume is the same */
757 ASSERT(RelatedVcb == Vcb);
758
759 /* Save VPB */
760 FileObject->Vpb = RelatedFO->Vpb;
761
762 /* Set parent DCB */
763 ParentDcb = RelatedDcb;
764
765 DPRINT("Opening file '%wZ' relatively to '%wZ'\n", &FileName, &ParentDcb->FullFileName);
766 }
767 else
768 {
769 /* Absolute open */
770 if ((FileName.Length == sizeof(WCHAR)) &&
771 (FileName.Buffer[0] == L'\\'))
772 {
773 /* Check if it's ok to open it */
774 if (NonDirectoryFile)
775 {
776 DPRINT1("Trying to open root dir as a file\n");
777 Status = STATUS_FILE_IS_A_DIRECTORY;
778
779 /* Cleanup and return */
780 FatReleaseVcb(IrpContext, Vcb);
781 FatCompleteRequest(IrpContext, Irp, Status);
782 return Status;
783 }
784
785 /* Check for target directory on a root dir */
786 if (OpenTargetDirectory)
787 {
788 Status = STATUS_INVALID_PARAMETER;
789
790 /* Cleanup and return */
791 FatReleaseVcb(IrpContext, Vcb);
792 FatCompleteRequest(IrpContext, Irp, Status);
793 return Status;
794 }
795
796 /* Check delete on close on a root dir */
797 if (DeleteOnClose)
798 {
799 Status = STATUS_CANNOT_DELETE;
800
801 /* Cleanup and return */
802 FatReleaseVcb(IrpContext, Vcb);
803 FatCompleteRequest(IrpContext, Irp, Status);
804 return Status;
805 }
806
807 /* Call root directory open routine */
808 Iosb = FatiOpenRootDcb(IrpContext,
809 FileObject,
810 Vcb,
811 DesiredAccess,
812 ShareAccess,
813 CreateDisposition);
814
815 Irp->IoStatus.Information = Iosb.Information;
816
817 /* Cleanup and return */
818 FatReleaseVcb(IrpContext, Vcb);
819 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
820 return Iosb.Status;
821 }
822 else
823 {
824 /* Not a root dir */
825 ParentDcb = Vcb->RootDcb;
826 DPRINT("ParentDcb %p\n", ParentDcb);
827 }
828 }
829
830 /* Check for backslash at the end */
831 if (FileName.Length &&
832 FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] == L'\\')
833 {
834 /* Cut it out */
835 FileName.Length -= sizeof(WCHAR);
836
837 /* Remember we cut it */
838 EndBackslash = TRUE;
839 }
840
841 /* Ensure the name is set */
842 if (!ParentDcb->FullFileName.Buffer)
843 {
844 /* Set it if it's missing */
845 FatSetFullFileNameInFcb(IrpContext, ParentDcb);
846 }
847
848 /* Check max path length */
849 if (ParentDcb->FullFileName.Length + FileName.Length + sizeof(WCHAR) <= FileName.Length)
850 {
851 DPRINT1("Max length is way off\n");
852 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
853 ASSERT(FALSE);
854 }
855
856 /* Loop through FCBs to find a good one */
857 while (TRUE)
858 {
859 Fcb = ParentDcb;
860
861 /* Dissect the name */
862 RemainingPart = FileName;
863 while (RemainingPart.Length)
864 {
865 FsRtlDissectName(RemainingPart, &FirstName, &NextName);
866
867 /* Check for validity */
868 if ((NextName.Length && NextName.Buffer[0] == L'\\') ||
869 (NextName.Length > 255 * sizeof(WCHAR)))
870 {
871 /* The name is invalid */
872 DPRINT1("Invalid name found\n");
873 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
874 ASSERT(FALSE);
875 }
876
877 /* Convert the name to ANSI */
878 AnsiFirstName.Buffer = ExAllocatePool(PagedPool, FirstName.Length);
879 AnsiFirstName.Length = 0;
880 AnsiFirstName.MaximumLength = FirstName.Length;
881 Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName, &FirstName, FALSE);
882
883 if (!NT_SUCCESS(Status))
884 {
885 DPRINT1("RtlUpcaseUnicodeStringToCountedOemString() failed with 0x%08x\n", Status);
886 ASSERT(FALSE);
887 NextFcb = NULL;
888 AnsiFirstName.Length = 0;
889 }
890 else
891 {
892 /* Find the coresponding FCB */
893 NextFcb = FatFindFcb(IrpContext,
894 &Fcb->Dcb.SplayLinksAnsi,
895 (PSTRING)&AnsiFirstName,
896 &OpenedAsDos);
897 }
898
899 /* If nothing found - try with unicode */
900 if (!NextFcb && Fcb->Dcb.SplayLinksUnicode)
901 {
902 FileNameUpcased.Buffer = FsRtlAllocatePool(PagedPool, FirstName.Length);
903 FileNameUpcased.Length = 0;
904 FileNameUpcased.MaximumLength = FirstName.Length;
905
906 /* Downcase and then upcase to normalize it */
907 Status = RtlDowncaseUnicodeString(&FileNameUpcased, &FirstName, FALSE);
908 Status = RtlUpcaseUnicodeString(&FileNameUpcased, &FileNameUpcased, FALSE);
909
910 /* Try to find FCB again using unicode name */
911 NextFcb = FatFindFcb(IrpContext,
912 &Fcb->Dcb.SplayLinksUnicode,
913 (PSTRING)&FileNameUpcased,
914 &OpenedAsDos);
915 }
916
917 /* Move to the next FCB */
918 if (NextFcb)
919 {
920 Fcb = NextFcb;
921 RemainingPart = NextName;
922 }
923
924 /* Break out of this loop if nothing can be found */
925 if (!NextFcb ||
926 NextName.Length == 0 ||
927 FatNodeType(NextFcb) == FAT_NTC_FCB)
928 {
929 break;
930 }
931 }
932
933 /* Ensure remaining name doesn't start from a backslash */
934 if (RemainingPart.Length &&
935 RemainingPart.Buffer[0] == L'\\')
936 {
937 /* Cut it */
938 RemainingPart.Buffer++;
939 RemainingPart.Length -= sizeof(WCHAR);
940 }
941
942 if (Fcb->Condition == FcbGood)
943 {
944 /* Good FCB, break out of the loop */
945 break;
946 }
947 else
948 {
949 ASSERT(FALSE);
950 }
951 }
952
953 /* Treat page file in a special way */
954 if (IsPagingFile)
955 {
956 UNIMPLEMENTED;
957 // FIXME: System file too
958 }
959
960 /* Make sure there is no pending delete on a higher-level FCB */
961 if (Fcb->State & FCB_STATE_DELETE_ON_CLOSE)
962 {
963 Iosb.Status = STATUS_DELETE_PENDING;
964
965 /* Cleanup and return */
966 FatReleaseVcb(IrpContext, Vcb);
967
968 /* Complete the request */
969 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
970
971 return Iosb.Status;
972 }
973
974 /* We have a valid FCB now */
975 if (!RemainingPart.Length)
976 {
977 /* Check for target dir open */
978 if (OpenTargetDirectory)
979 {
980 DPRINT1("Opening target dir is missing\n");
981 ASSERT(FALSE);
982 }
983
984 /* Check this FCB's type */
985 if (FatNodeType(Fcb) == FAT_NTC_ROOT_DCB ||
986 FatNodeType(Fcb) == FAT_NTC_DCB)
987 {
988 /* Open a directory */
989 if (NonDirectoryFile)
990 {
991 /* Forbidden */
992 Iosb.Status = STATUS_FILE_IS_A_DIRECTORY;
993 ASSERT(FALSE);
994 return Iosb.Status;
995 }
996
997 /* Open existing DCB */
998 Iosb = FatiOpenExistingDcb(IrpContext,
999 FileObject,
1000 Vcb,
1001 Fcb,
1002 DesiredAccess,
1003 ShareAccess,
1004 CreateDisposition,
1005 NoEaKnowledge,
1006 DeleteOnClose);
1007
1008 /* Save information */
1009 Irp->IoStatus.Information = Iosb.Information;
1010
1011 /* Unlock VCB */
1012 FatReleaseVcb(IrpContext, Vcb);
1013
1014 /* Complete the request */
1015 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
1016
1017 return Iosb.Status;
1018 }
1019 else if (FatNodeType(Fcb) == FAT_NTC_FCB)
1020 {
1021 /* Open a file */
1022 if (OpenDirectory)
1023 {
1024 /* Forbidden */
1025 Iosb.Status = STATUS_NOT_A_DIRECTORY;
1026 ASSERT(FALSE);
1027 return Iosb.Status;
1028 }
1029
1030 /* Check for trailing backslash */
1031 if (EndBackslash)
1032 {
1033 /* Forbidden */
1034 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
1035 ASSERT(FALSE);
1036 return Iosb.Status;
1037 }
1038
1039 Iosb = FatiOpenExistingFcb(IrpContext,
1040 FileObject,
1041 Vcb,
1042 Fcb,
1043 DesiredAccess,
1044 ShareAccess,
1045 AllocationSize,
1046 EaBuffer,
1047 EaLength,
1048 FileAttributes,
1049 CreateDisposition,
1050 NoEaKnowledge,
1051 DeleteOnClose,
1052 OpenedAsDos,
1053 &OplockPostIrp);
1054
1055 /* Check if it's pending */
1056 if (Iosb.Status != STATUS_PENDING)
1057 {
1058 /* In case of success set cache supported flag */
1059 if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering)
1060 {
1061 SetFlag(FileObject->Flags, FO_CACHE_SUPPORTED);
1062 }
1063
1064 /* Save information */
1065 Irp->IoStatus.Information = Iosb.Information;
1066
1067 /* Unlock VCB */
1068 FatReleaseVcb(IrpContext, Vcb);
1069
1070 /* Complete the request */
1071 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
1072
1073 return Iosb.Status;
1074 }
1075 else
1076 {
1077 /* Queue this IRP */
1078 UNIMPLEMENTED;
1079 ASSERT(FALSE);
1080 }
1081 }
1082 else
1083 {
1084 /* Unexpected FCB type */
1085 KeBugCheckEx(FAT_FILE_SYSTEM, __LINE__, (ULONG_PTR)Fcb, 0, 0);
1086 }
1087 }
1088
1089 /* During parsing we encountered a part which has no attached FCB/DCB.
1090 Check that the parent is really DCB and not FCB */
1091 if (FatNodeType(Fcb) != FAT_NTC_ROOT_DCB &&
1092 FatNodeType(Fcb) != FAT_NTC_DCB)
1093 {
1094 DPRINT1("Weird FCB node type %x, expected DCB or root DCB\n", FatNodeType(Fcb));
1095 ASSERT(FALSE);
1096 }
1097
1098 /* Create additional DCBs for all path items */
1099 ParentDcb = Fcb;
1100 while (TRUE)
1101 {
1102 if (FirstRun)
1103 {
1104 RemainingPart = NextName;
1105 if (AnsiFirstName.Length)
1106 Status = STATUS_SUCCESS;
1107 else
1108 Status = STATUS_UNMAPPABLE_CHARACTER;
1109
1110 /* First run init is done */
1111 FirstRun = FALSE;
1112 }
1113 else
1114 {
1115 FsRtlDissectName(RemainingPart, &FirstName, &RemainingPart);
1116
1117 /* Check for validity */
1118 if ((RemainingPart.Length && RemainingPart.Buffer[0] == L'\\') ||
1119 (NextName.Length > 255 * sizeof(WCHAR)))
1120 {
1121 /* The name is invalid */
1122 DPRINT1("Invalid name found\n");
1123 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
1124 ASSERT(FALSE);
1125 }
1126
1127 /* Convert the name to ANSI */
1128 AnsiFirstName.Buffer = ExAllocatePool(PagedPool, FirstName.Length);
1129 AnsiFirstName.Length = 0;
1130 AnsiFirstName.MaximumLength = FirstName.Length;
1131 Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName, &FirstName, FALSE);
1132 }
1133
1134 if (!NT_SUCCESS(Status))
1135 {
1136 ASSERT(FALSE);
1137 }
1138
1139 DPRINT("FirstName %wZ, RemainingPart %wZ\n", &FirstName, &RemainingPart);
1140
1141 /* Break if came to the end */
1142 if (!RemainingPart.Length) break;
1143
1144 /* Create a DCB for this entry */
1145 ParentDcb = FatCreateDcb(IrpContext,
1146 Vcb,
1147 ParentDcb,
1148 NULL);
1149
1150 /* Set its name */
1151 FatSetFullNameInFcb(ParentDcb, &FirstName);
1152 }
1153
1154 /* Try to open it and get a result, saying if this is a dir or a file */
1155 FfError = FatiTryToOpen(FileObject, Vcb);
1156
1157 /* Check if we need to open target directory */
1158 if (OpenTargetDirectory)
1159 {
1160 // TODO: Open target directory
1161 UNIMPLEMENTED;
1162 }
1163
1164 /* Check, if path is a existing directory or file */
1165 if (FfError == FF_ERR_FILE_OBJECT_IS_A_DIR ||
1166 FfError == FF_ERR_FILE_ALREADY_OPEN ||
1167 FfError == FF_ERR_NONE)
1168 {
1169 if (FfError == FF_ERR_FILE_OBJECT_IS_A_DIR)
1170 {
1171 if (NonDirectoryFile)
1172 {
1173 DPRINT1("Can't open dir as a file\n");
1174
1175 /* Unlock VCB */
1176 FatReleaseVcb(IrpContext, Vcb);
1177
1178 /* Complete the request */
1179 Iosb.Status = STATUS_FILE_IS_A_DIRECTORY;
1180 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
1181 return Iosb.Status;
1182 }
1183
1184 /* Open this directory */
1185 Iosb = FatiOpenExistingDir(IrpContext,
1186 FileObject,
1187 Vcb,
1188 ParentDcb,
1189 DesiredAccess,
1190 ShareAccess,
1191 AllocationSize,
1192 EaBuffer,
1193 EaLength,
1194 FileAttributes,
1195 CreateDisposition,
1196 DeleteOnClose);
1197
1198 Irp->IoStatus.Information = Iosb.Information;
1199
1200 /* Unlock VCB */
1201 FatReleaseVcb(IrpContext, Vcb);
1202
1203 /* Complete the request */
1204 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
1205
1206 return Iosb.Status;
1207 }
1208 else
1209 {
1210 /* This is opening an existing file */
1211 if (OpenDirectory)
1212 {
1213 /* But caller wanted a dir */
1214 Status = STATUS_NOT_A_DIRECTORY;
1215
1216 /* Unlock VCB */
1217 FatReleaseVcb(IrpContext, Vcb);
1218
1219 /* Complete the request */
1220 FatCompleteRequest(IrpContext, Irp, Status);
1221
1222 return Status;
1223 }
1224
1225 /* If end backslash here, then it's definately not permitted,
1226 since we're opening files here */
1227 if (EndBackslash)
1228 {
1229 /* Unlock VCB */
1230 FatReleaseVcb(IrpContext, Vcb);
1231
1232 /* Complete the request */
1233 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
1234 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
1235 return Iosb.Status;
1236 }
1237
1238 /* Try to open the file */
1239 Iosb = FatiOpenExistingFile(IrpContext,
1240 FileObject,
1241 Vcb,
1242 ParentDcb,
1243 DesiredAccess,
1244 ShareAccess,
1245 AllocationSize,
1246 EaBuffer,
1247 EaLength,
1248 FileAttributes,
1249 CreateDisposition,
1250 FALSE,
1251 DeleteOnClose,
1252 OpenedAsDos);
1253
1254 /* In case of success set cache supported flag */
1255 if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering)
1256 {
1257 SetFlag(FileObject->Flags, FO_CACHE_SUPPORTED);
1258 }
1259
1260 Irp->IoStatus.Information = Iosb.Information;
1261
1262 /* Unlock VCB */
1263 FatReleaseVcb(IrpContext, Vcb);
1264
1265 /* Complete the request */
1266 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
1267
1268 return Iosb.Status;
1269 }
1270 }
1271
1272 /* We come here only in the case when a new file is created */
1273 //ASSERT(FALSE);
1274 DPRINT1("TODO: Create a new file/directory, called '%wZ'\n", &IrpSp->FileObject->FileName);
1275
1276 Status = STATUS_NOT_IMPLEMENTED;
1277
1278 /* Unlock VCB */
1279 FatReleaseVcb(IrpContext, Vcb);
1280
1281 /* Complete the request */
1282 FatCompleteRequest(IrpContext, Irp, Status);
1283
1284 return Status;
1285 }
1286
1287 NTSTATUS
1288 NTAPI
1289 FatCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
1290 {
1291 PFAT_IRP_CONTEXT IrpContext;
1292 NTSTATUS Status;
1293
1294 /* If it's called with our Disk FS device object - it's always open */
1295 // TODO: Add check for CDROM FS device object
1296 if (DeviceObject == FatGlobalData.DiskDeviceObject)
1297 {
1298 /* Complete the request and return success */
1299 Irp->IoStatus.Status = STATUS_SUCCESS;
1300 Irp->IoStatus.Information = FILE_OPENED;
1301
1302 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1303
1304 return STATUS_SUCCESS;
1305 }
1306
1307 /* Enter FsRtl critical region */
1308 FsRtlEnterFileSystem();
1309
1310 /* Build an irp context */
1311 IrpContext = FatBuildIrpContext(Irp, TRUE);
1312
1313 /* Call internal function */
1314 Status = FatiCreate(IrpContext, Irp);
1315
1316 /* Leave FsRtl critical region */
1317 FsRtlExitFileSystem();
1318
1319 return Status;
1320 }
1321
1322 /* EOF */