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
34 if (BooleanFlagOn(pFcb
->Flags
, FCB_IS_FATX_ENTRY
))
36 SizeDirEntry
= sizeof(FATX_DIR_ENTRY
);
37 dirIndex
= pFcb
->startIndex
;
41 SizeDirEntry
= sizeof(FAT_DIR_ENTRY
);
42 dirIndex
= pFcb
->dirIndex
;
45 DPRINT("updEntry dirIndex %u, PathName \'%wZ\'\n", dirIndex
, &pFcb
->PathNameU
);
47 if (vfatFCBIsRoot(pFcb
) || BooleanFlagOn(pFcb
->Flags
, FCB_IS_FAT
| FCB_IS_VOLUME
))
49 return STATUS_SUCCESS
;
52 ASSERT(pFcb
->parentFcb
);
54 Offset
.u
.HighPart
= 0;
55 Offset
.u
.LowPart
= dirIndex
* SizeDirEntry
;
58 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, SizeDirEntry
, PIN_WAIT
, &Context
, (PVOID
*)&PinEntry
);
60 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
62 DPRINT1("Failed write to \'%wZ\'.\n", &pFcb
->parentFcb
->PathNameU
);
63 _SEH2_YIELD(return _SEH2_GetExceptionCode());
67 pFcb
->Flags
&= ~FCB_IS_DIRTY
;
68 RtlCopyMemory(PinEntry
, &pFcb
->entry
, SizeDirEntry
);
69 CcSetDirtyPinnedData(Context
, NULL
);
71 return STATUS_SUCCESS
;
75 * rename an existing FAT entry
79 IN PDEVICE_EXTENSION DeviceExt
,
81 IN PUNICODE_STRING FileName
,
82 IN BOOLEAN CaseChangeOnly
)
88 PFATX_DIR_ENTRY pDirEntry
;
91 DPRINT("vfatRenameEntry(%p, %p, %wZ, %d)\n", DeviceExt
, pFcb
, FileName
, CaseChangeOnly
);
93 if (BooleanFlagOn(pFcb
->Flags
, FCB_IS_FATX_ENTRY
))
95 VFAT_DIRENTRY_CONTEXT DirContext
;
97 /* Open associated dir entry */
98 StartIndex
= pFcb
->startIndex
;
99 Offset
.u
.HighPart
= 0;
100 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
103 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
105 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
107 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
108 _SEH2_YIELD(return _SEH2_GetExceptionCode());
112 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
115 NameA
.Buffer
= (PCHAR
)pDirEntry
->Filename
;
117 NameA
.MaximumLength
= 42;
118 RtlUnicodeStringToOemString(&NameA
, FileName
, FALSE
);
119 pDirEntry
->FilenameLength
= (unsigned char)NameA
.Length
;
122 DirContext
.ShortNameU
.Length
= 0;
123 DirContext
.ShortNameU
.MaximumLength
= 0;
124 DirContext
.ShortNameU
.Buffer
= NULL
;
125 DirContext
.LongNameU
= *FileName
;
126 DirContext
.DirEntry
.FatX
= *pDirEntry
;
128 CcSetDirtyPinnedData(Context
, NULL
);
129 CcUnpinData(Context
);
131 Status
= vfatUpdateFCB(DeviceExt
, pFcb
, &DirContext
, pFcb
->parentFcb
);
132 if (NT_SUCCESS(Status
))
134 CcPurgeCacheSection(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, FALSE
);
141 /* This we cannot handle properly, move file - would likely need love */
142 return VfatMoveEntry(DeviceExt
, pFcb
, FileName
, pFcb
->parentFcb
);
147 * try to find contiguous entries frees in directory,
148 * extend a directory if is necessary
152 IN PDEVICE_EXTENSION DeviceExt
,
157 LARGE_INTEGER FileOffset
;
158 ULONG i
, count
, size
, nbFree
= 0;
159 PDIR_ENTRY pFatEntry
= NULL
;
160 PVOID Context
= NULL
;
163 FileOffset
.QuadPart
= 0;
165 if (BooleanFlagOn(DeviceExt
->Flags
, VCB_IS_FATX
))
166 SizeDirEntry
= sizeof(FATX_DIR_ENTRY
);
168 SizeDirEntry
= sizeof(FAT_DIR_ENTRY
);
170 count
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
/ SizeDirEntry
;
171 size
= DeviceExt
->FatInfo
.BytesPerCluster
/ SizeDirEntry
;
172 for (i
= 0; i
< count
; i
++, pFatEntry
= (PDIR_ENTRY
)((ULONG_PTR
)pFatEntry
+ SizeDirEntry
))
174 if (Context
== NULL
|| (i
% size
) == 0)
178 CcUnpinData(Context
);
182 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
184 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
186 _SEH2_YIELD(return FALSE
);
190 FileOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.BytesPerCluster
;
192 if (ENTRY_END(DeviceExt
, pFatEntry
))
196 if (ENTRY_DELETED(DeviceExt
, pFatEntry
))
204 if (nbFree
== nbSlots
)
211 CcUnpinData(Context
);
214 if (nbFree
== nbSlots
)
216 /* found enough contiguous free slots */
217 *start
= i
- nbSlots
+ 1;
222 if (*start
+ nbSlots
> count
)
224 LARGE_INTEGER AllocationSize
;
225 /* extend the directory */
226 if (vfatFCBIsRoot(pDirFcb
) && DeviceExt
->FatInfo
.FatType
!= FAT32
)
228 /* We can't extend a root directory on a FAT12/FAT16/FATX partition */
231 AllocationSize
.QuadPart
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
+ DeviceExt
->FatInfo
.BytesPerCluster
;
232 Status
= VfatSetAllocationSizeInformation(pDirFcb
->FileObject
, pDirFcb
,
233 DeviceExt
, &AllocationSize
);
234 if (!NT_SUCCESS(Status
))
238 /* clear the new dir cluster */
239 FileOffset
.u
.LowPart
= (ULONG
)(pDirFcb
->RFCB
.FileSize
.QuadPart
-
240 DeviceExt
->FatInfo
.BytesPerCluster
);
243 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
245 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
247 _SEH2_YIELD(return FALSE
);
251 if (DeviceExt
->Flags
& VCB_IS_FATX
)
252 memset(pFatEntry
, 0xff, DeviceExt
->FatInfo
.BytesPerCluster
);
254 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
256 else if (*start
+ nbSlots
< count
)
258 /* clear the entry after the last new entry */
259 FileOffset
.u
.LowPart
= (*start
+ nbSlots
) * SizeDirEntry
;
262 CcPinRead(pDirFcb
->FileObject
, &FileOffset
, SizeDirEntry
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
264 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
266 _SEH2_YIELD(return FALSE
);
270 if (DeviceExt
->Flags
& VCB_IS_FATX
)
271 memset(pFatEntry
, 0xff, SizeDirEntry
);
273 RtlZeroMemory(pFatEntry
, SizeDirEntry
);
277 CcSetDirtyPinnedData(Context
, NULL
);
278 CcUnpinData(Context
);
281 DPRINT("nbSlots %u nbFree %u, entry number %u\n", nbSlots
, nbFree
, *start
);
286 create a new FAT entry
290 IN PDEVICE_EXTENSION DeviceExt
,
291 IN PUNICODE_STRING NameU
,
293 IN PVFATFCB ParentFcb
,
294 IN ULONG RequestedOptions
,
296 IN PVFAT_MOVE_CONTEXT MoveContext
)
298 PVOID Context
= NULL
;
299 PFAT_DIR_ENTRY pFatEntry
;
301 USHORT nbSlots
= 0, j
;
303 BOOLEAN needTilde
= FALSE
, needLong
= FALSE
;
304 BOOLEAN BaseAllLower
, BaseAllUpper
;
305 BOOLEAN ExtensionAllLower
, ExtensionAllUpper
;
309 ULONG CurrentCluster
;
310 LARGE_INTEGER SystemTime
, FileOffset
;
311 NTSTATUS Status
= STATUS_SUCCESS
;
320 VFAT_DIRENTRY_CONTEXT DirContext
;
321 WCHAR LongNameBuffer
[LONGNAME_MAX_LENGTH
+ 1];
322 WCHAR ShortNameBuffer
[13];
324 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
326 DirContext
.LongNameU
= *NameU
;
327 IsDirectory
= BooleanFlagOn(RequestedOptions
, FILE_DIRECTORY_FILE
);
329 /* nb of entry needed for long name+normal entry */
330 nbSlots
= (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 12) / 13 + 1;
331 DPRINT("NameLen= %u, nbSlots =%u\n", DirContext
.LongNameU
.Length
/ sizeof(WCHAR
), nbSlots
);
332 Buffer
= ExAllocatePoolWithTag(NonPagedPool
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
), TAG_VFAT
);
335 return STATUS_INSUFFICIENT_RESOURCES
;
337 RtlZeroMemory(Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
338 pSlots
= (slot
*) Buffer
;
340 NameA
.Buffer
= aName
;
342 NameA
.MaximumLength
= sizeof(aName
);
344 DirContext
.ShortNameU
.Buffer
= ShortNameBuffer
;
345 DirContext
.ShortNameU
.Length
= 0;
346 DirContext
.ShortNameU
.MaximumLength
= sizeof(ShortNameBuffer
);
348 RtlZeroMemory(&DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
350 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.LongNameU
, &NameA
, &SpacesFound
);
352 if (!IsNameLegal
|| SpacesFound
)
354 GENERATE_NAME_CONTEXT NameContext
;
355 VFAT_DIRENTRY_CONTEXT SearchContext
;
356 WCHAR ShortSearchName
[13];
359 RtlZeroMemory(&NameContext
, sizeof(GENERATE_NAME_CONTEXT
));
360 SearchContext
.LongNameU
.Buffer
= LongNameBuffer
;
361 SearchContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
362 SearchContext
.ShortNameU
.Buffer
= ShortSearchName
;
363 SearchContext
.ShortNameU
.MaximumLength
= sizeof(ShortSearchName
);
365 for (i
= 0; i
< 100; i
++)
367 RtlGenerate8dot3Name(&DirContext
.LongNameU
, FALSE
, &NameContext
, &DirContext
.ShortNameU
);
368 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
369 SearchContext
.DirIndex
= 0;
370 Status
= FindFile(DeviceExt
, ParentFcb
, &DirContext
.ShortNameU
, &SearchContext
, TRUE
);
371 if (!NT_SUCCESS(Status
))
375 else if (MoveContext
)
378 if (strncmp((char *)SearchContext
.DirEntry
.Fat
.ShortName
, (char *)(*Fcb
)->entry
.Fat
.ShortName
, 11) == 0)
380 if (MoveContext
->InPlace
)
382 ASSERT(SearchContext
.DirEntry
.Fat
.FileSize
== MoveContext
->FileSize
);
388 if (i
== 100) /* FIXME : what to do after this ? */
390 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
391 return STATUS_UNSUCCESSFUL
;
393 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.ShortNameU
, &NameA
, &SpacesFound
);
397 BaseAllLower
= BaseAllUpper
= TRUE
;
398 ExtensionAllLower
= ExtensionAllUpper
= TRUE
;
400 for (i
= 0; i
< DirContext
.LongNameU
.Length
/ sizeof(WCHAR
); i
++)
402 c
= DirContext
.LongNameU
.Buffer
[i
];
403 if (c
>= L
'A' && c
<= L
'Z')
406 ExtensionAllLower
= FALSE
;
408 BaseAllLower
= FALSE
;
410 else if (c
>= L
'a' && c
<= L
'z')
413 ExtensionAllUpper
= FALSE
;
415 BaseAllUpper
= FALSE
;
429 if ((!BaseAllLower
&& !BaseAllUpper
) ||
430 (!ExtensionAllLower
&& !ExtensionAllUpper
))
435 RtlUpcaseUnicodeString(&DirContext
.ShortNameU
, &DirContext
.LongNameU
, FALSE
);
436 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
438 aName
[NameA
.Length
] = 0;
439 DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
440 aName
, &DirContext
.LongNameU
, needTilde
, needLong
);
441 memset(DirContext
.DirEntry
.Fat
.ShortName
, ' ', 11);
442 for (i
= 0; i
< 8 && aName
[i
] && aName
[i
] != '.'; i
++)
444 DirContext
.DirEntry
.Fat
.Filename
[i
] = aName
[i
];
449 for (j
= 0; j
< 3 && aName
[i
]; j
++, i
++)
451 DirContext
.DirEntry
.Fat
.Ext
[j
] = aName
[i
];
454 if (DirContext
.DirEntry
.Fat
.Filename
[0] == 0xe5)
456 DirContext
.DirEntry
.Fat
.Filename
[0] = 0x05;
461 RtlCopyMemory(LongNameBuffer
, DirContext
.LongNameU
.Buffer
, DirContext
.LongNameU
.Length
);
462 DirContext
.LongNameU
.Buffer
= LongNameBuffer
;
463 DirContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
464 DirContext
.LongNameU
.Buffer
[DirContext
.LongNameU
.Length
/ sizeof(WCHAR
)] = 0;
465 memset(DirContext
.LongNameU
.Buffer
+ DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 1, 0xff,
466 DirContext
.LongNameU
.MaximumLength
- DirContext
.LongNameU
.Length
- sizeof(WCHAR
));
471 if (BaseAllLower
&& !BaseAllUpper
)
473 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_BASE
;
475 if (ExtensionAllLower
&& !ExtensionAllUpper
)
477 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_EXT
;
481 DPRINT ("dos name=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
484 DirContext
.DirEntry
.Fat
.Attrib
= ReqAttr
;
487 DirContext
.DirEntry
.Fat
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
489 /* set dates and times */
490 KeQuerySystemTime(&SystemTime
);
491 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.Fat
.CreationDate
,
492 &DirContext
.DirEntry
.Fat
.CreationTime
);
493 DirContext
.DirEntry
.Fat
.UpdateDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
494 DirContext
.DirEntry
.Fat
.UpdateTime
= DirContext
.DirEntry
.Fat
.CreationTime
;
495 DirContext
.DirEntry
.Fat
.AccessDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
496 /* If it's moving, preserve creation time and file size */
497 if (MoveContext
!= NULL
)
499 DirContext
.DirEntry
.Fat
.CreationDate
= MoveContext
->CreationDate
;
500 DirContext
.DirEntry
.Fat
.CreationTime
= MoveContext
->CreationTime
;
501 DirContext
.DirEntry
.Fat
.FileSize
= MoveContext
->FileSize
;
506 /* calculate checksum for 8.3 name */
507 for (pSlots
[0].alias_checksum
= 0, i
= 0; i
< 11; i
++)
509 pSlots
[0].alias_checksum
= (((pSlots
[0].alias_checksum
& 1) << 7
510 | ((pSlots
[0].alias_checksum
& 0xfe) >> 1))
511 + DirContext
.DirEntry
.Fat
.ShortName
[i
]);
513 /* construct slots and entry */
514 for (i
= nbSlots
- 2; i
>= 0; i
--)
516 DPRINT("construct slot %d\n", i
);
517 pSlots
[i
].attr
= 0xf;
520 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1);
524 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1 + 0x40);
526 pSlots
[i
].alias_checksum
= pSlots
[0].alias_checksum
;
527 RtlCopyMemory(pSlots
[i
].name0_4
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13, 10);
528 RtlCopyMemory(pSlots
[i
].name5_10
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 5, 12);
529 RtlCopyMemory(pSlots
[i
].name11_12
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 11, 4);
532 /* try to find nbSlots contiguous entries frees in directory */
533 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, nbSlots
, &DirContext
.StartIndex
))
535 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
536 return STATUS_DISK_FULL
;
538 DirContext
.DirIndex
= DirContext
.StartIndex
+ nbSlots
- 1;
541 /* If we aren't moving, use next */
542 if (MoveContext
== NULL
)
545 Status
= NextCluster(DeviceExt
, 0, &CurrentCluster
, TRUE
);
546 if (CurrentCluster
== 0xffffffff || !NT_SUCCESS(Status
))
548 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
549 if (!NT_SUCCESS(Status
))
553 return STATUS_DISK_FULL
;
558 CurrentCluster
= MoveContext
->FirstCluster
;
561 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
563 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
565 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
567 else if (MoveContext
!= NULL
)
569 CurrentCluster
= MoveContext
->FirstCluster
;
571 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
573 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
575 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
578 i
= DeviceExt
->FatInfo
.BytesPerCluster
/ sizeof(FAT_DIR_ENTRY
);
579 FileOffset
.u
.HighPart
= 0;
580 FileOffset
.u
.LowPart
= DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
);
581 if (DirContext
.StartIndex
/ i
== DirContext
.DirIndex
/ i
)
586 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
), PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
588 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
590 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
591 _SEH2_YIELD(return _SEH2_GetExceptionCode());
597 RtlCopyMemory(pFatEntry
, Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
599 RtlCopyMemory(pFatEntry
+ (nbSlots
- 1), &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
604 size
= DeviceExt
->FatInfo
.BytesPerCluster
-
605 (DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
)) % DeviceExt
->FatInfo
.BytesPerCluster
;
606 i
= size
/ sizeof(FAT_DIR_ENTRY
);
609 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, size
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
611 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
613 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
614 _SEH2_YIELD(return _SEH2_GetExceptionCode());
617 RtlCopyMemory(pFatEntry
, Buffer
, size
);
618 CcSetDirtyPinnedData(Context
, NULL
);
619 CcUnpinData(Context
);
620 FileOffset
.u
.LowPart
+= size
;
623 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
) - size
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
625 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
627 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
628 _SEH2_YIELD(return _SEH2_GetExceptionCode());
633 RtlCopyMemory(pFatEntry
, (PVOID
)(Buffer
+ size
), (nbSlots
- 1 - i
) * sizeof(FAT_DIR_ENTRY
));
635 RtlCopyMemory(pFatEntry
+ nbSlots
- 1 - i
, &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
637 CcSetDirtyPinnedData(Context
, NULL
);
638 CcUnpinData(Context
);
640 if (MoveContext
!= NULL
)
642 /* We're modifying an existing FCB - likely rename/move */
643 Status
= vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
647 Status
= vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
649 if (!NT_SUCCESS(Status
))
651 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
655 DPRINT("new : entry=%11.11s\n", (*Fcb
)->entry
.Fat
.Filename
);
656 DPRINT("new : entry=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
660 FileOffset
.QuadPart
= 0;
663 CcPinRead((*Fcb
)->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, PIN_WAIT
, &Context
, (PVOID
*)&pFatEntry
);
665 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
667 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
668 _SEH2_YIELD(return _SEH2_GetExceptionCode());
671 /* clear the new directory cluster if not moving */
672 if (MoveContext
== NULL
)
674 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
675 /* create '.' and '..' */
676 RtlCopyMemory(&pFatEntry
[0].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
677 RtlCopyMemory(pFatEntry
[0].ShortName
, ". ", 11);
678 RtlCopyMemory(&pFatEntry
[1].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
679 RtlCopyMemory(pFatEntry
[1].ShortName
, ".. ", 11);
682 pFatEntry
[1].FirstCluster
= ParentFcb
->entry
.Fat
.FirstCluster
;
683 pFatEntry
[1].FirstClusterHigh
= ParentFcb
->entry
.Fat
.FirstClusterHigh
;
684 if (vfatFCBIsRoot(ParentFcb
))
686 pFatEntry
[1].FirstCluster
= 0;
687 pFatEntry
[1].FirstClusterHigh
= 0;
689 CcSetDirtyPinnedData(Context
, NULL
);
690 CcUnpinData(Context
);
692 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
693 DPRINT("addentry ok\n");
694 return STATUS_SUCCESS
;
698 create a new FAT entry
702 IN PDEVICE_EXTENSION DeviceExt
,
703 IN PUNICODE_STRING NameU
,
705 IN PVFATFCB ParentFcb
,
706 IN ULONG RequestedOptions
,
708 IN PVFAT_MOVE_CONTEXT MoveContext
)
710 PVOID Context
= NULL
;
711 LARGE_INTEGER SystemTime
, FileOffset
;
713 VFAT_DIRENTRY_CONTEXT DirContext
;
714 PFATX_DIR_ENTRY pFatXDirEntry
;
717 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
719 DirContext
.LongNameU
= *NameU
;
721 if (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) > 42)
724 return STATUS_NAME_TOO_LONG
;
727 /* try to find 1 entry free in directory */
728 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, 1, &DirContext
.StartIndex
))
730 return STATUS_DISK_FULL
;
732 Index
= DirContext
.DirIndex
= DirContext
.StartIndex
;
733 if (!vfatFCBIsRoot(ParentFcb
))
735 DirContext
.DirIndex
+= 2;
736 DirContext
.StartIndex
+= 2;
739 DirContext
.ShortNameU
.Buffer
= 0;
740 DirContext
.ShortNameU
.Length
= 0;
741 DirContext
.ShortNameU
.MaximumLength
= 0;
742 RtlZeroMemory(&DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
743 memset(DirContext
.DirEntry
.FatX
.Filename
, 0xff, 42);
744 /* Use cluster, if moving */
745 if (MoveContext
!= NULL
)
747 DirContext
.DirEntry
.FatX
.FirstCluster
= MoveContext
->FirstCluster
;
751 DirContext
.DirEntry
.FatX
.FirstCluster
= 0;
753 DirContext
.DirEntry
.FatX
.FileSize
= 0;
756 NameA
.Buffer
= (PCHAR
)DirContext
.DirEntry
.FatX
.Filename
;
758 NameA
.MaximumLength
= 42;
759 RtlUnicodeStringToOemString(&NameA
, &DirContext
.LongNameU
, FALSE
);
760 DirContext
.DirEntry
.FatX
.FilenameLength
= (unsigned char)NameA
.Length
;
763 DirContext
.DirEntry
.FatX
.Attrib
= ReqAttr
;
764 if (BooleanFlagOn(RequestedOptions
, FILE_DIRECTORY_FILE
))
766 DirContext
.DirEntry
.FatX
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
769 /* set dates and times */
770 KeQuerySystemTime(&SystemTime
);
771 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.FatX
.CreationDate
,
772 &DirContext
.DirEntry
.FatX
.CreationTime
);
773 DirContext
.DirEntry
.FatX
.UpdateDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
774 DirContext
.DirEntry
.FatX
.UpdateTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
775 DirContext
.DirEntry
.FatX
.AccessDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
776 DirContext
.DirEntry
.FatX
.AccessTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
777 /* If it's moving, preserve creation time and file size */
778 if (MoveContext
!= NULL
)
780 DirContext
.DirEntry
.FatX
.CreationDate
= MoveContext
->CreationDate
;
781 DirContext
.DirEntry
.FatX
.CreationTime
= MoveContext
->CreationTime
;
782 DirContext
.DirEntry
.FatX
.FileSize
= MoveContext
->FileSize
;
785 /* add entry into parent directory */
786 FileOffset
.u
.HighPart
= 0;
787 FileOffset
.u
.LowPart
= Index
* sizeof(FATX_DIR_ENTRY
);
790 CcPinRead(ParentFcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
), PIN_WAIT
, &Context
, (PVOID
*)&pFatXDirEntry
);
792 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
794 _SEH2_YIELD(return _SEH2_GetExceptionCode());
797 RtlCopyMemory(pFatXDirEntry
, &DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
798 CcSetDirtyPinnedData(Context
, NULL
);
799 CcUnpinData(Context
);
801 if (MoveContext
!= NULL
)
803 /* We're modifying an existing FCB - likely rename/move */
804 /* FIXME: check status */
805 vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
809 /* FIXME: check status */
810 vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
813 DPRINT("addentry ok\n");
814 return STATUS_SUCCESS
;
819 IN PDEVICE_EXTENSION DeviceExt
,
820 IN PUNICODE_STRING NameU
,
822 IN PVFATFCB ParentFcb
,
823 IN ULONG RequestedOptions
,
825 IN PVFAT_MOVE_CONTEXT MoveContext
)
827 if (BooleanFlagOn(DeviceExt
->Flags
, VCB_IS_FATX
))
828 return FATXAddEntry(DeviceExt
, NameU
, Fcb
, ParentFcb
, RequestedOptions
, ReqAttr
, MoveContext
);
830 return FATAddEntry(DeviceExt
, NameU
, Fcb
, ParentFcb
, RequestedOptions
, ReqAttr
, MoveContext
);
834 * deleting an existing FAT entry
838 IN PDEVICE_EXTENSION DeviceExt
,
840 OUT PVFAT_MOVE_CONTEXT MoveContext
)
842 ULONG CurrentCluster
= 0, NextCluster
, i
;
843 PVOID Context
= NULL
;
844 LARGE_INTEGER Offset
;
845 PFAT_DIR_ENTRY pDirEntry
= NULL
;
848 ASSERT(pFcb
->parentFcb
);
850 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
851 DPRINT("delete entry: %u to %u\n", pFcb
->startIndex
, pFcb
->dirIndex
);
852 Offset
.u
.HighPart
= 0;
853 for (i
= pFcb
->startIndex
; i
<= pFcb
->dirIndex
; i
++)
855 if (Context
== NULL
|| ((i
* sizeof(FAT_DIR_ENTRY
)) % PAGE_SIZE
) == 0)
859 CcSetDirtyPinnedData(Context
, NULL
);
860 CcUnpinData(Context
);
862 Offset
.u
.LowPart
= (i
* sizeof(FAT_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
865 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
867 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
869 _SEH2_YIELD(return _SEH2_GetExceptionCode());
873 pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))].Filename
[0] = 0xe5;
874 if (i
== pFcb
->dirIndex
)
877 vfatDirEntryGetFirstCluster(DeviceExt
,
878 (PDIR_ENTRY
)&pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))]);
882 /* In case of moving, save properties */
883 if (MoveContext
!= NULL
)
885 pDirEntry
= &pDirEntry
[pFcb
->dirIndex
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))];
886 MoveContext
->FirstCluster
= CurrentCluster
;
887 MoveContext
->FileSize
= pDirEntry
->FileSize
;
888 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
889 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
894 CcSetDirtyPinnedData(Context
, NULL
);
895 CcUnpinData(Context
);
898 /* In case of moving, don't delete data */
899 if (MoveContext
== NULL
)
901 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
903 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
904 /* FIXME: check status */
905 WriteCluster(DeviceExt
, CurrentCluster
, 0);
906 CurrentCluster
= NextCluster
;
910 return STATUS_SUCCESS
;
914 * deleting an existing FAT entry
918 IN PDEVICE_EXTENSION DeviceExt
,
920 OUT PVFAT_MOVE_CONTEXT MoveContext
)
922 ULONG CurrentCluster
= 0, NextCluster
;
923 PVOID Context
= NULL
;
924 LARGE_INTEGER Offset
;
925 PFATX_DIR_ENTRY pDirEntry
;
929 ASSERT(pFcb
->parentFcb
);
930 ASSERT(BooleanFlagOn(pFcb
->Flags
, FCB_IS_FATX_ENTRY
));
932 StartIndex
= pFcb
->startIndex
;
934 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
935 DPRINT("delete entry: %u\n", StartIndex
);
936 Offset
.u
.HighPart
= 0;
937 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
940 CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, PAGE_SIZE
, PIN_WAIT
, &Context
, (PVOID
*)&pDirEntry
);
942 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
944 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
945 _SEH2_YIELD(return _SEH2_GetExceptionCode());
948 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
949 pDirEntry
->FilenameLength
= 0xe5;
950 CurrentCluster
= vfatDirEntryGetFirstCluster(DeviceExt
,
951 (PDIR_ENTRY
)pDirEntry
);
953 /* In case of moving, save properties */
954 if (MoveContext
!= NULL
)
956 MoveContext
->FirstCluster
= CurrentCluster
;
957 MoveContext
->FileSize
= pDirEntry
->FileSize
;
958 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
959 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
962 CcSetDirtyPinnedData(Context
, NULL
);
963 CcUnpinData(Context
);
965 /* In case of moving, don't delete data */
966 if (MoveContext
== NULL
)
968 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
970 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
971 /* FIXME: check status */
972 WriteCluster(DeviceExt
, CurrentCluster
, 0);
973 CurrentCluster
= NextCluster
;
977 return STATUS_SUCCESS
;
982 IN PDEVICE_EXTENSION DeviceExt
,
984 OUT PVFAT_MOVE_CONTEXT MoveContext
)
986 if (BooleanFlagOn(DeviceExt
->Flags
, VCB_IS_FATX
))
987 return FATXDelEntry(DeviceExt
, pFcb
, MoveContext
);
989 return FATDelEntry(DeviceExt
, pFcb
, MoveContext
);
993 * move an existing FAT entry
997 IN PDEVICE_EXTENSION DeviceExt
,
999 IN PUNICODE_STRING FileName
,
1000 IN PVFATFCB ParentFcb
)
1004 VFAT_MOVE_CONTEXT MoveContext
;
1006 DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt
, pFcb
, FileName
, ParentFcb
);
1008 /* Delete old entry while keeping data */
1009 Status
= VfatDelEntry(DeviceExt
, pFcb
, &MoveContext
);
1010 if (!NT_SUCCESS(Status
))
1015 OldParent
= pFcb
->parentFcb
;
1016 CcPurgeCacheSection(&OldParent
->SectionObjectPointers
, NULL
, 0, FALSE
);
1017 MoveContext
.InPlace
= (OldParent
== ParentFcb
);
1019 /* Add our new entry with our cluster */
1020 Status
= VfatAddEntry(DeviceExt
,
1024 (vfatFCBIsDirectory(pFcb
) ? FILE_DIRECTORY_FILE
: 0),
1028 CcPurgeCacheSection(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, FALSE
);