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
))
349 if (i
== 100) /* FIXME : what to do after this ? */
351 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
352 return STATUS_UNSUCCESSFUL
;
354 IsNameLegal
= RtlIsNameLegalDOS8Dot3(&DirContext
.ShortNameU
, &NameA
, &SpacesFound
);
355 aName
[NameA
.Length
]=0;
359 aName
[NameA
.Length
] = 0;
360 for (posCar
= 0; posCar
< DirContext
.LongNameU
.Length
/ sizeof(WCHAR
); posCar
++)
362 if (DirContext
.LongNameU
.Buffer
[posCar
] == L
'.')
367 /* check if the name and the extension contains upper case characters */
368 RtlDowncaseUnicodeString(&DirContext
.ShortNameU
, &DirContext
.LongNameU
, FALSE
);
369 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
370 uCaseBase
= wcsncmp(DirContext
.LongNameU
.Buffer
,
371 DirContext
.ShortNameU
.Buffer
, posCar
) ? TRUE
: FALSE
;
372 if (posCar
< DirContext
.LongNameU
.Length
/sizeof(WCHAR
))
374 i
= DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) - posCar
;
375 uCaseExt
= wcsncmp(DirContext
.LongNameU
.Buffer
+ posCar
,
376 DirContext
.ShortNameU
.Buffer
+ posCar
, i
) ? TRUE
: FALSE
;
382 /* check if the name and the extension contains lower case characters */
383 RtlUpcaseUnicodeString(&DirContext
.ShortNameU
, &DirContext
.LongNameU
, FALSE
);
384 DirContext
.ShortNameU
.Buffer
[DirContext
.ShortNameU
.Length
/ sizeof(WCHAR
)] = 0;
385 lCaseBase
= wcsncmp(DirContext
.LongNameU
.Buffer
,
386 DirContext
.ShortNameU
.Buffer
, posCar
) ? TRUE
: FALSE
;
387 if (posCar
< DirContext
.LongNameU
.Length
/ sizeof(WCHAR
))
389 i
= DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) - posCar
;
390 lCaseExt
= wcsncmp(DirContext
.LongNameU
.Buffer
+ posCar
,
391 DirContext
.ShortNameU
.Buffer
+ posCar
, i
) ? TRUE
: FALSE
;
397 if ((lCaseBase
&& uCaseBase
) || (lCaseExt
&& uCaseExt
))
402 DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
403 aName
, &DirContext
.LongNameU
, needTilde
, needLong
);
404 memset(DirContext
.DirEntry
.Fat
.ShortName
, ' ', 11);
405 for (i
= 0; i
< 8 && aName
[i
] && aName
[i
] != '.'; i
++)
407 DirContext
.DirEntry
.Fat
.Filename
[i
] = aName
[i
];
412 for (j
= 0; j
< 3 && aName
[i
]; j
++, i
++)
414 DirContext
.DirEntry
.Fat
.Ext
[j
] = aName
[i
];
417 if (DirContext
.DirEntry
.Fat
.Filename
[0] == 0xe5)
419 DirContext
.DirEntry
.Fat
.Filename
[0] = 0x05;
424 RtlCopyMemory(LongNameBuffer
, DirContext
.LongNameU
.Buffer
, DirContext
.LongNameU
.Length
);
425 DirContext
.LongNameU
.Buffer
= LongNameBuffer
;
426 DirContext
.LongNameU
.MaximumLength
= sizeof(LongNameBuffer
);
427 DirContext
.LongNameU
.Buffer
[DirContext
.LongNameU
.Length
/ sizeof(WCHAR
)] = 0;
428 memset(DirContext
.LongNameU
.Buffer
+ DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) + 1, 0xff,
429 DirContext
.LongNameU
.MaximumLength
- DirContext
.LongNameU
.Length
- sizeof(WCHAR
));
436 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_BASE
;
440 DirContext
.DirEntry
.Fat
.lCase
|= VFAT_CASE_LOWER_EXT
;
444 DPRINT ("dos name=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
447 DirContext
.DirEntry
.Fat
.Attrib
= ReqAttr
;
448 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
450 DirContext
.DirEntry
.Fat
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
452 /* set dates and times */
453 KeQuerySystemTime(&SystemTime
);
454 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.Fat
.CreationDate
,
455 &DirContext
.DirEntry
.Fat
.CreationTime
);
456 DirContext
.DirEntry
.Fat
.UpdateDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
457 DirContext
.DirEntry
.Fat
.UpdateTime
= DirContext
.DirEntry
.Fat
.CreationTime
;
458 DirContext
.DirEntry
.Fat
.AccessDate
= DirContext
.DirEntry
.Fat
.CreationDate
;
459 /* If it's moving, preserve creation time and file size */
460 if (MoveContext
!= NULL
)
462 DirContext
.DirEntry
.Fat
.CreationDate
= MoveContext
->CreationDate
;
463 DirContext
.DirEntry
.Fat
.CreationTime
= MoveContext
->CreationTime
;
464 DirContext
.DirEntry
.Fat
.FileSize
= MoveContext
->FileSize
;
469 /* calculate checksum for 8.3 name */
470 for (pSlots
[0].alias_checksum
= 0, i
= 0; i
< 11; i
++)
472 pSlots
[0].alias_checksum
= (((pSlots
[0].alias_checksum
& 1) << 7
473 | ((pSlots
[0].alias_checksum
& 0xfe) >> 1))
474 + DirContext
.DirEntry
.Fat
.ShortName
[i
]);
476 /* construct slots and entry */
477 for (i
= nbSlots
- 2; i
>= 0; i
--)
479 DPRINT("construct slot %d\n", i
);
480 pSlots
[i
].attr
= 0xf;
483 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1);
487 pSlots
[i
].id
= (unsigned char)(nbSlots
- i
- 1 + 0x40);
489 pSlots
[i
].alias_checksum
= pSlots
[0].alias_checksum
;
490 RtlCopyMemory(pSlots
[i
].name0_4
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13, 10);
491 RtlCopyMemory(pSlots
[i
].name5_10
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 5, 12);
492 RtlCopyMemory(pSlots
[i
].name11_12
, DirContext
.LongNameU
.Buffer
+ (nbSlots
- i
- 2) * 13 + 11, 4);
495 /* try to find nbSlots contiguous entries frees in directory */
496 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, nbSlots
, &DirContext
.StartIndex
))
498 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
499 return STATUS_DISK_FULL
;
501 DirContext
.DirIndex
= DirContext
.StartIndex
+ nbSlots
- 1;
502 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
504 /* If we aren't moving, use next */
505 if (MoveContext
== NULL
)
508 Status
= NextCluster(DeviceExt
, 0, &CurrentCluster
, TRUE
);
509 if (CurrentCluster
== 0xffffffff || !NT_SUCCESS(Status
))
511 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
512 if (!NT_SUCCESS(Status
))
516 return STATUS_DISK_FULL
;
521 CurrentCluster
= MoveContext
->FirstCluster
;
524 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
526 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
528 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
530 else if (MoveContext
!= NULL
)
532 CurrentCluster
= MoveContext
->FirstCluster
;
534 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
536 DirContext
.DirEntry
.Fat
.FirstClusterHigh
= (unsigned short)(CurrentCluster
>> 16);
538 DirContext
.DirEntry
.Fat
.FirstCluster
= (unsigned short)CurrentCluster
;
541 i
= DeviceExt
->FatInfo
.BytesPerCluster
/ sizeof(FAT_DIR_ENTRY
);
542 FileOffset
.u
.HighPart
= 0;
543 FileOffset
.u
.LowPart
= DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
);
544 if (DirContext
.StartIndex
/ i
== DirContext
.DirIndex
/ i
)
547 if (!CcPinRead(ParentFcb
->FileObject
, &FileOffset
, nbSlots
* sizeof(FAT_DIR_ENTRY
),
548 TRUE
, &Context
, (PVOID
*)&pFatEntry
))
550 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
551 return STATUS_UNSUCCESSFUL
;
555 RtlCopyMemory(pFatEntry
, Buffer
, (nbSlots
- 1) * sizeof(FAT_DIR_ENTRY
));
557 RtlCopyMemory(pFatEntry
+ (nbSlots
- 1), &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
562 size
= DeviceExt
->FatInfo
.BytesPerCluster
-
563 (DirContext
.StartIndex
* sizeof(FAT_DIR_ENTRY
)) % DeviceExt
->FatInfo
.BytesPerCluster
;
564 i
= size
/ sizeof(FAT_DIR_ENTRY
);
565 if (!CcPinRead(ParentFcb
->FileObject
, &FileOffset
, size
, TRUE
,
566 &Context
, (PVOID
*)&pFatEntry
))
568 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
569 return STATUS_UNSUCCESSFUL
;
571 RtlCopyMemory(pFatEntry
, Buffer
, size
);
572 CcSetDirtyPinnedData(Context
, NULL
);
573 CcUnpinData(Context
);
574 FileOffset
.u
.LowPart
+= size
;
575 if (!CcPinRead(ParentFcb
->FileObject
, &FileOffset
,
576 nbSlots
* sizeof(FAT_DIR_ENTRY
) - size
,
577 TRUE
, &Context
, (PVOID
*)&pFatEntry
))
579 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
580 return STATUS_UNSUCCESSFUL
;
584 RtlCopyMemory(pFatEntry
, (PVOID
)(Buffer
+ size
), (nbSlots
- 1 - i
) * sizeof(FAT_DIR_ENTRY
));
586 RtlCopyMemory(pFatEntry
+ nbSlots
- 1 - i
, &DirContext
.DirEntry
.Fat
, sizeof(FAT_DIR_ENTRY
));
588 CcSetDirtyPinnedData(Context
, NULL
);
589 CcUnpinData(Context
);
591 if (MoveContext
!= NULL
)
593 /* We're modifying an existing FCB - likely rename/move */
594 Status
= vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
595 (*Fcb
)->dirIndex
= DirContext
.DirIndex
;
596 (*Fcb
)->startIndex
= DirContext
.StartIndex
;
600 Status
= vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
602 if (!NT_SUCCESS(Status
))
604 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
608 DPRINT("new : entry=%11.11s\n", (*Fcb
)->entry
.Fat
.Filename
);
609 DPRINT("new : entry=%11.11s\n", DirContext
.DirEntry
.Fat
.Filename
);
611 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
613 FileOffset
.QuadPart
= 0;
614 if (!CcPinRead((*Fcb
)->FileObject
, &FileOffset
, DeviceExt
->FatInfo
.BytesPerCluster
, TRUE
,
615 &Context
, (PVOID
*)&pFatEntry
))
617 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
618 return STATUS_UNSUCCESSFUL
;
620 /* clear the new directory cluster if not moving */
621 if (MoveContext
== NULL
)
623 RtlZeroMemory(pFatEntry
, DeviceExt
->FatInfo
.BytesPerCluster
);
624 /* create '.' and '..' */
625 RtlCopyMemory(&pFatEntry
[0].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
626 RtlCopyMemory(pFatEntry
[0].ShortName
, ". ", 11);
627 RtlCopyMemory(&pFatEntry
[1].Attrib
, &DirContext
.DirEntry
.Fat
.Attrib
, sizeof(FAT_DIR_ENTRY
) - 11);
628 RtlCopyMemory(pFatEntry
[1].ShortName
, ".. ", 11);
631 pFatEntry
[1].FirstCluster
= ParentFcb
->entry
.Fat
.FirstCluster
;
632 pFatEntry
[1].FirstClusterHigh
= ParentFcb
->entry
.Fat
.FirstClusterHigh
;
633 if (vfatFCBIsRoot(ParentFcb
))
635 pFatEntry
[1].FirstCluster
= 0;
636 pFatEntry
[1].FirstClusterHigh
= 0;
638 CcSetDirtyPinnedData(Context
, NULL
);
639 CcUnpinData(Context
);
641 ExFreePoolWithTag(Buffer
, TAG_VFAT
);
642 DPRINT("addentry ok\n");
643 return STATUS_SUCCESS
;
647 create a new FAT entry
651 IN PDEVICE_EXTENSION DeviceExt
,
652 IN PUNICODE_STRING NameU
,
654 IN PVFATFCB ParentFcb
,
655 IN ULONG RequestedOptions
,
657 IN PVFAT_MOVE_CONTEXT MoveContext
)
659 PVOID Context
= NULL
;
660 LARGE_INTEGER SystemTime
, FileOffset
;
662 VFAT_DIRENTRY_CONTEXT DirContext
;
663 PFATX_DIR_ENTRY pFatXDirEntry
;
666 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU
, &ParentFcb
->PathNameU
);
668 DirContext
.LongNameU
= *NameU
;
670 if (DirContext
.LongNameU
.Length
/ sizeof(WCHAR
) > 42)
673 return STATUS_NAME_TOO_LONG
;
676 /* try to find 1 entry free in directory */
677 if (!vfatFindDirSpace(DeviceExt
, ParentFcb
, 1, &DirContext
.StartIndex
))
679 return STATUS_DISK_FULL
;
681 Index
= DirContext
.DirIndex
= DirContext
.StartIndex
;
682 if (!vfatFCBIsRoot(ParentFcb
))
684 DirContext
.DirIndex
+= 2;
685 DirContext
.StartIndex
+= 2;
688 DirContext
.ShortNameU
.Buffer
= 0;
689 DirContext
.ShortNameU
.Length
= 0;
690 DirContext
.ShortNameU
.MaximumLength
= 0;
691 RtlZeroMemory(&DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
692 memset(DirContext
.DirEntry
.FatX
.Filename
, 0xff, 42);
693 /* Use cluster, if moving */
694 if (MoveContext
!= NULL
)
696 DirContext
.DirEntry
.FatX
.FirstCluster
= MoveContext
->FirstCluster
;
700 DirContext
.DirEntry
.FatX
.FirstCluster
= 0;
702 DirContext
.DirEntry
.FatX
.FileSize
= 0;
705 NameA
.Buffer
= (PCHAR
)DirContext
.DirEntry
.FatX
.Filename
;
707 NameA
.MaximumLength
= 42;
708 RtlUnicodeStringToOemString(&NameA
, &DirContext
.LongNameU
, FALSE
);
709 DirContext
.DirEntry
.FatX
.FilenameLength
= (unsigned char)NameA
.Length
;
712 DirContext
.DirEntry
.FatX
.Attrib
= ReqAttr
;
713 if (RequestedOptions
& FILE_DIRECTORY_FILE
)
715 DirContext
.DirEntry
.FatX
.Attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
718 /* set dates and times */
719 KeQuerySystemTime(&SystemTime
);
720 FsdSystemTimeToDosDateTime(DeviceExt
, &SystemTime
, &DirContext
.DirEntry
.FatX
.CreationDate
,
721 &DirContext
.DirEntry
.FatX
.CreationTime
);
722 DirContext
.DirEntry
.FatX
.UpdateDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
723 DirContext
.DirEntry
.FatX
.UpdateTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
724 DirContext
.DirEntry
.FatX
.AccessDate
= DirContext
.DirEntry
.FatX
.CreationDate
;
725 DirContext
.DirEntry
.FatX
.AccessTime
= DirContext
.DirEntry
.FatX
.CreationTime
;
726 /* If it's moving, preserve creation time and file size */
727 if (MoveContext
!= NULL
)
729 DirContext
.DirEntry
.FatX
.CreationDate
= MoveContext
->CreationDate
;
730 DirContext
.DirEntry
.FatX
.CreationTime
= MoveContext
->CreationTime
;
731 DirContext
.DirEntry
.FatX
.FileSize
= MoveContext
->FileSize
;
734 /* add entry into parent directory */
735 FileOffset
.u
.HighPart
= 0;
736 FileOffset
.u
.LowPart
= Index
* sizeof(FATX_DIR_ENTRY
);
737 if (!CcPinRead(ParentFcb
->FileObject
, &FileOffset
, sizeof(FATX_DIR_ENTRY
),
738 TRUE
, &Context
, (PVOID
*)&pFatXDirEntry
))
740 return STATUS_UNSUCCESSFUL
;
742 RtlCopyMemory(pFatXDirEntry
, &DirContext
.DirEntry
.FatX
, sizeof(FATX_DIR_ENTRY
));
743 CcSetDirtyPinnedData(Context
, NULL
);
744 CcUnpinData(Context
);
746 if (MoveContext
!= NULL
)
748 /* We're modifying an existing FCB - likely rename/move */
749 /* FIXME: check status */
750 vfatUpdateFCB(DeviceExt
, *Fcb
, &DirContext
, ParentFcb
);
751 (*Fcb
)->dirIndex
= DirContext
.DirIndex
;
752 (*Fcb
)->startIndex
= DirContext
.StartIndex
;
756 /* FIXME: check status */
757 vfatMakeFCBFromDirEntry(DeviceExt
, ParentFcb
, &DirContext
, Fcb
);
760 DPRINT("addentry ok\n");
761 return STATUS_SUCCESS
;
766 IN PDEVICE_EXTENSION DeviceExt
,
767 IN PUNICODE_STRING NameU
,
769 IN PVFATFCB ParentFcb
,
770 IN ULONG RequestedOptions
,
772 IN PVFAT_MOVE_CONTEXT MoveContext
)
774 if (DeviceExt
->Flags
& VCB_IS_FATX
)
775 return FATXAddEntry(DeviceExt
, NameU
, Fcb
, ParentFcb
, RequestedOptions
, ReqAttr
, MoveContext
);
777 return FATAddEntry(DeviceExt
, NameU
, Fcb
, ParentFcb
, RequestedOptions
, ReqAttr
, MoveContext
);
781 * deleting an existing FAT entry
785 IN PDEVICE_EXTENSION DeviceExt
,
787 OUT PVFAT_MOVE_CONTEXT MoveContext
)
789 ULONG CurrentCluster
= 0, NextCluster
, i
;
790 PVOID Context
= NULL
;
791 LARGE_INTEGER Offset
;
792 PFAT_DIR_ENTRY pDirEntry
= NULL
;
795 ASSERT(pFcb
->parentFcb
);
797 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
798 DPRINT("delete entry: %u to %u\n", pFcb
->startIndex
, pFcb
->dirIndex
);
799 Offset
.u
.HighPart
= 0;
800 for (i
= pFcb
->startIndex
; i
<= pFcb
->dirIndex
; i
++)
802 if (Context
== NULL
|| ((i
* sizeof(FAT_DIR_ENTRY
)) % PAGE_SIZE
) == 0)
806 CcSetDirtyPinnedData(Context
, NULL
);
807 CcUnpinData(Context
);
809 Offset
.u
.LowPart
= (i
* sizeof(FAT_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
810 if (!CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, sizeof(FAT_DIR_ENTRY
), TRUE
,
811 &Context
, (PVOID
*)&pDirEntry
))
813 return STATUS_UNSUCCESSFUL
;
816 pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))].Filename
[0] = 0xe5;
817 if (i
== pFcb
->dirIndex
)
820 vfatDirEntryGetFirstCluster(DeviceExt
,
821 (PDIR_ENTRY
)&pDirEntry
[i
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))]);
826 CcSetDirtyPinnedData(Context
, NULL
);
827 CcUnpinData(Context
);
830 /* In case of moving, don't delete data */
831 if (MoveContext
!= NULL
)
833 pDirEntry
= &pDirEntry
[pFcb
->dirIndex
% (PAGE_SIZE
/ sizeof(FAT_DIR_ENTRY
))];
834 MoveContext
->FirstCluster
= CurrentCluster
;
835 MoveContext
->FileSize
= pDirEntry
->FileSize
;
836 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
837 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
841 while (CurrentCluster
&& CurrentCluster
!= 0xffffffff)
843 GetNextCluster(DeviceExt
, CurrentCluster
, &NextCluster
);
844 /* FIXME: check status */
845 WriteCluster(DeviceExt
, CurrentCluster
, 0);
846 CurrentCluster
= NextCluster
;
850 return STATUS_SUCCESS
;
854 * deleting an existing FAT entry
858 IN PDEVICE_EXTENSION DeviceExt
,
860 OUT PVFAT_MOVE_CONTEXT MoveContext
)
862 ULONG CurrentCluster
= 0, NextCluster
;
863 PVOID Context
= NULL
;
864 LARGE_INTEGER Offset
;
865 PFATX_DIR_ENTRY pDirEntry
;
869 ASSERT(pFcb
->parentFcb
);
870 ASSERT(pFcb
->Flags
& FCB_IS_FATX_ENTRY
);
872 StartIndex
= pFcb
->startIndex
;
874 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb
->PathNameU
);
875 DPRINT("delete entry: %u\n", StartIndex
);
876 Offset
.u
.HighPart
= 0;
877 Offset
.u
.LowPart
= (StartIndex
* sizeof(FATX_DIR_ENTRY
) / PAGE_SIZE
) * PAGE_SIZE
;
878 if (!CcPinRead(pFcb
->parentFcb
->FileObject
, &Offset
, sizeof(FATX_DIR_ENTRY
), TRUE
,
879 &Context
, (PVOID
*)&pDirEntry
))
881 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset
.u
.HighPart
, Offset
.u
.LowPart
, PAGE_SIZE
);
882 return STATUS_UNSUCCESSFUL
;
884 pDirEntry
= &pDirEntry
[StartIndex
% (PAGE_SIZE
/ sizeof(FATX_DIR_ENTRY
))];
885 pDirEntry
->FilenameLength
= 0xe5;
886 CurrentCluster
= vfatDirEntryGetFirstCluster(DeviceExt
,
887 (PDIR_ENTRY
)pDirEntry
);
888 CcSetDirtyPinnedData(Context
, NULL
);
889 CcUnpinData(Context
);
891 /* In case of moving, don't delete data */
892 if (MoveContext
!= NULL
)
894 MoveContext
->FirstCluster
= CurrentCluster
;
895 MoveContext
->FileSize
= pDirEntry
->FileSize
;
896 MoveContext
->CreationTime
= pDirEntry
->CreationTime
;
897 MoveContext
->CreationDate
= pDirEntry
->CreationDate
;
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
;
915 IN PDEVICE_EXTENSION DeviceExt
,
917 OUT PVFAT_MOVE_CONTEXT MoveContext
)
919 if (DeviceExt
->Flags
& VCB_IS_FATX
)
920 return FATXDelEntry(DeviceExt
, pFcb
, MoveContext
);
922 return FATDelEntry(DeviceExt
, pFcb
, MoveContext
);
926 * move an existing FAT entry
930 IN PDEVICE_EXTENSION DeviceExt
,
932 IN PUNICODE_STRING FileName
,
933 IN PVFATFCB ParentFcb
)
937 VFAT_MOVE_CONTEXT MoveContext
;
939 DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt
, pFcb
, FileName
, ParentFcb
);
941 /* Delete old entry while keeping data */
942 Status
= VfatDelEntry(DeviceExt
, pFcb
, &MoveContext
);
943 if (!NT_SUCCESS(Status
))
948 OldParent
= pFcb
->parentFcb
;
949 CcPurgeCacheSection(&OldParent
->SectionObjectPointers
, NULL
, 0, FALSE
);
951 /* Add our new entry with our cluster */
952 Status
= VfatAddEntry(DeviceExt
,
956 (vfatFCBIsDirectory(pFcb
) ? FILE_DIRECTORY_FILE
: 0),
960 CcPurgeCacheSection(&pFcb
->parentFcb
->SectionObjectPointers
, NULL
, 0, FALSE
);