1 /* $Id: create.c,v 1.31 2001/08/03 19:00:41 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 /* FUNCTIONS *****************************************************************/
27 IsLastEntry (PVOID Block
, ULONG Offset
)
29 * FUNCTION: Determine if the given directory entry is the last
32 return (((FATDirEntry
*) Block
)[Offset
].Filename
[0] == 0);
36 IsVolEntry (PVOID Block
, ULONG Offset
)
38 * FUNCTION: Determine if the given directory entry is a vol entry
41 if ((((FATDirEntry
*) Block
)[Offset
].Attrib
) == 0x28)
48 IsDeletedEntry (PVOID Block
, ULONG Offset
)
50 * FUNCTION: Determines if the given entry is a deleted one
53 /* Checks special character */
55 return ((((FATDirEntry
*) Block
)[Offset
].Filename
[0] == 0xe5) ||
56 (((FATDirEntry
*) Block
)[Offset
].Filename
[0] == 0));
59 void vfat8Dot3ToString (PCHAR pBasename
, PCHAR pExtension
, PWSTR pName
)
61 int fromIndex
, toIndex
;
63 fromIndex
= toIndex
= 0;
64 while (fromIndex
< 8 && pBasename
[fromIndex
] != ' ')
66 pName
[toIndex
++] = pBasename
[fromIndex
++];
68 if (pExtension
[0] != ' ')
70 pName
[toIndex
++] = L
'.';
72 while (fromIndex
< 3 && pExtension
[fromIndex
] != ' ')
74 pName
[toIndex
++] = pExtension
[fromIndex
++];
77 pName
[toIndex
] = L
'\0';
80 static void vfat8Dot3ToVolumeLabel (PCHAR pBasename
, PCHAR pExtension
, PWSTR pName
)
82 int fromIndex
, toIndex
;
84 fromIndex
= toIndex
= 0;
85 while (fromIndex
< 8 && pBasename
[fromIndex
] != ' ')
87 pName
[toIndex
++] = pBasename
[fromIndex
++];
89 if (pExtension
[0] != ' ')
92 while (fromIndex
< 3 && pBasename
[fromIndex
] != ' ')
94 pName
[toIndex
++] = pExtension
[fromIndex
++];
97 pName
[toIndex
] = L
'\0';
101 GetEntryName (PVOID Block
, PULONG _Offset
, PWSTR Name
, PULONG _jloop
,
102 PDEVICE_EXTENSION DeviceExt
, ULONG
* _StartingSector
)
104 * FUNCTION: Retrieves the file name, be it in short or long file name format
109 ULONG Offset
= *_Offset
;
110 ULONG StartingSector
= *_StartingSector
;
111 ULONG jloop
= *_jloop
;
114 test
= (FATDirEntry
*) Block
;
115 test2
= (slot
*) Block
;
119 if (IsDeletedEntry (Block
, Offset
))
124 if (test2
[Offset
].attr
== 0x0f)
126 vfat_initstr (Name
, 256);
127 vfat_wcsncpy (Name
, test2
[Offset
].name0_4
, 5);
128 vfat_wcsncat (Name
, test2
[Offset
].name5_10
, 5, 6);
129 vfat_wcsncat (Name
, test2
[Offset
].name11_12
, 11, 2);
132 while ((test2
[Offset
].id
!= 0x41) && (test2
[Offset
].id
!= 0x01) &&
133 (test2
[Offset
].attr
> 0))
136 if (Offset
== ENTRIES_PER_SECTOR
)
139 /* FIXME: Check status */
140 GetNextSector (DeviceExt
, StartingSector
, &StartingSector
, FALSE
);
142 /* FIXME: Check status */
143 VfatReadSectors (DeviceExt
->StorageDevice
,
144 StartingSector
, 1, Block
);
145 test2
= (slot
*) Block
;
148 vfat_movstr (Name
, 13, 0, cpos
* 13);
149 vfat_wcsncpy (Name
, test2
[Offset
].name0_4
, 5);
150 vfat_wcsncat (Name
, test2
[Offset
].name5_10
, 5, 6);
151 vfat_wcsncat (Name
, test2
[Offset
].name11_12
, 11, 2);
155 if (Offset
== ENTRIES_PER_SECTOR
)
158 /* FIXME: Check status */
159 GetNextSector (DeviceExt
, StartingSector
, &StartingSector
, FALSE
);
161 /* FIXME: Check status */
162 VfatReadSectors (DeviceExt
->StorageDevice
, StartingSector
, 1, Block
);
163 test2
= (slot
*) Block
;
168 *_StartingSector
= StartingSector
;
170 if (IsDeletedEntry (Block
, Offset
))
175 vfat8Dot3ToString (test
[Offset
].Filename
, test
[Offset
].Ext
, Name
);
181 ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt
, PVPB Vpb
)
183 * FUNCTION: Read the volume label
190 ULONG StartingSector
;
194 Size
= DeviceExt
->rootDirectorySectors
; /* FIXME : in fat32, no limit */
195 StartingSector
= DeviceExt
->rootStart
;
198 block
= ExAllocatePool (NonPagedPool
, BLOCKSIZE
);
199 DPRINT ("ReadVolumeLabel : start at sector %lx, entry %ld\n", StartingSector
, i
);
200 for (j
= 0; j
< Size
; j
++)
202 /* FIXME: Check status */
203 Status
= VfatReadSectors (DeviceExt
->StorageDevice
, StartingSector
, 1, block
);
204 if (!NT_SUCCESS(Status
))
206 *(Vpb
->VolumeLabel
) = 0;
207 Vpb
->VolumeLabelLength
= 0;
212 for (i
= 0; i
< ENTRIES_PER_SECTOR
; i
++)
214 if (IsVolEntry ((PVOID
) block
, i
))
216 FATDirEntry
*test
= (FATDirEntry
*) block
;
218 /* copy volume label */
219 vfat8Dot3ToVolumeLabel (test
[i
].Filename
, test
[i
].Ext
, Vpb
->VolumeLabel
);
220 Vpb
->VolumeLabelLength
= wcslen (Vpb
->VolumeLabel
);
223 return (STATUS_SUCCESS
);
225 if (IsLastEntry ((PVOID
) block
, i
))
227 *(Vpb
->VolumeLabel
) = 0;
228 Vpb
->VolumeLabelLength
= 0;
230 return (STATUS_UNSUCCESSFUL
);
233 /* not found in this sector, try next : */
235 /* directory can be fragmented although it is best to keep them
239 if (DeviceExt
->FatType
== FAT32
)
241 if (StartingSector
== ClusterToSector (DeviceExt
, NextCluster
+ 1))
243 Status
= GetNextCluster (DeviceExt
, NextCluster
, &NextCluster
,
245 if (NextCluster
== 0 || NextCluster
== 0xffffffff)
247 *(Vpb
->VolumeLabel
) = 0;
248 Vpb
->VolumeLabelLength
= 0;
250 return (STATUS_UNSUCCESSFUL
);
252 StartingSector
= ClusterToSector (DeviceExt
, NextCluster
);
256 *(Vpb
->VolumeLabel
) = 0;
257 Vpb
->VolumeLabelLength
= 0;
259 return (STATUS_UNSUCCESSFUL
);
264 FindFile (PDEVICE_EXTENSION DeviceExt
, PVFATFCB Fcb
,
265 PVFATFCB Parent
, PWSTR FileToFind
, ULONG
* StartSector
,
268 * FUNCTION: Find a file
276 ULONG StartingSector
;
282 // DPRINT ("FindFile(Parent %x, FileToFind '%S')\n", Parent, FileToFind);
283 DPRINT("FindFile: old Pathname %x, old Objectname %x)\n",Fcb
->PathName
, Fcb
->ObjectName
);
285 if (wcslen (FileToFind
) == 0)
288 TempStr
[0] = (WCHAR
) '.';
290 FileToFind
= (PWSTR
)&TempStr
;
293 if (Parent
== NULL
|| Parent
->entry
.FirstCluster
== 1)
295 Size
= DeviceExt
->rootDirectorySectors
; /* FIXME : in fat32, no limit */
296 StartingSector
= DeviceExt
->rootStart
;
298 if (FileToFind
[0] == 0 || (FileToFind
[0] == '\\' && FileToFind
[1] == 0)
299 || (FileToFind
[0] == '.' && FileToFind
[1] == 0))
301 /* it's root : complete essentials fields then return ok */
303 memset (Fcb
, 0, sizeof (VFATFCB
));
304 memset (Fcb
->entry
.Filename
, ' ', 11);
306 Fcb
->PathName
[0]='\\';
307 Fcb
->ObjectName
= &Fcb
->PathName
[1];
308 Fcb
->entry
.FileSize
= DeviceExt
->rootDirectorySectors
* BLOCKSIZE
;
309 Fcb
->entry
.Attrib
= FILE_ATTRIBUTE_DIRECTORY
;
310 if (DeviceExt
->FatType
== FAT32
)
311 Fcb
->entry
.FirstCluster
= 2;
313 Fcb
->entry
.FirstCluster
= 1;
315 *StartSector
= StartingSector
;
318 DPRINT("FindFile: new Pathname %S, new Objectname %S)\n",Fcb
->PathName
, Fcb
->ObjectName
);
319 return (STATUS_SUCCESS
);
324 DPRINT ("Parent->entry.FileSize %x\n", Parent
->entry
.FileSize
);
327 if (DeviceExt
->FatType
== FAT32
)
328 NextCluster
= Parent
->entry
.FirstCluster
329 + Parent
->entry
.FirstClusterHigh
* 65536;
331 NextCluster
= Parent
->entry
.FirstCluster
;
332 StartingSector
= ClusterToSector (DeviceExt
, NextCluster
);
333 if (Parent
->entry
.FirstCluster
== 1 && DeviceExt
->FatType
!= FAT32
)
335 /* read of root directory in FAT16 or FAT12 */
336 StartingSector
= DeviceExt
->rootStart
;
339 block
= ExAllocatePool (NonPagedPool
, BLOCKSIZE
);
340 if (StartSector
&& (*StartSector
))
341 StartingSector
= *StartSector
;
342 i
= (Entry
) ? (*Entry
) : 0;
343 for (j
= 0; j
< Size
; j
++)
345 /* FIXME: Check status */
346 VfatReadSectors (DeviceExt
->StorageDevice
, StartingSector
, 1, block
);
348 for (i
= (Entry
) ? (*Entry
) : 0; i
< ENTRIES_PER_SECTOR
; i
++)
350 if (IsVolEntry ((PVOID
) block
, i
))
352 if (IsLastEntry ((PVOID
) block
, i
))
355 *StartSector
= StartingSector
;
359 return (STATUS_UNSUCCESSFUL
);
362 ((PVOID
) block
, &i
, name
, &j
, DeviceExt
, &StartingSector
))
364 vfat8Dot3ToString(((FATDirEntry
*) block
)[i
].Filename
,((FATDirEntry
*) block
)[i
].Ext
, name2
);
365 if (wstrcmpjoki (name
, FileToFind
) || wstrcmpjoki (name2
, FileToFind
))
367 if (Parent
&& Parent
->PathName
)
369 len
= wcslen(Parent
->PathName
);
371 memcpy(Fcb
->PathName
, Parent
->PathName
, len
*sizeof(WCHAR
));
372 Fcb
->ObjectName
=&Fcb
->PathName
[len
];
373 if (len
!= 1 || Fcb
->PathName
[0] != '\\')
375 Fcb
->ObjectName
[0] = '\\';
376 Fcb
->ObjectName
= &Fcb
->ObjectName
[1];
381 Fcb
->ObjectName
=Fcb
->PathName
;
382 Fcb
->ObjectName
[0]='\\';
383 Fcb
->ObjectName
=&Fcb
->ObjectName
[1];
386 memcpy (&Fcb
->entry
, &((FATDirEntry
*) block
)[i
],
387 sizeof (FATDirEntry
));
388 vfat_wcsncpy (Fcb
->ObjectName
, name
, MAX_PATH
);
390 *StartSector
= StartingSector
;
394 DPRINT("FindFile: new Pathname %S, new Objectname %S)\n",Fcb
->PathName
, Fcb
->ObjectName
);
395 return (STATUS_SUCCESS
);
399 /* not found in this sector, try next : */
401 /* directory can be fragmented although it is best to keep them
402 unfragmented. Should we change this to also use GetNextSector?
403 GetNextSector was originally implemented to handle the case above */
407 /* FIXME: Check status */
408 GetNextSector (DeviceExt
, StartingSector
, &StartingSector
, FALSE
);
410 if ((Parent
!= NULL
&& Parent
->entry
.FirstCluster
!= 1)
411 || DeviceExt
->FatType
== FAT32
)
413 if (StartingSector
== ClusterToSector (DeviceExt
, NextCluster
+ 1))
415 Status
= GetNextCluster (DeviceExt
, NextCluster
, &NextCluster
,
417 if (NextCluster
== 0 || NextCluster
== 0xffffffff)
420 *StartSector
= StartingSector
;
424 return (STATUS_UNSUCCESSFUL
);
426 StartingSector
= ClusterToSector (DeviceExt
, NextCluster
);
431 *StartSector
= StartingSector
;
435 return (STATUS_UNSUCCESSFUL
);
439 vfatMakeAbsoluteFilename (PFILE_OBJECT pFileObject
,
440 PWSTR pRelativeFileName
,
441 PWSTR
*pAbsoluteFilename
)
447 DbgPrint ("try related for %S\n", pRelativeFileName
);
448 ccb
= pFileObject
->FsContext2
;
453 /* verify related object is a directory and target name
454 don't start with \. */
455 if (!(fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
456 || (pRelativeFileName
[0] != '\\'))
458 return STATUS_INVALID_PARAMETER
;
461 /* construct absolute path name */
462 assert (wcslen (fcb
->PathName
) + 1 + wcslen (pRelativeFileName
) + 1
464 rcName
= ExAllocatePool (NonPagedPool
, MAX_PATH
);
465 wcscpy (rcName
, fcb
->PathName
);
466 wcscat (rcName
, L
"\\");
467 wcscat (rcName
, pRelativeFileName
);
468 *pAbsoluteFilename
= rcName
;
470 return STATUS_SUCCESS
;
474 VfatOpenFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
477 * FUNCTION: Opens a file
483 PWSTR AbsFileName
= NULL
;
485 DPRINT ("VfatOpenFile(%08lx, %08lx, %S)\n", DeviceExt
, FileObject
, FileName
);
487 if (FileObject
->RelatedFileObject
)
489 DPRINT ("Converting relative filename to absolute filename\n");
490 Status
= vfatMakeAbsoluteFilename (FileObject
->RelatedFileObject
,
493 FileName
= AbsFileName
;
496 //FIXME: Get cannonical path name (remove .'s, ..'s and extra separators)
498 DPRINT ("PathName to open: %S\n", FileName
);
500 /* try first to find an existing FCB in memory */
501 DPRINT ("Checking for existing FCB in memory\n");
502 Fcb
= vfatGrabFCBFromTable (DeviceExt
, FileName
);
505 DPRINT ("No existing FCB found, making a new one if file exists.\n");
506 Status
= vfatGetFCBForFile (DeviceExt
, &ParentFcb
, &Fcb
, FileName
);
507 if (ParentFcb
!= NULL
)
509 vfatReleaseFCB (DeviceExt
, ParentFcb
);
511 if (!NT_SUCCESS (Status
))
513 DPRINT ("Could not make a new FCB, status: %x\n", Status
);
516 ExFreePool (AbsFileName
);
522 DPRINT ("Attaching FCB to fileObject\n");
523 Status
= vfatAttachFCBToFileObject (DeviceExt
, Fcb
, FileObject
);
526 ExFreePool (AbsFileName
);
532 VfatCreateFile (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
534 * FUNCTION: Create or open a file
537 PIO_STACK_LOCATION Stack
;
538 PFILE_OBJECT FileObject
;
539 NTSTATUS Status
= STATUS_SUCCESS
;
540 PDEVICE_EXTENSION DeviceExt
;
541 ULONG RequestedDisposition
, RequestedOptions
;
546 Stack
= IoGetCurrentIrpStackLocation (Irp
);
548 RequestedDisposition
= ((Stack
->Parameters
.Create
.Options
>> 24) & 0xff);
550 Stack
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
551 if ((RequestedOptions
& FILE_DIRECTORY_FILE
)
552 && RequestedDisposition
== FILE_SUPERSEDE
)
553 return STATUS_INVALID_PARAMETER
;
554 FileObject
= Stack
->FileObject
;
555 DeviceExt
= DeviceObject
->DeviceExtension
;
559 * Check for illegal characters in the file name
561 c
= FileObject
->FileName
.Buffer
;
564 if (*c
== L
'*' || *c
== L
'?')
566 Irp
->IoStatus
.Information
= 0;
567 Irp
->IoStatus
.Status
= STATUS_OBJECT_NAME_INVALID
;
568 return(STATUS_OBJECT_NAME_INVALID
);
573 Status
= VfatOpenFile (DeviceExt
, FileObject
, FileObject
->FileName
.Buffer
);
576 * If the directory containing the file to open doesn't exist then
579 Irp
->IoStatus
.Information
= 0;
580 if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
)
582 Irp
->IoStatus
.Status
= Status
;
586 if (!NT_SUCCESS (Status
))
589 * If the file open failed then create the required file
591 if (RequestedDisposition
== FILE_CREATE
||
592 RequestedDisposition
== FILE_OPEN_IF
||
593 RequestedDisposition
== FILE_OVERWRITE_IF
||
594 RequestedDisposition
== FILE_SUPERSEDE
)
598 addEntry (DeviceExt
, FileObject
, RequestedOptions
,
600 Create
.FileAttributes
& FILE_ATTRIBUTE_VALID_FLAGS
));
601 if (NT_SUCCESS (Status
))
602 Irp
->IoStatus
.Information
= FILE_CREATED
;
603 /* FIXME set size if AllocationSize requested */
604 /* FIXME set extended attributes? */
605 /* FIXME set share access */
606 /* IoSetShareAccess(DesiredAccess,ShareAccess,FileObject,
607 * ((PVfatCCB)(FileObject->FsContext2))->pFcb->FCBShareAccess);
614 * Otherwise fail if the caller wanted to create a new file
616 if (RequestedDisposition
== FILE_CREATE
)
618 Irp
->IoStatus
.Information
= FILE_EXISTS
;
619 Status
= STATUS_OBJECT_NAME_COLLISION
;
621 pCcb
= FileObject
->FsContext2
;
624 * If requested then delete the file and create a new one with the
627 if (RequestedDisposition
== FILE_SUPERSEDE
)
629 ULONG Cluster
, NextCluster
;
630 /* FIXME set size to 0 and free clusters */
631 pFcb
->entry
.FileSize
= 0;
632 if (DeviceExt
->FatType
== FAT32
)
633 Cluster
= pFcb
->entry
.FirstCluster
634 + pFcb
->entry
.FirstClusterHigh
* 65536;
636 Cluster
= pFcb
->entry
.FirstCluster
;
637 pFcb
->entry
.FirstCluster
= 0;
638 pFcb
->entry
.FirstClusterHigh
= 0;
639 updEntry (DeviceExt
, FileObject
);
640 while (Cluster
!= 0xffffffff && Cluster
> 1)
642 Status
= GetNextCluster (DeviceExt
, Cluster
, &NextCluster
, TRUE
);
643 WriteCluster (DeviceExt
, Cluster
, 0);
644 Cluster
= NextCluster
;
649 * Check the file has the requested attributes
651 if ((RequestedOptions
& FILE_NON_DIRECTORY_FILE
)
652 && (pFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
654 Status
= STATUS_FILE_IS_A_DIRECTORY
;
656 if ((RequestedOptions
& FILE_DIRECTORY_FILE
)
657 && !(pFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
659 Status
= STATUS_NOT_A_DIRECTORY
;
661 /* FIXME : test share access */
662 /* FIXME : test write access if requested */
663 if (!NT_SUCCESS (Status
))
664 VfatCloseFile (DeviceExt
, FileObject
);
666 Irp
->IoStatus
.Information
= FILE_OPENED
;
667 /* FIXME : make supersed or overwrite if requested */
670 Irp
->IoStatus
.Status
= Status
;
677 VfatCreate (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
679 * FUNCTION: Create or open a file
682 NTSTATUS Status
= STATUS_SUCCESS
;
683 PDEVICE_EXTENSION DeviceExt
;
685 assert (DeviceObject
);
688 if (DeviceObject
->Size
== sizeof (DEVICE_OBJECT
))
690 /* DeviceObject represents FileSystem instead of logical volume */
691 DbgPrint ("FsdCreate called with file system\n");
692 Irp
->IoStatus
.Status
= Status
;
693 Irp
->IoStatus
.Information
= FILE_OPENED
;
694 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
698 DeviceExt
= DeviceObject
->DeviceExtension
;
700 ExAcquireResourceExclusiveLite (&DeviceExt
->DirResource
, TRUE
);
702 Status
= VfatCreateFile (DeviceObject
, Irp
);
704 ExReleaseResourceLite (&DeviceExt
->DirResource
);
706 Irp
->IoStatus
.Status
= Status
;
707 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);