1 /* $Id: create.c,v 1.12 2001/01/08 02:14:06 dwelch 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 /* FUNCTIONS ****************************************************************/
25 IsLastEntry (PVOID Block
, ULONG Offset
)
27 * FUNCTION: Determine if the given directory entry is the last
30 return (((FATDirEntry
*) Block
)[Offset
].Filename
[0] == 0);
34 IsVolEntry (PVOID Block
, ULONG Offset
)
36 * FUNCTION: Determine if the given directory entry is a vol entry
39 if ((((FATDirEntry
*) Block
)[Offset
].Attrib
) == 0x28)
46 IsDeletedEntry (PVOID Block
, ULONG Offset
)
48 * FUNCTION: Determines if the given entry is a deleted one
51 /* Checks special character */
53 return ((((FATDirEntry
*) Block
)[Offset
].Filename
[0] == 0xe5) ||
54 (((FATDirEntry
*) Block
)[Offset
].Filename
[0] == 0));
58 GetEntryName (PVOID Block
, PULONG _Offset
, PWSTR Name
, PULONG _jloop
,
59 PDEVICE_EXTENSION DeviceExt
, ULONG
* _StartingSector
)
61 * FUNCTION: Retrieves the file name, be it in short or long file name format
66 ULONG Offset
= *_Offset
;
67 ULONG StartingSector
= *_StartingSector
;
68 ULONG jloop
= *_jloop
;
71 test
= (FATDirEntry
*) Block
;
72 test2
= (slot
*) Block
;
76 if (IsDeletedEntry (Block
, Offset
))
81 if (test2
[Offset
].attr
== 0x0f)
83 vfat_initstr (Name
, 256);
84 vfat_wcsncpy (Name
, test2
[Offset
].name0_4
, 5);
85 vfat_wcsncat (Name
, test2
[Offset
].name5_10
, 5, 6);
86 vfat_wcsncat (Name
, test2
[Offset
].name11_12
, 11, 2);
89 while ((test2
[Offset
].id
!= 0x41) && (test2
[Offset
].id
!= 0x01) &&
90 (test2
[Offset
].attr
> 0))
93 if (Offset
== ENTRIES_PER_SECTOR
)
96 StartingSector
++; //FIXME : nor always the next sector
98 /* FIXME: Check status */
99 VfatReadSectors (DeviceExt
->StorageDevice
,
100 StartingSector
, 1, Block
);
101 test2
= (slot
*) Block
;
104 vfat_movstr (Name
, 13, 0, cpos
* 13);
105 vfat_wcsncpy (Name
, test2
[Offset
].name0_4
, 5);
106 vfat_wcsncat (Name
, test2
[Offset
].name5_10
, 5, 6);
107 vfat_wcsncat (Name
, test2
[Offset
].name11_12
, 11, 2);
111 if (IsDeletedEntry (Block
, Offset
+ 1))
116 *_StartingSector
= StartingSector
;
122 *_StartingSector
= StartingSector
;
127 RtlAnsiToUnicode (Name
, test
[Offset
].Filename
, 8);
128 if (test
[Offset
].Ext
[0] != ' ')
130 RtlCatAnsiToUnicode (Name
, ".", 1);
132 RtlCatAnsiToUnicode (Name
, test
[Offset
].Ext
, 3);
140 ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt
, PVPB Vpb
)
142 * FUNCTION: Read the volume label
149 ULONG StartingSector
;
152 Size
= DeviceExt
->rootDirectorySectors
; //FIXME : in fat32, no limit
153 StartingSector
= DeviceExt
->rootStart
;
156 block
= ExAllocatePool (NonPagedPool
, BLOCKSIZE
);
157 DPRINT ("FindFile : start at sector %lx, entry %ld\n", StartingSector
, i
);
158 for (j
= 0; j
< Size
; j
++)
160 /* FIXME: Check status */
161 VfatReadSectors (DeviceExt
->StorageDevice
, StartingSector
, 1, block
);
163 for (i
= 0; i
< ENTRIES_PER_SECTOR
; i
++)
165 if (IsVolEntry ((PVOID
) block
, i
))
167 FATDirEntry
*test
= (FATDirEntry
*) block
;
169 /* copy volume label */
170 RtlAnsiToUnicode (Vpb
->VolumeLabel
, test
[i
].Filename
, 8);
171 RtlCatAnsiToUnicode (Vpb
->VolumeLabel
, test
[i
].Ext
, 3);
172 Vpb
->VolumeLabelLength
= wcslen (Vpb
->VolumeLabel
);
175 return (STATUS_SUCCESS
);
177 if (IsLastEntry ((PVOID
) block
, i
))
179 *(Vpb
->VolumeLabel
) = 0;
180 Vpb
->VolumeLabelLength
= 0;
182 return (STATUS_UNSUCCESSFUL
);
185 // not found in this sector, try next :
187 /* directory can be fragmented although it is best to keep them
190 if (DeviceExt
->FatType
== FAT32
)
192 if (StartingSector
== ClusterToSector (DeviceExt
, NextCluster
+ 1))
194 NextCluster
= GetNextCluster (DeviceExt
, NextCluster
);
195 if (NextCluster
== 0 || NextCluster
== 0xffffffff)
197 *(Vpb
->VolumeLabel
) = 0;
198 Vpb
->VolumeLabelLength
= 0;
200 return (STATUS_UNSUCCESSFUL
);
202 StartingSector
= ClusterToSector (DeviceExt
, NextCluster
);
206 *(Vpb
->VolumeLabel
) = 0;
207 Vpb
->VolumeLabelLength
= 0;
209 return (STATUS_UNSUCCESSFUL
);
214 FindFile (PDEVICE_EXTENSION DeviceExt
, PVFATFCB Fcb
,
215 PVFATFCB Parent
, PWSTR FileToFind
, ULONG
* StartSector
,
218 * FUNCTION: Find a file
225 ULONG StartingSector
;
229 DPRINT ("FindFile(Parent %x, FileToFind '%S')\n", Parent
, FileToFind
);
231 if (wcslen (FileToFind
) == 0)
234 TempStr
[0] = (WCHAR
) '.';
236 FileToFind
= (PWSTR
) & TempStr
;
240 DPRINT ("Parent->entry.FirstCluster %d\n", Parent
->entry
.FirstCluster
);
243 DPRINT ("FindFile '%S'\n", FileToFind
);
244 if (Parent
== NULL
|| Parent
->entry
.FirstCluster
== 1)
247 Size
= DeviceExt
->rootDirectorySectors
; /* FIXME : in fat32, no limit */
248 StartingSector
= DeviceExt
->rootStart
;
250 if (FileToFind
[0] == 0 || (FileToFind
[0] == '\\' && FileToFind
[1] == 0)
251 || (FileToFind
[0] == '.' && FileToFind
[1] == 0))
253 /* it's root : complete essentials fields then return ok */
255 memset (Fcb
, 0, sizeof (VFATFCB
));
256 memset (Fcb
->entry
.Filename
, ' ', 11);
257 Fcb
->entry
.FileSize
= DeviceExt
->rootDirectorySectors
* BLOCKSIZE
;
258 Fcb
->entry
.Attrib
= FILE_ATTRIBUTE_DIRECTORY
;
259 if (DeviceExt
->FatType
== FAT32
)
260 Fcb
->entry
.FirstCluster
= 2;
262 Fcb
->entry
.FirstCluster
= 1; /* FIXME : is 1 the good value for mark root? */
264 *StartSector
= StartingSector
;
267 return (STATUS_SUCCESS
);
272 DPRINT ("Parent->entry.FileSize %x\n", Parent
->entry
.FileSize
);
275 if (DeviceExt
->FatType
== FAT32
)
276 NextCluster
= Parent
->entry
.FirstCluster
277 + Parent
->entry
.FirstClusterHigh
* 65536;
279 NextCluster
= Parent
->entry
.FirstCluster
;
280 StartingSector
= ClusterToSector (DeviceExt
, NextCluster
);
281 if (Parent
->entry
.FirstCluster
== 1 && DeviceExt
->FatType
!= FAT32
)
283 /* read of root directory in FAT16 or FAT12 */
284 StartingSector
= DeviceExt
->rootStart
;
288 block
= ExAllocatePool (NonPagedPool
, BLOCKSIZE
);
290 if (StartSector
&& (*StartSector
))
291 StartingSector
= *StartSector
;
292 i
= (Entry
) ? (*Entry
) : 0;
293 DPRINT ("FindFile : start at sector %lx, entry %ld\n", StartingSector
, i
);
294 for (j
= 0; j
< Size
; j
++)
296 /* FIXME: Check status */
297 VfatReadSectors (DeviceExt
->StorageDevice
, StartingSector
, 1, block
);
299 for (i
= (Entry
) ? (*Entry
) : 0; i
< ENTRIES_PER_SECTOR
; i
++)
301 if (IsVolEntry ((PVOID
) block
, i
))
303 if (IsLastEntry ((PVOID
) block
, i
))
306 *StartSector
= StartingSector
;
310 return (STATUS_UNSUCCESSFUL
);
313 ((PVOID
) block
, &i
, name
, &j
, DeviceExt
, &StartingSector
))
315 if (wstrcmpjoki (name
, FileToFind
))
317 /* In the case of a long filename, the firstcluster is stored in
318 the next record -- where it's short name is */
319 if (((FATDirEntry
*) block
)[i
].Attrib
== 0x0f)
321 if (i
== (ENTRIES_PER_SECTOR
))
323 /* entry is in next sector */
325 /* FIXME : treat case of next sector fragmented */
326 /* FIXME: Check status */
327 VfatReadSectors (DeviceExt
->StorageDevice
,
328 StartingSector
, 1, block
);
331 memcpy (&Fcb
->entry
, &((FATDirEntry
*) block
)[i
],
332 sizeof (FATDirEntry
));
333 vfat_wcsncpy (Fcb
->ObjectName
, name
, MAX_PATH
);
335 *StartSector
= StartingSector
;
339 return (STATUS_SUCCESS
);
343 /* not found in this sector, try next : */
345 /* directory can be fragmented although it is best to keep them
350 if ((Parent
!= NULL
&& Parent
->entry
.FirstCluster
!= 1)
351 || DeviceExt
->FatType
== FAT32
)
353 if (StartingSector
== ClusterToSector (DeviceExt
, NextCluster
+ 1))
355 NextCluster
= GetNextCluster (DeviceExt
, NextCluster
);
356 if (NextCluster
== 0 || NextCluster
== 0xffffffff)
359 *StartSector
= StartingSector
;
363 return (STATUS_UNSUCCESSFUL
);
365 StartingSector
= ClusterToSector (DeviceExt
, NextCluster
);
370 *StartSector
= StartingSector
;
374 return (STATUS_UNSUCCESSFUL
);
380 FsdOpenFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
383 * FUNCTION: Opens a file
386 PWSTR current
= NULL
;
390 PVFATFCB Fcb
, pRelFcb
;
392 PVFATCCB newCCB
, pRelCcb
;
394 PFILE_OBJECT pRelFileObject
;
395 PWSTR AbsFileName
= NULL
;
397 PLIST_ENTRY current_entry
;
399 ULONG BytesPerCluster
;
401 DPRINT ("FsdOpenFile(%08lx, %08lx, %S)\n", DeviceExt
, FileObject
, FileName
);
403 /* FIXME : treat relative name */
404 if (FileObject
->RelatedFileObject
)
406 DbgPrint ("try related for %S\n", FileName
);
407 pRelFileObject
= FileObject
->RelatedFileObject
;
408 pRelCcb
= pRelFileObject
->FsContext2
;
410 pRelFcb
= pRelCcb
->pFcb
;
413 * verify related object is a directory and target name don't start with
416 if (!(pRelFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
417 || (FileName
[0] != '\\'))
419 Status
= STATUS_INVALID_PARAMETER
;
422 /* construct absolute path name */
423 AbsFileName
= ExAllocatePool (NonPagedPool
, MAX_PATH
);
424 for (i
= 0; pRelFcb
->PathName
[i
]; i
++)
425 AbsFileName
[i
] = pRelFcb
->PathName
[i
];
426 AbsFileName
[i
++] = '\\';
427 for (j
= 0; FileName
[j
] && i
< MAX_PATH
; j
++)
428 AbsFileName
[i
++] = FileName
[j
];
429 assert (i
< MAX_PATH
);
431 FileName
= AbsFileName
;
435 * try first to find an existing FCB in memory
439 KeAcquireSpinLock (&DeviceExt
->FcbListLock
, &oldIrql
);
440 current_entry
= DeviceExt
->FcbListHead
.Flink
;
441 while (current_entry
!= &DeviceExt
->FcbListHead
)
443 Fcb
= CONTAINING_RECORD (current_entry
, VFATFCB
, FcbListEntry
);
445 DPRINT ("Scanning %x\n", Fcb
);
446 DPRINT ("Scanning %S\n", Fcb
->PathName
);
448 if (DeviceExt
== Fcb
->pDevExt
&& wstrcmpi (FileName
, Fcb
->PathName
))
451 KeReleaseSpinLock (&DeviceExt
->FcbListLock
, oldIrql
);
452 FileObject
->FsContext
= (PVOID
)&Fcb
->RFCB
;
453 newCCB
= ExAllocatePool (NonPagedPool
, sizeof (VFATCCB
));
454 memset (newCCB
, 0, sizeof (VFATCCB
));
455 FileObject
->FsContext2
= newCCB
;
457 newCCB
->PtrFileObject
= FileObject
;
459 ExFreePool (AbsFileName
);
460 return (STATUS_SUCCESS
);
463 current_entry
= current_entry
->Flink
;
465 KeReleaseSpinLock (&DeviceExt
->FcbListLock
, oldIrql
);
468 DPRINT ("FileName %S\n", FileName
);
472 Fcb
= ExAllocatePool (NonPagedPool
, sizeof (VFATFCB
));
473 memset (Fcb
, 0, sizeof (VFATFCB
));
474 Fcb
->ObjectName
= Fcb
->PathName
;
478 if (*next
== 0) // root
480 memset (Fcb
->entry
.Filename
, ' ', 11);
481 Fcb
->entry
.FileSize
= DeviceExt
->rootDirectorySectors
* BLOCKSIZE
;
482 Fcb
->entry
.Attrib
= FILE_ATTRIBUTE_DIRECTORY
;
483 if (DeviceExt
->FatType
== FAT32
)
484 Fcb
->entry
.FirstCluster
= 2;
486 Fcb
->entry
.FirstCluster
= 1;
487 /* FIXME : is 1 the good value for mark root? */
498 next
= wcschr (next
+ 1, '\\');
505 /* reached the last path component */
506 DPRINT ("exiting: current '%S'\n", current
);
510 DPRINT ("current '%S'\n", current
);
511 Status
= FindFile (DeviceExt
, Fcb
, ParentFcb
, current
, NULL
, NULL
);
512 if (Status
!= STATUS_SUCCESS
)
517 if (ParentFcb
!= NULL
)
518 ExFreePool (ParentFcb
);
520 ExFreePool (AbsFileName
);
522 DPRINT ("error STATUS_OBJECT_PATH_NOT_FOUND\n");
523 return STATUS_OBJECT_PATH_NOT_FOUND
;
527 if (ParentFcb
== NULL
)
530 Fcb
= ExAllocatePool (NonPagedPool
, sizeof (VFATFCB
));
531 memset (Fcb
, 0, sizeof (VFATFCB
));
532 Fcb
->ObjectName
= Fcb
->PathName
;
540 /* searching for last path component */
541 DPRINT ("current '%S'\n", current
);
542 Status
= FindFile (DeviceExt
, Fcb
, ParentFcb
, current
, NULL
, NULL
);
543 if (Status
!= STATUS_SUCCESS
)
545 /* file does not exist */
549 if (ParentFcb
!= NULL
)
550 ExFreePool (ParentFcb
);
552 ExFreePool (AbsFileName
);
554 return STATUS_OBJECT_NAME_NOT_FOUND
;
558 if (ParentFcb
== NULL
)
561 Fcb
= ExAllocatePool (NonPagedPool
, sizeof (VFATFCB
));
562 memset (Fcb
, 0, sizeof (VFATFCB
));
563 Fcb
->ObjectName
= Fcb
->PathName
;
570 FileObject
->Flags
= FileObject
->Flags
|
571 FO_FCB_IS_VALID
| FO_DIRECT_CACHE_PAGING_READ
;
572 FileObject
->FsContext
= (PVOID
)&ParentFcb
->RFCB
;
573 newCCB
= ExAllocatePool (NonPagedPool
, sizeof (VFATCCB
));
574 memset (newCCB
, 0, sizeof (VFATCCB
));
575 FileObject
->FsContext2
= newCCB
;
576 newCCB
->pFcb
= ParentFcb
;
577 newCCB
->PtrFileObject
= FileObject
;
578 ParentFcb
->RefCount
++;
579 /* FIXME : initialize all fields in FCB and CCB */
581 KeAcquireSpinLock (&DeviceExt
->FcbListLock
, &oldIrql
);
582 InsertTailList (&DeviceExt
->FcbListHead
, &ParentFcb
->FcbListEntry
);
583 KeReleaseSpinLock (&DeviceExt
->FcbListLock
, oldIrql
);
585 vfat_wcsncpy (ParentFcb
->PathName
, FileName
, MAX_PATH
);
586 ParentFcb
->ObjectName
= ParentFcb
->PathName
+ (current
- FileName
);
587 ParentFcb
->pDevExt
= DeviceExt
;
588 BytesPerCluster
= DeviceExt
->Boot
->SectorsPerCluster
* BLOCKSIZE
;
589 if (BytesPerCluster
>= PAGESIZE
)
591 Status
= CcInitializeFileCache(FileObject
, &ParentFcb
->RFCB
.Bcb
,
596 Status
= CcInitializeFileCache(FileObject
, &ParentFcb
->RFCB
.Bcb
,
599 if (!NT_SUCCESS(Status
))
601 DbgPrint("CcInitializeFileCache failed\n");
604 DPRINT ("file open, fcb=%x\n", ParentFcb
);
605 DPRINT ("FileSize %d\n", ParentFcb
->entry
.FileSize
);
609 ExFreePool (AbsFileName
);
612 return (STATUS_SUCCESS
);
617 VfatCreateFile (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
619 * FUNCTION: Create or open a file
622 PIO_STACK_LOCATION Stack
;
623 PFILE_OBJECT FileObject
;
624 NTSTATUS Status
= STATUS_SUCCESS
;
625 PDEVICE_EXTENSION DeviceExt
;
626 ULONG RequestedDisposition
, RequestedOptions
;
630 Stack
= IoGetCurrentIrpStackLocation (Irp
);
632 RequestedDisposition
= ((Stack
->Parameters
.Create
.Options
>> 24) & 0xff);
634 Stack
->Parameters
.Create
.Options
& FILE_VALID_OPTION_FLAGS
;
635 if ((RequestedOptions
& FILE_DIRECTORY_FILE
)
636 && RequestedDisposition
== FILE_SUPERSEDE
)
637 return STATUS_INVALID_PARAMETER
;
638 FileObject
= Stack
->FileObject
;
639 DeviceExt
= DeviceObject
->DeviceExtension
;
642 Status
= FsdOpenFile (DeviceExt
, FileObject
, FileObject
->FileName
.Buffer
);
645 Irp
->IoStatus
.Information
= 0;
646 if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
)
648 Irp
->IoStatus
.Status
= Status
;
653 if (!NT_SUCCESS (Status
))
655 if (RequestedDisposition
== FILE_CREATE
656 || RequestedDisposition
== FILE_OPEN_IF
657 || RequestedDisposition
== FILE_OVERWRITE_IF
658 || RequestedDisposition
== FILE_SUPERSEDE
)
663 addEntry (DeviceExt
, FileObject
, RequestedOptions
,
665 Create
.FileAttributes
& FILE_ATTRIBUTE_VALID_FLAGS
));
667 Status
= STATUS_UNSUCCESSFUL
;
669 if (NT_SUCCESS (Status
))
670 Irp
->IoStatus
.Information
= FILE_CREATED
;
671 /* FIXME set size if AllocationSize requested */
672 /* FIXME set extended attributes? */
673 /* FIXME set share access */
674 /* IoSetShareAccess(DesiredAccess,ShareAccess,FileObject,
675 * ((PVfatCCB)(FileObject->FsContext2))->pFcb->FCBShareAccess);
681 if (RequestedDisposition
== FILE_CREATE
)
683 Irp
->IoStatus
.Information
= FILE_EXISTS
;
684 Status
= STATUS_OBJECT_NAME_COLLISION
;
686 pCcb
= FileObject
->FsContext2
;
688 if (RequestedDisposition
== FILE_SUPERSEDE
)
690 ULONG Cluster
, NextCluster
;
691 /* FIXME set size to 0 and free clusters */
692 pFcb
->entry
.FileSize
= 0;
693 if (DeviceExt
->FatType
== FAT32
)
694 Cluster
= pFcb
->entry
.FirstCluster
695 + pFcb
->entry
.FirstClusterHigh
* 65536;
697 Cluster
= pFcb
->entry
.FirstCluster
;
698 pFcb
->entry
.FirstCluster
= 0;
699 pFcb
->entry
.FirstClusterHigh
= 0;
700 // updEntry (DeviceExt, FileObject);
701 while (Cluster
!= 0xffffffff && Cluster
> 1)
703 NextCluster
= GetNextCluster (DeviceExt
, Cluster
);
704 WriteCluster (DeviceExt
, Cluster
, 0);
705 Cluster
= NextCluster
;
708 if ((RequestedOptions
& FILE_NON_DIRECTORY_FILE
)
709 && (pFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
711 Status
= STATUS_FILE_IS_A_DIRECTORY
;
713 if ((RequestedOptions
& FILE_DIRECTORY_FILE
)
714 && !(pFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
716 Status
= STATUS_NOT_A_DIRECTORY
;
718 /* FIXME : test share access */
719 /* FIXME : test write access if requested */
720 if (!NT_SUCCESS (Status
))
721 VfatCloseFile (DeviceExt
, FileObject
);
723 Irp
->IoStatus
.Information
= FILE_OPENED
;
724 /* FIXME : make supersed or overwrite if requested */
727 Irp
->IoStatus
.Status
= Status
;
734 VfatCreate (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
736 * FUNCTION: Create or open a file
739 NTSTATUS Status
= STATUS_SUCCESS
;
740 PDEVICE_EXTENSION DeviceExt
;
742 assert (DeviceObject
);
745 if (DeviceObject
->Size
== sizeof (DEVICE_OBJECT
))
747 /* DeviceObject represents FileSystem instead of logical volume */
748 DbgPrint ("FsdCreate called with file system\n");
749 Irp
->IoStatus
.Status
= Status
;
750 Irp
->IoStatus
.Information
= FILE_OPENED
;
751 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
755 DeviceExt
= DeviceObject
->DeviceExtension
;
757 ExAcquireResourceExclusiveLite (&DeviceExt
->DirResource
, TRUE
);
759 Status
= VfatCreateFile (DeviceObject
, Irp
);
761 ExReleaseResourceLite (&DeviceExt
->DirResource
);
763 Irp
->IoStatus
.Status
= Status
;
764 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);