1 /* $Id: create.c,v 1.33 2001/10/10 22:12:34 hbirr Exp $
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: services/fs/vfat/create.c
6 * PURPOSE: VFAT Filesystem
7 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
11 /* INCLUDES *****************************************************************/
13 #include <ddk/ntddk.h>
22 /* GLOBALS *******************************************************************/
24 #define ENTRIES_PER_PAGE (PAGESIZE / sizeof (FATDirEntry))
26 /* FUNCTIONS *****************************************************************/
29 IsLastEntry (PVOID Block
, ULONG Offset
)
31 * FUNCTION: Determine if the given directory entry is the last
34 return (((FATDirEntry
*) Block
)[Offset
].Filename
[0] == 0);
38 IsVolEntry (PVOID Block
, ULONG Offset
)
40 * FUNCTION: Determine if the given directory entry is a vol entry
43 if ((((FATDirEntry
*) Block
)[Offset
].Attrib
) == 0x28)
50 IsDeletedEntry (PVOID Block
, ULONG Offset
)
52 * FUNCTION: Determines if the given entry is a deleted one
55 /* Checks special character */
57 return ((((FATDirEntry
*) Block
)[Offset
].Filename
[0] == 0xe5) ||
58 (((FATDirEntry
*) Block
)[Offset
].Filename
[0] == 0));
61 void vfat8Dot3ToString (PCHAR pBasename
, PCHAR pExtension
, PWSTR pName
)
63 int fromIndex
, toIndex
;
65 fromIndex
= toIndex
= 0;
66 while (fromIndex
< 8 && pBasename
[fromIndex
] != ' ')
68 pName
[toIndex
++] = pBasename
[fromIndex
++];
70 if (pExtension
[0] != ' ')
72 pName
[toIndex
++] = L
'.';
74 while (fromIndex
< 3 && pExtension
[fromIndex
] != ' ')
76 pName
[toIndex
++] = pExtension
[fromIndex
++];
79 pName
[toIndex
] = L
'\0';
82 static void vfat8Dot3ToVolumeLabel (PCHAR pBasename
, PCHAR pExtension
, PWSTR pName
)
84 int fromIndex
, toIndex
;
86 fromIndex
= toIndex
= 0;
87 while (fromIndex
< 8 && pBasename
[fromIndex
] != ' ')
89 pName
[toIndex
++] = pBasename
[fromIndex
++];
91 if (pExtension
[0] != ' ')
94 while (fromIndex
< 3 && pBasename
[fromIndex
] != ' ')
96 pName
[toIndex
++] = pExtension
[fromIndex
++];
99 pName
[toIndex
] = L
'\0';
103 GetEntryName(PVOID
*pContext
,
105 PFILE_OBJECT FileObject
,
110 * FUNCTION: Retrieves the file name, be it in short or long file name format
117 ULONG Offset
= *pIndex
% ENTRIES_PER_PAGE
;
119 LARGE_INTEGER FileOffset
;
124 test
= (FATDirEntry
*) *Block
;
125 test2
= (slot
*) *Block
;
126 if (vfatIsDirEntryEndMarker(&test
[Offset
]))
128 return STATUS_NO_MORE_ENTRIES
;
130 if (test2
[Offset
].attr
== 0x0f && !vfatIsDirEntryDeleted(&test
[Offset
]))
134 *pIndex2
= *pIndex
; // start of dir entry
136 DPRINT (" long name entry found at %d\n", *pIndex
);
138 DPRINT (" name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
139 5, test2
[Offset
].name0_4
,
140 6, test2
[Offset
].name5_10
,
141 2, test2
[Offset
].name11_12
);
143 vfat_initstr (Name
, 255);
144 vfat_wcsncpy (Name
, test2
[Offset
].name0_4
, 5);
145 vfat_wcsncat (Name
, test2
[Offset
].name5_10
, 5, 6);
146 vfat_wcsncat (Name
, test2
[Offset
].name11_12
, 11, 2);
148 DPRINT (" longName: [%S]\n", Name
);
150 while ((test2
[Offset
].id
!= 0x41) && (test2
[Offset
].id
!= 0x01) &&
151 (test2
[Offset
].attr
> 0))
156 if (Offset
== ENTRIES_PER_PAGE
)
159 CcUnpinData(*pContext
);
160 FileOffset
.QuadPart
= *pIndex
* sizeof(FATDirEntry
);
161 if(!CcMapData(FileObject
, &FileOffset
, PAGESIZE
, TRUE
, pContext
, Block
))
164 return STATUS_NO_MORE_ENTRIES
;
166 test2
= (slot
*) *Block
;
168 DPRINT (" long name entry found at %d\n", *pIndex
);
170 DPRINT (" name chunk1:[%.*S] chunk2:[%.*S] chunk3:[%.*S]\n",
171 5, test2
[Offset
].name0_4
,
172 6, test2
[Offset
].name5_10
,
173 2, test2
[Offset
].name11_12
);
176 vfat_movstr (Name
, 13, 0, cpos
* 13);
177 vfat_wcsncpy (Name
, test2
[Offset
].name0_4
, 5);
178 vfat_wcsncat (Name
, test2
[Offset
].name5_10
, 5, 6);
179 vfat_wcsncat (Name
, test2
[Offset
].name11_12
, 11, 2);
181 DPRINT (" longName: [%S]\n", Name
);
185 if (Offset
== ENTRIES_PER_PAGE
)
188 CcUnpinData(*pContext
);
189 FileOffset
.QuadPart
= *pIndex
* sizeof(FATDirEntry
);
190 if(!CcMapData(FileObject
, &FileOffset
, PAGESIZE
, TRUE
, pContext
, Block
))
193 return STATUS_NO_MORE_ENTRIES
;
195 test2
= (slot
*) *Block
;
196 test
= (FATDirEntry
*) *Block
;
201 if (vfatIsDirEntryEndMarker(&test
[Offset
]))
202 return STATUS_NO_MORE_ENTRIES
;
203 if (vfatIsDirEntryDeleted(&test
[Offset
]))
204 return STATUS_UNSUCCESSFUL
;
207 vfat8Dot3ToString (test
[Offset
].Filename
, test
[Offset
].Ext
, Name
);
214 return STATUS_SUCCESS
;
218 ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt
, PVPB Vpb
)
220 * FUNCTION: Read the volume label
227 ULONG StartingSector
;
231 Size
= DeviceExt
->rootDirectorySectors
; /* FIXME : in fat32, no limit */
232 StartingSector
= DeviceExt
->rootStart
;
235 block
= ExAllocatePool (NonPagedPool
, BLOCKSIZE
);
236 DPRINT ("ReadVolumeLabel : start at sector %lx, entry %ld\n", StartingSector
, i
);
237 for (j
= 0; j
< Size
; j
++)
239 /* FIXME: Check status */
240 Status
= VfatReadSectors (DeviceExt
->StorageDevice
, StartingSector
, 1, block
);
241 if (!NT_SUCCESS(Status
))
243 *(Vpb
->VolumeLabel
) = 0;
244 Vpb
->VolumeLabelLength
= 0;
249 for (i
= 0; i
< ENTRIES_PER_SECTOR
; i
++)
251 if (IsVolEntry ((PVOID
) block
, i
))
253 FATDirEntry
*test
= (FATDirEntry
*) block
;
255 /* copy volume label */
256 vfat8Dot3ToVolumeLabel (test
[i
].Filename
, test
[i
].Ext
, Vpb
->VolumeLabel
);
257 Vpb
->VolumeLabelLength
= wcslen (Vpb
->VolumeLabel
);
260 return (STATUS_SUCCESS
);
262 if (IsLastEntry ((PVOID
) block
, i
))
264 *(Vpb
->VolumeLabel
) = 0;
265 Vpb
->VolumeLabelLength
= 0;
267 return (STATUS_UNSUCCESSFUL
);
270 /* not found in this sector, try next : */
272 /* directory can be fragmented although it is best to keep them
276 if (DeviceExt
->FatType
== FAT32
)
278 if (StartingSector
== ClusterToSector (DeviceExt
, NextCluster
+ 1))
280 Status
= GetNextCluster (DeviceExt
, NextCluster
, &NextCluster
,
282 if (NextCluster
== 0 || NextCluster
== 0xffffffff)
284 *(Vpb
->VolumeLabel
) = 0;
285 Vpb
->VolumeLabelLength
= 0;
287 return (STATUS_UNSUCCESSFUL
);
289 StartingSector
= ClusterToSector (DeviceExt
, NextCluster
);
293 *(Vpb
->VolumeLabel
) = 0;
294 Vpb
->VolumeLabelLength
= 0;
296 return (STATUS_UNSUCCESSFUL
);
300 FindFile (PDEVICE_EXTENSION DeviceExt
,
307 * FUNCTION: Find a file
321 LARGE_INTEGER FileOffset
;
322 PVOID Context
= NULL
;
324 DPRINT ("FindFile(Parent %x, FileToFind '%S', DirIndex: %d)\n", Parent
, FileToFind
, pDirIndex
? *pDirIndex
: 0);
325 DPRINT ("FindFile: old Pathname %x, old Objectname %x)\n",Fcb
->PathName
, Fcb
->ObjectName
);
329 if (wcslen (FileToFind
) == 0)
332 TempStr
[0] = (WCHAR
) '.';
334 FileToFind
= (PWSTR
)&TempStr
;
338 FirstCluster
= vfatDirEntryGetFirstCluster(DeviceExt
, &Parent
->entry
);
339 if (DeviceExt
->FatType
== FAT32
)
341 if (FirstCluster
== ((struct _BootSector32
*)(DeviceExt
->Boot
))->RootCluster
)
346 if (FirstCluster
== 1)
354 if (DeviceExt
->FatType
== FAT32
)
355 FirstCluster
= ((struct _BootSector32
*)(DeviceExt
->Boot
))->RootCluster
;
359 if (FileToFind
[0] == 0 || (FileToFind
[0] == '\\' && FileToFind
[1] == 0)
360 || (FileToFind
[0] == '.' && FileToFind
[1] == 0))
362 /* it's root : complete essentials fields then return ok */
364 memset (Fcb
, 0, sizeof (VFATFCB
));
365 memset (Fcb
->entry
.Filename
, ' ', 11);
367 Fcb
->PathName
[0]='\\';
368 Fcb
->ObjectName
= &Fcb
->PathName
[1];
369 Fcb
->entry
.FileSize
= DeviceExt
->rootDirectorySectors
* BLOCKSIZE
;
370 Fcb
->entry
.Attrib
= FILE_ATTRIBUTE_DIRECTORY
;
371 if (DeviceExt
->FatType
== FAT32
)
373 Fcb
->entry
.FirstCluster
= ((PUSHORT
)FirstCluster
)[0];
374 Fcb
->entry
.FirstClusterHigh
= ((PUSHORT
)FirstCluster
)[1];
377 Fcb
->entry
.FirstCluster
= 1;
382 DPRINT("FindFile: new Pathname %S, new Objectname %S)\n",Fcb
->PathName
, Fcb
->ObjectName
);
383 return (STATUS_SUCCESS
);
388 DPRINT ("Parent->entry.FileSize %x\n", Parent
->entry
.FileSize
);
389 FirstCluster
= vfatDirEntryGetFirstCluster (DeviceExt
, &Parent
->entry
);
391 if (pDirIndex
&& (*pDirIndex
))
392 DirIndex
= *pDirIndex
;
394 Offset
= DirIndex
% ENTRIES_PER_PAGE
;
397 if (Context
== NULL
|| Offset
== ENTRIES_PER_PAGE
)
399 if (Offset
== ENTRIES_PER_PAGE
)
403 CcUnpinData(Context
);
405 FileOffset
.QuadPart
= (DirIndex
- Offset
) * sizeof(FATDirEntry
);
406 if (!CcMapData(Parent
->FileObject
, &FileOffset
, PAGESIZE
, TRUE
,
407 &Context
, (PVOID
*)&block
))
413 if (vfatIsDirEntryVolume(&((FATDirEntry
*)block
)[Offset
]))
419 Status
= GetEntryName (&Context
, (PVOID
*)&block
, Parent
->FileObject
, name
,
420 &DirIndex
, pDirIndex2
);
421 if (Status
== STATUS_NO_MORE_ENTRIES
)
423 Offset
= DirIndex
% ENTRIES_PER_PAGE
;
424 if (NT_SUCCESS(Status
))
426 vfat8Dot3ToString(((FATDirEntry
*) block
)[Offset
].Filename
,((FATDirEntry
*) block
)[Offset
].Ext
, name2
);
427 if (wstrcmpjoki (name
, FileToFind
) || wstrcmpjoki (name2
, FileToFind
))
429 if (Parent
&& Parent
->PathName
)
431 len
= wcslen(Parent
->PathName
);
433 memcpy(Fcb
->PathName
, Parent
->PathName
, len
*sizeof(WCHAR
));
434 Fcb
->ObjectName
=&Fcb
->PathName
[len
];
435 if (len
!= 1 || Fcb
->PathName
[0] != '\\')
437 Fcb
->ObjectName
[0] = '\\';
438 Fcb
->ObjectName
= &Fcb
->ObjectName
[1];
443 Fcb
->ObjectName
=Fcb
->PathName
;
444 Fcb
->ObjectName
[0]='\\';
445 Fcb
->ObjectName
=&Fcb
->ObjectName
[1];
448 memcpy (&Fcb
->entry
, &((FATDirEntry
*) block
)[Offset
],
449 sizeof (FATDirEntry
));
450 vfat_wcsncpy (Fcb
->ObjectName
, name
, MAX_PATH
);
452 *pDirIndex
= DirIndex
;
453 DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d\n",Fcb
->PathName
, Fcb
->ObjectName
, DirIndex
);
455 CcUnpinData(Context
);
456 return STATUS_SUCCESS
;
463 *pDirIndex
= DirIndex
;
465 CcUnpinData(Context
);
466 return (STATUS_UNSUCCESSFUL
);
470 vfatMakeAbsoluteFilename (PFILE_OBJECT pFileObject
,
471 PWSTR pRelativeFileName
,
472 PWSTR
*pAbsoluteFilename
)
478 DbgPrint ("try related for %S\n", pRelativeFileName
);
479 ccb
= pFileObject
->FsContext2
;
484 /* verify related object is a directory and target name
485 don't start with \. */
486 if (!(fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
487 || (pRelativeFileName
[0] != '\\'))
489 return STATUS_INVALID_PARAMETER
;
492 /* construct absolute path name */
493 assert (wcslen (fcb
->PathName
) + 1 + wcslen (pRelativeFileName
) + 1
495 rcName
= ExAllocatePool (NonPagedPool
, MAX_PATH
);
496 wcscpy (rcName
, fcb
->PathName
);
497 wcscat (rcName
, L
"\\");
498 wcscat (rcName
, pRelativeFileName
);
499 *pAbsoluteFilename
= rcName
;
501 return STATUS_SUCCESS
;
505 VfatOpenFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
508 * FUNCTION: Opens a file
514 PWSTR AbsFileName
= NULL
;
516 DPRINT ("VfatOpenFile(%08lx, %08lx, %S)\n", DeviceExt
, FileObject
, FileName
);
518 if (FileObject
->RelatedFileObject
)
520 DPRINT ("Converting relative filename to absolute filename\n");
521 Status
= vfatMakeAbsoluteFilename (FileObject
->RelatedFileObject
,
524 FileName
= AbsFileName
;
527 //FIXME: Get cannonical path name (remove .'s, ..'s and extra separators)
529 DPRINT ("PathName to open: %S\n", FileName
);
531 /* try first to find an existing FCB in memory */
532 DPRINT ("Checking for existing FCB in memory\n");
533 Fcb
= vfatGrabFCBFromTable (DeviceExt
, FileName
);
536 DPRINT ("No existing FCB found, making a new one if file exists.\n");
537 Status
= vfatGetFCBForFile (DeviceExt
, &ParentFcb
, &Fcb
, FileName
);
538 if (ParentFcb
!= NULL
)
540 vfatReleaseFCB (DeviceExt
, ParentFcb
);
542 if (!NT_SUCCESS (Status
))
544 DPRINT ("Could not make a new FCB, status: %x\n", Status
);
547 ExFreePool (AbsFileName
);
552 if (Fcb
->Flags
& FCB_DELETE_PENDING
)
554 vfatReleaseFCB (DeviceExt
, Fcb
);
556 ExFreePool (AbsFileName
);
557 return STATUS_DELETE_PENDING
;
559 DPRINT ("Attaching FCB to fileObject\n");
560 Status
= vfatAttachFCBToFileObject (DeviceExt
, Fcb
, FileObject
);
563 ExFreePool (AbsFileName
);
569 VfatCreateFile (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
571 * FUNCTION: Create or open a file
574 PIO_STACK_LOCATION Stack
;
575 PFILE_OBJECT FileObject
;
576 NTSTATUS Status
= STATUS_SUCCESS
;
577 PDEVICE_EXTENSION DeviceExt
;
578 ULONG RequestedDisposition
, RequestedOptions
;
583 Stack
= IoGetCurrentIrpStackLocation (Irp
);
585 RequestedDisposition
= ((Stack
->Parameters
.Create
.Options
>> 24) & 0xff);
587 Stack
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
588 if ((RequestedOptions
& FILE_DIRECTORY_FILE
)
589 && RequestedDisposition
== FILE_SUPERSEDE
)
590 return STATUS_INVALID_PARAMETER
;
591 FileObject
= Stack
->FileObject
;
592 DeviceExt
= DeviceObject
->DeviceExtension
;
596 * Check for illegal characters in the file name
598 c
= FileObject
->FileName
.Buffer
;
601 if (*c
== L
'*' || *c
== L
'?' || (*c
== L
'\\' && c
[1] == L
'\\'))
603 Irp
->IoStatus
.Information
= 0;
604 Irp
->IoStatus
.Status
= STATUS_OBJECT_NAME_INVALID
;
605 return(STATUS_OBJECT_NAME_INVALID
);
610 Status
= VfatOpenFile (DeviceExt
, FileObject
, FileObject
->FileName
.Buffer
);
613 * If the directory containing the file to open doesn't exist then
616 Irp
->IoStatus
.Information
= 0;
617 if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
)
619 Irp
->IoStatus
.Status
= Status
;
623 if (Status
== STATUS_DELETE_PENDING
)
625 Irp
->IoStatus
.Status
= Status
;
628 if (!NT_SUCCESS (Status
))
631 * If the file open failed then create the required file
633 if (RequestedDisposition
== FILE_CREATE
||
634 RequestedDisposition
== FILE_OPEN_IF
||
635 RequestedDisposition
== FILE_OVERWRITE_IF
||
636 RequestedDisposition
== FILE_SUPERSEDE
)
640 addEntry (DeviceExt
, FileObject
, RequestedOptions
,
642 Create
.FileAttributes
& FILE_ATTRIBUTE_VALID_FLAGS
));
643 if (NT_SUCCESS (Status
))
644 Irp
->IoStatus
.Information
= FILE_CREATED
;
645 /* FIXME set size if AllocationSize requested */
646 /* FIXME set extended attributes? */
647 /* FIXME set share access */
648 /* IoSetShareAccess(DesiredAccess,ShareAccess,FileObject,
649 * ((PVfatCCB)(FileObject->FsContext2))->pFcb->FCBShareAccess);
656 * Otherwise fail if the caller wanted to create a new file
658 if (RequestedDisposition
== FILE_CREATE
)
660 Irp
->IoStatus
.Information
= FILE_EXISTS
;
661 Status
= STATUS_OBJECT_NAME_COLLISION
;
663 pCcb
= FileObject
->FsContext2
;
666 * If requested then delete the file and create a new one with the
669 if (RequestedDisposition
== FILE_SUPERSEDE
)
671 ULONG Cluster
, NextCluster
;
672 /* FIXME set size to 0 and free clusters */
673 pFcb
->entry
.FileSize
= 0;
674 if (DeviceExt
->FatType
== FAT32
)
675 Cluster
= pFcb
->entry
.FirstCluster
676 + pFcb
->entry
.FirstClusterHigh
* 65536;
678 Cluster
= pFcb
->entry
.FirstCluster
;
679 pFcb
->entry
.FirstCluster
= 0;
680 pFcb
->entry
.FirstClusterHigh
= 0;
681 updEntry (DeviceExt
, FileObject
);
682 if ((ULONG
)pFcb
->RFCB
.FileSize
.QuadPart
> 0)
684 pFcb
->RFCB
.AllocationSize
.QuadPart
= 0;
685 pFcb
->RFCB
.FileSize
.QuadPart
= 0;
686 pFcb
->RFCB
.ValidDataLength
.QuadPart
= 0;
687 CcSetFileSizes(FileObject
, (PCC_FILE_SIZES
)&pFcb
->RFCB
.AllocationSize
);
689 while (Cluster
!= 0xffffffff && Cluster
> 1)
691 Status
= GetNextCluster (DeviceExt
, Cluster
, &NextCluster
, FALSE
);
692 WriteCluster (DeviceExt
, Cluster
, 0);
693 Cluster
= NextCluster
;
698 * Check the file has the requested attributes
700 if ((RequestedOptions
& FILE_NON_DIRECTORY_FILE
)
701 && (pFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
703 Status
= STATUS_FILE_IS_A_DIRECTORY
;
705 if ((RequestedOptions
& FILE_DIRECTORY_FILE
)
706 && !(pFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
708 Status
= STATUS_NOT_A_DIRECTORY
;
710 /* FIXME : test share access */
711 /* FIXME : test write access if requested */
712 if (!NT_SUCCESS (Status
))
713 VfatCloseFile (DeviceExt
, FileObject
);
715 Irp
->IoStatus
.Information
= FILE_OPENED
;
716 /* FIXME : make supersed or overwrite if requested */
719 Irp
->IoStatus
.Status
= Status
;
726 VfatCreate (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
728 * FUNCTION: Create or open a file
731 NTSTATUS Status
= STATUS_SUCCESS
;
732 PDEVICE_EXTENSION DeviceExt
;
734 assert (DeviceObject
);
737 if (DeviceObject
->Size
== sizeof (DEVICE_OBJECT
))
739 /* DeviceObject represents FileSystem instead of logical volume */
740 DbgPrint ("FsdCreate called with file system\n");
741 Irp
->IoStatus
.Status
= Status
;
742 Irp
->IoStatus
.Information
= FILE_OPENED
;
743 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
747 DeviceExt
= DeviceObject
->DeviceExtension
;
749 ExAcquireResourceExclusiveLite (&DeviceExt
->DirResource
, TRUE
);
751 Status
= VfatCreateFile (DeviceObject
, Irp
);
753 ExReleaseResourceLite (&DeviceExt
->DirResource
);
755 Irp
->IoStatus
.Status
= Status
;
756 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);