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
);
52 if (DebugFile
.Buffer
!= NULL
&& FsRtlIsNameInExpression(&DebugFile
, &fcb
->LongNameU
, FALSE
, NULL
))
54 DPRINT1("Attaching %p to %p (%d)\n", fcb
, fileObject
, fcb
->RefCount
);
58 newCCB
= ExAllocateFromNPagedLookasideList(&VfatGlobalData
->CcbLookasideList
);
61 ObDereferenceObject(fileObject
);
62 return STATUS_INSUFFICIENT_RESOURCES
;
64 RtlZeroMemory(newCCB
, sizeof (VFATCCB
));
66 fileObject
->SectionObjectPointer
= &fcb
->SectionObjectPointers
;
67 fileObject
->FsContext
= fcb
;
68 fileObject
->FsContext2
= newCCB
;
69 fileObject
->Vpb
= vcb
->IoVPB
;
70 fcb
->FileObject
= fileObject
;
74 CcInitializeCacheMap(fileObject
,
75 (PCC_FILE_SIZES
)(&fcb
->RFCB
.AllocationSize
),
77 &VfatGlobalData
->CacheMgrCallbacks
,
80 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
82 status
= _SEH2_GetExceptionCode();
83 fcb
->FileObject
= NULL
;
84 ExFreeToNPagedLookasideList(&VfatGlobalData
->CcbLookasideList
, newCCB
);
85 ObDereferenceObject(fileObject
);
90 vfatGrabFCB(vcb
, fcb
);
91 SetFlag(fcb
->Flags
, FCB_CACHE_INITIALIZED
);
95 ExReleaseResourceLite(&vcb
->DirResource
);
98 return STATUS_SUCCESS
;
102 * update an existing FAT entry
106 IN PDEVICE_EXTENSION DeviceExt
,
111 LARGE_INTEGER Offset
;
118 if (vfatVolumeIsFatX(DeviceExt
))
120 SizeDirEntry
= sizeof(FATX_DIR_ENTRY
);
121 dirIndex
= pFcb
->startIndex
;
125 SizeDirEntry
= sizeof(FAT_DIR_ENTRY
);
126 dirIndex
= pFcb
->dirIndex
;
129 DPRINT("updEntry dirIndex %u, PathName \'%wZ\'\n", dirIndex
, &pFcb
->PathNameU
);
131 if (vfatFCBIsRoot(pFcb
) || BooleanFlagOn(pFcb
->Flags
, FCB_IS_FAT
| FCB_IS_VOLUME
))
133 return STATUS_SUCCESS
;
136 ASSERT(pFcb
->parentFcb
);
138 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pFcb
->parentFcb
);
139 if (!NT_SUCCESS(Status
))
144 Offset
.u
.HighPart
= 0;
145 Offset
.u
.LowPart
= dirIndex
* SizeDirEntry
;
148 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, SizeDirEntry
, PIN_WAIT
, &Context
, (PVOID
*)&PinEntry
);
150 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
152 DPRINT1("Failed write to \'%wZ\'.\n", &pFcb
->parentFcb
->PathNameU
);
153 _SEH2_YIELD(return _SEH2_GetExceptionCode());
157 pFcb
->Flags
&= ~FCB_IS_DIRTY
;
158 RtlCopyMemory(PinEntry
, &pFcb
->entry
, SizeDirEntry
);
159 CcSetDirtyPinnedData(Context
, NULL
);
160 CcUnpinData(Context
);
161 return STATUS_SUCCESS
;
165 * rename an existing FAT entry
169 IN PDEVICE_EXTENSION DeviceExt
,
171 IN PUNICODE_STRING FileName
,
172 IN BOOLEAN CaseChangeOnly
)
176 PVOID Context
= NULL
;
177 LARGE_INTEGER Offset
;
178 PFATX_DIR_ENTRY pDirEntry
;
181 DPRINT("vfatRenameEntry(%p, %p, %wZ, %d)\n", DeviceExt
, pFcb
, FileName
, CaseChangeOnly
);
183 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pFcb
->parentFcb
);
184 if (!NT_SUCCESS(Status
))
189 if (vfatVolumeIsFatX(DeviceExt
))
191 VFAT_DIRENTRY_CONTEXT DirContext
;
193 /* Open associated dir entry */
194 StartIndex
= pFcb
->startIndex
;
195 Offset
.u
.HighPart
= 0;
196 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
199 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
201 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
203 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
204 _SEH2_YIELD(return _SEH2_GetExceptionCode());
208 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
211 NameA
.Buffer
= (PCHAR
)pDirEntry
->Filename
;
213 NameA
.MaximumLength
= 42;
214 RtlUnicodeStringToOemString(&NameA
, FileName
, FALSE
);
215 pDirEntry
->FilenameLength
= (unsigned char)NameA
.Length
;
218 DirContext
.DeviceExt
= DeviceExt
;
219 DirContext
.ShortNameU
.Length
= 0;
220 DirContext
.ShortNameU
.MaximumLength
= 0;
221 DirContext
.ShortNameU
.Buffer
= NULL
;
222 DirContext
.LongNameU
= *FileName
;
223 DirContext
.DirEntry
.FatX
= *pDirEntry
;
225 CcSetDirtyPinnedData(Context
, NULL
);
226 CcUnpinData(Context
);
228 Status
= vfatUpdateFCB(DeviceExt
, pFcb
, &DirContext
, pFcb
->parentFcb
);
229 if (NT_SUCCESS(Status
))
231 CcFlushCache(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, NULL
);
238 /* This we cannot handle properly, move file - would likely need love */
239 return VfatMoveEntry(DeviceExt
, pFcb
, FileName
, pFcb
->parentFcb
);
244 * try to find contiguous entries frees in directory,
245 * extend a directory if is necessary
249 IN PDEVICE_EXTENSION DeviceExt
,
254 LARGE_INTEGER FileOffset
;
255 ULONG i
, count
, size
, nbFree
= 0;
256 PDIR_ENTRY pFatEntry
= NULL
;
257 PVOID Context
= NULL
;
260 BOOLEAN IsFatX
= vfatVolumeIsFatX(DeviceExt
);
261 FileOffset
.QuadPart
= 0;
264 SizeDirEntry
= sizeof(FATX_DIR_ENTRY
);
266 SizeDirEntry
= sizeof(FAT_DIR_ENTRY
);
268 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pDirFcb
);
269 if (!NT_SUCCESS(Status
))
274 count
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
/ SizeDirEntry
;
275 size
= DeviceExt
->FatInfo
.BytesPerCluster
/ SizeDirEntry
;
276 for (i
= 0; i
< count
; i
++, pFatEntry
= (PDIR_ENTRY
)((ULONG_PTR
)pFatEntry
+ SizeDirEntry
))
278 if (Context
== NULL
|| (i
% size
) == 0)
282 CcUnpinData(Context
);
286 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
288 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
290 _SEH2_YIELD(return FALSE
);
294 FileOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.BytesPerCluster
;
296 if (ENTRY_END(IsFatX
, pFatEntry
))
300 if (ENTRY_DELETED(IsFatX
, pFatEntry
))
308 if (nbFree
== nbSlots
)
315 CcUnpinData(Context
);
318 if (nbFree
== nbSlots
)
320 /* found enough contiguous free slots */
321 *start
= i
- nbSlots
+ 1;
326 if (*start
+ nbSlots
> count
)
328 LARGE_INTEGER AllocationSize
;
329 /* extend the directory */
330 if (vfatFCBIsRoot(pDirFcb
) && DeviceExt
->FatInfo
.FatType
!= FAT32
)
332 /* We can't extend a root directory on a FAT12/FAT16/FATX partition */
335 AllocationSize
.QuadPart
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
+ DeviceExt
->FatInfo
.BytesPerCluster
;
336 Status
= VfatSetAllocationSizeInformation(pDirFcb
->FileObject
, pDirFcb
,
337 DeviceExt
, &AllocationSize
);
338 if (!NT_SUCCESS(Status
))
342 /* clear the new dir cluster */
343 FileOffset
.u
.LowPart
= (ULONG
)(pDirFcb
->RFCB
.FileSize
.QuadPart
-
344 DeviceExt
->FatInfo
.BytesPerCluster
);
347 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
349 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
351 _SEH2_YIELD(return FALSE
);
356 memset(pFatEntry
, 0xff, DeviceExt
->FatInfo
.BytesPerCluster
);
358 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
360 else if (*start
+ nbSlots
< count
)
362 /* clear the entry after the last new entry */
363 FileOffset
.u
.LowPart
= (*start
+ nbSlots
) * SizeDirEntry
;
366 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, SizeDirEntry
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
368 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
370 _SEH2_YIELD(return FALSE
);
375 memset(pFatEntry
, 0xff, SizeDirEntry
);
377 RtlZeroMemory(pFatEntry
, SizeDirEntry
);
381 CcSetDirtyPinnedData(Context
, NULL
);
382 CcUnpinData(Context
);
385 DPRINT("nbSlots %u nbFree %u, entry number %u\n", nbSlots
, nbFree
, *start
);
390 create a new FAT entry
394 IN PDEVICE_EXTENSION DeviceExt
,
395 IN PUNICODE_STRING NameU
,
397 IN PVFATFCB ParentFcb
,
398 IN ULONG RequestedOptions
,
400 IN PVFAT_MOVE_CONTEXT MoveContext
)
402 PVOID Context
= NULL
;
403 PFAT_DIR_ENTRY pFatEntry
;
405 USHORT nbSlots
= 0, j
;
407 BOOLEAN needTilde
= FALSE
, needLong
= FALSE
;
408 BOOLEAN BaseAllLower
, BaseAllUpper
;
409 BOOLEAN ExtensionAllLower
, ExtensionAllUpper
;
413 ULONG CurrentCluster
;
414 LARGE_INTEGER SystemTime
, FileOffset
;
415 NTSTATUS Status
= STATUS_SUCCESS
;
424 VFAT_DIRENTRY_CONTEXT DirContext
;
425 WCHAR LongNameBuffer
[LONGNAME_MAX_LENGTH
+ 1];
426 WCHAR ShortNameBuffer
[13];
428 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
430 DirContext
.LongNameU
= *NameU
;
431 IsDirectory
= BooleanFlagOn(RequestedOptions
, FILE_DIRECTORY_FILE
);
433 /* nb of entry needed for long name+normal entry */
434 nbSlots
= (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 12) / 13 + 1;
435 DPRINT("NameLen= %u, nbSlots =%u\n", DirContext
.LongNameU
.Length
/ sizeof(WCHAR
), nbSlots
);
436 Buffer
= ExAllocatePoolWithTag(NonPagedPool
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
), TAG_VFAT
);
439 return STATUS_INSUFFICIENT_RESOURCES
;
441 RtlZeroMemory(Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
442 pSlots
= (slot
*) Buffer
;
444 NameA
.Buffer
= aName
;
446 NameA
.MaximumLength
= sizeof(aName
);
448 DirContext
.DeviceExt
= DeviceExt
;
449 DirContext
.ShortNameU
.Buffer
= ShortNameBuffer
;
450 DirContext
.ShortNameU
.Length
= 0;
451 DirContext
.ShortNameU
.MaximumLength
= sizeof(ShortNameBuffer
);
453 RtlZeroMemory(&DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
455 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.LongNameU
, &NameA
, &SpacesFound
);
457 if (!IsNameLegal
|| SpacesFound
)
459 GENERATE_NAME_CONTEXT NameContext
;
460 VFAT_DIRENTRY_CONTEXT SearchContext
;
461 WCHAR ShortSearchName
[13];
464 RtlZeroMemory(&NameContext
, sizeof(GENERATE_NAME_CONTEXT
));
465 SearchContext
.DeviceExt
= DeviceExt
;
466 SearchContext
.LongNameU
.Buffer
= LongNameBuffer
;
467 SearchContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
468 SearchContext
.ShortNameU
.Buffer
= ShortSearchName
;
469 SearchContext
.ShortNameU
.MaximumLength
= sizeof(ShortSearchName
);
471 for (i
= 0; i
< 100; i
++)
473 RtlGenerate8dot3Name(&DirContext
.LongNameU
, FALSE
, &NameContext
, &DirContext
.ShortNameU
);
474 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
475 SearchContext
.DirIndex
= 0;
476 Status
= FindFile(DeviceExt
, ParentFcb
, &DirContext
.ShortNameU
, &SearchContext
, TRUE
);
477 if (!NT_SUCCESS(Status
))
481 else if (MoveContext
)
484 if (strncmp((char *)SearchContext
.DirEntry
.Fat
.ShortName
, (char *)(*Fcb
)->entry
.Fat
.ShortName
, 11) == 0)
486 if (MoveContext
->InPlace
)
488 ASSERT(SearchContext
.DirEntry
.Fat
.FileSize
== MoveContext
->FileSize
);
494 if (i
== 100) /* FIXME : what to do after this ? */
496 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
497 return STATUS_UNSUCCESSFUL
;
499 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.ShortNameU
, &NameA
, &SpacesFound
);
503 BaseAllLower
= BaseAllUpper
= TRUE
;
504 ExtensionAllLower
= ExtensionAllUpper
= TRUE
;
506 for (i
= 0; i
< DirContext
.LongNameU
.Length
/ sizeof(WCHAR
); i
++)
508 c
= DirContext
.LongNameU
.Buffer
[i
];
509 if (c
>= L
'A' && c
<= L
'Z')
512 ExtensionAllLower
= FALSE
;
514 BaseAllLower
= FALSE
;
516 else if (c
>= L
'a' && c
<= L
'z')
519 ExtensionAllUpper
= FALSE
;
521 BaseAllUpper
= FALSE
;
535 if ((!BaseAllLower
&& !BaseAllUpper
) ||
536 (!ExtensionAllLower
&& !ExtensionAllUpper
))
541 RtlUpcaseUnicodeString(&DirContext
.ShortNameU
, &DirContext
.LongNameU
, FALSE
);
542 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
544 aName
[NameA
.Length
] = 0;
545 DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
546 aName
, &DirContext
.LongNameU
, needTilde
, needLong
);
547 memset(DirContext
.DirEntry
.Fat
.ShortName
, ' ', 11);
548 for (i
= 0; i
< 8 && aName
[i
] && aName
[i
] != '.'; i
++)
550 DirContext
.DirEntry
.Fat
.Filename
[i
] = aName
[i
];
555 for (j
= 0; j
< 3 && aName
[i
]; j
++, i
++)
557 DirContext
.DirEntry
.Fat
.Ext
[j
] = aName
[i
];
560 if (DirContext
.DirEntry
.Fat
.Filename
[0] == 0xe5)
562 DirContext
.DirEntry
.Fat
.Filename
[0] = 0x05;
567 RtlCopyMemory(LongNameBuffer
, DirContext
.LongNameU
.Buffer
, DirContext
.LongNameU
.Length
);
568 DirContext
.LongNameU
.Buffer
= LongNameBuffer
;
569 DirContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
570 DirContext
.LongNameU
.Buffer
[DirContext
.LongNameU
.Length
/ sizeof(WCHAR
)] = 0;
571 memset(DirContext
.LongNameU
.Buffer
+ DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 1, 0xff,
572 DirContext
.LongNameU
.MaximumLength
- DirContext
.LongNameU
.Length
- sizeof(WCHAR
));
577 if (BaseAllLower
&& !BaseAllUpper
)
579 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_BASE
;
581 if (ExtensionAllLower
&& !ExtensionAllUpper
)
583 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_EXT
;
587 DPRINT ("dos name=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
590 DirContext
.DirEntry
.Fat
.Attrib
= ReqAttr
;
593 DirContext
.DirEntry
.Fat
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
595 /* set dates and times */
596 KeQuerySystemTime(&SystemTime
);
597 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.Fat
.CreationDate
,
598 &DirContext
.DirEntry
.Fat
.CreationTime
);
599 DirContext
.DirEntry
.Fat
.UpdateDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
600 DirContext
.DirEntry
.Fat
.UpdateTime
= DirContext
.DirEntry
.Fat
.CreationTime
;
601 DirContext
.DirEntry
.Fat
.AccessDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
602 /* If it's moving, preserve creation time and file size */
603 if (MoveContext
!= NULL
)
605 DirContext
.DirEntry
.Fat
.CreationDate
= MoveContext
->CreationDate
;
606 DirContext
.DirEntry
.Fat
.CreationTime
= MoveContext
->CreationTime
;
607 DirContext
.DirEntry
.Fat
.FileSize
= MoveContext
->FileSize
;
612 /* calculate checksum for 8.3 name */
613 for (pSlots
[0].alias_checksum
= 0, i
= 0; i
< 11; i
++)
615 pSlots
[0].alias_checksum
= (((pSlots
[0].alias_checksum
& 1) << 7
616 | ((pSlots
[0].alias_checksum
& 0xfe) >> 1))
617 + DirContext
.DirEntry
.Fat
.ShortName
[i
]);
619 /* construct slots and entry */
620 for (i
= nbSlots
- 2; i
>= 0; i
--)
622 DPRINT("construct slot %d\n", i
);
623 pSlots
[i
].attr
= 0xf;
626 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1);
630 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1 + 0x40);
632 pSlots
[i
].alias_checksum
= pSlots
[0].alias_checksum
;
633 RtlCopyMemory(pSlots
[i
].name0_4
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13, 10);
634 RtlCopyMemory(pSlots
[i
].name5_10
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 5, 12);
635 RtlCopyMemory(pSlots
[i
].name11_12
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 11, 4);
638 /* try to find nbSlots contiguous entries frees in directory */
639 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, nbSlots
, &DirContext
.StartIndex
))
641 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
642 return STATUS_DISK_FULL
;
644 DirContext
.DirIndex
= DirContext
.StartIndex
+ nbSlots
- 1;
647 /* If we aren't moving, use next */
648 if (MoveContext
== NULL
)
651 Status
= NextCluster(DeviceExt
, 0, &CurrentCluster
, TRUE
);
652 if (CurrentCluster
== 0xffffffff || !NT_SUCCESS(Status
))
654 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
655 if (!NT_SUCCESS(Status
))
659 return STATUS_DISK_FULL
;
664 CurrentCluster
= MoveContext
->FirstCluster
;
667 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
669 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
671 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
673 else if (MoveContext
!= NULL
)
675 CurrentCluster
= MoveContext
->FirstCluster
;
677 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
679 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
681 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
684 /* No need to init cache here, vfatFindDirSpace() will have done it for us */
686 i
= DeviceExt
->FatInfo
.BytesPerCluster
/ sizeof(FAT_DIR_ENTRY
);
687 FileOffset
.u
.HighPart
= 0;
688 FileOffset
.u
.LowPart
= DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
);
689 if (DirContext
.StartIndex
/ i
== DirContext
.DirIndex
/ i
)
694 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
), PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
696 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
698 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
699 _SEH2_YIELD(return _SEH2_GetExceptionCode());
705 RtlCopyMemory(pFatEntry
, Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
707 RtlCopyMemory(pFatEntry
+ (nbSlots
- 1), &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
712 size
= DeviceExt
->FatInfo
.BytesPerCluster
-
713 (DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
)) % DeviceExt
->FatInfo
.BytesPerCluster
;
714 i
= size
/ sizeof(FAT_DIR_ENTRY
);
717 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, size
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
719 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
721 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
722 _SEH2_YIELD(return _SEH2_GetExceptionCode());
725 RtlCopyMemory(pFatEntry
, Buffer
, size
);
726 CcSetDirtyPinnedData(Context
, NULL
);
727 CcUnpinData(Context
);
728 FileOffset
.u
.LowPart
+= size
;
731 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
) - size
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
733 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
735 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
736 _SEH2_YIELD(return _SEH2_GetExceptionCode());
741 RtlCopyMemory(pFatEntry
, (PVOID
)(Buffer
+ size
), (nbSlots
- 1 - i
) * sizeof(FAT_DIR_ENTRY
));
743 RtlCopyMemory(pFatEntry
+ nbSlots
- 1 - i
, &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
745 CcSetDirtyPinnedData(Context
, NULL
);
746 CcUnpinData(Context
);
748 if (MoveContext
!= NULL
)
750 /* We're modifying an existing FCB - likely rename/move */
751 Status
= vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
755 Status
= vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
757 if (!NT_SUCCESS(Status
))
759 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
763 DPRINT("new : entry=%11.11s\n", (*Fcb
)->entry
.Fat
.Filename
);
764 DPRINT("new : entry=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
768 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, (*Fcb
));
769 if (!NT_SUCCESS(Status
))
774 FileOffset
.QuadPart
= 0;
777 CcPinRead((*Fcb
)->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
779 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
781 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
782 _SEH2_YIELD(return _SEH2_GetExceptionCode());
785 /* clear the new directory cluster if not moving */
786 if (MoveContext
== NULL
)
788 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
789 /* create '.' and '..' */
790 RtlCopyMemory(&pFatEntry
[0].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
791 RtlCopyMemory(pFatEntry
[0].ShortName
, ". ", 11);
792 RtlCopyMemory(&pFatEntry
[1].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
793 RtlCopyMemory(pFatEntry
[1].ShortName
, ".. ", 11);
796 pFatEntry
[1].FirstCluster
= ParentFcb
->entry
.Fat
.FirstCluster
;
797 pFatEntry
[1].FirstClusterHigh
= ParentFcb
->entry
.Fat
.FirstClusterHigh
;
798 if (vfatFCBIsRoot(ParentFcb
))
800 pFatEntry
[1].FirstCluster
= 0;
801 pFatEntry
[1].FirstClusterHigh
= 0;
803 CcSetDirtyPinnedData(Context
, NULL
);
804 CcUnpinData(Context
);
806 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
807 DPRINT("addentry ok\n");
808 return STATUS_SUCCESS
;
812 create a new FAT entry
816 IN PDEVICE_EXTENSION DeviceExt
,
817 IN PUNICODE_STRING NameU
,
819 IN PVFATFCB ParentFcb
,
820 IN ULONG RequestedOptions
,
822 IN PVFAT_MOVE_CONTEXT MoveContext
)
824 PVOID Context
= NULL
;
825 LARGE_INTEGER SystemTime
, FileOffset
;
827 VFAT_DIRENTRY_CONTEXT DirContext
;
828 PFATX_DIR_ENTRY pFatXDirEntry
;
831 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
833 DirContext
.LongNameU
= *NameU
;
835 if (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) > 42)
838 return STATUS_NAME_TOO_LONG
;
841 /* try to find 1 entry free in directory */
842 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, 1, &DirContext
.StartIndex
))
844 return STATUS_DISK_FULL
;
846 Index
= DirContext
.DirIndex
= DirContext
.StartIndex
;
847 if (!vfatFCBIsRoot(ParentFcb
))
849 DirContext
.DirIndex
+= 2;
850 DirContext
.StartIndex
+= 2;
853 DirContext
.ShortNameU
.Buffer
= 0;
854 DirContext
.ShortNameU
.Length
= 0;
855 DirContext
.ShortNameU
.MaximumLength
= 0;
856 DirContext
.DeviceExt
= DeviceExt
;
857 RtlZeroMemory(&DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
858 memset(DirContext
.DirEntry
.FatX
.Filename
, 0xff, 42);
859 /* Use cluster, if moving */
860 if (MoveContext
!= NULL
)
862 DirContext
.DirEntry
.FatX
.FirstCluster
= MoveContext
->FirstCluster
;
866 DirContext
.DirEntry
.FatX
.FirstCluster
= 0;
868 DirContext
.DirEntry
.FatX
.FileSize
= 0;
871 NameA
.Buffer
= (PCHAR
)DirContext
.DirEntry
.FatX
.Filename
;
873 NameA
.MaximumLength
= 42;
874 RtlUnicodeStringToOemString(&NameA
, &DirContext
.LongNameU
, FALSE
);
875 DirContext
.DirEntry
.FatX
.FilenameLength
= (unsigned char)NameA
.Length
;
878 DirContext
.DirEntry
.FatX
.Attrib
= ReqAttr
;
879 if (BooleanFlagOn(RequestedOptions
, FILE_DIRECTORY_FILE
))
881 DirContext
.DirEntry
.FatX
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
884 /* set dates and times */
885 KeQuerySystemTime(&SystemTime
);
886 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.FatX
.CreationDate
,
887 &DirContext
.DirEntry
.FatX
.CreationTime
);
888 DirContext
.DirEntry
.FatX
.UpdateDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
889 DirContext
.DirEntry
.FatX
.UpdateTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
890 DirContext
.DirEntry
.FatX
.AccessDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
891 DirContext
.DirEntry
.FatX
.AccessTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
892 /* If it's moving, preserve creation time and file size */
893 if (MoveContext
!= NULL
)
895 DirContext
.DirEntry
.FatX
.CreationDate
= MoveContext
->CreationDate
;
896 DirContext
.DirEntry
.FatX
.CreationTime
= MoveContext
->CreationTime
;
897 DirContext
.DirEntry
.FatX
.FileSize
= MoveContext
->FileSize
;
900 /* No need to init cache here, vfatFindDirSpace() will have done it for us */
902 /* add entry into parent directory */
903 FileOffset
.u
.HighPart
= 0;
904 FileOffset
.u
.LowPart
= Index
* sizeof(FATX_DIR_ENTRY
);
907 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
), PIN_WAIT
, &Context
, (PVOID
*)&pFatXDirEntry
);
909 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
911 _SEH2_YIELD(return _SEH2_GetExceptionCode());
914 RtlCopyMemory(pFatXDirEntry
, &DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
915 CcSetDirtyPinnedData(Context
, NULL
);
916 CcUnpinData(Context
);
918 if (MoveContext
!= NULL
)
920 /* We're modifying an existing FCB - likely rename/move */
921 /* FIXME: check status */
922 vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
926 /* FIXME: check status */
927 vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
930 DPRINT("addentry ok\n");
931 return STATUS_SUCCESS
;
935 * deleting an existing FAT entry
939 IN PDEVICE_EXTENSION DeviceExt
,
941 OUT PVFAT_MOVE_CONTEXT MoveContext
)
943 ULONG CurrentCluster
= 0, NextCluster
, i
;
944 PVOID Context
= NULL
;
945 LARGE_INTEGER Offset
;
946 PFAT_DIR_ENTRY pDirEntry
= NULL
;
950 ASSERT(pFcb
->parentFcb
);
952 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pFcb
->parentFcb
);
953 if (!NT_SUCCESS(Status
))
958 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
959 DPRINT("delete entry: %u to %u\n", pFcb
->startIndex
, pFcb
->dirIndex
);
960 Offset
.u
.HighPart
= 0;
961 for (i
= pFcb
->startIndex
; i
<= pFcb
->dirIndex
; i
++)
963 if (Context
== NULL
|| ((i
* sizeof(FAT_DIR_ENTRY
)) % PAGE_SIZE
) == 0)
967 CcSetDirtyPinnedData(Context
, NULL
);
968 CcUnpinData(Context
);
970 Offset
.u
.LowPart
= (i
* sizeof(FAT_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
973 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
975 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
977 _SEH2_YIELD(return _SEH2_GetExceptionCode());
981 pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))].Filename
[0] = 0xe5;
982 if (i
== pFcb
->dirIndex
)
985 vfatDirEntryGetFirstCluster(DeviceExt
,
986 (PDIR_ENTRY
)&pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))]);
990 /* In case of moving, save properties */
991 if (MoveContext
!= NULL
)
993 pDirEntry
= &pDirEntry
[pFcb
->dirIndex
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))];
994 MoveContext
->FirstCluster
= CurrentCluster
;
995 MoveContext
->FileSize
= pDirEntry
->FileSize
;
996 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
997 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
1002 CcSetDirtyPinnedData(Context
, NULL
);
1003 CcUnpinData(Context
);
1006 /* In case of moving, don't delete data */
1007 if (MoveContext
== NULL
)
1009 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
1011 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
1012 /* FIXME: check status */
1013 WriteCluster(DeviceExt
, CurrentCluster
, 0);
1014 CurrentCluster
= NextCluster
;
1018 return STATUS_SUCCESS
;
1022 * deleting an existing FAT entry
1026 IN PDEVICE_EXTENSION DeviceExt
,
1028 OUT PVFAT_MOVE_CONTEXT MoveContext
)
1030 ULONG CurrentCluster
= 0, NextCluster
;
1031 PVOID Context
= NULL
;
1032 LARGE_INTEGER Offset
;
1033 PFATX_DIR_ENTRY pDirEntry
;
1038 ASSERT(pFcb
->parentFcb
);
1039 ASSERT(vfatVolumeIsFatX(DeviceExt
));
1041 StartIndex
= pFcb
->startIndex
;
1043 Status
= vfatFCBInitializeCacheFromVolume(DeviceExt
, pFcb
->parentFcb
);
1044 if (!NT_SUCCESS(Status
))
1049 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
1050 DPRINT("delete entry: %u\n", StartIndex
);
1051 Offset
.u
.HighPart
= 0;
1052 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
1055 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
1057 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1059 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
1060 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1063 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
1064 pDirEntry
->FilenameLength
= 0xe5;
1065 CurrentCluster
= vfatDirEntryGetFirstCluster(DeviceExt
,
1066 (PDIR_ENTRY
)pDirEntry
);
1068 /* In case of moving, save properties */
1069 if (MoveContext
!= NULL
)
1071 MoveContext
->FirstCluster
= CurrentCluster
;
1072 MoveContext
->FileSize
= pDirEntry
->FileSize
;
1073 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
1074 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
1077 CcSetDirtyPinnedData(Context
, NULL
);
1078 CcUnpinData(Context
);
1080 /* In case of moving, don't delete data */
1081 if (MoveContext
== NULL
)
1083 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
1085 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
1086 /* FIXME: check status */
1087 WriteCluster(DeviceExt
, CurrentCluster
, 0);
1088 CurrentCluster
= NextCluster
;
1092 return STATUS_SUCCESS
;
1096 * move an existing FAT entry
1100 IN PDEVICE_EXTENSION DeviceExt
,
1102 IN PUNICODE_STRING FileName
,
1103 IN PVFATFCB ParentFcb
)
1107 VFAT_MOVE_CONTEXT MoveContext
;
1109 DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt
, pFcb
, FileName
, ParentFcb
);
1111 /* Delete old entry while keeping data */
1112 Status
= VfatDelEntry(DeviceExt
, pFcb
, &MoveContext
);
1113 if (!NT_SUCCESS(Status
))
1118 OldParent
= pFcb
->parentFcb
;
1119 CcFlushCache(&OldParent
->SectionObjectPointers
, NULL
, 0, NULL
);
1120 MoveContext
.InPlace
= (OldParent
== ParentFcb
);
1122 /* Add our new entry with our cluster */
1123 Status
= VfatAddEntry(DeviceExt
,
1127 (vfatFCBIsDirectory(pFcb
) ? FILE_DIRECTORY_FILE
: 0),
1131 CcFlushCache(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, NULL
);
1136 extern BOOLEAN
FATXIsDirectoryEmpty(PDEVICE_EXTENSION DeviceExt
, PVFATFCB Fcb
);
1137 extern BOOLEAN
FATIsDirectoryEmpty(PDEVICE_EXTENSION DeviceExt
, PVFATFCB Fcb
);
1138 extern NTSTATUS
FATGetNextDirEntry(PVOID
*pContext
, PVOID
*pPage
, PVFATFCB pDirFcb
, PVFAT_DIRENTRY_CONTEXT DirContext
, BOOLEAN First
);
1139 extern NTSTATUS
FATXGetNextDirEntry(PVOID
*pContext
, PVOID
*pPage
, PVFATFCB pDirFcb
, PVFAT_DIRENTRY_CONTEXT DirContext
, BOOLEAN First
);
1141 VFAT_DISPATCH FatXDispatch
= {
1142 FATXIsDirectoryEmpty
, // .IsDirectoryEmpty
1143 FATXAddEntry
, // .AddEntry
1144 FATXDelEntry
, // .DelEntry
1145 FATXGetNextDirEntry
, // .GetNextDirEntry
1148 VFAT_DISPATCH FatDispatch
= {
1149 FATIsDirectoryEmpty
, // .IsDirectoryEmpty
1150 FATAddEntry
, // .AddEntry
1151 FATDelEntry
, // .DelEntry
1152 FATGetNextDirEntry
, // .GetNextDirEntry