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 (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
) || (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
;
56 if (CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, SizeDirEntry
,
57 TRUE
, &Context
, (PVOID
*)&PinEntry
))
59 pFcb
->Flags
&= ~FCB_IS_DIRTY
;
60 RtlCopyMemory(PinEntry
, &pFcb
->entry
, SizeDirEntry
);
61 CcSetDirtyPinnedData(Context
, NULL
);
63 return STATUS_SUCCESS
;
67 DPRINT1("Failed write to \'%wZ\'.\n", &pFcb
->parentFcb
->PathNameU
);
68 return STATUS_UNSUCCESSFUL
;
73 * rename an existing FAT entry
77 IN PDEVICE_EXTENSION DeviceExt
,
79 IN PUNICODE_STRING FileName
,
80 IN BOOLEAN CaseChangeOnly
)
86 PFATX_DIR_ENTRY pDirEntry
;
87 UNICODE_STRING ShortName
;
90 DPRINT("vfatRenameEntry(%p, %p, %wZ, %d)\n", DeviceExt
, pFcb
, FileName
, CaseChangeOnly
);
92 if (pFcb
->Flags
& FCB_IS_FATX_ENTRY
)
94 /* Open associated dir entry */
95 StartIndex
= pFcb
->startIndex
;
96 Offset
.u
.HighPart
= 0;
97 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
98 if (!CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, sizeof(FATX_DIR_ENTRY
), TRUE
,
99 &Context
, (PVOID
*)&pDirEntry
))
101 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
102 return STATUS_UNSUCCESSFUL
;
105 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
108 NameA
.Buffer
= (PCHAR
)pDirEntry
->Filename
;
110 NameA
.MaximumLength
= 42;
111 RtlUnicodeStringToOemString(&NameA
, FileName
, FALSE
);
112 pDirEntry
->FilenameLength
= (unsigned char)NameA
.Length
;
114 CcSetDirtyPinnedData(Context
, NULL
);
115 CcUnpinData(Context
);
118 ShortName
.Length
= 0;
119 ShortName
.MaximumLength
= 0;
120 ShortName
.Buffer
= NULL
;
121 Status
= vfatUpdateFCB(DeviceExt
, pFcb
, FileName
, &ShortName
, pFcb
->parentFcb
);
122 if (NT_SUCCESS(Status
))
124 CcPurgeCacheSection(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, FALSE
);
131 /* This we cannot handle properly, move file - would likely need love */
132 return VfatMoveEntry(DeviceExt
, pFcb
, FileName
, pFcb
->parentFcb
);
137 * try to find contiguous entries frees in directory,
138 * extend a directory if is neccesary
142 IN PDEVICE_EXTENSION DeviceExt
,
147 LARGE_INTEGER FileOffset
;
148 ULONG i
, count
, size
, nbFree
= 0;
149 PDIR_ENTRY pFatEntry
= NULL
;
150 PVOID Context
= NULL
;
153 FileOffset
.QuadPart
= 0;
155 if (DeviceExt
->Flags
& VCB_IS_FATX
)
156 SizeDirEntry
= sizeof(FATX_DIR_ENTRY
);
158 SizeDirEntry
= sizeof(FAT_DIR_ENTRY
);
160 count
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
/ SizeDirEntry
;
161 size
= DeviceExt
->FatInfo
.BytesPerCluster
/ SizeDirEntry
;
162 for (i
= 0; i
< count
; i
++, pFatEntry
= (PDIR_ENTRY
)((ULONG_PTR
)pFatEntry
+ SizeDirEntry
))
164 if (Context
== NULL
|| (i
% size
) == 0)
168 CcUnpinData(Context
);
170 if (!pDirFcb
->FileObject
)
172 DPRINT1("Buggy FCB (cleaned up)! %S (%d / %u)\n", pDirFcb
->PathNameBuffer
, pDirFcb
->RefCount
, pDirFcb
->OpenHandleCount
);
175 if (!CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
,
176 TRUE
, &Context
, (PVOID
*)&pFatEntry
))
180 FileOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.BytesPerCluster
;
182 if (ENTRY_END(DeviceExt
, pFatEntry
))
186 if (ENTRY_DELETED(DeviceExt
, pFatEntry
))
194 if (nbFree
== nbSlots
)
201 CcUnpinData(Context
);
204 if (nbFree
== nbSlots
)
206 /* found enough contiguous free slots */
207 *start
= i
- nbSlots
+ 1;
212 if (*start
+ nbSlots
> count
)
214 LARGE_INTEGER AllocationSize
;
215 /* extend the directory */
216 if (vfatFCBIsRoot(pDirFcb
) && DeviceExt
->FatInfo
.FatType
!= FAT32
)
218 /* We can't extend a root directory on a FAT12/FAT16/FATX partition */
221 AllocationSize
.QuadPart
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
+ DeviceExt
->FatInfo
.BytesPerCluster
;
222 Status
= VfatSetAllocationSizeInformation(pDirFcb
->FileObject
, pDirFcb
,
223 DeviceExt
, &AllocationSize
);
224 if (!NT_SUCCESS(Status
))
228 /* clear the new dir cluster */
229 FileOffset
.u
.LowPart
= (ULONG
)(pDirFcb
->RFCB
.FileSize
.QuadPart
-
230 DeviceExt
->FatInfo
.BytesPerCluster
);
231 if (!CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
,
232 TRUE
, &Context
, (PVOID
*)&pFatEntry
))
236 if (DeviceExt
->Flags
& VCB_IS_FATX
)
237 memset(pFatEntry
, 0xff, DeviceExt
->FatInfo
.BytesPerCluster
);
239 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
241 else if (*start
+ nbSlots
< count
)
243 /* clear the entry after the last new entry */
244 FileOffset
.u
.LowPart
= (*start
+ nbSlots
) * SizeDirEntry
;
245 if (!CcPinRead(pDirFcb
->FileObject
, &FileOffset
, SizeDirEntry
,
246 TRUE
, &Context
, (PVOID
*)&pFatEntry
))
250 if (DeviceExt
->Flags
& VCB_IS_FATX
)
251 memset(pFatEntry
, 0xff, SizeDirEntry
);
253 RtlZeroMemory(pFatEntry
, SizeDirEntry
);
257 CcSetDirtyPinnedData(Context
, NULL
);
258 CcUnpinData(Context
);
261 DPRINT("nbSlots %u nbFree %u, entry number %u\n", nbSlots
, nbFree
, *start
);
266 create a new FAT entry
270 IN PDEVICE_EXTENSION DeviceExt
,
271 IN PUNICODE_STRING NameU
,
273 IN PVFATFCB ParentFcb
,
274 IN ULONG RequestedOptions
,
276 IN PVFAT_MOVE_CONTEXT MoveContext
)
278 PVOID Context
= NULL
;
279 PFAT_DIR_ENTRY pFatEntry
;
281 USHORT nbSlots
= 0, j
, posCar
;
283 BOOLEAN needTilde
= FALSE
, needLong
= FALSE
;
284 BOOLEAN lCaseBase
= FALSE
, uCaseBase
, lCaseExt
= FALSE
, uCaseExt
;
285 ULONG CurrentCluster
;
286 LARGE_INTEGER SystemTime
, FileOffset
;
287 NTSTATUS Status
= STATUS_SUCCESS
;
296 VFAT_DIRENTRY_CONTEXT DirContext
;
297 WCHAR LongNameBuffer
[LONGNAME_MAX_LENGTH
+ 1];
298 WCHAR ShortNameBuffer
[13];
300 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
302 DirContext
.LongNameU
= *NameU
;
304 /* nb of entry needed for long name+normal entry */
305 nbSlots
= (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 12) / 13 + 1;
306 DPRINT("NameLen= %u, nbSlots =%u\n", DirContext
.LongNameU
.Length
/ sizeof(WCHAR
), nbSlots
);
307 Buffer
= ExAllocatePoolWithTag(NonPagedPool
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
), TAG_VFAT
);
310 return STATUS_INSUFFICIENT_RESOURCES
;
312 RtlZeroMemory(Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
313 pSlots
= (slot
*) Buffer
;
315 NameA
.Buffer
= aName
;
317 NameA
.MaximumLength
= sizeof(aName
);
319 DirContext
.ShortNameU
.Buffer
= ShortNameBuffer
;
320 DirContext
.ShortNameU
.Length
= 0;
321 DirContext
.ShortNameU
.MaximumLength
= sizeof(ShortNameBuffer
);
323 RtlZeroMemory(&DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
325 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.LongNameU
, &NameA
, &SpacesFound
);
327 if (!IsNameLegal
|| SpacesFound
)
329 GENERATE_NAME_CONTEXT NameContext
;
330 VFAT_DIRENTRY_CONTEXT SearchContext
;
331 WCHAR ShortSearchName
[13];
334 RtlZeroMemory(&NameContext
, sizeof(GENERATE_NAME_CONTEXT
));
335 SearchContext
.LongNameU
.Buffer
= LongNameBuffer
;
336 SearchContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
337 SearchContext
.ShortNameU
.Buffer
= ShortSearchName
;
338 SearchContext
.ShortNameU
.MaximumLength
= sizeof(ShortSearchName
);
340 for (i
= 0; i
< 100; i
++)
342 RtlGenerate8dot3Name(&DirContext
.LongNameU
, FALSE
, &NameContext
, &DirContext
.ShortNameU
);
343 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
344 SearchContext
.DirIndex
= 0;
345 Status
= FindFile(DeviceExt
, ParentFcb
, &DirContext
.ShortNameU
, &SearchContext
, TRUE
);
346 if (!NT_SUCCESS(Status
))
351 if (i
== 100) /* FIXME : what to do after this ? */
353 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
354 return STATUS_UNSUCCESSFUL
;
356 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.ShortNameU
, &NameA
, &SpacesFound
);
357 aName
[NameA
.Length
]=0;
361 aName
[NameA
.Length
] = 0;
362 for (posCar
= 0; posCar
< DirContext
.LongNameU
.Length
/ sizeof(WCHAR
); posCar
++)
364 if (DirContext
.LongNameU
.Buffer
[posCar
] == L
'.')
369 /* check if the name and the extension contains upper case characters */
370 RtlDowncaseUnicodeString(&DirContext
.ShortNameU
, &DirContext
.LongNameU
, FALSE
);
371 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
372 uCaseBase
= wcsncmp(DirContext
.LongNameU
.Buffer
,
373 DirContext
.ShortNameU
.Buffer
, posCar
) ? TRUE
: FALSE
;
374 if (posCar
< DirContext
.LongNameU
.Length
/sizeof(WCHAR
))
376 i
= DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) - posCar
;
377 uCaseExt
= wcsncmp(DirContext
.LongNameU
.Buffer
+ posCar
,
378 DirContext
.ShortNameU
.Buffer
+ posCar
, i
) ? TRUE
: FALSE
;
384 /* check if the name and the extension contains lower case characters */
385 RtlUpcaseUnicodeString(&DirContext
.ShortNameU
, &DirContext
.LongNameU
, FALSE
);
386 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
387 lCaseBase
= wcsncmp(DirContext
.LongNameU
.Buffer
,
388 DirContext
.ShortNameU
.Buffer
, posCar
) ? TRUE
: FALSE
;
389 if (posCar
< DirContext
.LongNameU
.Length
/ sizeof(WCHAR
))
391 i
= DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) - posCar
;
392 lCaseExt
= wcsncmp(DirContext
.LongNameU
.Buffer
+ posCar
,
393 DirContext
.ShortNameU
.Buffer
+ posCar
, i
) ? TRUE
: FALSE
;
399 if ((lCaseBase
&& uCaseBase
) || (lCaseExt
&& uCaseExt
))
404 DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
405 aName
, &DirContext
.LongNameU
, needTilde
, needLong
);
406 memset(DirContext
.DirEntry
.Fat
.ShortName
, ' ', 11);
407 for (i
= 0; i
< 8 && aName
[i
] && aName
[i
] != '.'; i
++)
409 DirContext
.DirEntry
.Fat
.Filename
[i
] = aName
[i
];
414 for (j
= 0; j
< 3 && aName
[i
]; j
++, i
++)
416 DirContext
.DirEntry
.Fat
.Ext
[j
] = aName
[i
];
419 if (DirContext
.DirEntry
.Fat
.Filename
[0] == 0xe5)
421 DirContext
.DirEntry
.Fat
.Filename
[0] = 0x05;
426 RtlCopyMemory(LongNameBuffer
, DirContext
.LongNameU
.Buffer
, DirContext
.LongNameU
.Length
);
427 DirContext
.LongNameU
.Buffer
= LongNameBuffer
;
428 DirContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
429 DirContext
.LongNameU
.Buffer
[DirContext
.LongNameU
.Length
/ sizeof(WCHAR
)] = 0;
430 memset(DirContext
.LongNameU
.Buffer
+ DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 1, 0xff,
431 DirContext
.LongNameU
.MaximumLength
- DirContext
.LongNameU
.Length
- sizeof(WCHAR
));
438 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_BASE
;
442 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_EXT
;
446 DPRINT ("dos name=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
449 DirContext
.DirEntry
.Fat
.Attrib
= ReqAttr
;
450 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
452 DirContext
.DirEntry
.Fat
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
454 /* set dates and times */
455 KeQuerySystemTime(&SystemTime
);
456 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.Fat
.CreationDate
,
457 &DirContext
.DirEntry
.Fat
.CreationTime
);
458 DirContext
.DirEntry
.Fat
.UpdateDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
459 DirContext
.DirEntry
.Fat
.UpdateTime
= DirContext
.DirEntry
.Fat
.CreationTime
;
460 DirContext
.DirEntry
.Fat
.AccessDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
461 /* If it's moving, preserve creation time and file size */
462 if (MoveContext
!= NULL
)
464 DirContext
.DirEntry
.Fat
.CreationDate
= MoveContext
->CreationDate
;
465 DirContext
.DirEntry
.Fat
.CreationTime
= MoveContext
->CreationTime
;
466 DirContext
.DirEntry
.Fat
.FileSize
= MoveContext
->FileSize
;
471 /* calculate checksum for 8.3 name */
472 for (pSlots
[0].alias_checksum
= 0, i
= 0; i
< 11; i
++)
474 pSlots
[0].alias_checksum
= (((pSlots
[0].alias_checksum
& 1) << 7
475 | ((pSlots
[0].alias_checksum
& 0xfe) >> 1))
476 + DirContext
.DirEntry
.Fat
.ShortName
[i
]);
478 /* construct slots and entry */
479 for (i
= nbSlots
- 2; i
>= 0; i
--)
481 DPRINT("construct slot %d\n", i
);
482 pSlots
[i
].attr
= 0xf;
485 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1);
489 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1 + 0x40);
491 pSlots
[i
].alias_checksum
= pSlots
[0].alias_checksum
;
492 RtlCopyMemory(pSlots
[i
].name0_4
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13, 10);
493 RtlCopyMemory(pSlots
[i
].name5_10
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 5, 12);
494 RtlCopyMemory(pSlots
[i
].name11_12
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 11, 4);
497 /* try to find nbSlots contiguous entries frees in directory */
498 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, nbSlots
, &DirContext
.StartIndex
))
500 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
501 return STATUS_DISK_FULL
;
503 DirContext
.DirIndex
= DirContext
.StartIndex
+ nbSlots
- 1;
504 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
506 /* If we aren't moving, use next */
507 if (MoveContext
== NULL
)
510 Status
= NextCluster(DeviceExt
, 0, &CurrentCluster
, TRUE
);
511 if (CurrentCluster
== 0xffffffff || !NT_SUCCESS(Status
))
513 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
514 if (!NT_SUCCESS(Status
))
518 return STATUS_DISK_FULL
;
523 CurrentCluster
= MoveContext
->FirstCluster
;
526 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
528 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
530 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
532 else if (MoveContext
!= NULL
)
534 CurrentCluster
= MoveContext
->FirstCluster
;
536 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
538 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
540 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
543 i
= DeviceExt
->FatInfo
.BytesPerCluster
/ sizeof(FAT_DIR_ENTRY
);
544 FileOffset
.u
.HighPart
= 0;
545 FileOffset
.u
.LowPart
= DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
);
546 if (DirContext
.StartIndex
/ i
== DirContext
.DirIndex
/ i
)
549 if (!CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
),
550 TRUE
, &Context
, (PVOID
*)&pFatEntry
))
552 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
553 return STATUS_UNSUCCESSFUL
;
557 RtlCopyMemory(pFatEntry
, Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
559 RtlCopyMemory(pFatEntry
+ (nbSlots
- 1), &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
564 size
= DeviceExt
->FatInfo
.BytesPerCluster
-
565 (DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
)) % DeviceExt
->FatInfo
.BytesPerCluster
;
566 i
= size
/ sizeof(FAT_DIR_ENTRY
);
567 if (!CcPinRead(ParentFcb
->FileObject
, &FileOffset
, size
, TRUE
,
568 &Context
, (PVOID
*)&pFatEntry
))
570 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
571 return STATUS_UNSUCCESSFUL
;
573 RtlCopyMemory(pFatEntry
, Buffer
, size
);
574 CcSetDirtyPinnedData(Context
, NULL
);
575 CcUnpinData(Context
);
576 FileOffset
.u
.LowPart
+= size
;
577 if (!CcPinRead(ParentFcb
->FileObject
, &FileOffset
,
578 nbSlots
* sizeof(FAT_DIR_ENTRY
) - size
,
579 TRUE
, &Context
, (PVOID
*)&pFatEntry
))
581 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
582 return STATUS_UNSUCCESSFUL
;
586 RtlCopyMemory(pFatEntry
, (PVOID
)(Buffer
+ size
), (nbSlots
- 1 - i
) * sizeof(FAT_DIR_ENTRY
));
588 RtlCopyMemory(pFatEntry
+ nbSlots
- 1 - i
, &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
590 CcSetDirtyPinnedData(Context
, NULL
);
591 CcUnpinData(Context
);
593 if (MoveContext
!= NULL
)
595 /* We're modifying an existing FCB - likely rename/move */
596 Status
= vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
.LongNameU
, &DirContext
.ShortNameU
, ParentFcb
);
597 (*Fcb
)->dirIndex
= DirContext
.DirIndex
;
598 (*Fcb
)->startIndex
= DirContext
.StartIndex
;
602 Status
= vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
604 if (!NT_SUCCESS(Status
))
606 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
610 DPRINT("new : entry=%11.11s\n", (*Fcb
)->entry
.Fat
.Filename
);
611 DPRINT("new : entry=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
613 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
615 FileOffset
.QuadPart
= 0;
616 if (!CcPinRead((*Fcb
)->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, TRUE
,
617 &Context
, (PVOID
*)&pFatEntry
))
619 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
620 return STATUS_UNSUCCESSFUL
;
622 /* clear the new directory cluster if not moving */
623 if (MoveContext
== NULL
)
625 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
626 /* create '.' and '..' */
627 RtlCopyMemory(&pFatEntry
[0].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
628 RtlCopyMemory(pFatEntry
[0].ShortName
, ". ", 11);
629 RtlCopyMemory(&pFatEntry
[1].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
630 RtlCopyMemory(pFatEntry
[1].ShortName
, ".. ", 11);
633 pFatEntry
[1].FirstCluster
= ParentFcb
->entry
.Fat
.FirstCluster
;
634 pFatEntry
[1].FirstClusterHigh
= ParentFcb
->entry
.Fat
.FirstClusterHigh
;
635 if (vfatFCBIsRoot(ParentFcb
))
637 pFatEntry
[1].FirstCluster
= 0;
638 pFatEntry
[1].FirstClusterHigh
= 0;
640 CcSetDirtyPinnedData(Context
, NULL
);
641 CcUnpinData(Context
);
643 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
644 DPRINT("addentry ok\n");
645 return STATUS_SUCCESS
;
649 create a new FAT entry
653 IN PDEVICE_EXTENSION DeviceExt
,
654 IN PUNICODE_STRING NameU
,
656 IN PVFATFCB ParentFcb
,
657 IN ULONG RequestedOptions
,
659 IN PVFAT_MOVE_CONTEXT MoveContext
)
661 PVOID Context
= NULL
;
662 LARGE_INTEGER SystemTime
, FileOffset
;
664 VFAT_DIRENTRY_CONTEXT DirContext
;
665 PFATX_DIR_ENTRY pFatXDirEntry
;
668 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
670 DirContext
.LongNameU
= *NameU
;
672 if (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) > 42)
675 return STATUS_NAME_TOO_LONG
;
678 /* try to find 1 entry free in directory */
679 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, 1, &DirContext
.StartIndex
))
681 return STATUS_DISK_FULL
;
683 Index
= DirContext
.DirIndex
= DirContext
.StartIndex
;
684 if (!vfatFCBIsRoot(ParentFcb
))
686 DirContext
.DirIndex
+= 2;
687 DirContext
.StartIndex
+= 2;
690 DirContext
.ShortNameU
.Buffer
= 0;
691 DirContext
.ShortNameU
.Length
= 0;
692 DirContext
.ShortNameU
.MaximumLength
= 0;
693 RtlZeroMemory(&DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
694 memset(DirContext
.DirEntry
.FatX
.Filename
, 0xff, 42);
695 /* Use cluster, if moving */
696 if (MoveContext
!= NULL
)
698 DirContext
.DirEntry
.FatX
.FirstCluster
= MoveContext
->FirstCluster
;
702 DirContext
.DirEntry
.FatX
.FirstCluster
= 0;
704 DirContext
.DirEntry
.FatX
.FileSize
= 0;
707 NameA
.Buffer
= (PCHAR
)DirContext
.DirEntry
.FatX
.Filename
;
709 NameA
.MaximumLength
= 42;
710 RtlUnicodeStringToOemString(&NameA
, &DirContext
.LongNameU
, FALSE
);
711 DirContext
.DirEntry
.FatX
.FilenameLength
= (unsigned char)NameA
.Length
;
714 DirContext
.DirEntry
.FatX
.Attrib
= ReqAttr
;
715 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
717 DirContext
.DirEntry
.FatX
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
720 /* set dates and times */
721 KeQuerySystemTime(&SystemTime
);
722 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.FatX
.CreationDate
,
723 &DirContext
.DirEntry
.FatX
.CreationTime
);
724 DirContext
.DirEntry
.FatX
.UpdateDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
725 DirContext
.DirEntry
.FatX
.UpdateTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
726 DirContext
.DirEntry
.FatX
.AccessDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
727 DirContext
.DirEntry
.FatX
.AccessTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
728 /* If it's moving, preserve creation time and file size */
729 if (MoveContext
!= NULL
)
731 DirContext
.DirEntry
.FatX
.CreationDate
= MoveContext
->CreationDate
;
732 DirContext
.DirEntry
.FatX
.CreationTime
= MoveContext
->CreationTime
;
733 DirContext
.DirEntry
.FatX
.FileSize
= MoveContext
->FileSize
;
736 /* add entry into parent directory */
737 FileOffset
.u
.HighPart
= 0;
738 FileOffset
.u
.LowPart
= Index
* sizeof(FATX_DIR_ENTRY
);
739 if (!CcPinRead(ParentFcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
),
740 TRUE
, &Context
, (PVOID
*)&pFatXDirEntry
))
742 return STATUS_UNSUCCESSFUL
;
744 RtlCopyMemory(pFatXDirEntry
, &DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
745 CcSetDirtyPinnedData(Context
, NULL
);
746 CcUnpinData(Context
);
748 if (MoveContext
!= NULL
)
750 /* We're modifying an existing FCB - likely rename/move */
751 /* FIXME: check status */
752 vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
.LongNameU
, &DirContext
.ShortNameU
, ParentFcb
);
753 (*Fcb
)->dirIndex
= DirContext
.DirIndex
;
754 (*Fcb
)->startIndex
= DirContext
.StartIndex
;
758 /* FIXME: check status */
759 vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
762 DPRINT("addentry ok\n");
763 return STATUS_SUCCESS
;
768 IN PDEVICE_EXTENSION DeviceExt
,
769 IN PUNICODE_STRING NameU
,
771 IN PVFATFCB ParentFcb
,
772 IN ULONG RequestedOptions
,
774 IN PVFAT_MOVE_CONTEXT MoveContext
)
776 if (DeviceExt
->Flags
& VCB_IS_FATX
)
777 return FATXAddEntry(DeviceExt
, NameU
, Fcb
, ParentFcb
, RequestedOptions
, ReqAttr
, MoveContext
);
779 return FATAddEntry(DeviceExt
, NameU
, Fcb
, ParentFcb
, RequestedOptions
, ReqAttr
, MoveContext
);
783 * deleting an existing FAT entry
787 IN PDEVICE_EXTENSION DeviceExt
,
789 OUT PVFAT_MOVE_CONTEXT MoveContext
)
791 ULONG CurrentCluster
= 0, NextCluster
, i
;
792 PVOID Context
= NULL
;
793 LARGE_INTEGER Offset
;
794 PFAT_DIR_ENTRY pDirEntry
= NULL
;
797 ASSERT(pFcb
->parentFcb
);
799 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
800 DPRINT("delete entry: %u to %u\n", pFcb
->startIndex
, pFcb
->dirIndex
);
801 Offset
.u
.HighPart
= 0;
802 for (i
= pFcb
->startIndex
; i
<= pFcb
->dirIndex
; i
++)
804 if (Context
== NULL
|| ((i
* sizeof(FAT_DIR_ENTRY
)) % PAGE_SIZE
) == 0)
808 CcSetDirtyPinnedData(Context
, NULL
);
809 CcUnpinData(Context
);
811 Offset
.u
.LowPart
= (i
* sizeof(FAT_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
812 if (!CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, sizeof(FAT_DIR_ENTRY
), TRUE
,
813 &Context
, (PVOID
*)&pDirEntry
))
815 return STATUS_UNSUCCESSFUL
;
818 pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))].Filename
[0] = 0xe5;
819 if (i
== pFcb
->dirIndex
)
822 vfatDirEntryGetFirstCluster(DeviceExt
,
823 (PDIR_ENTRY
)&pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))]);
828 CcSetDirtyPinnedData(Context
, NULL
);
829 CcUnpinData(Context
);
832 /* In case of moving, don't delete data */
833 if (MoveContext
!= NULL
)
835 pDirEntry
= &pDirEntry
[pFcb
->dirIndex
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))];
836 MoveContext
->FirstCluster
= CurrentCluster
;
837 MoveContext
->FileSize
= pDirEntry
->FileSize
;
838 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
839 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
843 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
845 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
846 /* FIXME: check status */
847 WriteCluster(DeviceExt
, CurrentCluster
, 0);
848 CurrentCluster
= NextCluster
;
852 return STATUS_SUCCESS
;
856 * deleting an existing FAT entry
860 IN PDEVICE_EXTENSION DeviceExt
,
862 OUT PVFAT_MOVE_CONTEXT MoveContext
)
864 ULONG CurrentCluster
= 0, NextCluster
;
865 PVOID Context
= NULL
;
866 LARGE_INTEGER Offset
;
867 PFATX_DIR_ENTRY pDirEntry
;
871 ASSERT(pFcb
->parentFcb
);
872 ASSERT(pFcb
->Flags
& FCB_IS_FATX_ENTRY
);
874 StartIndex
= pFcb
->startIndex
;
876 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
877 DPRINT("delete entry: %u\n", StartIndex
);
878 Offset
.u
.HighPart
= 0;
879 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
880 if (!CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, sizeof(FATX_DIR_ENTRY
), TRUE
,
881 &Context
, (PVOID
*)&pDirEntry
))
883 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
884 return STATUS_UNSUCCESSFUL
;
886 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
887 pDirEntry
->FilenameLength
= 0xe5;
888 CurrentCluster
= vfatDirEntryGetFirstCluster(DeviceExt
,
889 (PDIR_ENTRY
)pDirEntry
);
890 CcSetDirtyPinnedData(Context
, NULL
);
891 CcUnpinData(Context
);
893 /* In case of moving, don't delete data */
894 if (MoveContext
!= NULL
)
896 MoveContext
->FirstCluster
= CurrentCluster
;
897 MoveContext
->FileSize
= pDirEntry
->FileSize
;
898 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
899 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
903 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
905 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
906 /* FIXME: check status */
907 WriteCluster(DeviceExt
, CurrentCluster
, 0);
908 CurrentCluster
= NextCluster
;
912 return STATUS_SUCCESS
;
917 IN PDEVICE_EXTENSION DeviceExt
,
919 OUT PVFAT_MOVE_CONTEXT MoveContext
)
921 if (DeviceExt
->Flags
& VCB_IS_FATX
)
922 return FATXDelEntry(DeviceExt
, pFcb
, MoveContext
);
924 return FATDelEntry(DeviceExt
, pFcb
, MoveContext
);
928 * move an existing FAT entry
932 IN PDEVICE_EXTENSION DeviceExt
,
934 IN PUNICODE_STRING FileName
,
935 IN PVFATFCB ParentFcb
)
939 VFAT_MOVE_CONTEXT MoveContext
;
941 DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt
, pFcb
, FileName
, ParentFcb
);
943 /* Delete old entry while keeping data */
944 Status
= VfatDelEntry(DeviceExt
, pFcb
, &MoveContext
);
945 if (!NT_SUCCESS(Status
))
950 OldParent
= pFcb
->parentFcb
;
951 CcPurgeCacheSection(&OldParent
->SectionObjectPointers
, NULL
, 0, FALSE
);
953 /* Add our new entry with our cluster */
954 Status
= VfatAddEntry(DeviceExt
,
958 (vfatFCBIsDirectory(pFcb
) ? FILE_DIRECTORY_FILE
: 0),
962 CcPurgeCacheSection(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, FALSE
);