2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/filesystems/fastfat/dirwr.c
5 * PURPOSE: VFAT Filesystem : write in directory
6 * PROGRAMMER: Rex Jolliff (rex@lvcablemodem.com)
7 * Herve Poussineau (reactos@poussine.freesurf.fr)
8 * Pierre Schweitzer (pierre@reactos.org)
12 /* INCLUDES *****************************************************************/
20 extern UNICODE_STRING DebugFile
;
24 vfatFCBInitializeCacheFromVolume(
28 PFILE_OBJECT fileObject
;
33 /* Don't re-initialize if already done */
34 if (BooleanFlagOn(fcb
->Flags
, FCB_CACHE_INITIALIZED
))
36 return STATUS_SUCCESS
;
39 ASSERT(vfatFCBIsDirectory(fcb
));
40 ASSERT(fcb
->FileObject
== NULL
);
43 if (!ExIsResourceAcquiredExclusive(&vcb
->DirResource
))
45 ExAcquireResourceExclusiveLite(&vcb
->DirResource
, TRUE
);
49 fileObject
= IoCreateStreamFileObject (NULL
, vcb
->StorageDevice
);
50 if (fileObject
== NULL
)
52 status
= STATUS_INSUFFICIENT_RESOURCES
;
57 if (DebugFile
.Buffer
!= NULL
&& FsRtlIsNameInExpression(&DebugFile
, &fcb
->LongNameU
, FALSE
, NULL
))
59 DPRINT1("Attaching %p to %p (%d)\n", fcb
, fileObject
, fcb
->RefCount
);
63 newCCB
= ExAllocateFromNPagedLookasideList(&VfatGlobalData
->CcbLookasideList
);
66 status
= STATUS_INSUFFICIENT_RESOURCES
;
67 ObDereferenceObject(fileObject
);
70 RtlZeroMemory(newCCB
, sizeof (VFATCCB
));
72 fileObject
->SectionObjectPointer
= &fcb
->SectionObjectPointers
;
73 fileObject
->FsContext
= fcb
;
74 fileObject
->FsContext2
= newCCB
;
75 fileObject
->Vpb
= vcb
->IoVPB
;
76 fcb
->FileObject
= fileObject
;
80 CcInitializeCacheMap(fileObject
,
81 (PCC_FILE_SIZES
)(&fcb
->RFCB
.AllocationSize
),
83 &VfatGlobalData
->CacheMgrCallbacks
,
86 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
88 status
= _SEH2_GetExceptionCode();
89 fcb
->FileObject
= NULL
;
90 ExFreeToNPagedLookasideList(&VfatGlobalData
->CcbLookasideList
, newCCB
);
91 ObDereferenceObject(fileObject
);
94 ExReleaseResourceLite(&vcb
->DirResource
);
100 vfatGrabFCB(vcb
, fcb
);
101 SetFlag(fcb
->Flags
, FCB_CACHE_INITIALIZED
);
102 status
= STATUS_SUCCESS
;
107 ExReleaseResourceLite(&vcb
->DirResource
);
114 * update an existing FAT entry
118 IN PDEVICE_EXTENSION DeviceExt
,
123 LARGE_INTEGER Offset
;
130 if (vfatVolumeIsFatX(DeviceExt
))
132 SizeDirEntry
= sizeof(FATX_DIR_ENTRY
);
133 dirIndex
= pFcb
->startIndex
;
137 SizeDirEntry
= sizeof(FAT_DIR_ENTRY
);
138 dirIndex
= pFcb
->dirIndex
;
141 DPRINT("updEntry dirIndex %u, PathName \'%wZ\'\n", dirIndex
, &pFcb
->PathNameU
);
143 if (vfatFCBIsRoot(pFcb
) || BooleanFlagOn(pFcb
->Flags
, FCB_IS_FAT
| FCB_IS_VOLUME
))
145 return STATUS_SUCCESS
;
148 ASSERT(pFcb
->parentFcb
);
150 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pFcb
->parentFcb
);
151 if (!NT_SUCCESS(Status
))
156 Offset
.u
.HighPart
= 0;
157 Offset
.u
.LowPart
= dirIndex
* SizeDirEntry
;
160 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, SizeDirEntry
, PIN_WAIT
, &Context
, (PVOID
*)&PinEntry
);
162 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
164 DPRINT1("Failed write to \'%wZ\'.\n", &pFcb
->parentFcb
->PathNameU
);
165 _SEH2_YIELD(return _SEH2_GetExceptionCode());
169 pFcb
->Flags
&= ~FCB_IS_DIRTY
;
170 RtlCopyMemory(PinEntry
, &pFcb
->entry
, SizeDirEntry
);
171 CcSetDirtyPinnedData(Context
, NULL
);
172 CcUnpinData(Context
);
173 return STATUS_SUCCESS
;
177 * rename an existing FAT entry
181 IN PDEVICE_EXTENSION DeviceExt
,
183 IN PUNICODE_STRING FileName
,
184 IN BOOLEAN CaseChangeOnly
)
188 PVOID Context
= NULL
;
189 LARGE_INTEGER Offset
;
190 PFATX_DIR_ENTRY pDirEntry
;
193 DPRINT("vfatRenameEntry(%p, %p, %wZ, %d)\n", DeviceExt
, pFcb
, FileName
, CaseChangeOnly
);
195 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pFcb
->parentFcb
);
196 if (!NT_SUCCESS(Status
))
201 if (vfatVolumeIsFatX(DeviceExt
))
203 VFAT_DIRENTRY_CONTEXT DirContext
;
205 /* Open associated dir entry */
206 StartIndex
= pFcb
->startIndex
;
207 Offset
.u
.HighPart
= 0;
208 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
211 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
213 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
215 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
216 _SEH2_YIELD(return _SEH2_GetExceptionCode());
220 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
223 NameA
.Buffer
= (PCHAR
)pDirEntry
->Filename
;
225 NameA
.MaximumLength
= 42;
226 RtlUnicodeStringToOemString(&NameA
, FileName
, FALSE
);
227 pDirEntry
->FilenameLength
= (unsigned char)NameA
.Length
;
230 DirContext
.DeviceExt
= DeviceExt
;
231 DirContext
.ShortNameU
.Length
= 0;
232 DirContext
.ShortNameU
.MaximumLength
= 0;
233 DirContext
.ShortNameU
.Buffer
= NULL
;
234 DirContext
.LongNameU
= *FileName
;
235 DirContext
.DirEntry
.FatX
= *pDirEntry
;
237 CcSetDirtyPinnedData(Context
, NULL
);
238 CcUnpinData(Context
);
240 Status
= vfatUpdateFCB(DeviceExt
, pFcb
, &DirContext
, pFcb
->parentFcb
);
241 if (NT_SUCCESS(Status
))
243 CcFlushCache(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, NULL
);
250 /* This we cannot handle properly, move file - would likely need love */
251 return VfatMoveEntry(DeviceExt
, pFcb
, FileName
, pFcb
->parentFcb
);
256 * try to find contiguous entries frees in directory,
257 * extend a directory if is necessary
261 IN PDEVICE_EXTENSION DeviceExt
,
266 LARGE_INTEGER FileOffset
;
267 ULONG i
, count
, size
, nbFree
= 0;
268 PDIR_ENTRY pFatEntry
= NULL
;
269 PVOID Context
= NULL
;
272 BOOLEAN IsFatX
= vfatVolumeIsFatX(DeviceExt
);
273 FileOffset
.QuadPart
= 0;
276 SizeDirEntry
= sizeof(FATX_DIR_ENTRY
);
278 SizeDirEntry
= sizeof(FAT_DIR_ENTRY
);
280 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pDirFcb
);
281 if (!NT_SUCCESS(Status
))
286 count
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
/ SizeDirEntry
;
287 size
= DeviceExt
->FatInfo
.BytesPerCluster
/ SizeDirEntry
;
288 for (i
= 0; i
< count
; i
++, pFatEntry
= (PDIR_ENTRY
)((ULONG_PTR
)pFatEntry
+ SizeDirEntry
))
290 if (Context
== NULL
|| (i
% size
) == 0)
294 CcUnpinData(Context
);
298 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
300 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
302 _SEH2_YIELD(return FALSE
);
306 FileOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.BytesPerCluster
;
308 if (ENTRY_END(IsFatX
, pFatEntry
))
312 if (ENTRY_DELETED(IsFatX
, pFatEntry
))
320 if (nbFree
== nbSlots
)
327 CcUnpinData(Context
);
330 if (nbFree
== nbSlots
)
332 /* found enough contiguous free slots */
333 *start
= i
- nbSlots
+ 1;
338 if (*start
+ nbSlots
> count
)
340 LARGE_INTEGER AllocationSize
;
341 /* extend the directory */
342 if (vfatFCBIsRoot(pDirFcb
) && DeviceExt
->FatInfo
.FatType
!= FAT32
)
344 /* We can't extend a root directory on a FAT12/FAT16/FATX partition */
347 AllocationSize
.QuadPart
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
+ DeviceExt
->FatInfo
.BytesPerCluster
;
348 Status
= VfatSetAllocationSizeInformation(pDirFcb
->FileObject
, pDirFcb
,
349 DeviceExt
, &AllocationSize
);
350 if (!NT_SUCCESS(Status
))
354 /* clear the new dir cluster */
355 FileOffset
.u
.LowPart
= (ULONG
)(pDirFcb
->RFCB
.FileSize
.QuadPart
-
356 DeviceExt
->FatInfo
.BytesPerCluster
);
359 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
361 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
363 _SEH2_YIELD(return FALSE
);
368 memset(pFatEntry
, 0xff, DeviceExt
->FatInfo
.BytesPerCluster
);
370 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
372 else if (*start
+ nbSlots
< count
)
374 /* clear the entry after the last new entry */
375 FileOffset
.u
.LowPart
= (*start
+ nbSlots
) * SizeDirEntry
;
378 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, SizeDirEntry
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
380 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
382 _SEH2_YIELD(return FALSE
);
387 memset(pFatEntry
, 0xff, SizeDirEntry
);
389 RtlZeroMemory(pFatEntry
, SizeDirEntry
);
393 CcSetDirtyPinnedData(Context
, NULL
);
394 CcUnpinData(Context
);
397 DPRINT("nbSlots %u nbFree %u, entry number %u\n", nbSlots
, nbFree
, *start
);
402 create a new FAT entry
406 IN PDEVICE_EXTENSION DeviceExt
,
407 IN PUNICODE_STRING NameU
,
409 IN PVFATFCB ParentFcb
,
410 IN ULONG RequestedOptions
,
412 IN PVFAT_MOVE_CONTEXT MoveContext
)
414 PVOID Context
= NULL
;
415 PFAT_DIR_ENTRY pFatEntry
;
417 USHORT nbSlots
= 0, j
;
419 BOOLEAN needTilde
= FALSE
, needLong
= FALSE
;
420 BOOLEAN BaseAllLower
, BaseAllUpper
;
421 BOOLEAN ExtensionAllLower
, ExtensionAllUpper
;
425 ULONG CurrentCluster
;
426 LARGE_INTEGER SystemTime
, FileOffset
;
427 NTSTATUS Status
= STATUS_SUCCESS
;
436 VFAT_DIRENTRY_CONTEXT DirContext
;
437 WCHAR LongNameBuffer
[LONGNAME_MAX_LENGTH
+ 1];
438 WCHAR ShortNameBuffer
[13];
440 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
442 DirContext
.LongNameU
= *NameU
;
443 IsDirectory
= BooleanFlagOn(RequestedOptions
, FILE_DIRECTORY_FILE
);
445 /* nb of entry needed for long name+normal entry */
446 nbSlots
= (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 12) / 13 + 1;
447 DPRINT("NameLen= %u, nbSlots =%u\n", DirContext
.LongNameU
.Length
/ sizeof(WCHAR
), nbSlots
);
448 Buffer
= ExAllocatePoolWithTag(NonPagedPool
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
), TAG_VFAT
);
451 return STATUS_INSUFFICIENT_RESOURCES
;
453 RtlZeroMemory(Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
454 pSlots
= (slot
*) Buffer
;
456 NameA
.Buffer
= aName
;
458 NameA
.MaximumLength
= sizeof(aName
);
460 DirContext
.DeviceExt
= DeviceExt
;
461 DirContext
.ShortNameU
.Buffer
= ShortNameBuffer
;
462 DirContext
.ShortNameU
.Length
= 0;
463 DirContext
.ShortNameU
.MaximumLength
= sizeof(ShortNameBuffer
);
465 RtlZeroMemory(&DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
467 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.LongNameU
, &NameA
, &SpacesFound
);
469 if (!IsNameLegal
|| SpacesFound
)
471 GENERATE_NAME_CONTEXT NameContext
;
472 VFAT_DIRENTRY_CONTEXT SearchContext
;
473 WCHAR ShortSearchName
[13];
476 RtlZeroMemory(&NameContext
, sizeof(GENERATE_NAME_CONTEXT
));
477 SearchContext
.DeviceExt
= DeviceExt
;
478 SearchContext
.LongNameU
.Buffer
= LongNameBuffer
;
479 SearchContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
480 SearchContext
.ShortNameU
.Buffer
= ShortSearchName
;
481 SearchContext
.ShortNameU
.MaximumLength
= sizeof(ShortSearchName
);
483 for (i
= 0; i
< 100; i
++)
485 RtlGenerate8dot3Name(&DirContext
.LongNameU
, FALSE
, &NameContext
, &DirContext
.ShortNameU
);
486 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
487 SearchContext
.DirIndex
= 0;
488 Status
= FindFile(DeviceExt
, ParentFcb
, &DirContext
.ShortNameU
, &SearchContext
, TRUE
);
489 if (!NT_SUCCESS(Status
))
493 else if (MoveContext
)
496 if (strncmp((char *)SearchContext
.DirEntry
.Fat
.ShortName
, (char *)(*Fcb
)->entry
.Fat
.ShortName
, 11) == 0)
498 if (MoveContext
->InPlace
)
500 ASSERT(SearchContext
.DirEntry
.Fat
.FileSize
== MoveContext
->FileSize
);
506 if (i
== 100) /* FIXME : what to do after this ? */
508 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
509 return STATUS_UNSUCCESSFUL
;
511 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.ShortNameU
, &NameA
, &SpacesFound
);
515 BaseAllLower
= BaseAllUpper
= TRUE
;
516 ExtensionAllLower
= ExtensionAllUpper
= TRUE
;
518 for (i
= 0; i
< DirContext
.LongNameU
.Length
/ sizeof(WCHAR
); i
++)
520 c
= DirContext
.LongNameU
.Buffer
[i
];
521 if (c
>= L
'A' && c
<= L
'Z')
524 ExtensionAllLower
= FALSE
;
526 BaseAllLower
= FALSE
;
528 else if (c
>= L
'a' && c
<= L
'z')
531 ExtensionAllUpper
= FALSE
;
533 BaseAllUpper
= FALSE
;
547 if ((!BaseAllLower
&& !BaseAllUpper
) ||
548 (!ExtensionAllLower
&& !ExtensionAllUpper
))
553 RtlUpcaseUnicodeString(&DirContext
.ShortNameU
, &DirContext
.LongNameU
, FALSE
);
554 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
556 aName
[NameA
.Length
] = 0;
557 DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
558 aName
, &DirContext
.LongNameU
, needTilde
, needLong
);
559 memset(DirContext
.DirEntry
.Fat
.ShortName
, ' ', 11);
560 for (i
= 0; i
< 8 && aName
[i
] && aName
[i
] != '.'; i
++)
562 DirContext
.DirEntry
.Fat
.Filename
[i
] = aName
[i
];
567 for (j
= 0; j
< 3 && aName
[i
]; j
++, i
++)
569 DirContext
.DirEntry
.Fat
.Ext
[j
] = aName
[i
];
572 if (DirContext
.DirEntry
.Fat
.Filename
[0] == 0xe5)
574 DirContext
.DirEntry
.Fat
.Filename
[0] = 0x05;
579 RtlCopyMemory(LongNameBuffer
, DirContext
.LongNameU
.Buffer
, DirContext
.LongNameU
.Length
);
580 DirContext
.LongNameU
.Buffer
= LongNameBuffer
;
581 DirContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
582 DirContext
.LongNameU
.Buffer
[DirContext
.LongNameU
.Length
/ sizeof(WCHAR
)] = 0;
583 memset(DirContext
.LongNameU
.Buffer
+ DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 1, 0xff,
584 DirContext
.LongNameU
.MaximumLength
- DirContext
.LongNameU
.Length
- sizeof(WCHAR
));
589 if (BaseAllLower
&& !BaseAllUpper
)
591 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_BASE
;
593 if (ExtensionAllLower
&& !ExtensionAllUpper
)
595 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_EXT
;
599 DPRINT ("dos name=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
602 DirContext
.DirEntry
.Fat
.Attrib
= ReqAttr
;
605 DirContext
.DirEntry
.Fat
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
607 /* set dates and times */
608 KeQuerySystemTime(&SystemTime
);
609 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.Fat
.CreationDate
,
610 &DirContext
.DirEntry
.Fat
.CreationTime
);
611 DirContext
.DirEntry
.Fat
.UpdateDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
612 DirContext
.DirEntry
.Fat
.UpdateTime
= DirContext
.DirEntry
.Fat
.CreationTime
;
613 DirContext
.DirEntry
.Fat
.AccessDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
614 /* If it's moving, preserve creation time and file size */
615 if (MoveContext
!= NULL
)
617 DirContext
.DirEntry
.Fat
.CreationDate
= MoveContext
->CreationDate
;
618 DirContext
.DirEntry
.Fat
.CreationTime
= MoveContext
->CreationTime
;
619 DirContext
.DirEntry
.Fat
.FileSize
= MoveContext
->FileSize
;
624 /* calculate checksum for 8.3 name */
625 for (pSlots
[0].alias_checksum
= 0, i
= 0; i
< 11; i
++)
627 pSlots
[0].alias_checksum
= (((pSlots
[0].alias_checksum
& 1) << 7
628 | ((pSlots
[0].alias_checksum
& 0xfe) >> 1))
629 + DirContext
.DirEntry
.Fat
.ShortName
[i
]);
631 /* construct slots and entry */
632 for (i
= nbSlots
- 2; i
>= 0; i
--)
634 DPRINT("construct slot %d\n", i
);
635 pSlots
[i
].attr
= 0xf;
638 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1);
642 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1 + 0x40);
644 pSlots
[i
].alias_checksum
= pSlots
[0].alias_checksum
;
645 RtlCopyMemory(pSlots
[i
].name0_4
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13, 10);
646 RtlCopyMemory(pSlots
[i
].name5_10
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 5, 12);
647 RtlCopyMemory(pSlots
[i
].name11_12
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 11, 4);
650 /* try to find nbSlots contiguous entries frees in directory */
651 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, nbSlots
, &DirContext
.StartIndex
))
653 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
654 return STATUS_DISK_FULL
;
656 DirContext
.DirIndex
= DirContext
.StartIndex
+ nbSlots
- 1;
659 /* If we aren't moving, use next */
660 if (MoveContext
== NULL
)
663 Status
= NextCluster(DeviceExt
, 0, &CurrentCluster
, TRUE
);
664 if (CurrentCluster
== 0xffffffff || !NT_SUCCESS(Status
))
666 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
667 if (!NT_SUCCESS(Status
))
671 return STATUS_DISK_FULL
;
674 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
676 FAT32UpdateFreeClustersCount(DeviceExt
);
681 CurrentCluster
= MoveContext
->FirstCluster
;
684 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
686 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
688 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
690 else if (MoveContext
!= NULL
)
692 CurrentCluster
= MoveContext
->FirstCluster
;
694 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
696 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
698 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
701 /* No need to init cache here, vfatFindDirSpace() will have done it for us */
702 ASSERT(BooleanFlagOn(ParentFcb
->Flags
, FCB_CACHE_INITIALIZED
));
704 i
= DeviceExt
->FatInfo
.BytesPerCluster
/ sizeof(FAT_DIR_ENTRY
);
705 FileOffset
.u
.HighPart
= 0;
706 FileOffset
.u
.LowPart
= DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
);
707 if (DirContext
.StartIndex
/ i
== DirContext
.DirIndex
/ i
)
712 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
), PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
714 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
716 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
717 _SEH2_YIELD(return _SEH2_GetExceptionCode());
723 RtlCopyMemory(pFatEntry
, Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
725 RtlCopyMemory(pFatEntry
+ (nbSlots
- 1), &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
730 size
= DeviceExt
->FatInfo
.BytesPerCluster
-
731 (DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
)) % DeviceExt
->FatInfo
.BytesPerCluster
;
732 i
= size
/ sizeof(FAT_DIR_ENTRY
);
735 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, size
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
737 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
739 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
740 _SEH2_YIELD(return _SEH2_GetExceptionCode());
743 RtlCopyMemory(pFatEntry
, Buffer
, size
);
744 CcSetDirtyPinnedData(Context
, NULL
);
745 CcUnpinData(Context
);
746 FileOffset
.u
.LowPart
+= size
;
749 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
) - size
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
751 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
753 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
754 _SEH2_YIELD(return _SEH2_GetExceptionCode());
759 RtlCopyMemory(pFatEntry
, (PVOID
)(Buffer
+ size
), (nbSlots
- 1 - i
) * sizeof(FAT_DIR_ENTRY
));
761 RtlCopyMemory(pFatEntry
+ nbSlots
- 1 - i
, &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
763 CcSetDirtyPinnedData(Context
, NULL
);
764 CcUnpinData(Context
);
766 if (MoveContext
!= NULL
)
768 /* We're modifying an existing FCB - likely rename/move */
769 Status
= vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
773 Status
= vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
775 if (!NT_SUCCESS(Status
))
777 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
781 DPRINT("new : entry=%11.11s\n", (*Fcb
)->entry
.Fat
.Filename
);
782 DPRINT("new : entry=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
786 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, (*Fcb
));
787 if (!NT_SUCCESS(Status
))
789 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
793 FileOffset
.QuadPart
= 0;
796 CcPinRead((*Fcb
)->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
798 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
800 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
801 _SEH2_YIELD(return _SEH2_GetExceptionCode());
804 /* clear the new directory cluster if not moving */
805 if (MoveContext
== NULL
)
807 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
808 /* create '.' and '..' */
809 RtlCopyMemory(&pFatEntry
[0].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
810 RtlCopyMemory(pFatEntry
[0].ShortName
, ". ", 11);
811 RtlCopyMemory(&pFatEntry
[1].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
812 RtlCopyMemory(pFatEntry
[1].ShortName
, ".. ", 11);
815 pFatEntry
[1].FirstCluster
= ParentFcb
->entry
.Fat
.FirstCluster
;
816 pFatEntry
[1].FirstClusterHigh
= ParentFcb
->entry
.Fat
.FirstClusterHigh
;
817 if (vfatFCBIsRoot(ParentFcb
))
819 pFatEntry
[1].FirstCluster
= 0;
820 pFatEntry
[1].FirstClusterHigh
= 0;
822 CcSetDirtyPinnedData(Context
, NULL
);
823 CcUnpinData(Context
);
825 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
826 DPRINT("addentry ok\n");
827 return STATUS_SUCCESS
;
831 create a new FAT entry
835 IN PDEVICE_EXTENSION DeviceExt
,
836 IN PUNICODE_STRING NameU
,
838 IN PVFATFCB ParentFcb
,
839 IN ULONG RequestedOptions
,
841 IN PVFAT_MOVE_CONTEXT MoveContext
)
843 PVOID Context
= NULL
;
844 LARGE_INTEGER SystemTime
, FileOffset
;
846 VFAT_DIRENTRY_CONTEXT DirContext
;
847 PFATX_DIR_ENTRY pFatXDirEntry
;
850 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
852 DirContext
.LongNameU
= *NameU
;
854 if (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) > 42)
857 return STATUS_NAME_TOO_LONG
;
860 /* try to find 1 entry free in directory */
861 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, 1, &DirContext
.StartIndex
))
863 return STATUS_DISK_FULL
;
865 Index
= DirContext
.DirIndex
= DirContext
.StartIndex
;
866 if (!vfatFCBIsRoot(ParentFcb
))
868 DirContext
.DirIndex
+= 2;
869 DirContext
.StartIndex
+= 2;
872 DirContext
.ShortNameU
.Buffer
= 0;
873 DirContext
.ShortNameU
.Length
= 0;
874 DirContext
.ShortNameU
.MaximumLength
= 0;
875 DirContext
.DeviceExt
= DeviceExt
;
876 RtlZeroMemory(&DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
877 memset(DirContext
.DirEntry
.FatX
.Filename
, 0xff, 42);
878 /* Use cluster, if moving */
879 if (MoveContext
!= NULL
)
881 DirContext
.DirEntry
.FatX
.FirstCluster
= MoveContext
->FirstCluster
;
885 DirContext
.DirEntry
.FatX
.FirstCluster
= 0;
887 DirContext
.DirEntry
.FatX
.FileSize
= 0;
890 NameA
.Buffer
= (PCHAR
)DirContext
.DirEntry
.FatX
.Filename
;
892 NameA
.MaximumLength
= 42;
893 RtlUnicodeStringToOemString(&NameA
, &DirContext
.LongNameU
, FALSE
);
894 DirContext
.DirEntry
.FatX
.FilenameLength
= (unsigned char)NameA
.Length
;
897 DirContext
.DirEntry
.FatX
.Attrib
= ReqAttr
;
898 if (BooleanFlagOn(RequestedOptions
, FILE_DIRECTORY_FILE
))
900 DirContext
.DirEntry
.FatX
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
903 /* set dates and times */
904 KeQuerySystemTime(&SystemTime
);
905 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.FatX
.CreationDate
,
906 &DirContext
.DirEntry
.FatX
.CreationTime
);
907 DirContext
.DirEntry
.FatX
.UpdateDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
908 DirContext
.DirEntry
.FatX
.UpdateTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
909 DirContext
.DirEntry
.FatX
.AccessDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
910 DirContext
.DirEntry
.FatX
.AccessTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
911 /* If it's moving, preserve creation time and file size */
912 if (MoveContext
!= NULL
)
914 DirContext
.DirEntry
.FatX
.CreationDate
= MoveContext
->CreationDate
;
915 DirContext
.DirEntry
.FatX
.CreationTime
= MoveContext
->CreationTime
;
916 DirContext
.DirEntry
.FatX
.FileSize
= MoveContext
->FileSize
;
919 /* No need to init cache here, vfatFindDirSpace() will have done it for us */
920 ASSERT(BooleanFlagOn(ParentFcb
->Flags
, FCB_CACHE_INITIALIZED
));
922 /* add entry into parent directory */
923 FileOffset
.u
.HighPart
= 0;
924 FileOffset
.u
.LowPart
= Index
* sizeof(FATX_DIR_ENTRY
);
927 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
), PIN_WAIT
, &Context
, (PVOID
*)&pFatXDirEntry
);
929 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
931 _SEH2_YIELD(return _SEH2_GetExceptionCode());
934 RtlCopyMemory(pFatXDirEntry
, &DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
935 CcSetDirtyPinnedData(Context
, NULL
);
936 CcUnpinData(Context
);
938 if (MoveContext
!= NULL
)
940 /* We're modifying an existing FCB - likely rename/move */
941 /* FIXME: check status */
942 vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
946 /* FIXME: check status */
947 vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
950 DPRINT("addentry ok\n");
951 return STATUS_SUCCESS
;
955 * deleting an existing FAT entry
959 IN PDEVICE_EXTENSION DeviceExt
,
961 OUT PVFAT_MOVE_CONTEXT MoveContext
)
963 ULONG CurrentCluster
= 0, NextCluster
, i
;
964 PVOID Context
= NULL
;
965 LARGE_INTEGER Offset
;
966 PFAT_DIR_ENTRY pDirEntry
= NULL
;
970 ASSERT(pFcb
->parentFcb
);
972 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pFcb
->parentFcb
);
973 if (!NT_SUCCESS(Status
))
978 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
979 DPRINT("delete entry: %u to %u\n", pFcb
->startIndex
, pFcb
->dirIndex
);
980 Offset
.u
.HighPart
= 0;
981 for (i
= pFcb
->startIndex
; i
<= pFcb
->dirIndex
; i
++)
983 if (Context
== NULL
|| ((i
* sizeof(FAT_DIR_ENTRY
)) % PAGE_SIZE
) == 0)
987 CcSetDirtyPinnedData(Context
, NULL
);
988 CcUnpinData(Context
);
990 Offset
.u
.LowPart
= (i
* sizeof(FAT_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
993 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
995 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
997 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1001 pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))].Filename
[0] = 0xe5;
1002 if (i
== pFcb
->dirIndex
)
1005 vfatDirEntryGetFirstCluster(DeviceExt
,
1006 (PDIR_ENTRY
)&pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))]);
1010 /* In case of moving, save properties */
1011 if (MoveContext
!= NULL
)
1013 pDirEntry
= &pDirEntry
[pFcb
->dirIndex
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))];
1014 MoveContext
->FirstCluster
= CurrentCluster
;
1015 MoveContext
->FileSize
= pDirEntry
->FileSize
;
1016 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
1017 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
1022 CcSetDirtyPinnedData(Context
, NULL
);
1023 CcUnpinData(Context
);
1026 /* In case of moving, don't delete data */
1027 if (MoveContext
== NULL
)
1029 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
1031 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
1032 /* FIXME: check status */
1033 WriteCluster(DeviceExt
, CurrentCluster
, 0);
1034 CurrentCluster
= NextCluster
;
1037 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
1039 FAT32UpdateFreeClustersCount(DeviceExt
);
1043 return STATUS_SUCCESS
;
1047 * deleting an existing FAT entry
1051 IN PDEVICE_EXTENSION DeviceExt
,
1053 OUT PVFAT_MOVE_CONTEXT MoveContext
)
1055 ULONG CurrentCluster
= 0, NextCluster
;
1056 PVOID Context
= NULL
;
1057 LARGE_INTEGER Offset
;
1058 PFATX_DIR_ENTRY pDirEntry
;
1063 ASSERT(pFcb
->parentFcb
);
1064 ASSERT(vfatVolumeIsFatX(DeviceExt
));
1066 StartIndex
= pFcb
->startIndex
;
1068 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pFcb
->parentFcb
);
1069 if (!NT_SUCCESS(Status
))
1074 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
1075 DPRINT("delete entry: %u\n", StartIndex
);
1076 Offset
.u
.HighPart
= 0;
1077 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
1080 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
1082 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1084 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
1085 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1088 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
1089 pDirEntry
->FilenameLength
= 0xe5;
1090 CurrentCluster
= vfatDirEntryGetFirstCluster(DeviceExt
,
1091 (PDIR_ENTRY
)pDirEntry
);
1093 /* In case of moving, save properties */
1094 if (MoveContext
!= NULL
)
1096 MoveContext
->FirstCluster
= CurrentCluster
;
1097 MoveContext
->FileSize
= pDirEntry
->FileSize
;
1098 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
1099 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
1102 CcSetDirtyPinnedData(Context
, NULL
);
1103 CcUnpinData(Context
);
1105 /* In case of moving, don't delete data */
1106 if (MoveContext
== NULL
)
1108 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
1110 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
1111 /* FIXME: check status */
1112 WriteCluster(DeviceExt
, CurrentCluster
, 0);
1113 CurrentCluster
= NextCluster
;
1117 return STATUS_SUCCESS
;
1121 * move an existing FAT entry
1125 IN PDEVICE_EXTENSION DeviceExt
,
1127 IN PUNICODE_STRING FileName
,
1128 IN PVFATFCB ParentFcb
)
1132 VFAT_MOVE_CONTEXT MoveContext
;
1134 DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt
, pFcb
, FileName
, ParentFcb
);
1136 /* Delete old entry while keeping data */
1137 Status
= VfatDelEntry(DeviceExt
, pFcb
, &MoveContext
);
1138 if (!NT_SUCCESS(Status
))
1143 OldParent
= pFcb
->parentFcb
;
1144 CcFlushCache(&OldParent
->SectionObjectPointers
, NULL
, 0, NULL
);
1145 MoveContext
.InPlace
= (OldParent
== ParentFcb
);
1147 /* Add our new entry with our cluster */
1148 Status
= VfatAddEntry(DeviceExt
,
1152 (vfatFCBIsDirectory(pFcb
) ? FILE_DIRECTORY_FILE
: 0),
1156 CcFlushCache(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, NULL
);
1161 extern BOOLEAN
FATXIsDirectoryEmpty(PDEVICE_EXTENSION DeviceExt
, PVFATFCB Fcb
);
1162 extern BOOLEAN
FATIsDirectoryEmpty(PDEVICE_EXTENSION DeviceExt
, PVFATFCB Fcb
);
1163 extern NTSTATUS
FATGetNextDirEntry(PVOID
*pContext
, PVOID
*pPage
, PVFATFCB pDirFcb
, PVFAT_DIRENTRY_CONTEXT DirContext
, BOOLEAN First
);
1164 extern NTSTATUS
FATXGetNextDirEntry(PVOID
*pContext
, PVOID
*pPage
, PVFATFCB pDirFcb
, PVFAT_DIRENTRY_CONTEXT DirContext
, BOOLEAN First
);
1166 VFAT_DISPATCH FatXDispatch
= {
1167 FATXIsDirectoryEmpty
, // .IsDirectoryEmpty
1168 FATXAddEntry
, // .AddEntry
1169 FATXDelEntry
, // .DelEntry
1170 FATXGetNextDirEntry
, // .GetNextDirEntry
1173 VFAT_DISPATCH FatDispatch
= {
1174 FATIsDirectoryEmpty
, // .IsDirectoryEmpty
1175 FATAddEntry
, // .AddEntry
1176 FATDelEntry
, // .DelEntry
1177 FATGetNextDirEntry
, // .GetNextDirEntry