1 /* $Id: create.c,v 1.29 2001/07/25 08:58:21 ekohl 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 && pBasename
[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
;
115 test
= (FATDirEntry
*) Block
;
116 test2
= (slot
*) Block
;
120 if (IsDeletedEntry (Block
, Offset
))
125 if (test2
[Offset
].attr
== 0x0f)
127 vfat_initstr (Name
, 256);
128 vfat_wcsncpy (Name
, test2
[Offset
].name0_4
, 5);
129 vfat_wcsncat (Name
, test2
[Offset
].name5_10
, 5, 6);
130 vfat_wcsncat (Name
, test2
[Offset
].name11_12
, 11, 2);
133 while ((test2
[Offset
].id
!= 0x41) && (test2
[Offset
].id
!= 0x01) &&
134 (test2
[Offset
].attr
> 0))
137 if (Offset
== ENTRIES_PER_SECTOR
)
140 /* FIXME: Check status */
141 GetNextSector (DeviceExt
, StartingSector
, &StartingSector
, FALSE
);
143 /* FIXME: Check status */
144 VfatReadSectors (DeviceExt
->StorageDevice
,
145 StartingSector
, 1, Block
);
146 test2
= (slot
*) Block
;
149 vfat_movstr (Name
, 13, 0, cpos
* 13);
150 vfat_wcsncpy (Name
, test2
[Offset
].name0_4
, 5);
151 vfat_wcsncat (Name
, test2
[Offset
].name5_10
, 5, 6);
152 vfat_wcsncat (Name
, test2
[Offset
].name11_12
, 11, 2);
156 // save values for returning
159 *_StartingSector
= StartingSector
;
161 if (Offset
+ 1 == ENTRIES_PER_SECTOR
)
163 // save the old sector
164 OldBlock
= ExAllocatePool (NonPagedPool
, BLOCKSIZE
);
165 memcpy (OldBlock
, Block
, BLOCKSIZE
);
167 // read the next sector
169 /* FIXME: Check status */
170 GetNextSector (DeviceExt
, StartingSector
, &StartingSector
, FALSE
);
172 /* FIXME: Check status */
173 VfatReadSectors (DeviceExt
->StorageDevice
, StartingSector
, 1, Block
);
175 test2
= (slot
*) Block
;
177 if (IsDeletedEntry (Block
, Offset
))
179 ExFreePool (OldBlock
);
182 *_StartingSector
= StartingSector
;
186 // restore the old sector
187 memcpy (Block
, OldBlock
, BLOCKSIZE
);
188 ExFreePool (OldBlock
);
192 if (IsDeletedEntry (Block
, Offset
+ 1))
197 *_StartingSector
= StartingSector
;
204 vfat8Dot3ToString (test
[Offset
].Filename
, test
[Offset
].Ext
, Name
);
212 ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt
, PVPB Vpb
)
214 * FUNCTION: Read the volume label
221 ULONG StartingSector
;
225 Size
= DeviceExt
->rootDirectorySectors
; /* FIXME : in fat32, no limit */
226 StartingSector
= DeviceExt
->rootStart
;
229 block
= ExAllocatePool (NonPagedPool
, BLOCKSIZE
);
230 DPRINT ("ReadVolumeLabel : start at sector %lx, entry %ld\n", StartingSector
, i
);
231 for (j
= 0; j
< Size
; j
++)
233 /* FIXME: Check status */
234 Status
= VfatReadSectors (DeviceExt
->StorageDevice
, StartingSector
, 1, block
);
235 if (!NT_SUCCESS(Status
))
237 *(Vpb
->VolumeLabel
) = 0;
238 Vpb
->VolumeLabelLength
= 0;
243 for (i
= 0; i
< ENTRIES_PER_SECTOR
; i
++)
245 if (IsVolEntry ((PVOID
) block
, i
))
247 FATDirEntry
*test
= (FATDirEntry
*) block
;
249 /* copy volume label */
250 vfat8Dot3ToVolumeLabel (test
[i
].Filename
, test
[i
].Ext
, Vpb
->VolumeLabel
);
251 Vpb
->VolumeLabelLength
= wcslen (Vpb
->VolumeLabel
);
254 return (STATUS_SUCCESS
);
256 if (IsLastEntry ((PVOID
) block
, i
))
258 *(Vpb
->VolumeLabel
) = 0;
259 Vpb
->VolumeLabelLength
= 0;
261 return (STATUS_UNSUCCESSFUL
);
264 /* not found in this sector, try next : */
266 /* directory can be fragmented although it is best to keep them
270 if (DeviceExt
->FatType
== FAT32
)
272 if (StartingSector
== ClusterToSector (DeviceExt
, NextCluster
+ 1))
274 Status
= GetNextCluster (DeviceExt
, NextCluster
, &NextCluster
,
276 if (NextCluster
== 0 || NextCluster
== 0xffffffff)
278 *(Vpb
->VolumeLabel
) = 0;
279 Vpb
->VolumeLabelLength
= 0;
281 return (STATUS_UNSUCCESSFUL
);
283 StartingSector
= ClusterToSector (DeviceExt
, NextCluster
);
287 *(Vpb
->VolumeLabel
) = 0;
288 Vpb
->VolumeLabelLength
= 0;
290 return (STATUS_UNSUCCESSFUL
);
295 FindFile (PDEVICE_EXTENSION DeviceExt
, PVFATFCB Fcb
,
296 PVFATFCB Parent
, PWSTR FileToFind
, ULONG
* StartSector
,
299 * FUNCTION: Find a file
306 ULONG StartingSector
;
312 // DPRINT ("FindFile(Parent %x, FileToFind '%S')\n", Parent, FileToFind);
313 DPRINT("FindFile: old Pathname %x, old Objectname %x)\n",Fcb
->PathName
, Fcb
->ObjectName
);
315 if (wcslen (FileToFind
) == 0)
318 TempStr
[0] = (WCHAR
) '.';
320 FileToFind
= (PWSTR
)&TempStr
;
323 if (Parent
== NULL
|| Parent
->entry
.FirstCluster
== 1)
325 Size
= DeviceExt
->rootDirectorySectors
; /* FIXME : in fat32, no limit */
326 StartingSector
= DeviceExt
->rootStart
;
328 if (FileToFind
[0] == 0 || (FileToFind
[0] == '\\' && FileToFind
[1] == 0)
329 || (FileToFind
[0] == '.' && FileToFind
[1] == 0))
331 /* it's root : complete essentials fields then return ok */
333 memset (Fcb
, 0, sizeof (VFATFCB
));
334 memset (Fcb
->entry
.Filename
, ' ', 11);
336 Fcb
->PathName
[0]='\\';
337 Fcb
->ObjectName
= &Fcb
->PathName
[1];
338 Fcb
->entry
.FileSize
= DeviceExt
->rootDirectorySectors
* BLOCKSIZE
;
339 Fcb
->entry
.Attrib
= FILE_ATTRIBUTE_DIRECTORY
;
340 if (DeviceExt
->FatType
== FAT32
)
341 Fcb
->entry
.FirstCluster
= 2;
343 Fcb
->entry
.FirstCluster
= 1;
345 *StartSector
= StartingSector
;
348 DPRINT("FindFile: new Pathname %S, new Objectname %S)\n",Fcb
->PathName
, Fcb
->ObjectName
);
349 return (STATUS_SUCCESS
);
354 DPRINT ("Parent->entry.FileSize %x\n", Parent
->entry
.FileSize
);
357 if (DeviceExt
->FatType
== FAT32
)
358 NextCluster
= Parent
->entry
.FirstCluster
359 + Parent
->entry
.FirstClusterHigh
* 65536;
361 NextCluster
= Parent
->entry
.FirstCluster
;
362 StartingSector
= ClusterToSector (DeviceExt
, NextCluster
);
363 if (Parent
->entry
.FirstCluster
== 1 && DeviceExt
->FatType
!= FAT32
)
365 /* read of root directory in FAT16 or FAT12 */
366 StartingSector
= DeviceExt
->rootStart
;
369 block
= ExAllocatePool (NonPagedPool
, BLOCKSIZE
);
370 if (StartSector
&& (*StartSector
))
371 StartingSector
= *StartSector
;
372 i
= (Entry
) ? (*Entry
) : 0;
373 for (j
= 0; j
< Size
; j
++)
375 /* FIXME: Check status */
376 VfatReadSectors (DeviceExt
->StorageDevice
, StartingSector
, 1, block
);
378 for (i
= (Entry
) ? (*Entry
) : 0; i
< ENTRIES_PER_SECTOR
; i
++)
380 if (IsVolEntry ((PVOID
) block
, i
))
382 if (IsLastEntry ((PVOID
) block
, i
))
385 *StartSector
= StartingSector
;
389 return (STATUS_UNSUCCESSFUL
);
392 ((PVOID
) block
, &i
, name
, &j
, DeviceExt
, &StartingSector
))
394 if (wstrcmpjoki (name
, FileToFind
))
396 /* In the case of a long filename, the firstcluster is
397 stored in the next record -- where it's short name is */
398 if (((FATDirEntry
*) block
)[i
].Attrib
== 0x0f)
400 if (i
== (ENTRIES_PER_SECTOR
))
402 /* FIXME: Check status */
403 GetNextSector (DeviceExt
, StartingSector
, &StartingSector
, FALSE
);
405 /* FIXME: Check status */
406 VfatReadSectors (DeviceExt
->StorageDevice
,
407 StartingSector
, 1, block
);
410 if (Parent
&& Parent
->PathName
)
412 len
= wcslen(Parent
->PathName
);
414 memcpy(Fcb
->PathName
, Parent
->PathName
, len
*sizeof(WCHAR
));
415 Fcb
->ObjectName
=&Fcb
->PathName
[len
];
416 if (len
!= 1 || Fcb
->PathName
[0] != '\\')
418 Fcb
->ObjectName
[0] = '\\';
419 Fcb
->ObjectName
= &Fcb
->ObjectName
[1];
424 Fcb
->ObjectName
=Fcb
->PathName
;
425 Fcb
->ObjectName
[0]='\\';
426 Fcb
->ObjectName
=&Fcb
->ObjectName
[1];
429 memcpy (&Fcb
->entry
, &((FATDirEntry
*) block
)[i
],
430 sizeof (FATDirEntry
));
431 vfat_wcsncpy (Fcb
->ObjectName
, name
, MAX_PATH
);
433 *StartSector
= StartingSector
;
437 DPRINT("FindFile: new Pathname %S, new Objectname %S)\n",Fcb
->PathName
, Fcb
->ObjectName
);
438 return (STATUS_SUCCESS
);
442 /* not found in this sector, try next : */
444 /* directory can be fragmented although it is best to keep them
445 unfragmented. Should we change this to also use GetNextSector?
446 GetNextSector was originally implemented to handle the case above */
450 /* FIXME: Check status */
451 GetNextSector (DeviceExt
, StartingSector
, &StartingSector
, FALSE
);
453 if ((Parent
!= NULL
&& Parent
->entry
.FirstCluster
!= 1)
454 || DeviceExt
->FatType
== FAT32
)
456 if (StartingSector
== ClusterToSector (DeviceExt
, NextCluster
+ 1))
458 Status
= GetNextCluster (DeviceExt
, NextCluster
, &NextCluster
,
460 if (NextCluster
== 0 || NextCluster
== 0xffffffff)
463 *StartSector
= StartingSector
;
467 return (STATUS_UNSUCCESSFUL
);
469 StartingSector
= ClusterToSector (DeviceExt
, NextCluster
);
474 *StartSector
= StartingSector
;
478 return (STATUS_UNSUCCESSFUL
);
482 vfatMakeAbsoluteFilename (PFILE_OBJECT pFileObject
,
483 PWSTR pRelativeFileName
,
484 PWSTR
*pAbsoluteFilename
)
490 DbgPrint ("try related for %S\n", pRelativeFileName
);
491 ccb
= pFileObject
->FsContext2
;
496 /* verify related object is a directory and target name
497 don't start with \. */
498 if (!(fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
499 || (pRelativeFileName
[0] != '\\'))
501 return STATUS_INVALID_PARAMETER
;
504 /* construct absolute path name */
505 assert (wcslen (fcb
->PathName
) + 1 + wcslen (pRelativeFileName
) + 1
507 rcName
= ExAllocatePool (NonPagedPool
, MAX_PATH
);
508 wcscpy (rcName
, fcb
->PathName
);
509 wcscat (rcName
, L
"\\");
510 wcscat (rcName
, pRelativeFileName
);
511 *pAbsoluteFilename
= rcName
;
513 return STATUS_SUCCESS
;
517 VfatOpenFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
520 * FUNCTION: Opens a file
526 PWSTR AbsFileName
= NULL
;
528 DPRINT ("VfatOpenFile(%08lx, %08lx, %S)\n", DeviceExt
, FileObject
, FileName
);
530 if (FileObject
->RelatedFileObject
)
532 DPRINT ("Converting relative filename to absolute filename\n");
533 Status
= vfatMakeAbsoluteFilename (FileObject
->RelatedFileObject
,
536 FileName
= AbsFileName
;
539 //FIXME: Get cannonical path name (remove .'s, ..'s and extra separators)
541 DPRINT ("PathName to open: %S\n", FileName
);
543 /* try first to find an existing FCB in memory */
544 DPRINT ("Checking for existing FCB in memory\n");
545 Fcb
= vfatGrabFCBFromTable (DeviceExt
, FileName
);
548 DPRINT ("No existing FCB found, making a new one if file exists.\n");
549 Status
= vfatGetFCBForFile (DeviceExt
, &ParentFcb
, &Fcb
, FileName
);
550 if (ParentFcb
!= NULL
)
552 vfatReleaseFCB (DeviceExt
, ParentFcb
);
554 if (!NT_SUCCESS (Status
))
556 DPRINT ("Could not make a new FCB, status: %x\n", Status
);
559 ExFreePool (AbsFileName
);
565 DPRINT ("Attaching FCB to fileObject\n");
566 Status
= vfatAttachFCBToFileObject (DeviceExt
, Fcb
, FileObject
);
569 ExFreePool (AbsFileName
);
575 VfatCreateFile (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
577 * FUNCTION: Create or open a file
580 PIO_STACK_LOCATION Stack
;
581 PFILE_OBJECT FileObject
;
582 NTSTATUS Status
= STATUS_SUCCESS
;
583 PDEVICE_EXTENSION DeviceExt
;
584 ULONG RequestedDisposition
, RequestedOptions
;
589 Stack
= IoGetCurrentIrpStackLocation (Irp
);
591 RequestedDisposition
= ((Stack
->Parameters
.Create
.Options
>> 24) & 0xff);
593 Stack
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
594 if ((RequestedOptions
& FILE_DIRECTORY_FILE
)
595 && RequestedDisposition
== FILE_SUPERSEDE
)
596 return STATUS_INVALID_PARAMETER
;
597 FileObject
= Stack
->FileObject
;
598 DeviceExt
= DeviceObject
->DeviceExtension
;
602 * Check for illegal characters in the file name
604 c
= FileObject
->FileName
.Buffer
;
607 if (*c
== L
'*' || *c
== L
'?')
609 Irp
->IoStatus
.Information
= 0;
610 Irp
->IoStatus
.Status
= STATUS_OBJECT_NAME_INVALID
;
611 return(STATUS_OBJECT_NAME_INVALID
);
616 Status
= VfatOpenFile (DeviceExt
, FileObject
, FileObject
->FileName
.Buffer
);
619 * If the directory containing the file to open doesn't exist then
622 Irp
->IoStatus
.Information
= 0;
623 if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
)
625 Irp
->IoStatus
.Status
= Status
;
629 if (!NT_SUCCESS (Status
))
632 * If the file open failed then create the required file
634 if (RequestedDisposition
== FILE_CREATE
||
635 RequestedDisposition
== FILE_OPEN_IF
||
636 RequestedDisposition
== FILE_OVERWRITE_IF
||
637 RequestedDisposition
== FILE_SUPERSEDE
)
641 addEntry (DeviceExt
, FileObject
, RequestedOptions
,
643 Create
.FileAttributes
& FILE_ATTRIBUTE_VALID_FLAGS
));
644 if (NT_SUCCESS (Status
))
645 Irp
->IoStatus
.Information
= FILE_CREATED
;
646 /* FIXME set size if AllocationSize requested */
647 /* FIXME set extended attributes? */
648 /* FIXME set share access */
649 /* IoSetShareAccess(DesiredAccess,ShareAccess,FileObject,
650 * ((PVfatCCB)(FileObject->FsContext2))->pFcb->FCBShareAccess);
657 * Otherwise fail if the caller wanted to create a new file
659 if (RequestedDisposition
== FILE_CREATE
)
661 Irp
->IoStatus
.Information
= FILE_EXISTS
;
662 Status
= STATUS_OBJECT_NAME_COLLISION
;
664 pCcb
= FileObject
->FsContext2
;
667 * If requested then delete the file and create a new one with the
670 if (RequestedDisposition
== FILE_SUPERSEDE
)
672 ULONG Cluster
, NextCluster
;
673 /* FIXME set size to 0 and free clusters */
674 pFcb
->entry
.FileSize
= 0;
675 if (DeviceExt
->FatType
== FAT32
)
676 Cluster
= pFcb
->entry
.FirstCluster
677 + pFcb
->entry
.FirstClusterHigh
* 65536;
679 Cluster
= pFcb
->entry
.FirstCluster
;
680 pFcb
->entry
.FirstCluster
= 0;
681 pFcb
->entry
.FirstClusterHigh
= 0;
682 updEntry (DeviceExt
, FileObject
);
683 while (Cluster
!= 0xffffffff && Cluster
> 1)
685 Status
= GetNextCluster (DeviceExt
, Cluster
, &NextCluster
, TRUE
);
686 WriteCluster (DeviceExt
, Cluster
, 0);
687 Cluster
= NextCluster
;
692 * Check the file has the requested attributes
694 if ((RequestedOptions
& FILE_NON_DIRECTORY_FILE
)
695 && (pFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
697 Status
= STATUS_FILE_IS_A_DIRECTORY
;
699 if ((RequestedOptions
& FILE_DIRECTORY_FILE
)
700 && !(pFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
702 Status
= STATUS_NOT_A_DIRECTORY
;
704 /* FIXME : test share access */
705 /* FIXME : test write access if requested */
706 if (!NT_SUCCESS (Status
))
707 VfatCloseFile (DeviceExt
, FileObject
);
709 Irp
->IoStatus
.Information
= FILE_OPENED
;
710 /* FIXME : make supersed or overwrite if requested */
713 Irp
->IoStatus
.Status
= Status
;
720 VfatCreate (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
722 * FUNCTION: Create or open a file
725 NTSTATUS Status
= STATUS_SUCCESS
;
726 PDEVICE_EXTENSION DeviceExt
;
728 assert (DeviceObject
);
731 if (DeviceObject
->Size
== sizeof (DEVICE_OBJECT
))
733 /* DeviceObject represents FileSystem instead of logical volume */
734 DbgPrint ("FsdCreate called with file system\n");
735 Irp
->IoStatus
.Status
= Status
;
736 Irp
->IoStatus
.Information
= FILE_OPENED
;
737 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
741 DeviceExt
= DeviceObject
->DeviceExtension
;
743 ExAcquireResourceExclusiveLite (&DeviceExt
->DirResource
, TRUE
);
745 Status
= VfatCreateFile (DeviceObject
, Irp
);
747 ExReleaseResourceLite (&DeviceExt
->DirResource
);
749 Irp
->IoStatus
.Status
= Status
;
750 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);