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
;
89 DPRINT("vfatRenameEntry(%p, %p, %wZ, %d)\n", DeviceExt
, pFcb
, FileName
, CaseChangeOnly
);
91 if (pFcb
->Flags
& FCB_IS_FATX_ENTRY
)
93 VFAT_DIRENTRY_CONTEXT DirContext
;
95 /* Open associated dir entry */
96 StartIndex
= pFcb
->startIndex
;
97 Offset
.u
.HighPart
= 0;
98 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
99 if (!CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, sizeof(FATX_DIR_ENTRY
), TRUE
,
100 &Context
, (PVOID
*)&pDirEntry
))
102 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
103 return STATUS_UNSUCCESSFUL
;
106 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
109 NameA
.Buffer
= (PCHAR
)pDirEntry
->Filename
;
111 NameA
.MaximumLength
= 42;
112 RtlUnicodeStringToOemString(&NameA
, FileName
, FALSE
);
113 pDirEntry
->FilenameLength
= (unsigned char)NameA
.Length
;
115 CcSetDirtyPinnedData(Context
, NULL
);
116 CcUnpinData(Context
);
119 DirContext
.ShortNameU
.Length
= 0;
120 DirContext
.ShortNameU
.MaximumLength
= 0;
121 DirContext
.ShortNameU
.Buffer
= NULL
;
122 DirContext
.LongNameU
= *FileName
;
123 DirContext
.DirEntry
.FatX
= *pDirEntry
;
124 Status
= vfatUpdateFCB(DeviceExt
, pFcb
, &DirContext
, pFcb
->parentFcb
);
125 if (NT_SUCCESS(Status
))
127 CcPurgeCacheSection(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, FALSE
);
134 /* This we cannot handle properly, move file - would likely need love */
135 return VfatMoveEntry(DeviceExt
, pFcb
, FileName
, pFcb
->parentFcb
);
140 * try to find contiguous entries frees in directory,
141 * extend a directory if is neccesary
145 IN PDEVICE_EXTENSION DeviceExt
,
150 LARGE_INTEGER FileOffset
;
151 ULONG i
, count
, size
, nbFree
= 0;
152 PDIR_ENTRY pFatEntry
= NULL
;
153 PVOID Context
= NULL
;
156 FileOffset
.QuadPart
= 0;
158 if (DeviceExt
->Flags
& VCB_IS_FATX
)
159 SizeDirEntry
= sizeof(FATX_DIR_ENTRY
);
161 SizeDirEntry
= sizeof(FAT_DIR_ENTRY
);
163 count
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
/ SizeDirEntry
;
164 size
= DeviceExt
->FatInfo
.BytesPerCluster
/ SizeDirEntry
;
165 for (i
= 0; i
< count
; i
++, pFatEntry
= (PDIR_ENTRY
)((ULONG_PTR
)pFatEntry
+ SizeDirEntry
))
167 if (Context
== NULL
|| (i
% size
) == 0)
171 CcUnpinData(Context
);
173 if (!CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
,
174 TRUE
, &Context
, (PVOID
*)&pFatEntry
))
178 FileOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.BytesPerCluster
;
180 if (ENTRY_END(DeviceExt
, pFatEntry
))
184 if (ENTRY_DELETED(DeviceExt
, pFatEntry
))
192 if (nbFree
== nbSlots
)
199 CcUnpinData(Context
);
202 if (nbFree
== nbSlots
)
204 /* found enough contiguous free slots */
205 *start
= i
- nbSlots
+ 1;
210 if (*start
+ nbSlots
> count
)
212 LARGE_INTEGER AllocationSize
;
213 /* extend the directory */
214 if (vfatFCBIsRoot(pDirFcb
) && DeviceExt
->FatInfo
.FatType
!= FAT32
)
216 /* We can't extend a root directory on a FAT12/FAT16/FATX partition */
219 AllocationSize
.QuadPart
= pDirFcb
->RFCB
.FileSize
.u
.LowPart
+ DeviceExt
->FatInfo
.BytesPerCluster
;
220 Status
= VfatSetAllocationSizeInformation(pDirFcb
->FileObject
, pDirFcb
,
221 DeviceExt
, &AllocationSize
);
222 if (!NT_SUCCESS(Status
))
226 /* clear the new dir cluster */
227 FileOffset
.u
.LowPart
= (ULONG
)(pDirFcb
->RFCB
.FileSize
.QuadPart
-
228 DeviceExt
->FatInfo
.BytesPerCluster
);
229 if (!CcPinRead(pDirFcb
->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
,
230 TRUE
, &Context
, (PVOID
*)&pFatEntry
))
234 if (DeviceExt
->Flags
& VCB_IS_FATX
)
235 memset(pFatEntry
, 0xff, DeviceExt
->FatInfo
.BytesPerCluster
);
237 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
239 else if (*start
+ nbSlots
< count
)
241 /* clear the entry after the last new entry */
242 FileOffset
.u
.LowPart
= (*start
+ nbSlots
) * SizeDirEntry
;
243 if (!CcPinRead(pDirFcb
->FileObject
, &FileOffset
, SizeDirEntry
,
244 TRUE
, &Context
, (PVOID
*)&pFatEntry
))
248 if (DeviceExt
->Flags
& VCB_IS_FATX
)
249 memset(pFatEntry
, 0xff, SizeDirEntry
);
251 RtlZeroMemory(pFatEntry
, SizeDirEntry
);
255 CcSetDirtyPinnedData(Context
, NULL
);
256 CcUnpinData(Context
);
259 DPRINT("nbSlots %u nbFree %u, entry number %u\n", nbSlots
, nbFree
, *start
);
264 create a new FAT entry
268 IN PDEVICE_EXTENSION DeviceExt
,
269 IN PUNICODE_STRING NameU
,
271 IN PVFATFCB ParentFcb
,
272 IN ULONG RequestedOptions
,
274 IN PVFAT_MOVE_CONTEXT MoveContext
)
276 PVOID Context
= NULL
;
277 PFAT_DIR_ENTRY pFatEntry
;
279 USHORT nbSlots
= 0, j
, posCar
;
281 BOOLEAN needTilde
= FALSE
, needLong
= FALSE
;
282 BOOLEAN lCaseBase
= FALSE
, uCaseBase
, lCaseExt
= FALSE
, uCaseExt
;
283 ULONG CurrentCluster
;
284 LARGE_INTEGER SystemTime
, FileOffset
;
285 NTSTATUS Status
= STATUS_SUCCESS
;
294 VFAT_DIRENTRY_CONTEXT DirContext
;
295 WCHAR LongNameBuffer
[LONGNAME_MAX_LENGTH
+ 1];
296 WCHAR ShortNameBuffer
[13];
298 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
300 DirContext
.LongNameU
= *NameU
;
302 /* nb of entry needed for long name+normal entry */
303 nbSlots
= (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 12) / 13 + 1;
304 DPRINT("NameLen= %u, nbSlots =%u\n", DirContext
.LongNameU
.Length
/ sizeof(WCHAR
), nbSlots
);
305 Buffer
= ExAllocatePoolWithTag(NonPagedPool
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
), TAG_VFAT
);
308 return STATUS_INSUFFICIENT_RESOURCES
;
310 RtlZeroMemory(Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
311 pSlots
= (slot
*) Buffer
;
313 NameA
.Buffer
= aName
;
315 NameA
.MaximumLength
= sizeof(aName
);
317 DirContext
.ShortNameU
.Buffer
= ShortNameBuffer
;
318 DirContext
.ShortNameU
.Length
= 0;
319 DirContext
.ShortNameU
.MaximumLength
= sizeof(ShortNameBuffer
);
321 RtlZeroMemory(&DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
323 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.LongNameU
, &NameA
, &SpacesFound
);
325 if (!IsNameLegal
|| SpacesFound
)
327 GENERATE_NAME_CONTEXT NameContext
;
328 VFAT_DIRENTRY_CONTEXT SearchContext
;
329 WCHAR ShortSearchName
[13];
332 RtlZeroMemory(&NameContext
, sizeof(GENERATE_NAME_CONTEXT
));
333 SearchContext
.LongNameU
.Buffer
= LongNameBuffer
;
334 SearchContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
335 SearchContext
.ShortNameU
.Buffer
= ShortSearchName
;
336 SearchContext
.ShortNameU
.MaximumLength
= sizeof(ShortSearchName
);
338 for (i
= 0; i
< 100; i
++)
340 RtlGenerate8dot3Name(&DirContext
.LongNameU
, FALSE
, &NameContext
, &DirContext
.ShortNameU
);
341 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
342 SearchContext
.DirIndex
= 0;
343 Status
= FindFile(DeviceExt
, ParentFcb
, &DirContext
.ShortNameU
, &SearchContext
, TRUE
);
344 if (!NT_SUCCESS(Status
))
348 else if (MoveContext
)
351 if (strncmp((char *)SearchContext
.DirEntry
.Fat
.ShortName
, (char *)(*Fcb
)->entry
.Fat
.ShortName
, 11) == 0)
353 if (MoveContext
->InPlace
)
355 ASSERT(SearchContext
.DirEntry
.Fat
.FileSize
== MoveContext
->FileSize
);
361 if (i
== 100) /* FIXME : what to do after this ? */
363 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
364 return STATUS_UNSUCCESSFUL
;
366 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.ShortNameU
, &NameA
, &SpacesFound
);
367 aName
[NameA
.Length
]=0;
371 aName
[NameA
.Length
] = 0;
372 for (posCar
= 0; posCar
< DirContext
.LongNameU
.Length
/ sizeof(WCHAR
); posCar
++)
374 if (DirContext
.LongNameU
.Buffer
[posCar
] == L
'.')
379 /* check if the name and the extension contains upper case characters */
380 RtlDowncaseUnicodeString(&DirContext
.ShortNameU
, &DirContext
.LongNameU
, FALSE
);
381 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
382 uCaseBase
= wcsncmp(DirContext
.LongNameU
.Buffer
,
383 DirContext
.ShortNameU
.Buffer
, posCar
) ? TRUE
: FALSE
;
384 if (posCar
< DirContext
.LongNameU
.Length
/sizeof(WCHAR
))
386 i
= DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) - posCar
;
387 uCaseExt
= wcsncmp(DirContext
.LongNameU
.Buffer
+ posCar
,
388 DirContext
.ShortNameU
.Buffer
+ posCar
, i
) ? TRUE
: FALSE
;
394 /* check if the name and the extension contains lower case characters */
395 RtlUpcaseUnicodeString(&DirContext
.ShortNameU
, &DirContext
.LongNameU
, FALSE
);
396 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
397 lCaseBase
= wcsncmp(DirContext
.LongNameU
.Buffer
,
398 DirContext
.ShortNameU
.Buffer
, posCar
) ? TRUE
: FALSE
;
399 if (posCar
< DirContext
.LongNameU
.Length
/ sizeof(WCHAR
))
401 i
= DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) - posCar
;
402 lCaseExt
= wcsncmp(DirContext
.LongNameU
.Buffer
+ posCar
,
403 DirContext
.ShortNameU
.Buffer
+ posCar
, i
) ? TRUE
: FALSE
;
409 if ((lCaseBase
&& uCaseBase
) || (lCaseExt
&& uCaseExt
))
414 DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
415 aName
, &DirContext
.LongNameU
, needTilde
, needLong
);
416 memset(DirContext
.DirEntry
.Fat
.ShortName
, ' ', 11);
417 for (i
= 0; i
< 8 && aName
[i
] && aName
[i
] != '.'; i
++)
419 DirContext
.DirEntry
.Fat
.Filename
[i
] = aName
[i
];
424 for (j
= 0; j
< 3 && aName
[i
]; j
++, i
++)
426 DirContext
.DirEntry
.Fat
.Ext
[j
] = aName
[i
];
429 if (DirContext
.DirEntry
.Fat
.Filename
[0] == 0xe5)
431 DirContext
.DirEntry
.Fat
.Filename
[0] = 0x05;
436 RtlCopyMemory(LongNameBuffer
, DirContext
.LongNameU
.Buffer
, DirContext
.LongNameU
.Length
);
437 DirContext
.LongNameU
.Buffer
= LongNameBuffer
;
438 DirContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
439 DirContext
.LongNameU
.Buffer
[DirContext
.LongNameU
.Length
/ sizeof(WCHAR
)] = 0;
440 memset(DirContext
.LongNameU
.Buffer
+ DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 1, 0xff,
441 DirContext
.LongNameU
.MaximumLength
- DirContext
.LongNameU
.Length
- sizeof(WCHAR
));
448 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_BASE
;
452 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_EXT
;
456 DPRINT ("dos name=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
459 DirContext
.DirEntry
.Fat
.Attrib
= ReqAttr
;
460 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
462 DirContext
.DirEntry
.Fat
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
464 /* set dates and times */
465 KeQuerySystemTime(&SystemTime
);
466 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.Fat
.CreationDate
,
467 &DirContext
.DirEntry
.Fat
.CreationTime
);
468 DirContext
.DirEntry
.Fat
.UpdateDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
469 DirContext
.DirEntry
.Fat
.UpdateTime
= DirContext
.DirEntry
.Fat
.CreationTime
;
470 DirContext
.DirEntry
.Fat
.AccessDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
471 /* If it's moving, preserve creation time and file size */
472 if (MoveContext
!= NULL
)
474 DirContext
.DirEntry
.Fat
.CreationDate
= MoveContext
->CreationDate
;
475 DirContext
.DirEntry
.Fat
.CreationTime
= MoveContext
->CreationTime
;
476 DirContext
.DirEntry
.Fat
.FileSize
= MoveContext
->FileSize
;
481 /* calculate checksum for 8.3 name */
482 for (pSlots
[0].alias_checksum
= 0, i
= 0; i
< 11; i
++)
484 pSlots
[0].alias_checksum
= (((pSlots
[0].alias_checksum
& 1) << 7
485 | ((pSlots
[0].alias_checksum
& 0xfe) >> 1))
486 + DirContext
.DirEntry
.Fat
.ShortName
[i
]);
488 /* construct slots and entry */
489 for (i
= nbSlots
- 2; i
>= 0; i
--)
491 DPRINT("construct slot %d\n", i
);
492 pSlots
[i
].attr
= 0xf;
495 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1);
499 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1 + 0x40);
501 pSlots
[i
].alias_checksum
= pSlots
[0].alias_checksum
;
502 RtlCopyMemory(pSlots
[i
].name0_4
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13, 10);
503 RtlCopyMemory(pSlots
[i
].name5_10
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 5, 12);
504 RtlCopyMemory(pSlots
[i
].name11_12
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 11, 4);
507 /* try to find nbSlots contiguous entries frees in directory */
508 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, nbSlots
, &DirContext
.StartIndex
))
510 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
511 return STATUS_DISK_FULL
;
513 DirContext
.DirIndex
= DirContext
.StartIndex
+ nbSlots
- 1;
514 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
516 /* If we aren't moving, use next */
517 if (MoveContext
== NULL
)
520 Status
= NextCluster(DeviceExt
, 0, &CurrentCluster
, TRUE
);
521 if (CurrentCluster
== 0xffffffff || !NT_SUCCESS(Status
))
523 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
524 if (!NT_SUCCESS(Status
))
528 return STATUS_DISK_FULL
;
533 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
;
542 else if (MoveContext
!= NULL
)
544 CurrentCluster
= MoveContext
->FirstCluster
;
546 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
548 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
550 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
553 i
= DeviceExt
->FatInfo
.BytesPerCluster
/ sizeof(FAT_DIR_ENTRY
);
554 FileOffset
.u
.HighPart
= 0;
555 FileOffset
.u
.LowPart
= DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
);
556 if (DirContext
.StartIndex
/ i
== DirContext
.DirIndex
/ i
)
559 if (!CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
),
560 TRUE
, &Context
, (PVOID
*)&pFatEntry
))
562 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
563 return STATUS_UNSUCCESSFUL
;
567 RtlCopyMemory(pFatEntry
, Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
569 RtlCopyMemory(pFatEntry
+ (nbSlots
- 1), &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
574 size
= DeviceExt
->FatInfo
.BytesPerCluster
-
575 (DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
)) % DeviceExt
->FatInfo
.BytesPerCluster
;
576 i
= size
/ sizeof(FAT_DIR_ENTRY
);
577 if (!CcPinRead(ParentFcb
->FileObject
, &FileOffset
, size
, TRUE
,
578 &Context
, (PVOID
*)&pFatEntry
))
580 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
581 return STATUS_UNSUCCESSFUL
;
583 RtlCopyMemory(pFatEntry
, Buffer
, size
);
584 CcSetDirtyPinnedData(Context
, NULL
);
585 CcUnpinData(Context
);
586 FileOffset
.u
.LowPart
+= size
;
587 if (!CcPinRead(ParentFcb
->FileObject
, &FileOffset
,
588 nbSlots
* sizeof(FAT_DIR_ENTRY
) - size
,
589 TRUE
, &Context
, (PVOID
*)&pFatEntry
))
591 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
592 return STATUS_UNSUCCESSFUL
;
596 RtlCopyMemory(pFatEntry
, (PVOID
)(Buffer
+ size
), (nbSlots
- 1 - i
) * sizeof(FAT_DIR_ENTRY
));
598 RtlCopyMemory(pFatEntry
+ nbSlots
- 1 - i
, &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
600 CcSetDirtyPinnedData(Context
, NULL
);
601 CcUnpinData(Context
);
603 if (MoveContext
!= NULL
)
605 /* We're modifying an existing FCB - likely rename/move */
606 Status
= vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
607 (*Fcb
)->dirIndex
= DirContext
.DirIndex
;
608 (*Fcb
)->startIndex
= DirContext
.StartIndex
;
612 Status
= vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
614 if (!NT_SUCCESS(Status
))
616 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
620 DPRINT("new : entry=%11.11s\n", (*Fcb
)->entry
.Fat
.Filename
);
621 DPRINT("new : entry=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
623 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
625 FileOffset
.QuadPart
= 0;
626 if (!CcPinRead((*Fcb
)->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, TRUE
,
627 &Context
, (PVOID
*)&pFatEntry
))
629 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
630 return STATUS_UNSUCCESSFUL
;
632 /* clear the new directory cluster if not moving */
633 if (MoveContext
== NULL
)
635 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
636 /* create '.' and '..' */
637 RtlCopyMemory(&pFatEntry
[0].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
638 RtlCopyMemory(pFatEntry
[0].ShortName
, ". ", 11);
639 RtlCopyMemory(&pFatEntry
[1].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
640 RtlCopyMemory(pFatEntry
[1].ShortName
, ".. ", 11);
643 pFatEntry
[1].FirstCluster
= ParentFcb
->entry
.Fat
.FirstCluster
;
644 pFatEntry
[1].FirstClusterHigh
= ParentFcb
->entry
.Fat
.FirstClusterHigh
;
645 if (vfatFCBIsRoot(ParentFcb
))
647 pFatEntry
[1].FirstCluster
= 0;
648 pFatEntry
[1].FirstClusterHigh
= 0;
650 CcSetDirtyPinnedData(Context
, NULL
);
651 CcUnpinData(Context
);
653 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
654 DPRINT("addentry ok\n");
655 return STATUS_SUCCESS
;
659 create a new FAT entry
663 IN PDEVICE_EXTENSION DeviceExt
,
664 IN PUNICODE_STRING NameU
,
666 IN PVFATFCB ParentFcb
,
667 IN ULONG RequestedOptions
,
669 IN PVFAT_MOVE_CONTEXT MoveContext
)
671 PVOID Context
= NULL
;
672 LARGE_INTEGER SystemTime
, FileOffset
;
674 VFAT_DIRENTRY_CONTEXT DirContext
;
675 PFATX_DIR_ENTRY pFatXDirEntry
;
678 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
680 DirContext
.LongNameU
= *NameU
;
682 if (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) > 42)
685 return STATUS_NAME_TOO_LONG
;
688 /* try to find 1 entry free in directory */
689 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, 1, &DirContext
.StartIndex
))
691 return STATUS_DISK_FULL
;
693 Index
= DirContext
.DirIndex
= DirContext
.StartIndex
;
694 if (!vfatFCBIsRoot(ParentFcb
))
696 DirContext
.DirIndex
+= 2;
697 DirContext
.StartIndex
+= 2;
700 DirContext
.ShortNameU
.Buffer
= 0;
701 DirContext
.ShortNameU
.Length
= 0;
702 DirContext
.ShortNameU
.MaximumLength
= 0;
703 RtlZeroMemory(&DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
704 memset(DirContext
.DirEntry
.FatX
.Filename
, 0xff, 42);
705 /* Use cluster, if moving */
706 if (MoveContext
!= NULL
)
708 DirContext
.DirEntry
.FatX
.FirstCluster
= MoveContext
->FirstCluster
;
712 DirContext
.DirEntry
.FatX
.FirstCluster
= 0;
714 DirContext
.DirEntry
.FatX
.FileSize
= 0;
717 NameA
.Buffer
= (PCHAR
)DirContext
.DirEntry
.FatX
.Filename
;
719 NameA
.MaximumLength
= 42;
720 RtlUnicodeStringToOemString(&NameA
, &DirContext
.LongNameU
, FALSE
);
721 DirContext
.DirEntry
.FatX
.FilenameLength
= (unsigned char)NameA
.Length
;
724 DirContext
.DirEntry
.FatX
.Attrib
= ReqAttr
;
725 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
727 DirContext
.DirEntry
.FatX
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
730 /* set dates and times */
731 KeQuerySystemTime(&SystemTime
);
732 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.FatX
.CreationDate
,
733 &DirContext
.DirEntry
.FatX
.CreationTime
);
734 DirContext
.DirEntry
.FatX
.UpdateDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
735 DirContext
.DirEntry
.FatX
.UpdateTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
736 DirContext
.DirEntry
.FatX
.AccessDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
737 DirContext
.DirEntry
.FatX
.AccessTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
738 /* If it's moving, preserve creation time and file size */
739 if (MoveContext
!= NULL
)
741 DirContext
.DirEntry
.FatX
.CreationDate
= MoveContext
->CreationDate
;
742 DirContext
.DirEntry
.FatX
.CreationTime
= MoveContext
->CreationTime
;
743 DirContext
.DirEntry
.FatX
.FileSize
= MoveContext
->FileSize
;
746 /* add entry into parent directory */
747 FileOffset
.u
.HighPart
= 0;
748 FileOffset
.u
.LowPart
= Index
* sizeof(FATX_DIR_ENTRY
);
749 if (!CcPinRead(ParentFcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
),
750 TRUE
, &Context
, (PVOID
*)&pFatXDirEntry
))
752 return STATUS_UNSUCCESSFUL
;
754 RtlCopyMemory(pFatXDirEntry
, &DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
755 CcSetDirtyPinnedData(Context
, NULL
);
756 CcUnpinData(Context
);
758 if (MoveContext
!= NULL
)
760 /* We're modifying an existing FCB - likely rename/move */
761 /* FIXME: check status */
762 vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
763 (*Fcb
)->dirIndex
= DirContext
.DirIndex
;
764 (*Fcb
)->startIndex
= DirContext
.StartIndex
;
768 /* FIXME: check status */
769 vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
772 DPRINT("addentry ok\n");
773 return STATUS_SUCCESS
;
778 IN PDEVICE_EXTENSION DeviceExt
,
779 IN PUNICODE_STRING NameU
,
781 IN PVFATFCB ParentFcb
,
782 IN ULONG RequestedOptions
,
784 IN PVFAT_MOVE_CONTEXT MoveContext
)
786 if (DeviceExt
->Flags
& VCB_IS_FATX
)
787 return FATXAddEntry(DeviceExt
, NameU
, Fcb
, ParentFcb
, RequestedOptions
, ReqAttr
, MoveContext
);
789 return FATAddEntry(DeviceExt
, NameU
, Fcb
, ParentFcb
, RequestedOptions
, ReqAttr
, MoveContext
);
793 * deleting an existing FAT entry
797 IN PDEVICE_EXTENSION DeviceExt
,
799 OUT PVFAT_MOVE_CONTEXT MoveContext
)
801 ULONG CurrentCluster
= 0, NextCluster
, i
;
802 PVOID Context
= NULL
;
803 LARGE_INTEGER Offset
;
804 PFAT_DIR_ENTRY pDirEntry
= NULL
;
807 ASSERT(pFcb
->parentFcb
);
809 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
810 DPRINT("delete entry: %u to %u\n", pFcb
->startIndex
, pFcb
->dirIndex
);
811 Offset
.u
.HighPart
= 0;
812 for (i
= pFcb
->startIndex
; i
<= pFcb
->dirIndex
; i
++)
814 if (Context
== NULL
|| ((i
* sizeof(FAT_DIR_ENTRY
)) % PAGE_SIZE
) == 0)
818 CcSetDirtyPinnedData(Context
, NULL
);
819 CcUnpinData(Context
);
821 Offset
.u
.LowPart
= (i
* sizeof(FAT_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
822 if (!CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, sizeof(FAT_DIR_ENTRY
), TRUE
,
823 &Context
, (PVOID
*)&pDirEntry
))
825 return STATUS_UNSUCCESSFUL
;
828 pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))].Filename
[0] = 0xe5;
829 if (i
== pFcb
->dirIndex
)
832 vfatDirEntryGetFirstCluster(DeviceExt
,
833 (PDIR_ENTRY
)&pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))]);
838 CcSetDirtyPinnedData(Context
, NULL
);
839 CcUnpinData(Context
);
842 /* In case of moving, don't delete data */
843 if (MoveContext
!= NULL
)
845 pDirEntry
= &pDirEntry
[pFcb
->dirIndex
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))];
846 MoveContext
->FirstCluster
= CurrentCluster
;
847 MoveContext
->FileSize
= pDirEntry
->FileSize
;
848 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
849 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
853 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
855 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
856 /* FIXME: check status */
857 WriteCluster(DeviceExt
, CurrentCluster
, 0);
858 CurrentCluster
= NextCluster
;
862 return STATUS_SUCCESS
;
866 * deleting an existing FAT entry
870 IN PDEVICE_EXTENSION DeviceExt
,
872 OUT PVFAT_MOVE_CONTEXT MoveContext
)
874 ULONG CurrentCluster
= 0, NextCluster
;
875 PVOID Context
= NULL
;
876 LARGE_INTEGER Offset
;
877 PFATX_DIR_ENTRY pDirEntry
;
881 ASSERT(pFcb
->parentFcb
);
882 ASSERT(pFcb
->Flags
& FCB_IS_FATX_ENTRY
);
884 StartIndex
= pFcb
->startIndex
;
886 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
887 DPRINT("delete entry: %u\n", StartIndex
);
888 Offset
.u
.HighPart
= 0;
889 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
890 if (!CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, sizeof(FATX_DIR_ENTRY
), TRUE
,
891 &Context
, (PVOID
*)&pDirEntry
))
893 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
894 return STATUS_UNSUCCESSFUL
;
896 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
897 pDirEntry
->FilenameLength
= 0xe5;
898 CurrentCluster
= vfatDirEntryGetFirstCluster(DeviceExt
,
899 (PDIR_ENTRY
)pDirEntry
);
900 CcSetDirtyPinnedData(Context
, NULL
);
901 CcUnpinData(Context
);
903 /* In case of moving, don't delete data */
904 if (MoveContext
!= NULL
)
906 MoveContext
->FirstCluster
= CurrentCluster
;
907 MoveContext
->FileSize
= pDirEntry
->FileSize
;
908 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
909 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
913 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
915 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
916 /* FIXME: check status */
917 WriteCluster(DeviceExt
, CurrentCluster
, 0);
918 CurrentCluster
= NextCluster
;
922 return STATUS_SUCCESS
;
927 IN PDEVICE_EXTENSION DeviceExt
,
929 OUT PVFAT_MOVE_CONTEXT MoveContext
)
931 if (DeviceExt
->Flags
& VCB_IS_FATX
)
932 return FATXDelEntry(DeviceExt
, pFcb
, MoveContext
);
934 return FATDelEntry(DeviceExt
, pFcb
, MoveContext
);
938 * move an existing FAT entry
942 IN PDEVICE_EXTENSION DeviceExt
,
944 IN PUNICODE_STRING FileName
,
945 IN PVFATFCB ParentFcb
)
949 VFAT_MOVE_CONTEXT MoveContext
;
951 DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt
, pFcb
, FileName
, ParentFcb
);
953 /* Delete old entry while keeping data */
954 Status
= VfatDelEntry(DeviceExt
, pFcb
, &MoveContext
);
955 if (!NT_SUCCESS(Status
))
960 OldParent
= pFcb
->parentFcb
;
961 CcPurgeCacheSection(&OldParent
->SectionObjectPointers
, NULL
, 0, FALSE
);
962 MoveContext
.InPlace
= (OldParent
== ParentFcb
);
964 /* Add our new entry with our cluster */
965 Status
= VfatAddEntry(DeviceExt
,
969 (vfatFCBIsDirectory(pFcb
) ? FILE_DIRECTORY_FILE
: 0),
973 CcPurgeCacheSection(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, FALSE
);