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