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
;
34 /* Don't re-initialize if already done */
35 if (BooleanFlagOn(fcb
->Flags
, FCB_CACHE_INITIALIZED
))
37 return STATUS_SUCCESS
;
40 ASSERT(vfatFCBIsDirectory(fcb
));
41 ASSERT(fcb
->FileObject
== NULL
);
44 if (!ExIsResourceAcquiredExclusive(&vcb
->DirResource
))
46 ExAcquireResourceExclusiveLite(&vcb
->DirResource
, TRUE
);
50 fileObject
= IoCreateStreamFileObject (NULL
, vcb
->StorageDevice
);
53 if (DebugFile
.Buffer
!= NULL
&& FsRtlIsNameInExpression(&DebugFile
, &fcb
->LongNameU
, FALSE
, NULL
))
55 DPRINT1("Attaching %p to %p (%d)\n", fcb
, fileObject
, fcb
->RefCount
);
59 newCCB
= ExAllocateFromNPagedLookasideList(&VfatGlobalData
->CcbLookasideList
);
62 Status
= STATUS_INSUFFICIENT_RESOURCES
;
63 ObDereferenceObject(fileObject
);
66 RtlZeroMemory(newCCB
, sizeof (VFATCCB
));
68 fileObject
->SectionObjectPointer
= &fcb
->SectionObjectPointers
;
69 fileObject
->FsContext
= fcb
;
70 fileObject
->FsContext2
= newCCB
;
71 fileObject
->Vpb
= vcb
->IoVPB
;
72 fcb
->FileObject
= fileObject
;
76 CcInitializeCacheMap(fileObject
,
77 (PCC_FILE_SIZES
)(&fcb
->RFCB
.AllocationSize
),
79 &VfatGlobalData
->CacheMgrCallbacks
,
82 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
84 status
= _SEH2_GetExceptionCode();
85 fcb
->FileObject
= NULL
;
86 ExFreeToNPagedLookasideList(&VfatGlobalData
->CcbLookasideList
, newCCB
);
87 ObDereferenceObject(fileObject
);
90 ExReleaseResourceLite(&vcb
->DirResource
);
96 vfatGrabFCB(vcb
, fcb
);
97 SetFlag(fcb
->Flags
, FCB_CACHE_INITIALIZED
);
98 Status
= STATUS_SUCCESS
;
103 ExReleaseResourceLite(&vcb
->DirResource
);
110 * update an existing FAT entry
114 IN PDEVICE_EXTENSION DeviceExt
,
119 LARGE_INTEGER Offset
;
126 if (vfatVolumeIsFatX(DeviceExt
))
128 SizeDirEntry
= sizeof(FATX_DIR_ENTRY
);
129 dirIndex
= pFcb
->startIndex
;
133 SizeDirEntry
= sizeof(FAT_DIR_ENTRY
);
134 dirIndex
= pFcb
->dirIndex
;
137 DPRINT("updEntry dirIndex %u, PathName \'%wZ\'\n", dirIndex
, &pFcb
->PathNameU
);
139 if (vfatFCBIsRoot(pFcb
) || BooleanFlagOn(pFcb
->Flags
, FCB_IS_FAT
| FCB_IS_VOLUME
))
141 return STATUS_SUCCESS
;
144 ASSERT(pFcb
->parentFcb
);
146 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pFcb
->parentFcb
);
147 if (!NT_SUCCESS(Status
))
152 Offset
.u
.HighPart
= 0;
153 Offset
.u
.LowPart
= dirIndex
* SizeDirEntry
;
156 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, SizeDirEntry
, PIN_WAIT
, &Context
, (PVOID
*)&PinEntry
);
158 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
160 DPRINT1("Failed write to \'%wZ\'.\n", &pFcb
->parentFcb
->PathNameU
);
161 _SEH2_YIELD(return _SEH2_GetExceptionCode());
165 pFcb
->Flags
&= ~FCB_IS_DIRTY
;
166 RtlCopyMemory(PinEntry
, &pFcb
->entry
, SizeDirEntry
);
167 CcSetDirtyPinnedData(Context
, NULL
);
168 CcUnpinData(Context
);
169 return STATUS_SUCCESS
;
173 * rename an existing FAT entry
177 IN PDEVICE_EXTENSION DeviceExt
,
179 IN PUNICODE_STRING FileName
,
180 IN BOOLEAN CaseChangeOnly
)
184 PVOID Context
= NULL
;
185 LARGE_INTEGER Offset
;
186 PFATX_DIR_ENTRY pDirEntry
;
189 DPRINT("vfatRenameEntry(%p, %p, %wZ, %d)\n", DeviceExt
, pFcb
, FileName
, CaseChangeOnly
);
191 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pFcb
->parentFcb
);
192 if (!NT_SUCCESS(Status
))
197 if (vfatVolumeIsFatX(DeviceExt
))
199 VFAT_DIRENTRY_CONTEXT DirContext
;
201 /* Open associated dir entry */
202 StartIndex
= pFcb
->startIndex
;
203 Offset
.u
.HighPart
= 0;
204 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
207 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
209 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
211 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
212 _SEH2_YIELD(return _SEH2_GetExceptionCode());
216 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
219 NameA
.Buffer
= (PCHAR
)pDirEntry
->Filename
;
221 NameA
.MaximumLength
= 42;
222 RtlUnicodeStringToOemString(&NameA
, FileName
, FALSE
);
223 pDirEntry
->FilenameLength
= (unsigned char)NameA
.Length
;
226 DirContext
.DeviceExt
= DeviceExt
;
227 DirContext
.ShortNameU
.Length
= 0;
228 DirContext
.ShortNameU
.MaximumLength
= 0;
229 DirContext
.ShortNameU
.Buffer
= NULL
;
230 DirContext
.LongNameU
= *FileName
;
231 DirContext
.DirEntry
.FatX
= *pDirEntry
;
233 CcSetDirtyPinnedData(Context
, NULL
);
234 CcUnpinData(Context
);
236 Status
= vfatUpdateFCB(DeviceExt
, pFcb
, &DirContext
, pFcb
->parentFcb
);
237 if (NT_SUCCESS(Status
))
239 CcFlushCache(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, NULL
);
246 /* This we cannot handle properly, move file - would likely need love */
247 return VfatMoveEntry(DeviceExt
, pFcb
, FileName
, pFcb
->parentFcb
);
252 * try to find contiguous entries frees in directory,
253 * extend a directory if is necessary
257 IN PDEVICE_EXTENSION DeviceExt
,
262 LARGE_INTEGER FileOffset
;
263 ULONG i
, count
, size
, nbFree
= 0;
264 PDIR_ENTRY pFatEntry
= NULL
;
265 PVOID Context
= NULL
;
268 BOOLEAN IsFatX
= vfatVolumeIsFatX(DeviceExt
);
269 FileOffset
.QuadPart
= 0;
272 SizeDirEntry
= sizeof(FATX_DIR_ENTRY
);
274 SizeDirEntry
= sizeof(FAT_DIR_ENTRY
);
276 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pDirFcb
);
277 if (!NT_SUCCESS(Status
))
282 count
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
/ SizeDirEntry
;
283 size
= DeviceExt
->FatInfo
.BytesPerCluster
/ SizeDirEntry
;
284 for (i
= 0; i
< count
; i
++, pFatEntry
= (PDIR_ENTRY
)((ULONG_PTR
)pFatEntry
+ SizeDirEntry
))
286 if (Context
== NULL
|| (i
% size
) == 0)
290 CcUnpinData(Context
);
294 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
296 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
298 _SEH2_YIELD(return FALSE
);
302 FileOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.BytesPerCluster
;
304 if (ENTRY_END(IsFatX
, pFatEntry
))
308 if (ENTRY_DELETED(IsFatX
, pFatEntry
))
316 if (nbFree
== nbSlots
)
323 CcUnpinData(Context
);
326 if (nbFree
== nbSlots
)
328 /* found enough contiguous free slots */
329 *start
= i
- nbSlots
+ 1;
334 if (*start
+ nbSlots
> count
)
336 LARGE_INTEGER AllocationSize
;
337 /* extend the directory */
338 if (vfatFCBIsRoot(pDirFcb
) && DeviceExt
->FatInfo
.FatType
!= FAT32
)
340 /* We can't extend a root directory on a FAT12/FAT16/FATX partition */
343 AllocationSize
.QuadPart
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
+ DeviceExt
->FatInfo
.BytesPerCluster
;
344 Status
= VfatSetAllocationSizeInformation(pDirFcb
->FileObject
, pDirFcb
,
345 DeviceExt
, &AllocationSize
);
346 if (!NT_SUCCESS(Status
))
350 /* clear the new dir cluster */
351 FileOffset
.u
.LowPart
= (ULONG
)(pDirFcb
->RFCB
.FileSize
.QuadPart
-
352 DeviceExt
->FatInfo
.BytesPerCluster
);
355 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
357 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
359 _SEH2_YIELD(return FALSE
);
364 memset(pFatEntry
, 0xff, DeviceExt
->FatInfo
.BytesPerCluster
);
366 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
368 else if (*start
+ nbSlots
< count
)
370 /* clear the entry after the last new entry */
371 FileOffset
.u
.LowPart
= (*start
+ nbSlots
) * SizeDirEntry
;
374 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, SizeDirEntry
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
376 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
378 _SEH2_YIELD(return FALSE
);
383 memset(pFatEntry
, 0xff, SizeDirEntry
);
385 RtlZeroMemory(pFatEntry
, SizeDirEntry
);
389 CcSetDirtyPinnedData(Context
, NULL
);
390 CcUnpinData(Context
);
393 DPRINT("nbSlots %u nbFree %u, entry number %u\n", nbSlots
, nbFree
, *start
);
398 create a new FAT entry
402 IN PDEVICE_EXTENSION DeviceExt
,
403 IN PUNICODE_STRING NameU
,
405 IN PVFATFCB ParentFcb
,
406 IN ULONG RequestedOptions
,
408 IN PVFAT_MOVE_CONTEXT MoveContext
)
410 PVOID Context
= NULL
;
411 PFAT_DIR_ENTRY pFatEntry
;
413 USHORT nbSlots
= 0, j
;
415 BOOLEAN needTilde
= FALSE
, needLong
= FALSE
;
416 BOOLEAN BaseAllLower
, BaseAllUpper
;
417 BOOLEAN ExtensionAllLower
, ExtensionAllUpper
;
421 ULONG CurrentCluster
;
422 LARGE_INTEGER SystemTime
, FileOffset
;
423 NTSTATUS Status
= STATUS_SUCCESS
;
432 VFAT_DIRENTRY_CONTEXT DirContext
;
433 WCHAR LongNameBuffer
[LONGNAME_MAX_LENGTH
+ 1];
434 WCHAR ShortNameBuffer
[13];
436 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
438 DirContext
.LongNameU
= *NameU
;
439 IsDirectory
= BooleanFlagOn(RequestedOptions
, FILE_DIRECTORY_FILE
);
441 /* nb of entry needed for long name+normal entry */
442 nbSlots
= (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 12) / 13 + 1;
443 DPRINT("NameLen= %u, nbSlots =%u\n", DirContext
.LongNameU
.Length
/ sizeof(WCHAR
), nbSlots
);
444 Buffer
= ExAllocatePoolWithTag(NonPagedPool
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
), TAG_VFAT
);
447 return STATUS_INSUFFICIENT_RESOURCES
;
449 RtlZeroMemory(Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
450 pSlots
= (slot
*) Buffer
;
452 NameA
.Buffer
= aName
;
454 NameA
.MaximumLength
= sizeof(aName
);
456 DirContext
.DeviceExt
= DeviceExt
;
457 DirContext
.ShortNameU
.Buffer
= ShortNameBuffer
;
458 DirContext
.ShortNameU
.Length
= 0;
459 DirContext
.ShortNameU
.MaximumLength
= sizeof(ShortNameBuffer
);
461 RtlZeroMemory(&DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
463 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.LongNameU
, &NameA
, &SpacesFound
);
465 if (!IsNameLegal
|| SpacesFound
)
467 GENERATE_NAME_CONTEXT NameContext
;
468 VFAT_DIRENTRY_CONTEXT SearchContext
;
469 WCHAR ShortSearchName
[13];
472 RtlZeroMemory(&NameContext
, sizeof(GENERATE_NAME_CONTEXT
));
473 SearchContext
.DeviceExt
= DeviceExt
;
474 SearchContext
.LongNameU
.Buffer
= LongNameBuffer
;
475 SearchContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
476 SearchContext
.ShortNameU
.Buffer
= ShortSearchName
;
477 SearchContext
.ShortNameU
.MaximumLength
= sizeof(ShortSearchName
);
479 for (i
= 0; i
< 100; i
++)
481 RtlGenerate8dot3Name(&DirContext
.LongNameU
, FALSE
, &NameContext
, &DirContext
.ShortNameU
);
482 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
483 SearchContext
.DirIndex
= 0;
484 Status
= FindFile(DeviceExt
, ParentFcb
, &DirContext
.ShortNameU
, &SearchContext
, TRUE
);
485 if (!NT_SUCCESS(Status
))
489 else if (MoveContext
)
492 if (strncmp((char *)SearchContext
.DirEntry
.Fat
.ShortName
, (char *)(*Fcb
)->entry
.Fat
.ShortName
, 11) == 0)
494 if (MoveContext
->InPlace
)
496 ASSERT(SearchContext
.DirEntry
.Fat
.FileSize
== MoveContext
->FileSize
);
502 if (i
== 100) /* FIXME : what to do after this ? */
504 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
505 return STATUS_UNSUCCESSFUL
;
507 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.ShortNameU
, &NameA
, &SpacesFound
);
511 BaseAllLower
= BaseAllUpper
= TRUE
;
512 ExtensionAllLower
= ExtensionAllUpper
= TRUE
;
514 for (i
= 0; i
< DirContext
.LongNameU
.Length
/ sizeof(WCHAR
); i
++)
516 c
= DirContext
.LongNameU
.Buffer
[i
];
517 if (c
>= L
'A' && c
<= L
'Z')
520 ExtensionAllLower
= FALSE
;
522 BaseAllLower
= FALSE
;
524 else if (c
>= L
'a' && c
<= L
'z')
527 ExtensionAllUpper
= FALSE
;
529 BaseAllUpper
= FALSE
;
543 if ((!BaseAllLower
&& !BaseAllUpper
) ||
544 (!ExtensionAllLower
&& !ExtensionAllUpper
))
549 RtlUpcaseUnicodeString(&DirContext
.ShortNameU
, &DirContext
.LongNameU
, FALSE
);
550 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
552 aName
[NameA
.Length
] = 0;
553 DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
554 aName
, &DirContext
.LongNameU
, needTilde
, needLong
);
555 memset(DirContext
.DirEntry
.Fat
.ShortName
, ' ', 11);
556 for (i
= 0; i
< 8 && aName
[i
] && aName
[i
] != '.'; i
++)
558 DirContext
.DirEntry
.Fat
.Filename
[i
] = aName
[i
];
563 for (j
= 0; j
< 3 && aName
[i
]; j
++, i
++)
565 DirContext
.DirEntry
.Fat
.Ext
[j
] = aName
[i
];
568 if (DirContext
.DirEntry
.Fat
.Filename
[0] == 0xe5)
570 DirContext
.DirEntry
.Fat
.Filename
[0] = 0x05;
575 RtlCopyMemory(LongNameBuffer
, DirContext
.LongNameU
.Buffer
, DirContext
.LongNameU
.Length
);
576 DirContext
.LongNameU
.Buffer
= LongNameBuffer
;
577 DirContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
578 DirContext
.LongNameU
.Buffer
[DirContext
.LongNameU
.Length
/ sizeof(WCHAR
)] = 0;
579 memset(DirContext
.LongNameU
.Buffer
+ DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 1, 0xff,
580 DirContext
.LongNameU
.MaximumLength
- DirContext
.LongNameU
.Length
- sizeof(WCHAR
));
585 if (BaseAllLower
&& !BaseAllUpper
)
587 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_BASE
;
589 if (ExtensionAllLower
&& !ExtensionAllUpper
)
591 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_EXT
;
595 DPRINT ("dos name=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
598 DirContext
.DirEntry
.Fat
.Attrib
= ReqAttr
;
601 DirContext
.DirEntry
.Fat
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
603 /* set dates and times */
604 KeQuerySystemTime(&SystemTime
);
605 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.Fat
.CreationDate
,
606 &DirContext
.DirEntry
.Fat
.CreationTime
);
607 DirContext
.DirEntry
.Fat
.UpdateDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
608 DirContext
.DirEntry
.Fat
.UpdateTime
= DirContext
.DirEntry
.Fat
.CreationTime
;
609 DirContext
.DirEntry
.Fat
.AccessDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
610 /* If it's moving, preserve creation time and file size */
611 if (MoveContext
!= NULL
)
613 DirContext
.DirEntry
.Fat
.CreationDate
= MoveContext
->CreationDate
;
614 DirContext
.DirEntry
.Fat
.CreationTime
= MoveContext
->CreationTime
;
615 DirContext
.DirEntry
.Fat
.FileSize
= MoveContext
->FileSize
;
620 /* calculate checksum for 8.3 name */
621 for (pSlots
[0].alias_checksum
= 0, i
= 0; i
< 11; i
++)
623 pSlots
[0].alias_checksum
= (((pSlots
[0].alias_checksum
& 1) << 7
624 | ((pSlots
[0].alias_checksum
& 0xfe) >> 1))
625 + DirContext
.DirEntry
.Fat
.ShortName
[i
]);
627 /* construct slots and entry */
628 for (i
= nbSlots
- 2; i
>= 0; i
--)
630 DPRINT("construct slot %d\n", i
);
631 pSlots
[i
].attr
= 0xf;
634 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1);
638 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1 + 0x40);
640 pSlots
[i
].alias_checksum
= pSlots
[0].alias_checksum
;
641 RtlCopyMemory(pSlots
[i
].name0_4
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13, 10);
642 RtlCopyMemory(pSlots
[i
].name5_10
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 5, 12);
643 RtlCopyMemory(pSlots
[i
].name11_12
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 11, 4);
646 /* try to find nbSlots contiguous entries frees in directory */
647 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, nbSlots
, &DirContext
.StartIndex
))
649 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
650 return STATUS_DISK_FULL
;
652 DirContext
.DirIndex
= DirContext
.StartIndex
+ nbSlots
- 1;
655 /* If we aren't moving, use next */
656 if (MoveContext
== NULL
)
659 Status
= NextCluster(DeviceExt
, 0, &CurrentCluster
, TRUE
);
660 if (CurrentCluster
== 0xffffffff || !NT_SUCCESS(Status
))
662 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
663 if (!NT_SUCCESS(Status
))
667 return STATUS_DISK_FULL
;
672 CurrentCluster
= MoveContext
->FirstCluster
;
675 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
677 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
679 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
681 else if (MoveContext
!= NULL
)
683 CurrentCluster
= MoveContext
->FirstCluster
;
685 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
687 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
689 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
692 /* No need to init cache here, vfatFindDirSpace() will have done it for us */
693 ASSERT(BooleanFlagOn(ParentFcb
->Flags
, FCB_CACHE_INITIALIZED
));
695 i
= DeviceExt
->FatInfo
.BytesPerCluster
/ sizeof(FAT_DIR_ENTRY
);
696 FileOffset
.u
.HighPart
= 0;
697 FileOffset
.u
.LowPart
= DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
);
698 if (DirContext
.StartIndex
/ i
== DirContext
.DirIndex
/ i
)
703 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
), PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
705 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
707 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
708 _SEH2_YIELD(return _SEH2_GetExceptionCode());
714 RtlCopyMemory(pFatEntry
, Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
716 RtlCopyMemory(pFatEntry
+ (nbSlots
- 1), &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
721 size
= DeviceExt
->FatInfo
.BytesPerCluster
-
722 (DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
)) % DeviceExt
->FatInfo
.BytesPerCluster
;
723 i
= size
/ sizeof(FAT_DIR_ENTRY
);
726 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, size
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
728 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
730 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
731 _SEH2_YIELD(return _SEH2_GetExceptionCode());
734 RtlCopyMemory(pFatEntry
, Buffer
, size
);
735 CcSetDirtyPinnedData(Context
, NULL
);
736 CcUnpinData(Context
);
737 FileOffset
.u
.LowPart
+= size
;
740 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
) - size
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
742 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
744 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
745 _SEH2_YIELD(return _SEH2_GetExceptionCode());
750 RtlCopyMemory(pFatEntry
, (PVOID
)(Buffer
+ size
), (nbSlots
- 1 - i
) * sizeof(FAT_DIR_ENTRY
));
752 RtlCopyMemory(pFatEntry
+ nbSlots
- 1 - i
, &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
754 CcSetDirtyPinnedData(Context
, NULL
);
755 CcUnpinData(Context
);
757 if (MoveContext
!= NULL
)
759 /* We're modifying an existing FCB - likely rename/move */
760 Status
= vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
764 Status
= vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
766 if (!NT_SUCCESS(Status
))
768 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
772 DPRINT("new : entry=%11.11s\n", (*Fcb
)->entry
.Fat
.Filename
);
773 DPRINT("new : entry=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
777 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, (*Fcb
));
778 if (!NT_SUCCESS(Status
))
780 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
784 FileOffset
.QuadPart
= 0;
787 CcPinRead((*Fcb
)->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
789 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
791 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
792 _SEH2_YIELD(return _SEH2_GetExceptionCode());
795 /* clear the new directory cluster if not moving */
796 if (MoveContext
== NULL
)
798 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
799 /* create '.' and '..' */
800 RtlCopyMemory(&pFatEntry
[0].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
801 RtlCopyMemory(pFatEntry
[0].ShortName
, ". ", 11);
802 RtlCopyMemory(&pFatEntry
[1].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
803 RtlCopyMemory(pFatEntry
[1].ShortName
, ".. ", 11);
806 pFatEntry
[1].FirstCluster
= ParentFcb
->entry
.Fat
.FirstCluster
;
807 pFatEntry
[1].FirstClusterHigh
= ParentFcb
->entry
.Fat
.FirstClusterHigh
;
808 if (vfatFCBIsRoot(ParentFcb
))
810 pFatEntry
[1].FirstCluster
= 0;
811 pFatEntry
[1].FirstClusterHigh
= 0;
813 CcSetDirtyPinnedData(Context
, NULL
);
814 CcUnpinData(Context
);
816 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
817 DPRINT("addentry ok\n");
818 return STATUS_SUCCESS
;
822 create a new FAT entry
826 IN PDEVICE_EXTENSION DeviceExt
,
827 IN PUNICODE_STRING NameU
,
829 IN PVFATFCB ParentFcb
,
830 IN ULONG RequestedOptions
,
832 IN PVFAT_MOVE_CONTEXT MoveContext
)
834 PVOID Context
= NULL
;
835 LARGE_INTEGER SystemTime
, FileOffset
;
837 VFAT_DIRENTRY_CONTEXT DirContext
;
838 PFATX_DIR_ENTRY pFatXDirEntry
;
841 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
843 DirContext
.LongNameU
= *NameU
;
845 if (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) > 42)
848 return STATUS_NAME_TOO_LONG
;
851 /* try to find 1 entry free in directory */
852 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, 1, &DirContext
.StartIndex
))
854 return STATUS_DISK_FULL
;
856 Index
= DirContext
.DirIndex
= DirContext
.StartIndex
;
857 if (!vfatFCBIsRoot(ParentFcb
))
859 DirContext
.DirIndex
+= 2;
860 DirContext
.StartIndex
+= 2;
863 DirContext
.ShortNameU
.Buffer
= 0;
864 DirContext
.ShortNameU
.Length
= 0;
865 DirContext
.ShortNameU
.MaximumLength
= 0;
866 DirContext
.DeviceExt
= DeviceExt
;
867 RtlZeroMemory(&DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
868 memset(DirContext
.DirEntry
.FatX
.Filename
, 0xff, 42);
869 /* Use cluster, if moving */
870 if (MoveContext
!= NULL
)
872 DirContext
.DirEntry
.FatX
.FirstCluster
= MoveContext
->FirstCluster
;
876 DirContext
.DirEntry
.FatX
.FirstCluster
= 0;
878 DirContext
.DirEntry
.FatX
.FileSize
= 0;
881 NameA
.Buffer
= (PCHAR
)DirContext
.DirEntry
.FatX
.Filename
;
883 NameA
.MaximumLength
= 42;
884 RtlUnicodeStringToOemString(&NameA
, &DirContext
.LongNameU
, FALSE
);
885 DirContext
.DirEntry
.FatX
.FilenameLength
= (unsigned char)NameA
.Length
;
888 DirContext
.DirEntry
.FatX
.Attrib
= ReqAttr
;
889 if (BooleanFlagOn(RequestedOptions
, FILE_DIRECTORY_FILE
))
891 DirContext
.DirEntry
.FatX
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
894 /* set dates and times */
895 KeQuerySystemTime(&SystemTime
);
896 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.FatX
.CreationDate
,
897 &DirContext
.DirEntry
.FatX
.CreationTime
);
898 DirContext
.DirEntry
.FatX
.UpdateDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
899 DirContext
.DirEntry
.FatX
.UpdateTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
900 DirContext
.DirEntry
.FatX
.AccessDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
901 DirContext
.DirEntry
.FatX
.AccessTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
902 /* If it's moving, preserve creation time and file size */
903 if (MoveContext
!= NULL
)
905 DirContext
.DirEntry
.FatX
.CreationDate
= MoveContext
->CreationDate
;
906 DirContext
.DirEntry
.FatX
.CreationTime
= MoveContext
->CreationTime
;
907 DirContext
.DirEntry
.FatX
.FileSize
= MoveContext
->FileSize
;
910 /* No need to init cache here, vfatFindDirSpace() will have done it for us */
911 ASSERT(BooleanFlagOn(ParentFcb
->Flags
, FCB_CACHE_INITIALIZED
));
913 /* add entry into parent directory */
914 FileOffset
.u
.HighPart
= 0;
915 FileOffset
.u
.LowPart
= Index
* sizeof(FATX_DIR_ENTRY
);
918 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
), PIN_WAIT
, &Context
, (PVOID
*)&pFatXDirEntry
);
920 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
922 _SEH2_YIELD(return _SEH2_GetExceptionCode());
925 RtlCopyMemory(pFatXDirEntry
, &DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
926 CcSetDirtyPinnedData(Context
, NULL
);
927 CcUnpinData(Context
);
929 if (MoveContext
!= NULL
)
931 /* We're modifying an existing FCB - likely rename/move */
932 /* FIXME: check status */
933 vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
937 /* FIXME: check status */
938 vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
941 DPRINT("addentry ok\n");
942 return STATUS_SUCCESS
;
946 * deleting an existing FAT entry
950 IN PDEVICE_EXTENSION DeviceExt
,
952 OUT PVFAT_MOVE_CONTEXT MoveContext
)
954 ULONG CurrentCluster
= 0, NextCluster
, i
;
955 PVOID Context
= NULL
;
956 LARGE_INTEGER Offset
;
957 PFAT_DIR_ENTRY pDirEntry
= NULL
;
961 ASSERT(pFcb
->parentFcb
);
963 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pFcb
->parentFcb
);
964 if (!NT_SUCCESS(Status
))
969 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
970 DPRINT("delete entry: %u to %u\n", pFcb
->startIndex
, pFcb
->dirIndex
);
971 Offset
.u
.HighPart
= 0;
972 for (i
= pFcb
->startIndex
; i
<= pFcb
->dirIndex
; i
++)
974 if (Context
== NULL
|| ((i
* sizeof(FAT_DIR_ENTRY
)) % PAGE_SIZE
) == 0)
978 CcSetDirtyPinnedData(Context
, NULL
);
979 CcUnpinData(Context
);
981 Offset
.u
.LowPart
= (i
* sizeof(FAT_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
984 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
986 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
988 _SEH2_YIELD(return _SEH2_GetExceptionCode());
992 pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))].Filename
[0] = 0xe5;
993 if (i
== pFcb
->dirIndex
)
996 vfatDirEntryGetFirstCluster(DeviceExt
,
997 (PDIR_ENTRY
)&pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))]);
1001 /* In case of moving, save properties */
1002 if (MoveContext
!= NULL
)
1004 pDirEntry
= &pDirEntry
[pFcb
->dirIndex
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))];
1005 MoveContext
->FirstCluster
= CurrentCluster
;
1006 MoveContext
->FileSize
= pDirEntry
->FileSize
;
1007 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
1008 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
1013 CcSetDirtyPinnedData(Context
, NULL
);
1014 CcUnpinData(Context
);
1017 /* In case of moving, don't delete data */
1018 if (MoveContext
== NULL
)
1020 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
1022 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
1023 /* FIXME: check status */
1024 WriteCluster(DeviceExt
, CurrentCluster
, 0);
1025 CurrentCluster
= NextCluster
;
1029 return STATUS_SUCCESS
;
1033 * deleting an existing FAT entry
1037 IN PDEVICE_EXTENSION DeviceExt
,
1039 OUT PVFAT_MOVE_CONTEXT MoveContext
)
1041 ULONG CurrentCluster
= 0, NextCluster
;
1042 PVOID Context
= NULL
;
1043 LARGE_INTEGER Offset
;
1044 PFATX_DIR_ENTRY pDirEntry
;
1049 ASSERT(pFcb
->parentFcb
);
1050 ASSERT(vfatVolumeIsFatX(DeviceExt
));
1052 StartIndex
= pFcb
->startIndex
;
1054 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pFcb
->parentFcb
);
1055 if (!NT_SUCCESS(Status
))
1060 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
1061 DPRINT("delete entry: %u\n", StartIndex
);
1062 Offset
.u
.HighPart
= 0;
1063 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
1066 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
1068 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1070 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
1071 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1074 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
1075 pDirEntry
->FilenameLength
= 0xe5;
1076 CurrentCluster
= vfatDirEntryGetFirstCluster(DeviceExt
,
1077 (PDIR_ENTRY
)pDirEntry
);
1079 /* In case of moving, save properties */
1080 if (MoveContext
!= NULL
)
1082 MoveContext
->FirstCluster
= CurrentCluster
;
1083 MoveContext
->FileSize
= pDirEntry
->FileSize
;
1084 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
1085 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
1088 CcSetDirtyPinnedData(Context
, NULL
);
1089 CcUnpinData(Context
);
1091 /* In case of moving, don't delete data */
1092 if (MoveContext
== NULL
)
1094 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
1096 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
1097 /* FIXME: check status */
1098 WriteCluster(DeviceExt
, CurrentCluster
, 0);
1099 CurrentCluster
= NextCluster
;
1103 return STATUS_SUCCESS
;
1107 * move an existing FAT entry
1111 IN PDEVICE_EXTENSION DeviceExt
,
1113 IN PUNICODE_STRING FileName
,
1114 IN PVFATFCB ParentFcb
)
1118 VFAT_MOVE_CONTEXT MoveContext
;
1120 DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt
, pFcb
, FileName
, ParentFcb
);
1122 /* Delete old entry while keeping data */
1123 Status
= VfatDelEntry(DeviceExt
, pFcb
, &MoveContext
);
1124 if (!NT_SUCCESS(Status
))
1129 OldParent
= pFcb
->parentFcb
;
1130 CcFlushCache(&OldParent
->SectionObjectPointers
, NULL
, 0, NULL
);
1131 MoveContext
.InPlace
= (OldParent
== ParentFcb
);
1133 /* Add our new entry with our cluster */
1134 Status
= VfatAddEntry(DeviceExt
,
1138 (vfatFCBIsDirectory(pFcb
) ? FILE_DIRECTORY_FILE
: 0),
1142 CcFlushCache(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, NULL
);
1147 extern BOOLEAN
FATXIsDirectoryEmpty(PDEVICE_EXTENSION DeviceExt
, PVFATFCB Fcb
);
1148 extern BOOLEAN
FATIsDirectoryEmpty(PDEVICE_EXTENSION DeviceExt
, PVFATFCB Fcb
);
1149 extern NTSTATUS
FATGetNextDirEntry(PVOID
*pContext
, PVOID
*pPage
, PVFATFCB pDirFcb
, PVFAT_DIRENTRY_CONTEXT DirContext
, BOOLEAN First
);
1150 extern NTSTATUS
FATXGetNextDirEntry(PVOID
*pContext
, PVOID
*pPage
, PVFATFCB pDirFcb
, PVFAT_DIRENTRY_CONTEXT DirContext
, BOOLEAN First
);
1152 VFAT_DISPATCH FatXDispatch
= {
1153 FATXIsDirectoryEmpty
, // .IsDirectoryEmpty
1154 FATXAddEntry
, // .AddEntry
1155 FATXDelEntry
, // .DelEntry
1156 FATXGetNextDirEntry
, // .GetNextDirEntry
1159 VFAT_DISPATCH FatDispatch
= {
1160 FATIsDirectoryEmpty
, // .IsDirectoryEmpty
1161 FATAddEntry
, // .AddEntry
1162 FATDelEntry
, // .DelEntry
1163 FATGetNextDirEntry
, // .GetNextDirEntry