2 * PROJECT: ReactOS FAT file system driver
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/filesystems/fastfat/create.c
5 * PURPOSE: Create routines
6 * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org)
9 /* INCLUDES *****************************************************************/
17 RtlUpcaseUnicodeStringToCountedOemString(
18 IN OUT POEM_STRING DestinationString
,
19 IN PCUNICODE_STRING SourceString
,
20 IN BOOLEAN AllocateDestinationString
24 /* FUNCTIONS *****************************************************************/
28 FatiOpenRootDcb(IN PFAT_IRP_CONTEXT IrpContext
,
29 IN PFILE_OBJECT FileObject
,
31 IN PACCESS_MASK DesiredAccess
,
32 IN USHORT ShareAccess
,
33 IN ULONG CreateDisposition
)
37 DPRINT1("Opening root directory\n");
39 Iosb
.Status
= STATUS_NOT_IMPLEMENTED
;
46 FatiOpenExistingFile(IN PFAT_IRP_CONTEXT IrpContext
,
47 IN PFILE_OBJECT FileObject
,
50 IN PACCESS_MASK DesiredAccess
,
51 IN USHORT ShareAccess
,
52 IN ULONG AllocationSize
,
53 IN PFILE_FULL_EA_INFORMATION EaBuffer
,
55 IN UCHAR FileAttributes
,
56 IN ULONG CreateDisposition
,
57 IN BOOLEAN IsPagingFile
,
58 IN BOOLEAN DeleteOnClose
,
61 IO_STATUS_BLOCK Iosb
= {{0}};
63 CHAR AnsiNameBuf
[512];
68 /* Check for create file option and fail */
69 if (CreateDisposition
== FILE_CREATE
)
71 Iosb
.Status
= STATUS_OBJECT_NAME_COLLISION
;
75 // TODO: Check more params
77 /* Convert the name to ANSI */
78 AnsiName
.Buffer
= AnsiNameBuf
;
80 AnsiName
.MaximumLength
= sizeof(AnsiNameBuf
);
81 RtlZeroMemory(AnsiNameBuf
, sizeof(AnsiNameBuf
));
82 Status
= RtlUpcaseUnicodeStringToCountedOemString(&AnsiName
, &FileObject
->FileName
, FALSE
);
83 if (!NT_SUCCESS(Status
))
88 /* Open the file with FullFAT */
89 FileHandle
= FF_Open(Vcb
->Ioman
, AnsiName
.Buffer
, FF_MODE_READ
, NULL
);
93 Iosb
.Status
= STATUS_OBJECT_NAME_NOT_FOUND
; // FIXME: A shortcut for now
97 /* Create a new FCB for this file */
98 Fcb
= FatCreateFcb(IrpContext
, Vcb
, ParentDcb
, FileHandle
);
100 // TODO: Check if overwrite is needed
102 // This is usual file open branch, without overwriting!
103 /* Set context and section object pointers */
104 FatSetFileObject(FileObject
,
108 FileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
110 Iosb
.Status
= STATUS_SUCCESS
;
111 Iosb
.Information
= FILE_OPENED
;
118 FatiOpenVolume(IN PFAT_IRP_CONTEXT IrpContext
,
119 IN PFILE_OBJECT FileObject
,
121 IN PACCESS_MASK DesiredAccess
,
122 IN USHORT ShareAccess
,
123 IN ULONG CreateDisposition
)
126 IO_STATUS_BLOCK Iosb
= {{0}};
127 BOOLEAN VolumeFlushed
= FALSE
;
129 /* Check parameters */
130 if (CreateDisposition
!= FILE_OPEN
&&
131 CreateDisposition
!= FILE_OPEN_IF
)
134 Iosb
.Status
= STATUS_ACCESS_DENIED
;
137 /* Check if it's exclusive open */
138 if (!FlagOn(ShareAccess
, FILE_SHARE_WRITE
) &&
139 !FlagOn(ShareAccess
, FILE_SHARE_DELETE
))
141 // TODO: Check if exclusive read access requested
142 // and opened handles count is not 0
143 //if (!FlagOn(ShareAccess, FILE_SHARE_READ)
145 DPRINT1("Exclusive voume open\n");
147 // TODO: Flush the volume
148 VolumeFlushed
= TRUE
;
150 else if (FlagOn(*DesiredAccess
, FILE_READ_DATA
| FILE_WRITE_DATA
| FILE_APPEND_DATA
))
152 DPRINT1("Shared open\n");
154 // TODO: Flush the volume
155 VolumeFlushed
= TRUE
;
159 !FlagOn(Vcb
->State
, VCB_STATE_MOUNTED_DIRTY
) &&
160 FlagOn(Vcb
->State
, VCB_STATE_FLAG_DIRTY
) &&
161 CcIsThereDirtyData(Vcb
->Vpb
))
166 /* Set share access */
167 if (Vcb
->DirectOpenCount
> 0)
169 /* This volume has already been opened */
170 Iosb
.Status
= IoCheckShareAccess(*DesiredAccess
,
176 if (!NT_SUCCESS(Iosb
.Status
))
183 /* This is the first time open */
184 IoSetShareAccess(*DesiredAccess
,
190 /* Set file object pointers */
191 Ccb
= FatCreateCcb(IrpContext
);
192 FatSetFileObject(FileObject
, UserVolumeOpen
, Vcb
, Ccb
);
193 FileObject
->SectionObjectPointer
= &Vcb
->SectionObjectPointers
;
195 /* Increase direct open count */
196 Vcb
->DirectOpenCount
++;
197 Vcb
->OpenFileCount
++;
199 /* Set no buffering flag */
200 FileObject
->Flags
|= FO_NO_INTERMEDIATE_BUFFERING
;
202 // TODO: User's access check
204 Iosb
.Status
= STATUS_SUCCESS
;
205 Iosb
.Information
= FILE_OPENED
;
212 FatiCreate(IN PFAT_IRP_CONTEXT IrpContext
,
215 /* Boolean options */
216 BOOLEAN CreateDirectory
;
217 BOOLEAN SequentialOnly
;
218 BOOLEAN NoIntermediateBuffering
;
219 BOOLEAN OpenDirectory
;
220 BOOLEAN IsPagingFile
;
221 BOOLEAN OpenTargetDirectory
;
222 BOOLEAN DirectoryFile
;
223 BOOLEAN NonDirectoryFile
;
224 BOOLEAN NoEaKnowledge
;
225 BOOLEAN DeleteOnClose
;
226 BOOLEAN TemporaryFile
;
227 ULONG CreateDisposition
;
230 PVCB Vcb
, DecodedVcb
;
236 PFILE_OBJECT FileObject
;
237 PFILE_OBJECT RelatedFO
;
238 UNICODE_STRING FileName
;
239 ULONG AllocationSize
;
240 PFILE_FULL_EA_INFORMATION EaBuffer
;
241 PACCESS_MASK DesiredAccess
;
243 UCHAR FileAttributes
;
249 IO_STATUS_BLOCK Iosb
;
250 PIO_STACK_LOCATION IrpSp
;
251 BOOLEAN EndBackslash
= FALSE
, OpenedAsDos
;
252 UNICODE_STRING RemainingPart
, FirstName
, NextName
;
253 OEM_STRING AnsiFirstName
;
255 Iosb
.Status
= STATUS_SUCCESS
;
257 /* Get current IRP stack location */
258 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
260 DPRINT("FatCommonCreate\n", 0 );
261 DPRINT("Irp = %08lx\n", Irp
);
262 DPRINT("\t->Flags = %08lx\n", Irp
->Flags
);
263 DPRINT("\t->FileObject = %08lx\n", IrpSp
->FileObject
);
264 DPRINT("\t->RelatedFileObject = %08lx\n", IrpSp
->FileObject
->RelatedFileObject
);
265 DPRINT("\t->FileName = %wZ\n", &IrpSp
->FileObject
->FileName
);
266 DPRINT("\t->AllocationSize.LowPart = %08lx\n", Irp
->Overlay
.AllocationSize
.LowPart
);
267 DPRINT("\t->AllocationSize.HighPart = %08lx\n", Irp
->Overlay
.AllocationSize
.HighPart
);
268 DPRINT("\t->SystemBuffer = %08lx\n", Irp
->AssociatedIrp
.SystemBuffer
);
269 DPRINT("\t->DesiredAccess = %08lx\n", IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
);
270 DPRINT("\t->Options = %08lx\n", IrpSp
->Parameters
.Create
.Options
);
271 DPRINT("\t->FileAttributes = %04x\n", IrpSp
->Parameters
.Create
.FileAttributes
);
272 DPRINT("\t->ShareAccess = %04x\n", IrpSp
->Parameters
.Create
.ShareAccess
);
273 DPRINT("\t->EaLength = %08lx\n", IrpSp
->Parameters
.Create
.EaLength
);
275 /* Apply a special hack for Win32, idea taken from FASTFAT reference driver from WDK */
276 if ((IrpSp
->FileObject
->FileName
.Length
> sizeof(WCHAR
)) &&
277 (IrpSp
->FileObject
->FileName
.Buffer
[1] == L
'\\') &&
278 (IrpSp
->FileObject
->FileName
.Buffer
[0] == L
'\\'))
280 /* Remove a leading slash */
281 IrpSp
->FileObject
->FileName
.Length
-= sizeof(WCHAR
);
282 RtlMoveMemory(&IrpSp
->FileObject
->FileName
.Buffer
[0],
283 &IrpSp
->FileObject
->FileName
.Buffer
[1],
284 IrpSp
->FileObject
->FileName
.Length
);
286 /* Check again: if there are still two leading slashes,
287 exit with an error */
288 if ((IrpSp
->FileObject
->FileName
.Length
> sizeof(WCHAR
)) &&
289 (IrpSp
->FileObject
->FileName
.Buffer
[1] == L
'\\') &&
290 (IrpSp
->FileObject
->FileName
.Buffer
[0] == L
'\\'))
292 FatCompleteRequest( IrpContext
, Irp
, STATUS_OBJECT_NAME_INVALID
);
294 DPRINT1("FatiCreate: STATUS_OBJECT_NAME_INVALID\n");
295 return STATUS_OBJECT_NAME_INVALID
;
299 /* Make sure we have SecurityContext */
300 ASSERT(IrpSp
->Parameters
.Create
.SecurityContext
!= NULL
);
302 /* Get necessary data out of IRP */
303 FileObject
= IrpSp
->FileObject
;
304 FileName
= FileObject
->FileName
;
305 RelatedFO
= FileObject
->RelatedFileObject
;
306 AllocationSize
= Irp
->Overlay
.AllocationSize
.LowPart
;
307 EaBuffer
= Irp
->AssociatedIrp
.SystemBuffer
;
308 DesiredAccess
= &IrpSp
->Parameters
.Create
.SecurityContext
->DesiredAccess
;
309 Options
= IrpSp
->Parameters
.Create
.Options
;
310 FileAttributes
= (UCHAR
)(IrpSp
->Parameters
.Create
.FileAttributes
& ~FILE_ATTRIBUTE_NORMAL
);
311 ShareAccess
= IrpSp
->Parameters
.Create
.ShareAccess
;
312 EaLength
= IrpSp
->Parameters
.Create
.EaLength
;
314 /* Set VPB to related object's VPB if it exists */
316 FileObject
->Vpb
= RelatedFO
->Vpb
;
318 /* Prepare file attributes mask */
319 FileAttributes
&= (FILE_ATTRIBUTE_READONLY
|
320 FILE_ATTRIBUTE_HIDDEN
|
321 FILE_ATTRIBUTE_SYSTEM
|
322 FILE_ATTRIBUTE_ARCHIVE
);
324 /* Get the volume control object */
325 Vcb
= &((PVOLUME_DEVICE_OBJECT
)IrpSp
->DeviceObject
)->Vcb
;
328 DirectoryFile
= BooleanFlagOn(Options
, FILE_DIRECTORY_FILE
);
329 NonDirectoryFile
= BooleanFlagOn(Options
, FILE_NON_DIRECTORY_FILE
);
330 SequentialOnly
= BooleanFlagOn(Options
, FILE_SEQUENTIAL_ONLY
);
331 NoIntermediateBuffering
= BooleanFlagOn(Options
, FILE_NO_INTERMEDIATE_BUFFERING
);
332 NoEaKnowledge
= BooleanFlagOn(Options
, FILE_NO_EA_KNOWLEDGE
);
333 DeleteOnClose
= BooleanFlagOn(Options
, FILE_DELETE_ON_CLOSE
);
334 TemporaryFile
= BooleanFlagOn(IrpSp
->Parameters
.Create
.FileAttributes
,
335 FILE_ATTRIBUTE_TEMPORARY
);
336 IsPagingFile
= BooleanFlagOn(IrpSp
->Flags
, SL_OPEN_PAGING_FILE
);
337 OpenTargetDirectory
= BooleanFlagOn(IrpSp
->Flags
, SL_OPEN_TARGET_DIRECTORY
);
339 /* Calculate create disposition */
340 CreateDisposition
= (Options
>> 24) & 0x000000ff;
342 /* Get Create/Open directory flags based on it */
343 CreateDirectory
= (BOOLEAN
)(DirectoryFile
&&
344 ((CreateDisposition
== FILE_CREATE
) ||
345 (CreateDisposition
== FILE_OPEN_IF
)));
347 OpenDirectory
= (BOOLEAN
)(DirectoryFile
&&
348 ((CreateDisposition
== FILE_OPEN
) ||
349 (CreateDisposition
== FILE_OPEN_IF
)));
351 /* Validate parameters: directory/nondirectory mismatch and
352 AllocationSize being more than 4GB */
353 if ((DirectoryFile
&& NonDirectoryFile
) ||
354 Irp
->Overlay
.AllocationSize
.HighPart
!= 0)
356 FatCompleteRequest(IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
358 DPRINT1("FatiCreate: STATUS_INVALID_PARAMETER\n", 0);
359 return STATUS_INVALID_PARAMETER
;
362 /* Acquire the VCB lock exclusively */
363 if (!FatAcquireExclusiveVcb(IrpContext
, Vcb
))
365 // TODO: Postpone the IRP for later processing
367 return STATUS_NOT_IMPLEMENTED
;
370 // TODO: Verify the VCB
372 /* If VCB is locked, then no file openings are possible */
373 if (Vcb
->State
& VCB_STATE_FLAG_LOCKED
)
375 DPRINT1("This volume is locked\n");
376 Status
= STATUS_ACCESS_DENIED
;
378 /* Cleanup and return */
379 FatReleaseVcb(IrpContext
, Vcb
);
383 // TODO: Check if the volume is write protected and disallow DELETE_ON_CLOSE
385 // TODO: Make sure EAs aren't supported on FAT32
387 /* Check if it's a volume open request */
388 if (FileName
.Length
== 0)
390 /* Test related FO to be sure */
392 FatDecodeFileObject(RelatedFO
, &DecodedVcb
, &Fcb
, &Ccb
) == UserVolumeOpen
)
394 /* Check parameters */
395 if (DirectoryFile
|| OpenTargetDirectory
)
397 Status
= DirectoryFile
? STATUS_NOT_A_DIRECTORY
: STATUS_INVALID_PARAMETER
;
400 FatReleaseVcb(IrpContext
, Vcb
);
402 /* Complete the request and return */
403 FatCompleteRequest(IrpContext
, Irp
, Status
);
407 /* It is indeed a volume open request */
408 Iosb
= FatiOpenVolume(IrpContext
,
415 /* Set resulting information */
416 Irp
->IoStatus
.Information
= Iosb
.Information
;
419 FatReleaseVcb(IrpContext
, Vcb
);
421 /* Complete the request and return */
422 FatCompleteRequest(IrpContext
, Irp
, Iosb
.Status
);
427 /* Check if this is a relative open */
430 // RelatedFO will be a parent directory
436 if ((FileName
.Length
== sizeof(WCHAR
)) &&
437 (FileName
.Buffer
[0] == L
'\\'))
439 /* Check if it's ok to open it */
440 if (NonDirectoryFile
)
442 DPRINT1("Trying to open root dir as a file\n");
444 /* Cleanup and return */
445 FatReleaseVcb(IrpContext
, Vcb
);
446 return STATUS_FILE_IS_A_DIRECTORY
;
449 /* Check delete on close on a root dir */
452 /* Cleanup and return */
453 FatReleaseVcb(IrpContext
, Vcb
);
454 return STATUS_CANNOT_DELETE
;
457 /* Call root directory open routine */
458 Iosb
= FatiOpenRootDcb(IrpContext
,
465 Irp
->IoStatus
.Information
= Iosb
.Information
;
467 /* Cleanup and return */
468 FatReleaseVcb(IrpContext
, Vcb
);
474 ParentDcb
= Vcb
->RootDcb
;
475 DPRINT("ParentDcb %p\n", ParentDcb
);
478 /* Check for backslash at the end */
479 if (FileName
.Length
&&
480 FileName
.Buffer
[FileName
.Length
/ sizeof(WCHAR
) - 1] == L
'\\')
483 FileName
.Length
-= sizeof(WCHAR
);
485 /* Remember we cut it */
489 /* Ensure the name is set */
490 if (!ParentDcb
->FullFileName
.Buffer
)
492 /* Set it if it's missing */
493 FatSetFullFileNameInFcb(IrpContext
, ParentDcb
);
496 /* Check max path length */
497 if (ParentDcb
->FullFileName
.Length
+ FileName
.Length
+ sizeof(WCHAR
) <= FileName
.Length
)
499 DPRINT1("Max length is way off\n");
500 Iosb
.Status
= STATUS_OBJECT_NAME_INVALID
;
504 /* Loop through FCBs to find a good one */
509 /* Dissect the name */
510 RemainingPart
= FileName
;
511 while (RemainingPart
.Length
)
513 FsRtlDissectName(RemainingPart
, &FirstName
, &NextName
);
515 /* Check for validity */
516 if ((NextName
.Length
&& NextName
.Buffer
[0] == L
'\\') ||
517 (NextName
.Length
> 255 * sizeof(WCHAR
)))
519 /* The name is invalid */
520 DPRINT1("Invalid name found\n");
521 Iosb
.Status
= STATUS_OBJECT_NAME_INVALID
;
525 /* Convert the name to ANSI */
526 AnsiFirstName
.Buffer
= ExAllocatePool(PagedPool
, FirstName
.Length
);
527 AnsiFirstName
.Length
= 0;
528 AnsiFirstName
.MaximumLength
= FirstName
.Length
;
529 Status
= RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName
, &FirstName
, FALSE
);
531 if (!NT_SUCCESS(Status
))
533 DPRINT1("RtlUpcaseUnicodeStringToCountedOemString() failed with 0x%08x\n", Status
);
536 AnsiFirstName
.Length
= 0;
540 /* Find the coresponding FCB */
541 NextFcb
= FatFindFcb(IrpContext
,
542 &Fcb
->Dcb
.SplayLinksAnsi
,
543 (PSTRING
)&AnsiFirstName
,
547 /* Check if we found anything */
548 if (!NextFcb
&& Fcb
->Dcb
.SplayLinksUnicode
)
553 /* Move to the next FCB */
557 RemainingPart
= NextName
;
560 /* Break out of this loop if nothing can be found */
562 NextName
.Length
== 0 ||
563 FatNodeType(NextFcb
) == FAT_NTC_FCB
)
569 /* Ensure remaining name doesn't start from a backslash */
570 if (RemainingPart
.Length
&&
571 RemainingPart
.Buffer
[0] == L
'\\')
574 RemainingPart
.Buffer
++;
575 RemainingPart
.Length
-= sizeof(WCHAR
);
578 if (Fcb
->Condition
== FcbGood
)
580 /* Good FCB, break out of the loop */
589 /* We have a valid FCB now */
590 if (!RemainingPart
.Length
)
592 DPRINT1("It's possible to open an existing FCB\n");
596 /* During parsing we encountered a part which has no attached FCB/DCB.
597 Check that the parent is really DCB and not FCB */
598 if (FatNodeType(Fcb
) != FAT_NTC_ROOT_DCB
&&
599 FatNodeType(Fcb
) != FAT_NTC_DCB
)
601 DPRINT1("Weird FCB node type %x, expected DCB or root DCB\n", FatNodeType(Fcb
));
605 /* Create additional DCBs for all path items */
609 FsRtlDissectName(RemainingPart
, &FirstName
, &RemainingPart
);
611 /* Check for validity */
612 if ((RemainingPart
.Length
&& RemainingPart
.Buffer
[0] == L
'\\') ||
613 (NextName
.Length
> 255 * sizeof(WCHAR
)))
615 /* The name is invalid */
616 DPRINT1("Invalid name found\n");
617 Iosb
.Status
= STATUS_OBJECT_NAME_INVALID
;
621 /* Convert the name to ANSI */
622 AnsiFirstName
.Buffer
= ExAllocatePool(PagedPool
, FirstName
.Length
);
623 AnsiFirstName
.Length
= 0;
624 AnsiFirstName
.MaximumLength
= FirstName
.Length
;
625 Status
= RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName
, &FirstName
, FALSE
);
627 if (!NT_SUCCESS(Status
))
632 DPRINT("FirstName %wZ, RemainingPart %wZ\n", &FirstName
, &RemainingPart
);
634 /* Break if came to the end */
635 if (!RemainingPart
.Length
) break;
637 /* Create a DCB for this entry */
638 ParentDcb
= FatCreateDcb(IrpContext
,
643 FatSetFullNameInFcb(ParentDcb
, &FirstName
);
646 // TODO: Try to open directory
648 /* If end backslash here, then it's definately not permitted,
649 since we're opening files here */
653 FatReleaseVcb(IrpContext
, Vcb
);
655 /* Complete the request */
656 Iosb
.Status
= STATUS_OBJECT_NAME_INVALID
;
657 FatCompleteRequest(IrpContext
, Irp
, Iosb
.Status
);
661 /* Try to open the file */
662 Iosb
= FatiOpenExistingFile(IrpContext
,
677 Irp
->IoStatus
.Information
= Iosb
.Information
;
681 FatReleaseVcb(IrpContext
, Vcb
);
683 /* Complete the request */
684 FatCompleteRequest(IrpContext
, Irp
, Iosb
.Status
);
691 FatCreate(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
693 PFAT_IRP_CONTEXT IrpContext
;
696 /* If it's called with our Disk FS device object - it's always open */
697 // TODO: Add check for CDROM FS device object
698 if (DeviceObject
== FatGlobalData
.DiskDeviceObject
)
700 /* Complete the request and return success */
701 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
702 Irp
->IoStatus
.Information
= FILE_OPENED
;
704 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
706 return STATUS_SUCCESS
;
709 /* Enter FsRtl critical region */
710 FsRtlEnterFileSystem();
712 /* Build an irp context */
713 IrpContext
= FatBuildIrpContext(Irp
, TRUE
);
715 /* Call internal function */
716 Status
= FatiCreate(IrpContext
, Irp
);
718 /* Leave FsRtl critical region */
719 FsRtlExitFileSystem();