1 /* $Id: create.c,v 1.6 2000/07/07 02:14:14 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>
14 #include <ddk/cctypes.h>
23 /* FUNCTIONS ****************************************************************/
25 BOOLEAN
IsLastEntry(PVOID Block
, ULONG Offset
)
27 * FUNCTION: Determine if the given directory entry is the last
30 return(((FATDirEntry
*)Block
)[Offset
].Filename
[0] == 0);
33 BOOLEAN
IsVolEntry(PVOID Block
, ULONG Offset
)
35 * FUNCTION: Determine if the given directory entry is a vol entry
38 if( (((FATDirEntry
*)Block
)[Offset
].Attrib
)==0x28 ) return TRUE
;
42 BOOLEAN
IsDeletedEntry(PVOID Block
, ULONG Offset
)
44 * FUNCTION: Determines if the given entry is a deleted one
47 /* Checks special character */
49 return ((((FATDirEntry
*)Block
)[Offset
].Filename
[0] == 0xe5) || (((FATDirEntry
*)Block
)[Offset
].Filename
[0] == 0));
52 BOOLEAN
GetEntryName(PVOID Block
, PULONG _Offset
, PWSTR Name
, PULONG _jloop
,
53 PDEVICE_EXTENSION DeviceExt
, ULONG
* _StartingSector
)
55 * FUNCTION: Retrieves the file name, be it in short or long file name format
60 ULONG Offset
= *_Offset
;
61 ULONG StartingSector
= *_StartingSector
;
62 ULONG jloop
= *_jloop
;
65 test
= (FATDirEntry
*)Block
;
66 test2
= (slot
*)Block
;
70 if (IsDeletedEntry(Block
,Offset
))
75 if(test2
[Offset
].attr
== 0x0f)
77 vfat_initstr(Name
, 256);
78 vfat_wcsncpy(Name
,test2
[Offset
].name0_4
,5);
79 vfat_wcsncat(Name
,test2
[Offset
].name5_10
,5,6);
80 vfat_wcsncat(Name
,test2
[Offset
].name11_12
,11,2);
83 while((test2
[Offset
].id
!=0x41) && (test2
[Offset
].id
!=0x01) &&
84 (test2
[Offset
].attr
>0))
87 if(Offset
==ENTRIES_PER_SECTOR
) {
89 StartingSector
++;//FIXME : nor always the next sector
91 VFATReadSectors(DeviceExt
->StorageDevice
,StartingSector
,1,Block
);
92 test2
= (slot
*)Block
;
95 vfat_movstr(Name
, 13, 0, cpos
*13);
96 vfat_wcsncpy(Name
, test2
[Offset
].name0_4
, 5);
97 vfat_wcsncat(Name
,test2
[Offset
].name5_10
,5,6);
98 vfat_wcsncat(Name
,test2
[Offset
].name11_12
,11,2);
102 if (IsDeletedEntry(Block
,Offset
+1))
107 *_StartingSector
= StartingSector
;
113 *_StartingSector
= StartingSector
;
118 RtlAnsiToUnicode(Name
,test
[Offset
].Filename
,8);
119 if (test
[Offset
].Ext
[0]!=' ')
121 RtlCatAnsiToUnicode(Name
,".",1);
123 RtlCatAnsiToUnicode(Name
,test
[Offset
].Ext
,3);
130 NTSTATUS
ReadVolumeLabel(PDEVICE_EXTENSION DeviceExt
, PVPB Vpb
)
132 * FUNCTION: Read the volume label
139 ULONG StartingSector
;
142 Size
= DeviceExt
->rootDirectorySectors
;//FIXME : in fat32, no limit
143 StartingSector
= DeviceExt
->rootStart
;
146 block
= ExAllocatePool(NonPagedPool
,BLOCKSIZE
);
147 DPRINT("FindFile : start at sector %lx, entry %ld\n",StartingSector
,i
);
148 for (j
=0; j
<Size
; j
++)
150 VFATReadSectors(DeviceExt
->StorageDevice
,StartingSector
,1,block
);
152 for (i
=0; i
<ENTRIES_PER_SECTOR
; i
++)
154 if (IsVolEntry((PVOID
)block
,i
))
156 FATDirEntry
*test
= (FATDirEntry
*)block
;
158 /* copy volume label */
159 RtlAnsiToUnicode(Vpb
->VolumeLabel
,test
[i
].Filename
,8);
160 RtlCatAnsiToUnicode(Vpb
->VolumeLabel
,test
[i
].Ext
,3);
161 Vpb
->VolumeLabelLength
= wcslen(Vpb
->VolumeLabel
);
164 return(STATUS_SUCCESS
);
166 if (IsLastEntry((PVOID
)block
,i
))
168 *(Vpb
->VolumeLabel
) = 0;
169 Vpb
->VolumeLabelLength
= 0;
171 return(STATUS_UNSUCCESSFUL
);
174 // not found in this sector, try next :
176 /* directory can be fragmented although it is best to keep them
179 if (DeviceExt
->FatType
==FAT32
)
181 if(StartingSector
==ClusterToSector(DeviceExt
,NextCluster
+1))
183 NextCluster
= GetNextCluster(DeviceExt
,NextCluster
);
184 if (NextCluster
== 0||NextCluster
==0xffffffff)
186 *(Vpb
->VolumeLabel
) = 0;
187 Vpb
->VolumeLabelLength
= 0;
189 return(STATUS_UNSUCCESSFUL
);
191 StartingSector
= ClusterToSector(DeviceExt
,NextCluster
);
195 *(Vpb
->VolumeLabel
) = 0;
196 Vpb
->VolumeLabelLength
= 0;
198 return(STATUS_UNSUCCESSFUL
);
202 NTSTATUS
FindFile(PDEVICE_EXTENSION DeviceExt
, PVFATFCB Fcb
,
203 PVFATFCB Parent
, PWSTR FileToFind
,ULONG
*StartSector
,ULONG
*Entry
)
205 * FUNCTION: Find a file
212 ULONG StartingSector
;
216 DPRINT("FindFile(Parent %x, FileToFind '%S')\n",Parent
,FileToFind
);
218 if (wcslen(FileToFind
)==0)
221 TempStr
[0] = (WCHAR
)'.';
223 FileToFind
=(PWSTR
)&TempStr
;
227 DPRINT("Parent->entry.FirstCluster %d\n",Parent
->entry
.FirstCluster
);
230 DPRINT("FindFile '%S'\n", FileToFind
);
231 if (Parent
== NULL
||Parent
->entry
.FirstCluster
==1)
234 Size
= DeviceExt
->rootDirectorySectors
; /* FIXME : in fat32, no limit */
235 StartingSector
= DeviceExt
->rootStart
;
237 if(FileToFind
[0]==0 ||(FileToFind
[0]=='\\' && FileToFind
[1]==0) ||
238 (FileToFind
[0]=='.' && FileToFind
[1]==0))
240 /* it's root : complete essentials fields then return ok */
242 memset(Fcb
,0,sizeof(VFATFCB
));
243 memset(Fcb
->entry
.Filename
,' ',11);
244 Fcb
->entry
.FileSize
=DeviceExt
->rootDirectorySectors
*BLOCKSIZE
;
245 Fcb
->entry
.Attrib
=FILE_ATTRIBUTE_DIRECTORY
;
246 if (DeviceExt
->FatType
== FAT32
)
247 Fcb
->entry
.FirstCluster
=2;
249 Fcb
->entry
.FirstCluster
=1; /* FIXME : is 1 the good value for mark root? */
251 *StartSector
=StartingSector
;
254 return(STATUS_SUCCESS
);
259 DPRINT("Parent->entry.FileSize %x\n",Parent
->entry
.FileSize
);
262 if (DeviceExt
->FatType
== FAT32
)
263 NextCluster
= Parent
->entry
.FirstCluster
264 +Parent
->entry
.FirstClusterHigh
*65536;
266 NextCluster
= Parent
->entry
.FirstCluster
;
267 StartingSector
= ClusterToSector(DeviceExt
, NextCluster
);
268 if(Parent
->entry
.FirstCluster
==1 && DeviceExt
->FatType
!=FAT32
)
270 /* read of root directory in FAT16 or FAT12 */
271 StartingSector
=DeviceExt
->rootStart
;
275 block
= ExAllocatePool(NonPagedPool
,BLOCKSIZE
);
277 if (StartSector
&& (*StartSector
)) StartingSector
=*StartSector
;
278 i
=(Entry
)?(*Entry
):0;
279 DPRINT("FindFile : start at sector %lx, entry %ld\n",StartingSector
,i
);
280 for (j
=0; j
<Size
; j
++)
282 VFATReadSectors(DeviceExt
->StorageDevice
,StartingSector
,1,block
);
284 for (i
=(Entry
)?(*Entry
):0; i
<ENTRIES_PER_SECTOR
; i
++)
286 if (IsVolEntry((PVOID
)block
,i
))
288 if (IsLastEntry((PVOID
)block
,i
))
290 if(StartSector
) *StartSector
=StartingSector
;
293 return(STATUS_UNSUCCESSFUL
);
295 if (GetEntryName((PVOID
)block
,&i
,name
,&j
,DeviceExt
,&StartingSector
))
297 DPRINT("Comparing '%S' '%S'\n",name
,FileToFind
);
298 if (wstrcmpjoki(name
,FileToFind
))
300 /* In the case of a long filename, the firstcluster is stored in
301 the next record -- where it's short name is */
302 if(((FATDirEntry
*)block
)[i
].Attrib
==0x0f) i
++;
303 if( i
==(ENTRIES_PER_SECTOR
))
305 /* entry is in next sector */
307 /* FIXME : treat case of next sector fragmented */
308 VFATReadSectors(DeviceExt
->StorageDevice
,StartingSector
,1,block
);
311 memcpy(&Fcb
->entry
,&((FATDirEntry
*)block
)[i
],
312 sizeof(FATDirEntry
));
313 vfat_wcsncpy(Fcb
->ObjectName
,name
,MAX_PATH
);
314 if(StartSector
) *StartSector
=StartingSector
;
317 return(STATUS_SUCCESS
);
321 /* not found in this sector, try next : */
323 /* directory can be fragmented although it is best to keep them
327 if ((Parent
!= NULL
&& Parent
->entry
.FirstCluster
!=1)
328 || DeviceExt
->FatType
==FAT32
)
330 if(StartingSector
==ClusterToSector(DeviceExt
,NextCluster
+1))
332 NextCluster
= GetNextCluster(DeviceExt
,NextCluster
);
333 if (NextCluster
== 0||NextCluster
==0xffffffff)
335 if(StartSector
) *StartSector
=StartingSector
;
338 return(STATUS_UNSUCCESSFUL
);
340 StartingSector
= ClusterToSector(DeviceExt
,NextCluster
);
344 if(StartSector
) *StartSector
=StartingSector
;
347 return(STATUS_UNSUCCESSFUL
);
352 NTSTATUS
FsdOpenFile(PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
355 * FUNCTION: Opens a file
358 PWSTR current
= NULL
;
362 PVFATFCB Fcb
,pRelFcb
;
364 PVFATCCB newCCB
,pRelCcb
;
366 PFILE_OBJECT pRelFileObject
;
367 PWSTR AbsFileName
=NULL
;
369 PLIST_ENTRY current_entry
;
372 DPRINT("FsdOpenFile(%08lx, %08lx, %S)\n",
377 /* FIXME : treat relative name */
378 if(FileObject
->RelatedFileObject
)
380 DbgPrint("try related for %S\n",FileName
);
381 pRelFileObject
=FileObject
->RelatedFileObject
;
382 pRelCcb
=pRelFileObject
->FsContext2
;
384 pRelFcb
=pRelCcb
->pFcb
;
387 * verify related object is a directory and target name don't start with \.
389 if( !(pRelFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
390 || (FileName
[0]!= '\\') )
392 Status
=STATUS_INVALID_PARAMETER
;
395 /* construct absolute path name */
396 AbsFileName
=ExAllocatePool(NonPagedPool
,MAX_PATH
);
397 for (i
=0;pRelFcb
->PathName
[i
];i
++)
398 AbsFileName
[i
]=pRelFcb
->PathName
[i
];
399 AbsFileName
[i
++]='\\';
400 for (j
=0;FileName
[j
]&&i
<MAX_PATH
;j
++)
401 AbsFileName
[i
++]=FileName
[j
];
404 FileName
=AbsFileName
;
408 * try first to find an existing FCB in memory
412 KeAcquireSpinLock(&DeviceExt
->FcbListLock
, &oldIrql
);
413 current_entry
= DeviceExt
->FcbListHead
.Flink
;
414 while (current_entry
!= &DeviceExt
->FcbListHead
)
416 Fcb
= CONTAINING_RECORD(current_entry
, VFATFCB
, FcbListEntry
);
418 DPRINT("Scanning %x\n", Fcb
);
419 DPRINT("Scanning %S\n", Fcb
->PathName
);
421 if (DeviceExt
==Fcb
->pDevExt
422 && wstrcmpi(FileName
,Fcb
->PathName
))
425 KeReleaseSpinLock(&DeviceExt
->FcbListLock
, oldIrql
);
426 FileObject
->FsContext
=(PVOID
) &Fcb
->NTRequiredFCB
;
427 newCCB
= ExAllocatePool(NonPagedPool
,sizeof(VFATCCB
));
428 memset(newCCB
,0,sizeof(VFATCCB
));
429 FileObject
->FsContext2
= newCCB
;
431 newCCB
->PtrFileObject
=FileObject
;
432 if(AbsFileName
)ExFreePool(AbsFileName
);
433 return(STATUS_SUCCESS
);
436 current_entry
= current_entry
->Flink
;
438 KeReleaseSpinLock(&DeviceExt
->FcbListLock
, oldIrql
);
441 DPRINT("FileName %S\n", FileName
);
445 Fcb
= ExAllocatePool(NonPagedPool
, sizeof(VFATFCB
));
446 memset(Fcb
,0,sizeof(VFATFCB
));
447 Fcb
->ObjectName
=Fcb
->PathName
;
456 next
= wcschr(next
+1,'\\');
463 /* reached the last path component */
464 DPRINT("exiting: current '%S'\n",current
);
468 DPRINT("current '%S'\n",current
);
469 Status
= FindFile(DeviceExt
,Fcb
,ParentFcb
,current
,NULL
,NULL
);
470 if (Status
!= STATUS_SUCCESS
)
475 if (ParentFcb
!= NULL
)
476 ExFreePool(ParentFcb
);
478 ExFreePool(AbsFileName
);
480 DPRINT("error STATUS_OBJECT_PATH_NOT_FOUND\n");
481 return STATUS_OBJECT_PATH_NOT_FOUND
;
485 if (ParentFcb
== NULL
)
488 Fcb
= ExAllocatePool(NonPagedPool
,sizeof(VFATFCB
));
489 memset(Fcb
,0,sizeof(VFATFCB
));
490 Fcb
->ObjectName
=Fcb
->PathName
;
498 /* searching for last path component */
499 DPRINT("current '%S'\n",current
);
500 Status
= FindFile(DeviceExt
,Fcb
,ParentFcb
,current
,NULL
,NULL
);
501 if (Status
!= STATUS_SUCCESS
)
503 /* file does not exist */
507 if (ParentFcb
!= NULL
)
508 ExFreePool(ParentFcb
);
510 ExFreePool(AbsFileName
);
512 return STATUS_OBJECT_NAME_NOT_FOUND
;
517 if (ParentFcb
== NULL
)
520 Fcb
= ExAllocatePool(NonPagedPool
,sizeof(VFATFCB
));
521 memset(Fcb
,0,sizeof(VFATFCB
));
522 Fcb
->ObjectName
=Fcb
->PathName
;
531 FileObject
->FsContext
=(PVOID
) &ParentFcb
->NTRequiredFCB
;
532 newCCB
= ExAllocatePool(NonPagedPool
,sizeof(VFATCCB
));
533 memset(newCCB
,0,sizeof(VFATCCB
));
534 FileObject
->FsContext2
= newCCB
;
535 newCCB
->pFcb
=ParentFcb
;
536 newCCB
->PtrFileObject
=FileObject
;
537 ParentFcb
->RefCount
++;
538 //FIXME : initialize all fields in FCB and CCB
540 KeAcquireSpinLock(&DeviceExt
->FcbListLock
, &oldIrql
);
541 InsertTailList(&DeviceExt
->FcbListHead
, &ParentFcb
->FcbListEntry
);
542 KeReleaseSpinLock(&DeviceExt
->FcbListLock
, oldIrql
);
544 vfat_wcsncpy(ParentFcb
->PathName
,FileName
,MAX_PATH
);
545 ParentFcb
->ObjectName
=ParentFcb
->PathName
+(current
-FileName
);
546 ParentFcb
->pDevExt
=DeviceExt
;
547 DPRINT("file open, fcb=%x\n",ParentFcb
);
548 DPRINT("FileSize %d\n",ParentFcb
->entry
.FileSize
);
552 ExFreePool(AbsFileName
);
555 return(STATUS_SUCCESS
);
559 NTSTATUS
FsdCreateFile (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
561 * FUNCTION: Create or open a file
564 PIO_STACK_LOCATION Stack
;
565 PFILE_OBJECT FileObject
;
566 NTSTATUS Status
=STATUS_SUCCESS
;
567 PDEVICE_EXTENSION DeviceExt
;
568 ULONG RequestedDisposition
,RequestedOptions
;
572 Stack
= IoGetCurrentIrpStackLocation(Irp
);
574 RequestedDisposition
= ((Stack
->Parameters
.Create
.Options
>>24)&0xff);
575 RequestedOptions
=Stack
->Parameters
.Create
.Options
&FILE_VALID_OPTION_FLAGS
;
576 FileObject
= Stack
->FileObject
;
577 DeviceExt
= DeviceObject
->DeviceExtension
;
580 Status
= FsdOpenFile(DeviceExt
,FileObject
,FileObject
->FileName
.Buffer
);
583 Irp
->IoStatus
.Information
= 0;
584 if (Status
== STATUS_OBJECT_PATH_NOT_FOUND
)
586 Irp
->IoStatus
.Status
= Status
;
591 if(!NT_SUCCESS(Status
))
593 if(RequestedDisposition
==FILE_CREATE
594 ||RequestedDisposition
==FILE_OPEN_IF
595 ||RequestedDisposition
==FILE_OVERWRITE_IF
)
598 Status
=addEntry(DeviceExt
,FileObject
,RequestedOptions
599 ,(Stack
->Parameters
.Create
.FileAttributes
& FILE_ATTRIBUTE_VALID_FLAGS
));
600 if(NT_SUCCESS(Status
))
601 Irp
->IoStatus
.Information
= FILE_CREATED
;
602 // FIXME set size if AllocationSize requested
603 // FIXME set extended attributes ?
604 // FIXME set share access
605 // IoSetShareAccess(DesiredAccess,ShareAccess,FileObject
606 // ,((PVfatCCB)(FileObject->FsContext2))->pFcb->FCBShareAccess);
611 if(RequestedDisposition
==FILE_CREATE
)
613 Irp
->IoStatus
.Information
= FILE_EXISTS
;
614 Status
=STATUS_OBJECT_NAME_COLLISION
;
616 pCcb
=FileObject
->FsContext2
;
618 if( (RequestedOptions
&FILE_NON_DIRECTORY_FILE
)
619 && (pFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
621 Status
=STATUS_FILE_IS_A_DIRECTORY
;
623 if( (RequestedOptions
&FILE_DIRECTORY_FILE
)
624 && !(pFcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
626 Status
=STATUS_NOT_A_DIRECTORY
;
628 // FIXME : test share access
629 // FIXME : test write access if requested
630 if(!NT_SUCCESS(Status
))
631 FsdCloseFile(DeviceExt
,FileObject
);
632 else Irp
->IoStatus
.Information
= FILE_OPENED
;
633 // FIXME : make supersed or overwrite if requested
637 Irp
->IoStatus
.Status
= Status
;
643 NTSTATUS
FsdCreate(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
645 * FUNCTION: Create or open a file
648 NTSTATUS Status
=STATUS_SUCCESS
;
649 PDEVICE_EXTENSION DeviceExt
;
651 assert(DeviceObject
);
654 if (DeviceObject
->Size
==sizeof(DEVICE_OBJECT
))
656 /* DeviceObject represent FileSystem instead of logical volume */
657 DbgPrint("FsdCreate called with file system\n");
658 Irp
->IoStatus
.Status
=Status
;
659 Irp
->IoStatus
.Information
=FILE_OPENED
;
660 IoCompleteRequest(Irp
,IO_NO_INCREMENT
);
664 DeviceExt
= DeviceObject
->DeviceExtension
;
666 ExAcquireResourceExclusiveLite(&DeviceExt
->DirResource
, TRUE
);
668 Status
= FsdCreateFile (DeviceObject
, Irp
);
670 ExReleaseResourceLite(&DeviceExt
->DirResource
);
672 Irp
->IoStatus
.Status
= Status
;
673 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);