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 * update an existing FAT entry
37 SizeDirEntry
= sizeof(FATX_DIR_ENTRY
);
38 dirIndex
= pFcb
->startIndex
;
42 SizeDirEntry
= sizeof(FAT_DIR_ENTRY
);
43 dirIndex
= pFcb
->dirIndex
;
46 DPRINT("updEntry dirIndex %u, PathName \'%wZ\'\n", dirIndex
, &pFcb
->PathNameU
);
48 if (vfatFCBIsRoot(pFcb
) || BooleanFlagOn(pFcb
->Flags
, FCB_IS_FAT
| FCB_IS_VOLUME
))
50 return STATUS_SUCCESS
;
53 ASSERT(pFcb
->parentFcb
);
55 Offset
.u
.HighPart
= 0;
56 Offset
.u
.LowPart
= dirIndex
* SizeDirEntry
;
59 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, SizeDirEntry
, PIN_WAIT
, &Context
, (PVOID
*)&PinEntry
);
61 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
63 DPRINT1("Failed write to \'%wZ\'.\n", &pFcb
->parentFcb
->PathNameU
);
64 _SEH2_YIELD(return _SEH2_GetExceptionCode());
68 pFcb
->Flags
&= ~FCB_IS_DIRTY
;
69 RtlCopyMemory(PinEntry
, &pFcb
->entry
, SizeDirEntry
);
70 CcSetDirtyPinnedData(Context
, NULL
);
72 return STATUS_SUCCESS
;
76 * rename an existing FAT entry
80 IN PDEVICE_EXTENSION DeviceExt
,
82 IN PUNICODE_STRING FileName
,
83 IN BOOLEAN CaseChangeOnly
)
89 PFATX_DIR_ENTRY pDirEntry
;
92 DPRINT("vfatRenameEntry(%p, %p, %wZ, %d)\n", DeviceExt
, pFcb
, FileName
, CaseChangeOnly
);
94 if (vfatVolumeIsFatX(DeviceExt
))
96 VFAT_DIRENTRY_CONTEXT DirContext
;
98 /* Open associated dir entry */
99 StartIndex
= pFcb
->startIndex
;
100 Offset
.u
.HighPart
= 0;
101 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
104 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
106 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
108 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
109 _SEH2_YIELD(return _SEH2_GetExceptionCode());
113 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
116 NameA
.Buffer
= (PCHAR
)pDirEntry
->Filename
;
118 NameA
.MaximumLength
= 42;
119 RtlUnicodeStringToOemString(&NameA
, FileName
, FALSE
);
120 pDirEntry
->FilenameLength
= (unsigned char)NameA
.Length
;
123 DirContext
.ShortNameU
.Length
= 0;
124 DirContext
.ShortNameU
.MaximumLength
= 0;
125 DirContext
.ShortNameU
.Buffer
= NULL
;
126 DirContext
.LongNameU
= *FileName
;
127 DirContext
.DirEntry
.FatX
= *pDirEntry
;
129 CcSetDirtyPinnedData(Context
, NULL
);
130 CcUnpinData(Context
);
132 Status
= vfatUpdateFCB(DeviceExt
, pFcb
, &DirContext
, pFcb
->parentFcb
);
133 if (NT_SUCCESS(Status
))
135 CcFlushCache(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, NULL
);
142 /* This we cannot handle properly, move file - would likely need love */
143 return VfatMoveEntry(DeviceExt
, pFcb
, FileName
, pFcb
->parentFcb
);
148 * try to find contiguous entries frees in directory,
149 * extend a directory if is necessary
153 IN PDEVICE_EXTENSION DeviceExt
,
158 LARGE_INTEGER FileOffset
;
159 ULONG i
, count
, size
, nbFree
= 0;
160 PDIR_ENTRY pFatEntry
= NULL
;
161 PVOID Context
= NULL
;
164 BOOLEAN IsFatX
= vfatVolumeIsFatX(DeviceExt
);
165 FileOffset
.QuadPart
= 0;
168 SizeDirEntry
= sizeof(FATX_DIR_ENTRY
);
170 SizeDirEntry
= sizeof(FAT_DIR_ENTRY
);
172 count
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
/ SizeDirEntry
;
173 size
= DeviceExt
->FatInfo
.BytesPerCluster
/ SizeDirEntry
;
174 for (i
= 0; i
< count
; i
++, pFatEntry
= (PDIR_ENTRY
)((ULONG_PTR
)pFatEntry
+ SizeDirEntry
))
176 if (Context
== NULL
|| (i
% size
) == 0)
180 CcUnpinData(Context
);
184 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
186 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
188 _SEH2_YIELD(return FALSE
);
192 FileOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.BytesPerCluster
;
194 if (ENTRY_END(IsFatX
, pFatEntry
))
198 if (ENTRY_DELETED(IsFatX
, pFatEntry
))
206 if (nbFree
== nbSlots
)
213 CcUnpinData(Context
);
216 if (nbFree
== nbSlots
)
218 /* found enough contiguous free slots */
219 *start
= i
- nbSlots
+ 1;
224 if (*start
+ nbSlots
> count
)
226 LARGE_INTEGER AllocationSize
;
227 /* extend the directory */
228 if (vfatFCBIsRoot(pDirFcb
) && DeviceExt
->FatInfo
.FatType
!= FAT32
)
230 /* We can't extend a root directory on a FAT12/FAT16/FATX partition */
233 AllocationSize
.QuadPart
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
+ DeviceExt
->FatInfo
.BytesPerCluster
;
234 Status
= VfatSetAllocationSizeInformation(pDirFcb
->FileObject
, pDirFcb
,
235 DeviceExt
, &AllocationSize
);
236 if (!NT_SUCCESS(Status
))
240 /* clear the new dir cluster */
241 FileOffset
.u
.LowPart
= (ULONG
)(pDirFcb
->RFCB
.FileSize
.QuadPart
-
242 DeviceExt
->FatInfo
.BytesPerCluster
);
245 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
247 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
249 _SEH2_YIELD(return FALSE
);
254 memset(pFatEntry
, 0xff, DeviceExt
->FatInfo
.BytesPerCluster
);
256 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
258 else if (*start
+ nbSlots
< count
)
260 /* clear the entry after the last new entry */
261 FileOffset
.u
.LowPart
= (*start
+ nbSlots
) * SizeDirEntry
;
264 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, SizeDirEntry
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
266 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
268 _SEH2_YIELD(return FALSE
);
273 memset(pFatEntry
, 0xff, SizeDirEntry
);
275 RtlZeroMemory(pFatEntry
, SizeDirEntry
);
279 CcSetDirtyPinnedData(Context
, NULL
);
280 CcUnpinData(Context
);
283 DPRINT("nbSlots %u nbFree %u, entry number %u\n", nbSlots
, nbFree
, *start
);
288 create a new FAT entry
292 IN PDEVICE_EXTENSION DeviceExt
,
293 IN PUNICODE_STRING NameU
,
295 IN PVFATFCB ParentFcb
,
296 IN ULONG RequestedOptions
,
298 IN PVFAT_MOVE_CONTEXT MoveContext
)
300 PVOID Context
= NULL
;
301 PFAT_DIR_ENTRY pFatEntry
;
303 USHORT nbSlots
= 0, j
;
305 BOOLEAN needTilde
= FALSE
, needLong
= FALSE
;
306 BOOLEAN BaseAllLower
, BaseAllUpper
;
307 BOOLEAN ExtensionAllLower
, ExtensionAllUpper
;
311 ULONG CurrentCluster
;
312 LARGE_INTEGER SystemTime
, FileOffset
;
313 NTSTATUS Status
= STATUS_SUCCESS
;
322 VFAT_DIRENTRY_CONTEXT DirContext
;
323 WCHAR LongNameBuffer
[LONGNAME_MAX_LENGTH
+ 1];
324 WCHAR ShortNameBuffer
[13];
326 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
328 DirContext
.LongNameU
= *NameU
;
329 IsDirectory
= BooleanFlagOn(RequestedOptions
, FILE_DIRECTORY_FILE
);
331 /* nb of entry needed for long name+normal entry */
332 nbSlots
= (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 12) / 13 + 1;
333 DPRINT("NameLen= %u, nbSlots =%u\n", DirContext
.LongNameU
.Length
/ sizeof(WCHAR
), nbSlots
);
334 Buffer
= ExAllocatePoolWithTag(NonPagedPool
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
), TAG_VFAT
);
337 return STATUS_INSUFFICIENT_RESOURCES
;
339 RtlZeroMemory(Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
340 pSlots
= (slot
*) Buffer
;
342 NameA
.Buffer
= aName
;
344 NameA
.MaximumLength
= sizeof(aName
);
346 DirContext
.ShortNameU
.Buffer
= ShortNameBuffer
;
347 DirContext
.ShortNameU
.Length
= 0;
348 DirContext
.ShortNameU
.MaximumLength
= sizeof(ShortNameBuffer
);
350 RtlZeroMemory(&DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
352 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.LongNameU
, &NameA
, &SpacesFound
);
354 if (!IsNameLegal
|| SpacesFound
)
356 GENERATE_NAME_CONTEXT NameContext
;
357 VFAT_DIRENTRY_CONTEXT SearchContext
;
358 WCHAR ShortSearchName
[13];
361 RtlZeroMemory(&NameContext
, sizeof(GENERATE_NAME_CONTEXT
));
362 SearchContext
.LongNameU
.Buffer
= LongNameBuffer
;
363 SearchContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
364 SearchContext
.ShortNameU
.Buffer
= ShortSearchName
;
365 SearchContext
.ShortNameU
.MaximumLength
= sizeof(ShortSearchName
);
367 for (i
= 0; i
< 100; i
++)
369 RtlGenerate8dot3Name(&DirContext
.LongNameU
, FALSE
, &NameContext
, &DirContext
.ShortNameU
);
370 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
371 SearchContext
.DirIndex
= 0;
372 Status
= FindFile(DeviceExt
, ParentFcb
, &DirContext
.ShortNameU
, &SearchContext
, TRUE
);
373 if (!NT_SUCCESS(Status
))
377 else if (MoveContext
)
380 if (strncmp((char *)SearchContext
.DirEntry
.Fat
.ShortName
, (char *)(*Fcb
)->entry
.Fat
.ShortName
, 11) == 0)
382 if (MoveContext
->InPlace
)
384 ASSERT(SearchContext
.DirEntry
.Fat
.FileSize
== MoveContext
->FileSize
);
390 if (i
== 100) /* FIXME : what to do after this ? */
392 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
393 return STATUS_UNSUCCESSFUL
;
395 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.ShortNameU
, &NameA
, &SpacesFound
);
399 BaseAllLower
= BaseAllUpper
= TRUE
;
400 ExtensionAllLower
= ExtensionAllUpper
= TRUE
;
402 for (i
= 0; i
< DirContext
.LongNameU
.Length
/ sizeof(WCHAR
); i
++)
404 c
= DirContext
.LongNameU
.Buffer
[i
];
405 if (c
>= L
'A' && c
<= L
'Z')
408 ExtensionAllLower
= FALSE
;
410 BaseAllLower
= FALSE
;
412 else if (c
>= L
'a' && c
<= L
'z')
415 ExtensionAllUpper
= FALSE
;
417 BaseAllUpper
= FALSE
;
431 if ((!BaseAllLower
&& !BaseAllUpper
) ||
432 (!ExtensionAllLower
&& !ExtensionAllUpper
))
437 RtlUpcaseUnicodeString(&DirContext
.ShortNameU
, &DirContext
.LongNameU
, FALSE
);
438 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
440 aName
[NameA
.Length
] = 0;
441 DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
442 aName
, &DirContext
.LongNameU
, needTilde
, needLong
);
443 memset(DirContext
.DirEntry
.Fat
.ShortName
, ' ', 11);
444 for (i
= 0; i
< 8 && aName
[i
] && aName
[i
] != '.'; i
++)
446 DirContext
.DirEntry
.Fat
.Filename
[i
] = aName
[i
];
451 for (j
= 0; j
< 3 && aName
[i
]; j
++, i
++)
453 DirContext
.DirEntry
.Fat
.Ext
[j
] = aName
[i
];
456 if (DirContext
.DirEntry
.Fat
.Filename
[0] == 0xe5)
458 DirContext
.DirEntry
.Fat
.Filename
[0] = 0x05;
463 RtlCopyMemory(LongNameBuffer
, DirContext
.LongNameU
.Buffer
, DirContext
.LongNameU
.Length
);
464 DirContext
.LongNameU
.Buffer
= LongNameBuffer
;
465 DirContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
466 DirContext
.LongNameU
.Buffer
[DirContext
.LongNameU
.Length
/ sizeof(WCHAR
)] = 0;
467 memset(DirContext
.LongNameU
.Buffer
+ DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 1, 0xff,
468 DirContext
.LongNameU
.MaximumLength
- DirContext
.LongNameU
.Length
- sizeof(WCHAR
));
473 if (BaseAllLower
&& !BaseAllUpper
)
475 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_BASE
;
477 if (ExtensionAllLower
&& !ExtensionAllUpper
)
479 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_EXT
;
483 DPRINT ("dos name=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
486 DirContext
.DirEntry
.Fat
.Attrib
= ReqAttr
;
489 DirContext
.DirEntry
.Fat
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
491 /* set dates and times */
492 KeQuerySystemTime(&SystemTime
);
493 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.Fat
.CreationDate
,
494 &DirContext
.DirEntry
.Fat
.CreationTime
);
495 DirContext
.DirEntry
.Fat
.UpdateDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
496 DirContext
.DirEntry
.Fat
.UpdateTime
= DirContext
.DirEntry
.Fat
.CreationTime
;
497 DirContext
.DirEntry
.Fat
.AccessDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
498 /* If it's moving, preserve creation time and file size */
499 if (MoveContext
!= NULL
)
501 DirContext
.DirEntry
.Fat
.CreationDate
= MoveContext
->CreationDate
;
502 DirContext
.DirEntry
.Fat
.CreationTime
= MoveContext
->CreationTime
;
503 DirContext
.DirEntry
.Fat
.FileSize
= MoveContext
->FileSize
;
508 /* calculate checksum for 8.3 name */
509 for (pSlots
[0].alias_checksum
= 0, i
= 0; i
< 11; i
++)
511 pSlots
[0].alias_checksum
= (((pSlots
[0].alias_checksum
& 1) << 7
512 | ((pSlots
[0].alias_checksum
& 0xfe) >> 1))
513 + DirContext
.DirEntry
.Fat
.ShortName
[i
]);
515 /* construct slots and entry */
516 for (i
= nbSlots
- 2; i
>= 0; i
--)
518 DPRINT("construct slot %d\n", i
);
519 pSlots
[i
].attr
= 0xf;
522 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1);
526 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1 + 0x40);
528 pSlots
[i
].alias_checksum
= pSlots
[0].alias_checksum
;
529 RtlCopyMemory(pSlots
[i
].name0_4
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13, 10);
530 RtlCopyMemory(pSlots
[i
].name5_10
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 5, 12);
531 RtlCopyMemory(pSlots
[i
].name11_12
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 11, 4);
534 /* try to find nbSlots contiguous entries frees in directory */
535 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, nbSlots
, &DirContext
.StartIndex
))
537 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
538 return STATUS_DISK_FULL
;
540 DirContext
.DirIndex
= DirContext
.StartIndex
+ nbSlots
- 1;
543 /* If we aren't moving, use next */
544 if (MoveContext
== NULL
)
547 Status
= NextCluster(DeviceExt
, 0, &CurrentCluster
, TRUE
);
548 if (CurrentCluster
== 0xffffffff || !NT_SUCCESS(Status
))
550 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
551 if (!NT_SUCCESS(Status
))
555 return STATUS_DISK_FULL
;
560 CurrentCluster
= MoveContext
->FirstCluster
;
563 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
565 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
567 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
569 else if (MoveContext
!= NULL
)
571 CurrentCluster
= MoveContext
->FirstCluster
;
573 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
575 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
577 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
580 i
= DeviceExt
->FatInfo
.BytesPerCluster
/ sizeof(FAT_DIR_ENTRY
);
581 FileOffset
.u
.HighPart
= 0;
582 FileOffset
.u
.LowPart
= DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
);
583 if (DirContext
.StartIndex
/ i
== DirContext
.DirIndex
/ i
)
588 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
), PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
590 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
592 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
593 _SEH2_YIELD(return _SEH2_GetExceptionCode());
599 RtlCopyMemory(pFatEntry
, Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
601 RtlCopyMemory(pFatEntry
+ (nbSlots
- 1), &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
606 size
= DeviceExt
->FatInfo
.BytesPerCluster
-
607 (DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
)) % DeviceExt
->FatInfo
.BytesPerCluster
;
608 i
= size
/ sizeof(FAT_DIR_ENTRY
);
611 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, size
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
613 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
615 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
616 _SEH2_YIELD(return _SEH2_GetExceptionCode());
619 RtlCopyMemory(pFatEntry
, Buffer
, size
);
620 CcSetDirtyPinnedData(Context
, NULL
);
621 CcUnpinData(Context
);
622 FileOffset
.u
.LowPart
+= size
;
625 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
) - size
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
627 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
629 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
630 _SEH2_YIELD(return _SEH2_GetExceptionCode());
635 RtlCopyMemory(pFatEntry
, (PVOID
)(Buffer
+ size
), (nbSlots
- 1 - i
) * sizeof(FAT_DIR_ENTRY
));
637 RtlCopyMemory(pFatEntry
+ nbSlots
- 1 - i
, &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
639 CcSetDirtyPinnedData(Context
, NULL
);
640 CcUnpinData(Context
);
642 if (MoveContext
!= NULL
)
644 /* We're modifying an existing FCB - likely rename/move */
645 Status
= vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
649 Status
= vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
651 if (!NT_SUCCESS(Status
))
653 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
657 DPRINT("new : entry=%11.11s\n", (*Fcb
)->entry
.Fat
.Filename
);
658 DPRINT("new : entry=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
662 FileOffset
.QuadPart
= 0;
665 CcPinRead((*Fcb
)->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
667 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
669 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
670 _SEH2_YIELD(return _SEH2_GetExceptionCode());
673 /* clear the new directory cluster if not moving */
674 if (MoveContext
== NULL
)
676 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
677 /* create '.' and '..' */
678 RtlCopyMemory(&pFatEntry
[0].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
679 RtlCopyMemory(pFatEntry
[0].ShortName
, ". ", 11);
680 RtlCopyMemory(&pFatEntry
[1].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
681 RtlCopyMemory(pFatEntry
[1].ShortName
, ".. ", 11);
684 pFatEntry
[1].FirstCluster
= ParentFcb
->entry
.Fat
.FirstCluster
;
685 pFatEntry
[1].FirstClusterHigh
= ParentFcb
->entry
.Fat
.FirstClusterHigh
;
686 if (vfatFCBIsRoot(ParentFcb
))
688 pFatEntry
[1].FirstCluster
= 0;
689 pFatEntry
[1].FirstClusterHigh
= 0;
691 CcSetDirtyPinnedData(Context
, NULL
);
692 CcUnpinData(Context
);
694 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
695 DPRINT("addentry ok\n");
696 return STATUS_SUCCESS
;
700 create a new FAT entry
704 IN PDEVICE_EXTENSION DeviceExt
,
705 IN PUNICODE_STRING NameU
,
707 IN PVFATFCB ParentFcb
,
708 IN ULONG RequestedOptions
,
710 IN PVFAT_MOVE_CONTEXT MoveContext
)
712 PVOID Context
= NULL
;
713 LARGE_INTEGER SystemTime
, FileOffset
;
715 VFAT_DIRENTRY_CONTEXT DirContext
;
716 PFATX_DIR_ENTRY pFatXDirEntry
;
719 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
721 DirContext
.LongNameU
= *NameU
;
723 if (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) > 42)
726 return STATUS_NAME_TOO_LONG
;
729 /* try to find 1 entry free in directory */
730 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, 1, &DirContext
.StartIndex
))
732 return STATUS_DISK_FULL
;
734 Index
= DirContext
.DirIndex
= DirContext
.StartIndex
;
735 if (!vfatFCBIsRoot(ParentFcb
))
737 DirContext
.DirIndex
+= 2;
738 DirContext
.StartIndex
+= 2;
741 DirContext
.ShortNameU
.Buffer
= 0;
742 DirContext
.ShortNameU
.Length
= 0;
743 DirContext
.ShortNameU
.MaximumLength
= 0;
744 RtlZeroMemory(&DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
745 memset(DirContext
.DirEntry
.FatX
.Filename
, 0xff, 42);
746 /* Use cluster, if moving */
747 if (MoveContext
!= NULL
)
749 DirContext
.DirEntry
.FatX
.FirstCluster
= MoveContext
->FirstCluster
;
753 DirContext
.DirEntry
.FatX
.FirstCluster
= 0;
755 DirContext
.DirEntry
.FatX
.FileSize
= 0;
758 NameA
.Buffer
= (PCHAR
)DirContext
.DirEntry
.FatX
.Filename
;
760 NameA
.MaximumLength
= 42;
761 RtlUnicodeStringToOemString(&NameA
, &DirContext
.LongNameU
, FALSE
);
762 DirContext
.DirEntry
.FatX
.FilenameLength
= (unsigned char)NameA
.Length
;
765 DirContext
.DirEntry
.FatX
.Attrib
= ReqAttr
;
766 if (BooleanFlagOn(RequestedOptions
, FILE_DIRECTORY_FILE
))
768 DirContext
.DirEntry
.FatX
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
771 /* set dates and times */
772 KeQuerySystemTime(&SystemTime
);
773 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.FatX
.CreationDate
,
774 &DirContext
.DirEntry
.FatX
.CreationTime
);
775 DirContext
.DirEntry
.FatX
.UpdateDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
776 DirContext
.DirEntry
.FatX
.UpdateTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
777 DirContext
.DirEntry
.FatX
.AccessDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
778 DirContext
.DirEntry
.FatX
.AccessTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
779 /* If it's moving, preserve creation time and file size */
780 if (MoveContext
!= NULL
)
782 DirContext
.DirEntry
.FatX
.CreationDate
= MoveContext
->CreationDate
;
783 DirContext
.DirEntry
.FatX
.CreationTime
= MoveContext
->CreationTime
;
784 DirContext
.DirEntry
.FatX
.FileSize
= MoveContext
->FileSize
;
787 /* add entry into parent directory */
788 FileOffset
.u
.HighPart
= 0;
789 FileOffset
.u
.LowPart
= Index
* sizeof(FATX_DIR_ENTRY
);
792 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
), PIN_WAIT
, &Context
, (PVOID
*)&pFatXDirEntry
);
794 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
796 _SEH2_YIELD(return _SEH2_GetExceptionCode());
799 RtlCopyMemory(pFatXDirEntry
, &DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
800 CcSetDirtyPinnedData(Context
, NULL
);
801 CcUnpinData(Context
);
803 if (MoveContext
!= NULL
)
805 /* We're modifying an existing FCB - likely rename/move */
806 /* FIXME: check status */
807 vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
811 /* FIXME: check status */
812 vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
815 DPRINT("addentry ok\n");
816 return STATUS_SUCCESS
;
820 * deleting an existing FAT entry
824 IN PDEVICE_EXTENSION DeviceExt
,
826 OUT PVFAT_MOVE_CONTEXT MoveContext
)
828 ULONG CurrentCluster
= 0, NextCluster
, i
;
829 PVOID Context
= NULL
;
830 LARGE_INTEGER Offset
;
831 PFAT_DIR_ENTRY pDirEntry
= NULL
;
834 ASSERT(pFcb
->parentFcb
);
836 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
837 DPRINT("delete entry: %u to %u\n", pFcb
->startIndex
, pFcb
->dirIndex
);
838 Offset
.u
.HighPart
= 0;
839 for (i
= pFcb
->startIndex
; i
<= pFcb
->dirIndex
; i
++)
841 if (Context
== NULL
|| ((i
* sizeof(FAT_DIR_ENTRY
)) % PAGE_SIZE
) == 0)
845 CcSetDirtyPinnedData(Context
, NULL
);
846 CcUnpinData(Context
);
848 Offset
.u
.LowPart
= (i
* sizeof(FAT_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
851 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
853 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
855 _SEH2_YIELD(return _SEH2_GetExceptionCode());
859 pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))].Filename
[0] = 0xe5;
860 if (i
== pFcb
->dirIndex
)
863 vfatDirEntryGetFirstCluster(DeviceExt
,
864 (PDIR_ENTRY
)&pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))]);
868 /* In case of moving, save properties */
869 if (MoveContext
!= NULL
)
871 pDirEntry
= &pDirEntry
[pFcb
->dirIndex
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))];
872 MoveContext
->FirstCluster
= CurrentCluster
;
873 MoveContext
->FileSize
= pDirEntry
->FileSize
;
874 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
875 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
880 CcSetDirtyPinnedData(Context
, NULL
);
881 CcUnpinData(Context
);
884 /* In case of moving, don't delete data */
885 if (MoveContext
== NULL
)
887 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
889 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
890 /* FIXME: check status */
891 WriteCluster(DeviceExt
, CurrentCluster
, 0);
892 CurrentCluster
= NextCluster
;
896 return STATUS_SUCCESS
;
900 * deleting an existing FAT entry
904 IN PDEVICE_EXTENSION DeviceExt
,
906 OUT PVFAT_MOVE_CONTEXT MoveContext
)
908 ULONG CurrentCluster
= 0, NextCluster
;
909 PVOID Context
= NULL
;
910 LARGE_INTEGER Offset
;
911 PFATX_DIR_ENTRY pDirEntry
;
915 ASSERT(pFcb
->parentFcb
);
916 ASSERT(vfatVolumeIsFatX(DeviceExt
));
918 StartIndex
= pFcb
->startIndex
;
920 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
921 DPRINT("delete entry: %u\n", StartIndex
);
922 Offset
.u
.HighPart
= 0;
923 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
926 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
928 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
930 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
931 _SEH2_YIELD(return _SEH2_GetExceptionCode());
934 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
935 pDirEntry
->FilenameLength
= 0xe5;
936 CurrentCluster
= vfatDirEntryGetFirstCluster(DeviceExt
,
937 (PDIR_ENTRY
)pDirEntry
);
939 /* In case of moving, save properties */
940 if (MoveContext
!= NULL
)
942 MoveContext
->FirstCluster
= CurrentCluster
;
943 MoveContext
->FileSize
= pDirEntry
->FileSize
;
944 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
945 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
948 CcSetDirtyPinnedData(Context
, NULL
);
949 CcUnpinData(Context
);
951 /* In case of moving, don't delete data */
952 if (MoveContext
== NULL
)
954 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
956 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
957 /* FIXME: check status */
958 WriteCluster(DeviceExt
, CurrentCluster
, 0);
959 CurrentCluster
= NextCluster
;
963 return STATUS_SUCCESS
;
967 * move an existing FAT entry
971 IN PDEVICE_EXTENSION DeviceExt
,
973 IN PUNICODE_STRING FileName
,
974 IN PVFATFCB ParentFcb
)
978 VFAT_MOVE_CONTEXT MoveContext
;
980 DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt
, pFcb
, FileName
, ParentFcb
);
982 /* Delete old entry while keeping data */
983 Status
= VfatDelEntry(DeviceExt
, pFcb
, &MoveContext
);
984 if (!NT_SUCCESS(Status
))
989 OldParent
= pFcb
->parentFcb
;
990 CcFlushCache(&OldParent
->SectionObjectPointers
, NULL
, 0, NULL
);
991 MoveContext
.InPlace
= (OldParent
== ParentFcb
);
993 /* Add our new entry with our cluster */
994 Status
= VfatAddEntry(DeviceExt
,
998 (vfatFCBIsDirectory(pFcb
) ? FILE_DIRECTORY_FILE
: 0),
1002 CcFlushCache(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, NULL
);
1007 extern BOOLEAN
FATXIsDirectoryEmpty(PVFATFCB Fcb
);
1008 extern BOOLEAN
FATIsDirectoryEmpty(PVFATFCB Fcb
);
1009 extern NTSTATUS
FATGetNextDirEntry(PVOID
*pContext
, PVOID
*pPage
, PVFATFCB pDirFcb
, PVFAT_DIRENTRY_CONTEXT DirContext
, BOOLEAN First
);
1010 extern NTSTATUS
FATXGetNextDirEntry(PVOID
*pContext
, PVOID
*pPage
, PVFATFCB pDirFcb
, PVFAT_DIRENTRY_CONTEXT DirContext
, BOOLEAN First
);
1012 VFAT_DISPATCH FatXDispatch
= {
1013 FATXIsDirectoryEmpty
, // .IsDirectoryEmpty
1014 FATXAddEntry
, // .AddEntry
1015 FATXDelEntry
, // .DelEntry
1016 FATXGetNextDirEntry
, // .GetNextDirEntry
1019 VFAT_DISPATCH FatDispatch
= {
1020 FATIsDirectoryEmpty
, // .IsDirectoryEmpty
1021 FATAddEntry
, // .AddEntry
1022 FATDelEntry
, // .DelEntry
1023 FATGetNextDirEntry
, // .GetNextDirEntry