3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* $Id: create.c,v 1.63 2003/07/24 20:52:58 chorns Exp $
21 * PROJECT: ReactOS kernel
22 * FILE: services/fs/vfat/create.c
23 * PURPOSE: VFAT Filesystem
24 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
28 /* INCLUDES *****************************************************************/
30 #include <ddk/ntddk.h>
39 /* GLOBALS *******************************************************************/
41 #define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof (FATDirEntry))
43 /* FUNCTIONS *****************************************************************/
45 void vfat8Dot3ToString (PFAT_DIR_ENTRY pEntry
, PWSTR pName
)
47 int fromIndex
, toIndex
;
49 fromIndex
= toIndex
= 0;
50 while (fromIndex
< 8 && pEntry
->Filename
[fromIndex
] != ' ')
52 if (pEntry
->lCase
& VFAT_CASE_LOWER_BASE
)
54 pName
[toIndex
++] = tolower(pEntry
->Filename
[fromIndex
++]);
58 pName
[toIndex
++] = pEntry
->Filename
[fromIndex
++];
61 if (pEntry
->Ext
[0] != ' ')
63 pName
[toIndex
++] = L
'.';
65 while (fromIndex
< 3 && pEntry
->Ext
[fromIndex
] != ' ')
67 if (pEntry
->lCase
& VFAT_CASE_LOWER_EXT
)
69 pName
[toIndex
++] = tolower(pEntry
->Ext
[fromIndex
++]);
73 pName
[toIndex
++] = pEntry
->Ext
[fromIndex
++];
77 pName
[toIndex
] = L
'\0';
80 static void vfat8Dot3ToVolumeLabel (PFAT_DIR_ENTRY pEntry
, PWSTR pName
)
82 int fromIndex
, toIndex
;
84 fromIndex
= toIndex
= 0;
85 while (fromIndex
< 8 && pEntry
->Filename
[fromIndex
] != ' ')
87 if (pEntry
->lCase
& VFAT_CASE_LOWER_BASE
)
89 pName
[toIndex
++] = tolower(pEntry
->Filename
[fromIndex
++]);
93 pName
[toIndex
++] = pEntry
->Filename
[fromIndex
++];
96 if (pEntry
->Ext
[0] != ' ')
99 while (fromIndex
< 3 && pEntry
->Ext
[fromIndex
] != ' ')
101 if (pEntry
->lCase
& VFAT_CASE_LOWER_EXT
)
103 pName
[toIndex
++] = tolower(pEntry
->Ext
[fromIndex
++]);
107 pName
[toIndex
++] = pEntry
->Ext
[fromIndex
++];
111 pName
[toIndex
] = L
'\0';
115 ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt
, PVPB Vpb
)
117 * FUNCTION: Read the volume label
120 PVOID Context
= NULL
;
124 LARGE_INTEGER FileOffset
;
126 *(Vpb
->VolumeLabel
) = 0;
127 Vpb
->VolumeLabelLength
= 0;
129 pFcb
= vfatOpenRootFCB (DeviceExt
);
131 FileOffset
.QuadPart
= 0;
132 if (CcMapData(pFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, &Context
, (PVOID
*)&Entry
))
136 if (vfatIsDirEntryVolume(Entry
))
138 /* copy volume label */
139 vfat8Dot3ToVolumeLabel (Entry
, Vpb
->VolumeLabel
);
140 Vpb
->VolumeLabelLength
= wcslen (Vpb
->VolumeLabel
) * sizeof(WCHAR
);
143 if (vfatIsDirEntryEndMarker(Entry
))
149 if ((DirIndex
% ENTRIES_PER_PAGE
) == 0)
151 CcUnpinData(Context
);
152 FileOffset
.u
.LowPart
+= PAGE_SIZE
;
153 if (!CcMapData(pFcb
->FileObject
, &FileOffset
, PAGE_SIZE
, TRUE
, &Context
, (PVOID
*)&Entry
))
162 CcUnpinData(Context
);
165 vfatReleaseFCB (DeviceExt
, pFcb
);
167 return STATUS_SUCCESS
;
171 FindFile (PDEVICE_EXTENSION DeviceExt
,
178 * FUNCTION: Find a file
189 PVOID Context
= NULL
;
193 FATDirEntry fatDirEntry
;
195 DPRINT ("FindFile(Parent %x, FileToFind '%S', DirIndex: %d)\n", Parent
, FileToFind
, pDirIndex
? *pDirIndex
: 0);
196 DPRINT ("FindFile: old Pathname %x, old Objectname %x)\n",Fcb
->PathName
, Fcb
->ObjectName
);
200 if (wcslen (FileToFind
) == 0)
203 TempStr
[0] = (WCHAR
) '*';
205 FileToFind
= (PWSTR
)&TempStr
;
209 FirstCluster
= vfatDirEntryGetFirstCluster(DeviceExt
, &Parent
->entry
);
210 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
212 if (FirstCluster
== DeviceExt
->FatInfo
.RootCluster
)
217 if (FirstCluster
== 1)
225 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
226 FirstCluster
= DeviceExt
->FatInfo
.RootCluster
;
230 if (FileToFind
[0] == 0 || (FileToFind
[0] == '\\' && FileToFind
[1] == 0)
231 || (FileToFind
[0] == '.' && FileToFind
[1] == 0))
233 /* it's root : complete essentials fields then return ok */
235 memset (Fcb
, 0, sizeof (VFATFCB
));
236 memset (Fcb
->entry
.Filename
, ' ', 11);
238 Fcb
->PathName
[0]='\\';
239 Fcb
->ObjectName
= &Fcb
->PathName
[1];
240 Fcb
->entry
.Attrib
= FILE_ATTRIBUTE_DIRECTORY
;
241 Fcb
->entry
.CreationDate
= 0x0021; /* 1.1.1980 */
242 Fcb
->entry
.AccessDate
= 0x0021;
243 Fcb
->entry
.UpdateDate
= 0x0021;
244 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
246 Fcb
->entry
.FirstCluster
= ((PUSHORT
)&FirstCluster
)[0];
247 Fcb
->entry
.FirstClusterHigh
= ((PUSHORT
)&FirstCluster
)[1];
250 Fcb
->entry
.FirstCluster
= 1;
255 DPRINT("FindFile: new Pathname %S, new Objectname %S)\n",Fcb
->PathName
, Fcb
->ObjectName
);
256 return (STATUS_SUCCESS
);
261 DPRINT ("Parent->entry.FileSize %x\n", Parent
->entry
.FileSize
);
262 FirstCluster
= vfatDirEntryGetFirstCluster (DeviceExt
, &Parent
->entry
);
264 if (pDirIndex
&& (*pDirIndex
))
265 DirIndex
= *pDirIndex
;
267 if (NULL
== wcschr(FileToFind
, L
'?') && NULL
== wcschr(FileToFind
, L
'*'))
269 /* if there is no '*?' in the search name, than look first for an existing fcb */
270 len
= wcslen(Parent
->PathName
);
271 memcpy(name
, Parent
->PathName
, len
* sizeof(WCHAR
));
272 if (!vfatFCBIsRoot(Parent
))
276 wcscpy(name
+ len
, FileToFind
);
277 rcFcb
= vfatGrabFCBFromTable(DeviceExt
, name
);
280 if(rcFcb
->startIndex
>= DirIndex
)
282 wcscpy(Fcb
->PathName
, name
);
283 Fcb
->ObjectName
= &Fcb
->PathName
[len
];
284 memcpy(&Fcb
->entry
, &rcFcb
->entry
, sizeof(FATDirEntry
));
287 *pDirIndex
= rcFcb
->dirIndex
;
291 *pDirIndex2
= rcFcb
->startIndex
;
293 DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d (%d)\n",Fcb
->PathName
, Fcb
->ObjectName
, rcFcb
->dirIndex
, rcFcb
->startIndex
);
294 vfatReleaseFCB(DeviceExt
, rcFcb
);
295 return STATUS_SUCCESS
;
299 vfatReleaseFCB(DeviceExt
, rcFcb
);
300 return STATUS_UNSUCCESSFUL
;
302 vfatReleaseFCB(DeviceExt
, rcFcb
);
308 Status
= vfatGetNextDirEntry(&Context
, &Page
, Parent
, &DirIndex
, name
, &fatDirEntry
, pDirIndex2
);
309 if (Status
== STATUS_NO_MORE_ENTRIES
)
313 if (vfatIsDirEntryVolume(&fatDirEntry
))
318 vfat8Dot3ToString(&fatDirEntry
, name2
);
319 if (wstrcmpjoki (name
, FileToFind
) || wstrcmpjoki (name2
, FileToFind
))
321 if (Parent
&& Parent
->PathName
)
323 len
= wcslen(Parent
->PathName
);
325 memcpy(Fcb
->PathName
, Parent
->PathName
, len
*sizeof(WCHAR
));
326 Fcb
->ObjectName
=&Fcb
->PathName
[len
];
327 if (len
!= 1 || Fcb
->PathName
[0] != '\\')
329 Fcb
->ObjectName
[0] = '\\';
330 Fcb
->ObjectName
= &Fcb
->ObjectName
[1];
335 Fcb
->ObjectName
=Fcb
->PathName
;
336 Fcb
->ObjectName
[0]='\\';
337 Fcb
->ObjectName
=&Fcb
->ObjectName
[1];
339 memcpy(&Fcb
->entry
, &fatDirEntry
, sizeof(FATDirEntry
));
340 wcsncpy(Fcb
->ObjectName
, *name
== 0 ? name2
: name
, MAX_PATH
);
342 *pDirIndex
= DirIndex
;
343 DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d\n",Fcb
->PathName
, Fcb
->ObjectName
, DirIndex
);
346 CcUnpinData(Context
);
348 return STATUS_SUCCESS
;
353 *pDirIndex
= DirIndex
;
356 CcUnpinData(Context
);
358 return (STATUS_UNSUCCESSFUL
);
362 vfatMakeAbsoluteFilename (PFILE_OBJECT pFileObject
,
363 PWSTR pRelativeFileName
,
364 PWSTR
*pAbsoluteFilename
)
369 DPRINT ("try related for %S\n", pRelativeFileName
);
370 fcb
= pFileObject
->FsContext
;
373 /* verify related object is a directory and target name
374 don't start with \. */
375 if (!(fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
376 || (pRelativeFileName
[0] == L
'\\'))
378 return STATUS_INVALID_PARAMETER
;
381 /* construct absolute path name */
382 assert (wcslen (fcb
->PathName
) + 1 + wcslen (pRelativeFileName
) + 1
384 rcName
= ExAllocatePool (NonPagedPool
, MAX_PATH
* sizeof(WCHAR
));
387 return STATUS_INSUFFICIENT_RESOURCES
;
389 wcscpy (rcName
, fcb
->PathName
);
390 if (!vfatFCBIsRoot(fcb
))
391 wcscat (rcName
, L
"\\");
392 wcscat (rcName
, pRelativeFileName
);
393 *pAbsoluteFilename
= rcName
;
395 return STATUS_SUCCESS
;
399 VfatOpenFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
402 * FUNCTION: Opens a file
408 PWSTR AbsFileName
= NULL
;
410 DPRINT ("VfatOpenFile(%08lx, %08lx, %S)\n", DeviceExt
, FileObject
, FileName
);
412 if (FileObject
->RelatedFileObject
)
414 DPRINT ("Converting relative filename to absolute filename\n");
415 Status
= vfatMakeAbsoluteFilename (FileObject
->RelatedFileObject
,
418 FileName
= AbsFileName
;
419 if (!NT_SUCCESS(Status
))
425 //FIXME: Get cannonical path name (remove .'s, ..'s and extra separators)
427 DPRINT ("PathName to open: %S\n", FileName
);
429 /* try first to find an existing FCB in memory */
430 DPRINT ("Checking for existing FCB in memory\n");
431 Fcb
= vfatGrabFCBFromTable (DeviceExt
, FileName
);
434 DPRINT ("No existing FCB found, making a new one if file exists.\n");
435 Status
= vfatGetFCBForFile (DeviceExt
, &ParentFcb
, &Fcb
, FileName
);
436 if (ParentFcb
!= NULL
)
438 vfatReleaseFCB (DeviceExt
, ParentFcb
);
440 if (!NT_SUCCESS (Status
))
442 DPRINT ("Could not make a new FCB, status: %x\n", Status
);
445 ExFreePool (AbsFileName
);
450 if (Fcb
->Flags
& FCB_DELETE_PENDING
)
452 vfatReleaseFCB (DeviceExt
, Fcb
);
454 ExFreePool (AbsFileName
);
455 return STATUS_DELETE_PENDING
;
457 DPRINT ("Attaching FCB to fileObject\n");
458 Status
= vfatAttachFCBToFileObject (DeviceExt
, Fcb
, FileObject
);
461 ExFreePool (AbsFileName
);
467 VfatSupersedeFile(PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
470 ULONG Cluster
, NextCluster
;
473 Fcb
->entry
.FileSize
= 0;
474 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
476 Cluster
= Fcb
->entry
.FirstCluster
+ Fcb
->entry
.FirstClusterHigh
* 65536;
480 Cluster
= Fcb
->entry
.FirstCluster
;
482 Fcb
->entry
.FirstCluster
= 0;
483 Fcb
->entry
.FirstClusterHigh
= 0;
484 VfatUpdateEntry (DeviceExt
, FileObject
);
485 if (Fcb
->RFCB
.FileSize
.QuadPart
> 0)
487 Fcb
->RFCB
.AllocationSize
.QuadPart
= 0;
488 Fcb
->RFCB
.FileSize
.QuadPart
= 0;
489 Fcb
->RFCB
.ValidDataLength
.QuadPart
= 0;
490 /* Notify cache manager about the change in file size if caching is
491 initialized on the file stream */
492 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
494 CcSetFileSizes(FileObject
, (PCC_FILE_SIZES
)&Fcb
->RFCB
.AllocationSize
);
497 while (Cluster
!= 0xffffffff && Cluster
> 1)
499 Status
= GetNextCluster (DeviceExt
, Cluster
, &NextCluster
, FALSE
);
500 WriteCluster (DeviceExt
, Cluster
, 0);
501 Cluster
= NextCluster
;
506 VfatCreateFile (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
508 * FUNCTION: Create or open a file
511 PIO_STACK_LOCATION Stack
;
512 PFILE_OBJECT FileObject
;
513 NTSTATUS Status
= STATUS_SUCCESS
;
514 PDEVICE_EXTENSION DeviceExt
;
515 ULONG RequestedDisposition
, RequestedOptions
;
519 BOOLEAN PagingFileCreate
= FALSE
;
520 LARGE_INTEGER AllocationSize
;
522 /* Unpack the various parameters. */
523 Stack
= IoGetCurrentIrpStackLocation (Irp
);
524 RequestedDisposition
= ((Stack
->Parameters
.Create
.Options
>> 24) & 0xff);
526 Stack
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
527 PagingFileCreate
= (Stack
->Flags
& SL_OPEN_PAGING_FILE
) ? TRUE
: FALSE
;
528 FileObject
= Stack
->FileObject
;
529 DeviceExt
= DeviceObject
->DeviceExtension
;
531 /* Check their validity. */
532 if (RequestedOptions
& FILE_DIRECTORY_FILE
&&
533 RequestedDisposition
== FILE_SUPERSEDE
)
535 return(STATUS_INVALID_PARAMETER
);
538 /* This a open operation for the volume itself */
539 if (FileObject
->FileName
.Length
== 0 &&
540 FileObject
->RelatedFileObject
== NULL
)
542 if (RequestedDisposition
== FILE_CREATE
||
543 RequestedDisposition
== FILE_OVERWRITE_IF
||
544 RequestedDisposition
== FILE_SUPERSEDE
)
546 return(STATUS_ACCESS_DENIED
);
548 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
550 return(STATUS_NOT_A_DIRECTORY
);
552 pFcb
= DeviceExt
->VolumeFcb
;
553 pCcb
= ExAllocateFromNPagedLookasideList(&VfatGlobalData
->CcbLookasideList
);
556 return (STATUS_INSUFFICIENT_RESOURCES
);
558 memset(pCcb
, 0, sizeof(VFATCCB
));
559 FileObject
->Flags
|= FO_FCB_IS_VALID
;
560 FileObject
->SectionObjectPointer
= &pFcb
->SectionObjectPointers
;
561 FileObject
->FsContext
= pFcb
;
562 FileObject
->FsContext2
= pCcb
;
565 Irp
->IoStatus
.Information
= FILE_OPENED
;
566 return(STATUS_SUCCESS
);
570 * Check for illegal characters in the file name
572 c
= FileObject
->FileName
.Buffer
;
575 if (*c
== L
'*' || *c
== L
'?' || *c
== L
'<' || *c
== L
'>' ||
576 *c
== L
'/' || *c
== L
'|' || *c
== L
':' || *c
== L
'"' ||
577 (*c
== L
'\\' && c
[1] == L
'\\'))
579 return(STATUS_OBJECT_NAME_INVALID
);
584 /* Try opening the file. */
585 Status
= VfatOpenFile (DeviceExt
, FileObject
, FileObject
->FileName
.Buffer
);
588 * If the directory containing the file to open doesn't exist then
591 if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
||
592 Status
== STATUS_INVALID_PARAMETER
||
593 Status
== STATUS_DELETE_PENDING
)
599 * If the file open failed then create the required file
601 if (!NT_SUCCESS (Status
))
603 if (RequestedDisposition
== FILE_CREATE
||
604 RequestedDisposition
== FILE_OPEN_IF
||
605 RequestedDisposition
== FILE_OVERWRITE_IF
||
606 RequestedDisposition
== FILE_SUPERSEDE
)
609 Attributes
= Stack
->Parameters
.Create
.FileAttributes
;
610 Status
= VfatAddEntry (DeviceExt
, FileObject
, RequestedOptions
,
611 (UCHAR
)(Attributes
& FILE_ATTRIBUTE_VALID_FLAGS
));
612 if (NT_SUCCESS (Status
))
614 pFcb
= FileObject
->FsContext
;
615 Irp
->IoStatus
.Information
= FILE_CREATED
;
616 VfatSetAllocationSizeInformation(FileObject
,
619 &Irp
->Overlay
.AllocationSize
);
620 VfatSetExtendedAttributes(FileObject
,
621 Irp
->AssociatedIrp
.SystemBuffer
,
622 Stack
->Parameters
.Create
.EaLength
);
623 IoSetShareAccess(0 /*DesiredAccess*/,
624 Stack
->Parameters
.Create
.ShareAccess
,
626 &pFcb
->FCBShareAccess
);
628 if (PagingFileCreate
)
630 pFcb
->Flags
|= FCB_IS_PAGE_FILE
;
645 /* Otherwise fail if the caller wanted to create a new file */
646 if (RequestedDisposition
== FILE_CREATE
)
648 Irp
->IoStatus
.Information
= FILE_EXISTS
;
649 VfatCloseFile (DeviceExt
, FileObject
);
650 return(STATUS_OBJECT_NAME_COLLISION
);
653 pFcb
= FileObject
->FsContext
;
656 * Check the file has the requested attributes
658 if (RequestedOptions
& FILE_NON_DIRECTORY_FILE
&&
659 pFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
661 VfatCloseFile (DeviceExt
, FileObject
);
662 return(STATUS_FILE_IS_A_DIRECTORY
);
664 if (RequestedOptions
& FILE_DIRECTORY_FILE
&&
665 !(pFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
667 VfatCloseFile (DeviceExt
, FileObject
);
668 return(STATUS_NOT_A_DIRECTORY
);
671 if (PagingFileCreate
)
674 * Do more checking for page files. It is possible,
675 * that the file was opened and closed previously
676 * as a normal cached file. In this case, the cache
677 * manager has referenced the fileobject and the fcb
678 * is held in memory. Try to remove the fileobject
679 * from cache manager and use the fcb.
681 if (pFcb
->RefCount
> 1)
683 if(!(pFcb
->Flags
& FCB_IS_PAGE_FILE
))
685 VfatCloseFile(DeviceExt
, FileObject
);
686 return(STATUS_INVALID_PARAMETER
);
691 pFcb
->Flags
|= FCB_IS_PAGE_FILE
;
696 if (pFcb
->Flags
& FCB_IS_PAGE_FILE
)
698 VfatCloseFile(DeviceExt
, FileObject
);
699 return(STATUS_INVALID_PARAMETER
);
704 if (RequestedDisposition
== FILE_OVERWRITE
||
705 RequestedDisposition
== FILE_OVERWRITE_IF
)
707 AllocationSize
.QuadPart
= 0;
708 Status
= VfatSetAllocationSizeInformation (FileObject
,
712 if (!NT_SUCCESS (Status
))
714 VfatCloseFile (DeviceExt
, FileObject
);
720 /* Supersede the file */
721 if (RequestedDisposition
== FILE_SUPERSEDE
)
723 VfatSupersedeFile(DeviceExt
, FileObject
, pFcb
);
724 Irp
->IoStatus
.Information
= FILE_SUPERSEDED
;
728 Irp
->IoStatus
.Information
= FILE_OPENED
;
732 /* FIXME : test share access */
733 /* FIXME : test write access if requested */
739 NTSTATUS
VfatCreate (PVFAT_IRP_CONTEXT IrpContext
)
741 * FUNCTION: Create or open a file
748 if (IrpContext
->DeviceObject
== VfatGlobalData
->DeviceObject
)
750 /* DeviceObject represents FileSystem instead of logical volume */
751 DPRINT ("FsdCreate called with file system\n");
752 IrpContext
->Irp
->IoStatus
.Information
= FILE_OPENED
;
753 IrpContext
->Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
754 IoCompleteRequest (IrpContext
->Irp
, IO_DISK_INCREMENT
);
755 VfatFreeIrpContext(IrpContext
);
756 return(STATUS_SUCCESS
);
759 if (!(IrpContext
->Flags
& IRPCONTEXT_CANWAIT
))
761 return(VfatQueueRequest (IrpContext
));
764 IrpContext
->Irp
->IoStatus
.Information
= 0;
765 ExAcquireResourceExclusiveLite (&IrpContext
->DeviceExt
->DirResource
, TRUE
);
766 Status
= VfatCreateFile (IrpContext
->DeviceObject
, IrpContext
->Irp
);
767 ExReleaseResourceLite (&IrpContext
->DeviceExt
->DirResource
);
769 IrpContext
->Irp
->IoStatus
.Status
= Status
;
770 IoCompleteRequest (IrpContext
->Irp
,
771 (CCHAR
)(NT_SUCCESS(Status
) ? IO_DISK_INCREMENT
: IO_NO_INCREMENT
));
772 VfatFreeIrpContext(IrpContext
);