1 /*
2 * PROJECT: VFAT Filesystem
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Write in directory
5 * COPYRIGHT: Copyright 1999-2001 Rex Jolliff <rex@lvcablemodem.com>
6 * Copyright 2004-2008 Hervé Poussineau <hpoussin@reactos.org>
7 * Copyright 2010-2018 Pierre Schweitzer <pierre@reactos.org>
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include "vfat.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 #ifdef KDBG
18 extern UNICODE_STRING DebugFile;
19 #endif
20
21 NTSTATUS
vfatFCBInitializeCacheFromVolume(PVCB vcb,PVFATFCB fcb)22 vfatFCBInitializeCacheFromVolume(
23 PVCB vcb,
24 PVFATFCB fcb)
25 {
26 PFILE_OBJECT fileObject;
27 PVFATCCB newCCB;
28 NTSTATUS status;
29 BOOLEAN Acquired;
30
31 /* Don't re-initialize if already done */
32 if (BooleanFlagOn(fcb->Flags, FCB_CACHE_INITIALIZED))
33 {
34 return STATUS_SUCCESS;
35 }
36
37 ASSERT(vfatFCBIsDirectory(fcb));
38 ASSERT(fcb->FileObject == NULL);
39
40 Acquired = FALSE;
41 if (!ExIsResourceAcquiredExclusive(&vcb->DirResource))
42 {
43 ExAcquireResourceExclusiveLite(&vcb->DirResource, TRUE);
44 Acquired = TRUE;
45 }
46
47 fileObject = IoCreateStreamFileObject (NULL, vcb->StorageDevice);
48 if (fileObject == NULL)
49 {
50 status = STATUS_INSUFFICIENT_RESOURCES;
51 goto Quit;
52 }
53
54 #ifdef KDBG
55 if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &fcb->LongNameU, FALSE, NULL))
56 {
57 DPRINT1("Attaching %p to %p (%d)\n", fcb, fileObject, fcb->RefCount);
58 }
59 #endif
60
61 newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
62 if (newCCB == NULL)
63 {
64 status = STATUS_INSUFFICIENT_RESOURCES;
65 ObDereferenceObject(fileObject);
66 goto Quit;
67 }
68 RtlZeroMemory(newCCB, sizeof (VFATCCB));
69
70 fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
71 fileObject->FsContext = fcb;
72 fileObject->FsContext2 = newCCB;
73 fileObject->Vpb = vcb->IoVPB;
74 fcb->FileObject = fileObject;
75
76 _SEH2_TRY
77 {
78 CcInitializeCacheMap(fileObject,
79 (PCC_FILE_SIZES)(&fcb->RFCB.AllocationSize),
80 TRUE,
81 &VfatGlobalData->CacheMgrCallbacks,
82 fcb);
83 }
84 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
85 {
86 status = _SEH2_GetExceptionCode();
87 fcb->FileObject = NULL;
88 ExFreeToNPagedLookasideList(&VfatGlobalData->CcbLookasideList, newCCB);
89 ObDereferenceObject(fileObject);
90 if (Acquired)
91 {
92 ExReleaseResourceLite(&vcb->DirResource);
93 }
94 return status;
95 }
96 _SEH2_END;
97
98 vfatGrabFCB(vcb, fcb);
99 SetFlag(fcb->Flags, FCB_CACHE_INITIALIZED);
100 status = STATUS_SUCCESS;
101
102 Quit:
103 if (Acquired)
104 {
105 ExReleaseResourceLite(&vcb->DirResource);
106 }
107
108 return status;
109 }
110
111 /*
112 * update an existing FAT entry
113 */
114 NTSTATUS
VfatUpdateEntry(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB pFcb)115 VfatUpdateEntry(
116 IN PDEVICE_EXTENSION DeviceExt,
117 IN PVFATFCB pFcb)
118 {
119 PVOID Context;
120 PDIR_ENTRY PinEntry;
121 LARGE_INTEGER Offset;
122 ULONG SizeDirEntry;
123 ULONG dirIndex;
124 NTSTATUS Status;
125
126 ASSERT(pFcb);
127
128 if (vfatVolumeIsFatX(DeviceExt))
129 {
130 SizeDirEntry = sizeof(FATX_DIR_ENTRY);
131 dirIndex = pFcb->startIndex;
132 }
133 else
134 {
135 SizeDirEntry = sizeof(FAT_DIR_ENTRY);
136 dirIndex = pFcb->dirIndex;
137 }
138
139 DPRINT("updEntry dirIndex %u, PathName \'%wZ\'\n", dirIndex, &pFcb->PathNameU);
140
141 if (vfatFCBIsRoot(pFcb) || BooleanFlagOn(pFcb->Flags, FCB_IS_FAT | FCB_IS_VOLUME))
142 {
143 return STATUS_SUCCESS;
144 }
145
146 ASSERT(pFcb->parentFcb);
147
148 Status = vfatFCBInitializeCacheFromVolume(DeviceExt, pFcb->parentFcb);
149 if (!NT_SUCCESS(Status))
150 {
151 return Status;
152 }
153
154 Offset.u.HighPart = 0;
155 Offset.u.LowPart = dirIndex * SizeDirEntry;
156 _SEH2_TRY
157 {
158 CcPinRead(pFcb->parentFcb->FileObject, &Offset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&PinEntry);
159 }
160 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
161 {
162 DPRINT1("Failed write to \'%wZ\'.\n", &pFcb->parentFcb->PathNameU);
163 _SEH2_YIELD(return _SEH2_GetExceptionCode());
164 }
165 _SEH2_END;
166
167 pFcb->Flags &= ~FCB_IS_DIRTY;
168 RtlCopyMemory(PinEntry, &pFcb->entry, SizeDirEntry);
169 CcSetDirtyPinnedData(Context, NULL);
170 CcUnpinData(Context);
171 return STATUS_SUCCESS;
172 }
173
174 /*
175 * rename an existing FAT entry
176 */
177 NTSTATUS
vfatRenameEntry(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB pFcb,IN PUNICODE_STRING FileName,IN BOOLEAN CaseChangeOnly)178 vfatRenameEntry(
179 IN PDEVICE_EXTENSION DeviceExt,
180 IN PVFATFCB pFcb,
181 IN PUNICODE_STRING FileName,
182 IN BOOLEAN CaseChangeOnly)
183 {
184 OEM_STRING NameA;
185 ULONG StartIndex;
186 PVOID Context = NULL;
187 LARGE_INTEGER Offset;
188 PFATX_DIR_ENTRY pDirEntry;
189 NTSTATUS Status;
190
191 DPRINT("vfatRenameEntry(%p, %p, %wZ, %d)\n", DeviceExt, pFcb, FileName, CaseChangeOnly);
192
193 Status = vfatFCBInitializeCacheFromVolume(DeviceExt, pFcb->parentFcb);
194 if (!NT_SUCCESS(Status))
195 {
196 return Status;
197 }
198
199 if (vfatVolumeIsFatX(DeviceExt))
200 {
201 VFAT_DIRENTRY_CONTEXT DirContext;
202
203 /* Open associated dir entry */
204 StartIndex = pFcb->startIndex;
205 Offset.u.HighPart = 0;
206 Offset.u.LowPart = (StartIndex * sizeof(FATX_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
207 _SEH2_TRY
208 {
209 CcPinRead(pFcb->parentFcb->FileObject, &Offset, PAGE_SIZE, PIN_WAIT, &Context, (PVOID*)&pDirEntry);
210 }
211 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
212 {
213 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset.u.HighPart, Offset.u.LowPart, PAGE_SIZE);
214 _SEH2_YIELD(return _SEH2_GetExceptionCode());
215 }
216 _SEH2_END;
217
218 pDirEntry = &pDirEntry[StartIndex % (PAGE_SIZE / sizeof(FATX_DIR_ENTRY))];
219
220 /* Set file name */
221 NameA.Buffer = (PCHAR)pDirEntry->Filename;
222 NameA.Length = 0;
223 NameA.MaximumLength = 42;
224 RtlUnicodeStringToOemString(&NameA, FileName, FALSE);
225 pDirEntry->FilenameLength = (unsigned char)NameA.Length;
226
227 /* Update FCB */
228 DirContext.DeviceExt = DeviceExt;
229 DirContext.ShortNameU.Length = 0;
230 DirContext.ShortNameU.MaximumLength = 0;
231 DirContext.ShortNameU.Buffer = NULL;
232 DirContext.LongNameU = *FileName;
233 DirContext.DirEntry.FatX = *pDirEntry;
234
235 CcSetDirtyPinnedData(Context, NULL);
236 CcUnpinData(Context);
237
238 Status = vfatUpdateFCB(DeviceExt, pFcb, &DirContext, pFcb->parentFcb);
239 if (NT_SUCCESS(Status))
240 {
241 CcFlushCache(&pFcb->parentFcb->SectionObjectPointers, NULL, 0, NULL);
242 }
243
244 return Status;
245 }
246 else
247 {
248 /* This we cannot handle properly, move file - would likely need love */
249 return VfatMoveEntry(DeviceExt, pFcb, FileName, pFcb->parentFcb);
250 }
251 }
252
253 /*
254 * try to find contiguous entries frees in directory,
255 * extend a directory if is necessary
256 */
257 BOOLEAN
vfatFindDirSpace(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB pDirFcb,IN ULONG nbSlots,OUT PULONG start)258 vfatFindDirSpace(
259 IN PDEVICE_EXTENSION DeviceExt,
260 IN PVFATFCB pDirFcb,
261 IN ULONG nbSlots,
262 OUT PULONG start)
263 {
264 LARGE_INTEGER FileOffset;
265 ULONG i, count, size, nbFree = 0;
266 PDIR_ENTRY pFatEntry = NULL;
267 PVOID Context = NULL;
268 NTSTATUS Status;
269 ULONG SizeDirEntry;
270 BOOLEAN IsFatX = vfatVolumeIsFatX(DeviceExt);
271 FileOffset.QuadPart = 0;
272
273 if (IsFatX)
274 SizeDirEntry = sizeof(FATX_DIR_ENTRY);
275 else
276 SizeDirEntry = sizeof(FAT_DIR_ENTRY);
277
278 Status = vfatFCBInitializeCacheFromVolume(DeviceExt, pDirFcb);
279 if (!NT_SUCCESS(Status))
280 {
281 return FALSE;
282 }
283
284 count = pDirFcb->RFCB.FileSize.u.LowPart / SizeDirEntry;
285 size = DeviceExt->FatInfo.BytesPerCluster / SizeDirEntry;
286 for (i = 0; i < count; i++, pFatEntry = (PDIR_ENTRY)((ULONG_PTR)pFatEntry + SizeDirEntry))
287 {
288 if (Context == NULL || (i % size) == 0)
289 {
290 if (Context)
291 {
292 CcUnpinData(Context);
293 }
294 _SEH2_TRY
295 {
296 CcPinRead(pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, PIN_WAIT, &Context, (PVOID*)&pFatEntry);
297 }
298 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
299 {
300 _SEH2_YIELD(return FALSE);
301 }
302 _SEH2_END;
303
304 FileOffset.u.LowPart += DeviceExt->FatInfo.BytesPerCluster;
305 }
306 if (ENTRY_END(IsFatX, pFatEntry))
307 {
308 break;
309 }
310 if (ENTRY_DELETED(IsFatX, pFatEntry))
311 {
312 nbFree++;
313 }
314 else
315 {
316 nbFree = 0;
317 }
318 if (nbFree == nbSlots)
319 {
320 break;
321 }
322 }
323 if (Context)
324 {
325 CcUnpinData(Context);
326 Context = NULL;
327 }
328 if (nbFree == nbSlots)
329 {
330 /* found enough contiguous free slots */
331 *start = i - nbSlots + 1;
332 }
333 else
334 {
335 *start = i - nbFree;
336 if (*start + nbSlots > count)
337 {
338 LARGE_INTEGER AllocationSize;
339 /* extend the directory */
340 if (vfatFCBIsRoot(pDirFcb) && DeviceExt->FatInfo.FatType != FAT32)
341 {
342 /* We can't extend a root directory on a FAT12/FAT16/FATX partition */
343 return FALSE;
344 }
345 AllocationSize.QuadPart = pDirFcb->RFCB.FileSize.u.LowPart + DeviceExt->FatInfo.BytesPerCluster;
346 Status = VfatSetAllocationSizeInformation(pDirFcb->FileObject, pDirFcb,
347 DeviceExt, &AllocationSize);
348 if (!NT_SUCCESS(Status))
349 {
350 return FALSE;
351 }
352 /* clear the new dir cluster */
353 FileOffset.u.LowPart = (ULONG)(pDirFcb->RFCB.FileSize.QuadPart -
354 DeviceExt->FatInfo.BytesPerCluster);
355 _SEH2_TRY
356 {
357 CcPinRead(pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, PIN_WAIT, &Context, (PVOID*)&pFatEntry);
358 }
359 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
360 {
361 _SEH2_YIELD(return FALSE);
362 }
363 _SEH2_END;
364
365 if (IsFatX)
366 memset(pFatEntry, 0xff, DeviceExt->FatInfo.BytesPerCluster);
367 else
368 RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
369 }
370 else if (*start + nbSlots < count)
371 {
372 /* clear the entry after the last new entry */
373 FileOffset.u.LowPart = (*start + nbSlots) * SizeDirEntry;
374 _SEH2_TRY
375 {
376 CcPinRead(pDirFcb->FileObject, &FileOffset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&pFatEntry);
377 }
378 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
379 {
380 _SEH2_YIELD(return FALSE);
381 }
382 _SEH2_END;
383
384 if (IsFatX)
385 memset(pFatEntry, 0xff, SizeDirEntry);
386 else
387 RtlZeroMemory(pFatEntry, SizeDirEntry);
388 }
389 if (Context)
390 {
391 CcSetDirtyPinnedData(Context, NULL);
392 CcUnpinData(Context);
393 }
394 }
395 DPRINT("nbSlots %u nbFree %u, entry number %u\n", nbSlots, nbFree, *start);
396 return TRUE;
397 }
398
399 /*
400 create a new FAT entry
401 */
402 static NTSTATUS
FATAddEntry(IN PDEVICE_EXTENSION DeviceExt,IN PUNICODE_STRING NameU,IN PVFATFCB * Fcb,IN PVFATFCB ParentFcb,IN ULONG RequestedOptions,IN UCHAR ReqAttr,IN PVFAT_MOVE_CONTEXT MoveContext)403 FATAddEntry(
404 IN PDEVICE_EXTENSION DeviceExt,
405 IN PUNICODE_STRING NameU,
406 IN PVFATFCB* Fcb,
407 IN PVFATFCB ParentFcb,
408 IN ULONG RequestedOptions,
409 IN UCHAR ReqAttr,
410 IN PVFAT_MOVE_CONTEXT MoveContext)
411 {
412 PVOID Context = NULL;
413 PFAT_DIR_ENTRY pFatEntry;
414 slot *pSlots;
415 USHORT nbSlots = 0, j;
416 PUCHAR Buffer;
417 BOOLEAN needTilde = FALSE, needLong = FALSE;
418 BOOLEAN BaseAllLower, BaseAllUpper;
419 BOOLEAN ExtensionAllLower, ExtensionAllUpper;
420 BOOLEAN InExtension;
421 BOOLEAN IsDirectory;
422 WCHAR c;
423 ULONG CurrentCluster;
424 LARGE_INTEGER SystemTime, FileOffset;
425 NTSTATUS Status = STATUS_SUCCESS;
426 ULONG size;
427 long i;
428
429 OEM_STRING NameA;
430 CHAR aName[13];
431 BOOLEAN IsNameLegal;
432 BOOLEAN SpacesFound;
433
434 VFAT_DIRENTRY_CONTEXT DirContext;
435 WCHAR LongNameBuffer[LONGNAME_MAX_LENGTH + 1];
436 WCHAR ShortNameBuffer[13];
437
438 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU, &ParentFcb->PathNameU);
439
440 DirContext.LongNameU = *NameU;
441 IsDirectory = BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE);
442
443 /* nb of entry needed for long name+normal entry */
444 nbSlots = (DirContext.LongNameU.Length / sizeof(WCHAR) + 12) / 13 + 1;
445 DPRINT("NameLen= %u, nbSlots =%u\n", DirContext.LongNameU.Length / sizeof(WCHAR), nbSlots);
446 Buffer = ExAllocatePoolWithTag(NonPagedPool, (nbSlots - 1) * sizeof(FAT_DIR_ENTRY), TAG_DIRENT);
447 if (Buffer == NULL)
448 {
449 return STATUS_INSUFFICIENT_RESOURCES;
450 }
451 RtlZeroMemory(Buffer, (nbSlots - 1) * sizeof(FAT_DIR_ENTRY));
452 pSlots = (slot *) Buffer;
453
454 NameA.Buffer = aName;
455 NameA.Length = 0;
456 NameA.MaximumLength = sizeof(aName);
457
458 DirContext.DeviceExt = DeviceExt;
459 DirContext.ShortNameU.Buffer = ShortNameBuffer;
460 DirContext.ShortNameU.Length = 0;
461 DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer);
462
463 RtlZeroMemory(&DirContext.DirEntry.Fat, sizeof(FAT_DIR_ENTRY));
464
465 IsNameLegal = RtlIsNameLegalDOS8Dot3(&DirContext.LongNameU, &NameA, &SpacesFound);
466
467 if (!IsNameLegal || SpacesFound)
468 {
469 GENERATE_NAME_CONTEXT NameContext;
470 VFAT_DIRENTRY_CONTEXT SearchContext;
471 WCHAR ShortSearchName[13];
472 needTilde = TRUE;
473 needLong = TRUE;
474 RtlZeroMemory(&NameContext, sizeof(GENERATE_NAME_CONTEXT));
475 SearchContext.DeviceExt = DeviceExt;
476 SearchContext.LongNameU.Buffer = LongNameBuffer;
477 SearchContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
478 SearchContext.ShortNameU.Buffer = ShortSearchName;
479 SearchContext.ShortNameU.MaximumLength = sizeof(ShortSearchName);
480
481 for (i = 0; i < 100; i++)
482 {
483 RtlGenerate8dot3Name(&DirContext.LongNameU, FALSE, &NameContext, &DirContext.ShortNameU);
484 DirContext.ShortNameU.Buffer[DirContext.ShortNameU.Length / sizeof(WCHAR)] = 0;
485 SearchContext.DirIndex = 0;
486 Status = FindFile(DeviceExt, ParentFcb, &DirContext.ShortNameU, &SearchContext, TRUE);
487 if (!NT_SUCCESS(Status))
488 {
489 break;
490 }
491 else if (MoveContext)
492 {
493 ASSERT(*Fcb);
494 if (strncmp((char *)SearchContext.DirEntry.Fat.ShortName, (char *)(*Fcb)->entry.Fat.ShortName, 11) == 0)
495 {
496 if (MoveContext->InPlace)
497 {
498 ASSERT(SearchContext.DirEntry.Fat.FileSize == MoveContext->FileSize);
499 break;
500 }
501 }
502 }
503 }
504 if (i == 100) /* FIXME : what to do after this ? */
505 {
506 ExFreePoolWithTag(Buffer, TAG_DIRENT);
507 return STATUS_UNSUCCESSFUL;
508 }
509 IsNameLegal = RtlIsNameLegalDOS8Dot3(&DirContext.ShortNameU, &NameA, &SpacesFound);
510 }
511 else
512 {
513 BaseAllLower = BaseAllUpper = TRUE;
514 ExtensionAllLower = ExtensionAllUpper = TRUE;
515 InExtension = FALSE;
516 for (i = 0; i < DirContext.LongNameU.Length / sizeof(WCHAR); i++)
517 {
518 c = DirContext.LongNameU.Buffer[i];
519 if (c >= L'A' && c <= L'Z')
520 {
521 if (InExtension)
522 ExtensionAllLower = FALSE;
523 else
524 BaseAllLower = FALSE;
525 }
526 else if (c >= L'a' && c <= L'z')
527 {
528 if (InExtension)
529 ExtensionAllUpper = FALSE;
530 else
531 BaseAllUpper = FALSE;
532 }
533 else if (c > 0x7f)
534 {
535 needLong = TRUE;
536 break;
537 }
538
539 if (c == L'.')
540 {
541 InExtension = TRUE;
542 }
543 }
544
545 if ((!BaseAllLower && !BaseAllUpper) ||
546 (!ExtensionAllLower && !ExtensionAllUpper))
547 {
548 needLong = TRUE;
549 }
550
551 RtlUpcaseUnicodeString(&DirContext.ShortNameU, &DirContext.LongNameU, FALSE);
552 DirContext.ShortNameU.Buffer[DirContext.ShortNameU.Length / sizeof(WCHAR)] = 0;
553 }
554 aName[NameA.Length] = 0;
555 DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
556 aName, &DirContext.LongNameU, needTilde, needLong);
557 memset(DirContext.DirEntry.Fat.ShortName, ' ', 11);
558 for (i = 0; i < 8 && aName[i] && aName[i] != '.'; i++)
559 {
560 DirContext.DirEntry.Fat.Filename[i] = aName[i];
561 }
562 if (aName[i] == '.')
563 {
564 i++;
565 for (j = 0; j < 3 && aName[i]; j++, i++)
566 {
567 DirContext.DirEntry.Fat.Ext[j] = aName[i];
568 }
569 }
570 if (DirContext.DirEntry.Fat.Filename[0] == 0xe5)
571 {
572 DirContext.DirEntry.Fat.Filename[0] = 0x05;
573 }
574
575 if (needLong)
576 {
577 RtlCopyMemory(LongNameBuffer, DirContext.LongNameU.Buffer, DirContext.LongNameU.Length);
578 DirContext.LongNameU.Buffer = LongNameBuffer;
579 DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
580 DirContext.LongNameU.Buffer[DirContext.LongNameU.Length / sizeof(WCHAR)] = 0;
581 memset(DirContext.LongNameU.Buffer + DirContext.LongNameU.Length / sizeof(WCHAR) + 1, 0xff,
582 DirContext.LongNameU.MaximumLength - DirContext.LongNameU.Length - sizeof(WCHAR));
583 }
584 else
585 {
586 nbSlots = 1;
587 if (BaseAllLower && !BaseAllUpper)
588 {
589 DirContext.DirEntry.Fat.lCase |= VFAT_CASE_LOWER_BASE;
590 }
591 if (ExtensionAllLower && !ExtensionAllUpper)
592 {
593 DirContext.DirEntry.Fat.lCase |= VFAT_CASE_LOWER_EXT;
594 }
595 }
596
597 DPRINT ("dos name=%11.11s\n", DirContext.DirEntry.Fat.Filename);
598
599 /* set attributes */
600 DirContext.DirEntry.Fat.Attrib = ReqAttr;
601 if (IsDirectory)
602 {
603 DirContext.DirEntry.Fat.Attrib |= FILE_ATTRIBUTE_DIRECTORY;
604 }
605 /* set dates and times */
606 KeQuerySystemTime(&SystemTime);
607 FsdSystemTimeToDosDateTime(DeviceExt, &SystemTime, &DirContext.DirEntry.Fat.CreationDate,
608 &DirContext.DirEntry.Fat.CreationTime);
609 DirContext.DirEntry.Fat.UpdateDate = DirContext.DirEntry.Fat.CreationDate;
610 DirContext.DirEntry.Fat.UpdateTime = DirContext.DirEntry.Fat.CreationTime;
611 DirContext.DirEntry.Fat.AccessDate = DirContext.DirEntry.Fat.CreationDate;
612 /* If it's moving, preserve creation time and file size */
613 if (MoveContext != NULL)
614 {
615 DirContext.DirEntry.Fat.CreationDate = MoveContext->CreationDate;
616 DirContext.DirEntry.Fat.CreationTime = MoveContext->CreationTime;
617 DirContext.DirEntry.Fat.FileSize = MoveContext->FileSize;
618 }
619
620 if (needLong)
621 {
622 /* calculate checksum for 8.3 name */
623 for (pSlots[0].alias_checksum = 0, i = 0; i < 11; i++)
624 {
625 pSlots[0].alias_checksum = (((pSlots[0].alias_checksum & 1) << 7
626 | ((pSlots[0].alias_checksum & 0xfe) >> 1))
627 + DirContext.DirEntry.Fat.ShortName[i]);
628 }
629 /* construct slots and entry */
630 for (i = nbSlots - 2; i >= 0; i--)
631 {
632 DPRINT("construct slot %d\n", i);
633 pSlots[i].attr = 0xf;
634 if (i)
635 {
636 pSlots[i].id = (unsigned char)(nbSlots - i - 1);
637 }
638 else
639 {
640 pSlots[i].id = (unsigned char)(nbSlots - i - 1 + 0x40);
641 }
642 pSlots[i].alias_checksum = pSlots[0].alias_checksum;
643 RtlCopyMemory(pSlots[i].name0_4, DirContext.LongNameU.Buffer + (nbSlots - i - 2) * 13, 10);
644 RtlCopyMemory(pSlots[i].name5_10, DirContext.LongNameU.Buffer + (nbSlots - i - 2) * 13 + 5, 12);
645 RtlCopyMemory(pSlots[i].name11_12, DirContext.LongNameU.Buffer + (nbSlots - i - 2) * 13 + 11, 4);
646 }
647 }
648 /* try to find nbSlots contiguous entries frees in directory */
649 if (!vfatFindDirSpace(DeviceExt, ParentFcb, nbSlots, &DirContext.StartIndex))
650 {
651 ExFreePoolWithTag(Buffer, TAG_DIRENT);
652 return STATUS_DISK_FULL;
653 }
654 DirContext.DirIndex = DirContext.StartIndex + nbSlots - 1;
655 if (IsDirectory)
656 {
657 /* If we aren't moving, use next */
658 if (MoveContext == NULL)
659 {
660 CurrentCluster = 0;
661 Status = NextCluster(DeviceExt, 0, &CurrentCluster, TRUE);
662 if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status))
663 {
664 ExFreePoolWithTag(Buffer, TAG_DIRENT);
665 if (!NT_SUCCESS(Status))
666 {
667 return Status;
668 }
669 return STATUS_DISK_FULL;
670 }
671
672 if (DeviceExt->FatInfo.FatType == FAT32)
673 {
674 FAT32UpdateFreeClustersCount(DeviceExt);
675 }
676 }
677 else
678 {
679 CurrentCluster = MoveContext->FirstCluster;
680 }
681
682 if (DeviceExt->FatInfo.FatType == FAT32)
683 {
684 DirContext.DirEntry.Fat.FirstClusterHigh = (unsigned short)(CurrentCluster >> 16);
685 }
686 DirContext.DirEntry.Fat.FirstCluster = (unsigned short)CurrentCluster;
687 }
688 else if (MoveContext != NULL)
689 {
690 CurrentCluster = MoveContext->FirstCluster;
691
692 if (DeviceExt->FatInfo.FatType == FAT32)
693 {
694 DirContext.DirEntry.Fat.FirstClusterHigh = (unsigned short)(CurrentCluster >> 16);
695 }
696 DirContext.DirEntry.Fat.FirstCluster = (unsigned short)CurrentCluster;
697 }
698
699 /* No need to init cache here, vfatFindDirSpace() will have done it for us */
700 ASSERT(BooleanFlagOn(ParentFcb->Flags, FCB_CACHE_INITIALIZED));
701
702 i = DeviceExt->FatInfo.BytesPerCluster / sizeof(FAT_DIR_ENTRY);
703 FileOffset.u.HighPart = 0;
704 FileOffset.u.LowPart = DirContext.StartIndex * sizeof(FAT_DIR_ENTRY);
705 if (DirContext.StartIndex / i == DirContext.DirIndex / i)
706 {
707 /* one cluster */
708 _SEH2_TRY
709 {
710 CcPinRead(ParentFcb->FileObject, &FileOffset, nbSlots * sizeof(FAT_DIR_ENTRY), PIN_WAIT, &Context, (PVOID*)&pFatEntry);
711 }
712 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
713 {
714 ExFreePoolWithTag(Buffer, TAG_DIRENT);
715 _SEH2_YIELD(return _SEH2_GetExceptionCode());
716 }
717 _SEH2_END;
718
719 if (nbSlots > 1)
720 {
721 RtlCopyMemory(pFatEntry, Buffer, (nbSlots - 1) * sizeof(FAT_DIR_ENTRY));
722 }
723 RtlCopyMemory(pFatEntry + (nbSlots - 1), &DirContext.DirEntry.Fat, sizeof(FAT_DIR_ENTRY));
724 }
725 else
726 {
727 /* two clusters */
728 size = DeviceExt->FatInfo.BytesPerCluster -
729 (DirContext.StartIndex * sizeof(FAT_DIR_ENTRY)) % DeviceExt->FatInfo.BytesPerCluster;
730 i = size / sizeof(FAT_DIR_ENTRY);
731 _SEH2_TRY
732 {
733 CcPinRead(ParentFcb->FileObject, &FileOffset, size, PIN_WAIT, &Context, (PVOID*)&pFatEntry);
734 }
735 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
736 {
737 ExFreePoolWithTag(Buffer, TAG_DIRENT);
738 _SEH2_YIELD(return _SEH2_GetExceptionCode());
739 }
740 _SEH2_END;
741 RtlCopyMemory(pFatEntry, Buffer, size);
742 CcSetDirtyPinnedData(Context, NULL);
743 CcUnpinData(Context);
744 FileOffset.u.LowPart += size;
745 _SEH2_TRY
746 {
747 CcPinRead(ParentFcb->FileObject, &FileOffset, nbSlots * sizeof(FAT_DIR_ENTRY) - size, PIN_WAIT, &Context, (PVOID*)&pFatEntry);
748 }
749 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
750 {
751 ExFreePoolWithTag(Buffer, TAG_DIRENT);
752 _SEH2_YIELD(return _SEH2_GetExceptionCode());
753 }
754 _SEH2_END;
755 if (nbSlots - 1 > i)
756 {
757 RtlCopyMemory(pFatEntry, (PVOID)(Buffer + size), (nbSlots - 1 - i) * sizeof(FAT_DIR_ENTRY));
758 }
759 RtlCopyMemory(pFatEntry + nbSlots - 1 - i, &DirContext.DirEntry.Fat, sizeof(FAT_DIR_ENTRY));
760 }
761 CcSetDirtyPinnedData(Context, NULL);
762 CcUnpinData(Context);
763
764 if (MoveContext != NULL)
765 {
766 /* We're modifying an existing FCB - likely rename/move */
767 Status = vfatUpdateFCB(DeviceExt, *Fcb, &DirContext, ParentFcb);
768 }
769 else
770 {
771 Status = vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb);
772 }
773 if (!NT_SUCCESS(Status))
774 {
775 ExFreePoolWithTag(Buffer, TAG_DIRENT);
776 return Status;
777 }
778
779 DPRINT("new : entry=%11.11s\n", (*Fcb)->entry.Fat.Filename);
780 DPRINT("new : entry=%11.11s\n", DirContext.DirEntry.Fat.Filename);
781
782 if (IsDirectory)
783 {
784 Status = vfatFCBInitializeCacheFromVolume(DeviceExt, (*Fcb));
785 if (!NT_SUCCESS(Status))
786 {
787 ExFreePoolWithTag(Buffer, TAG_DIRENT);
788 return Status;
789 }
790
791 FileOffset.QuadPart = 0;
792 _SEH2_TRY
793 {
794 CcPinRead((*Fcb)->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, PIN_WAIT, &Context, (PVOID*)&pFatEntry);
795 }
796 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
797 {
798 ExFreePoolWithTag(Buffer, TAG_DIRENT);
799 _SEH2_YIELD(return _SEH2_GetExceptionCode());
800 }
801 _SEH2_END;
802 /* clear the new directory cluster if not moving */
803 if (MoveContext == NULL)
804 {
805 RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
806 /* create '.' and '..' */
807 pFatEntry[0] = DirContext.DirEntry.Fat;
808 RtlCopyMemory(pFatEntry[0].ShortName, ". ", 11);
809 pFatEntry[1] = DirContext.DirEntry.Fat;
810 RtlCopyMemory(pFatEntry[1].ShortName, ".. ", 11);
811 }
812
813 pFatEntry[1].FirstCluster = ParentFcb->entry.Fat.FirstCluster;
814 pFatEntry[1].FirstClusterHigh = ParentFcb->entry.Fat.FirstClusterHigh;
815 if (vfatFCBIsRoot(ParentFcb))
816 {
817 pFatEntry[1].FirstCluster = 0;
818 pFatEntry[1].FirstClusterHigh = 0;
819 }
820 CcSetDirtyPinnedData(Context, NULL);
821 CcUnpinData(Context);
822 }
823 ExFreePoolWithTag(Buffer, TAG_DIRENT);
824 DPRINT("addentry ok\n");
825 return STATUS_SUCCESS;
826 }
827
828 /*
829 create a new FAT entry
830 */
831 static NTSTATUS
FATXAddEntry(IN PDEVICE_EXTENSION DeviceExt,IN PUNICODE_STRING NameU,IN PVFATFCB * Fcb,IN PVFATFCB ParentFcb,IN ULONG RequestedOptions,IN UCHAR ReqAttr,IN PVFAT_MOVE_CONTEXT MoveContext)832 FATXAddEntry(
833 IN PDEVICE_EXTENSION DeviceExt,
834 IN PUNICODE_STRING NameU,
835 IN PVFATFCB* Fcb,
836 IN PVFATFCB ParentFcb,
837 IN ULONG RequestedOptions,
838 IN UCHAR ReqAttr,
839 IN PVFAT_MOVE_CONTEXT MoveContext)
840 {
841 PVOID Context = NULL;
842 LARGE_INTEGER SystemTime, FileOffset;
843 OEM_STRING NameA;
844 VFAT_DIRENTRY_CONTEXT DirContext;
845 PFATX_DIR_ENTRY pFatXDirEntry;
846 ULONG Index;
847
848 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU, &ParentFcb->PathNameU);
849
850 DirContext.LongNameU = *NameU;
851
852 if (DirContext.LongNameU.Length / sizeof(WCHAR) > 42)
853 {
854 /* name too long */
855 return STATUS_NAME_TOO_LONG;
856 }
857
858 /* try to find 1 entry free in directory */
859 if (!vfatFindDirSpace(DeviceExt, ParentFcb, 1, &DirContext.StartIndex))
860 {
861 return STATUS_DISK_FULL;
862 }
863 Index = DirContext.DirIndex = DirContext.StartIndex;
864 if (!vfatFCBIsRoot(ParentFcb))
865 {
866 DirContext.DirIndex += 2;
867 DirContext.StartIndex += 2;
868 }
869
870 DirContext.ShortNameU.Buffer = 0;
871 DirContext.ShortNameU.Length = 0;
872 DirContext.ShortNameU.MaximumLength = 0;
873 DirContext.DeviceExt = DeviceExt;
874 RtlZeroMemory(&DirContext.DirEntry.FatX, sizeof(FATX_DIR_ENTRY));
875 memset(DirContext.DirEntry.FatX.Filename, 0xff, 42);
876 /* Use cluster, if moving */
877 if (MoveContext != NULL)
878 {
879 DirContext.DirEntry.FatX.FirstCluster = MoveContext->FirstCluster;
880 }
881 else
882 {
883 DirContext.DirEntry.FatX.FirstCluster = 0;
884 }
885 DirContext.DirEntry.FatX.FileSize = 0;
886
887 /* set file name */
888 NameA.Buffer = (PCHAR)DirContext.DirEntry.FatX.Filename;
889 NameA.Length = 0;
890 NameA.MaximumLength = 42;
891 RtlUnicodeStringToOemString(&NameA, &DirContext.LongNameU, FALSE);
892 DirContext.DirEntry.FatX.FilenameLength = (unsigned char)NameA.Length;
893
894 /* set attributes */
895 DirContext.DirEntry.FatX.Attrib = ReqAttr;
896 if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE))
897 {
898 DirContext.DirEntry.FatX.Attrib |= FILE_ATTRIBUTE_DIRECTORY;
899 }
900
901 /* set dates and times */
902 KeQuerySystemTime(&SystemTime);
903 FsdSystemTimeToDosDateTime(DeviceExt, &SystemTime, &DirContext.DirEntry.FatX.CreationDate,
904 &DirContext.DirEntry.FatX.CreationTime);
905 DirContext.DirEntry.FatX.UpdateDate = DirContext.DirEntry.FatX.CreationDate;
906 DirContext.DirEntry.FatX.UpdateTime = DirContext.DirEntry.FatX.CreationTime;
907 DirContext.DirEntry.FatX.AccessDate = DirContext.DirEntry.FatX.CreationDate;
908 DirContext.DirEntry.FatX.AccessTime = DirContext.DirEntry.FatX.CreationTime;
909 /* If it's moving, preserve creation time and file size */
910 if (MoveContext != NULL)
911 {
912 DirContext.DirEntry.FatX.CreationDate = MoveContext->CreationDate;
913 DirContext.DirEntry.FatX.CreationTime = MoveContext->CreationTime;
914 DirContext.DirEntry.FatX.FileSize = MoveContext->FileSize;
915 }
916
917 /* No need to init cache here, vfatFindDirSpace() will have done it for us */
918 ASSERT(BooleanFlagOn(ParentFcb->Flags, FCB_CACHE_INITIALIZED));
919
920 /* add entry into parent directory */
921 FileOffset.u.HighPart = 0;
922 FileOffset.u.LowPart = Index * sizeof(FATX_DIR_ENTRY);
923 _SEH2_TRY
924 {
925 CcPinRead(ParentFcb->FileObject, &FileOffset, sizeof(FATX_DIR_ENTRY), PIN_WAIT, &Context, (PVOID*)&pFatXDirEntry);
926 }
927 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
928 {
929 _SEH2_YIELD(return _SEH2_GetExceptionCode());
930 }
931 _SEH2_END;
932 RtlCopyMemory(pFatXDirEntry, &DirContext.DirEntry.FatX, sizeof(FATX_DIR_ENTRY));
933 CcSetDirtyPinnedData(Context, NULL);
934 CcUnpinData(Context);
935
936 if (MoveContext != NULL)
937 {
938 /* We're modifying an existing FCB - likely rename/move */
939 /* FIXME: check status */
940 vfatUpdateFCB(DeviceExt, *Fcb, &DirContext, ParentFcb);
941 }
942 else
943 {
944 /* FIXME: check status */
945 vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb);
946 }
947
948 DPRINT("addentry ok\n");
949 return STATUS_SUCCESS;
950 }
951
952 /*
953 * deleting an existing FAT entry
954 */
955 static NTSTATUS
FATDelEntry(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB pFcb,OUT PVFAT_MOVE_CONTEXT MoveContext)956 FATDelEntry(
957 IN PDEVICE_EXTENSION DeviceExt,
958 IN PVFATFCB pFcb,
959 OUT PVFAT_MOVE_CONTEXT MoveContext)
960 {
961 ULONG CurrentCluster = 0, NextCluster, i;
962 PVOID Context = NULL;
963 LARGE_INTEGER Offset;
964 PFAT_DIR_ENTRY pDirEntry = NULL;
965 NTSTATUS Status;
966
967 ASSERT(pFcb);
968 ASSERT(pFcb->parentFcb);
969
970 Status = vfatFCBInitializeCacheFromVolume(DeviceExt, pFcb->parentFcb);
971 if (!NT_SUCCESS(Status))
972 {
973 return Status;
974 }
975
976 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb->PathNameU);
977 DPRINT("delete entry: %u to %u\n", pFcb->startIndex, pFcb->dirIndex);
978 Offset.u.HighPart = 0;
979 for (i = pFcb->startIndex; i <= pFcb->dirIndex; i++)
980 {
981 if (Context == NULL || ((i * sizeof(FAT_DIR_ENTRY)) % PAGE_SIZE) == 0)
982 {
983 if (Context)
984 {
985 CcSetDirtyPinnedData(Context, NULL);
986 CcUnpinData(Context);
987 }
988 Offset.u.LowPart = (i * sizeof(FAT_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
989 _SEH2_TRY
990 {
991 CcPinRead(pFcb->parentFcb->FileObject, &Offset, PAGE_SIZE, PIN_WAIT, &Context, (PVOID*)&pDirEntry);
992 }
993 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
994 {
995 _SEH2_YIELD(return _SEH2_GetExceptionCode());
996 }
997 _SEH2_END;
998 }
999 pDirEntry[i % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))].Filename[0] = 0xe5;
1000 if (i == pFcb->dirIndex)
1001 {
1002 CurrentCluster =
1003 vfatDirEntryGetFirstCluster(DeviceExt,
1004 (PDIR_ENTRY)&pDirEntry[i % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))]);
1005 }
1006 }
1007
1008 /* In case of moving, save properties */
1009 if (MoveContext != NULL)
1010 {
1011 pDirEntry = &pDirEntry[pFcb->dirIndex % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))];
1012 MoveContext->FirstCluster = CurrentCluster;
1013 MoveContext->FileSize = pDirEntry->FileSize;
1014 MoveContext->CreationTime = pDirEntry->CreationTime;
1015 MoveContext->CreationDate = pDirEntry->CreationDate;
1016 }
1017
1018 if (Context)
1019 {
1020 CcSetDirtyPinnedData(Context, NULL);
1021 CcUnpinData(Context);
1022 }
1023
1024 /* In case of moving, don't delete data */
1025 if (MoveContext == NULL)
1026 {
1027 while (CurrentCluster && CurrentCluster != 0xffffffff)
1028 {
1029 GetNextCluster(DeviceExt, CurrentCluster, &NextCluster);
1030 /* FIXME: check status */
1031 WriteCluster(DeviceExt, CurrentCluster, 0);
1032 CurrentCluster = NextCluster;
1033 }
1034
1035 if (DeviceExt->FatInfo.FatType == FAT32)
1036 {
1037 FAT32UpdateFreeClustersCount(DeviceExt);
1038 }
1039 }
1040
1041 return STATUS_SUCCESS;
1042 }
1043
1044 /*
1045 * deleting an existing FAT entry
1046 */
1047 static NTSTATUS
FATXDelEntry(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB pFcb,OUT PVFAT_MOVE_CONTEXT MoveContext)1048 FATXDelEntry(
1049 IN PDEVICE_EXTENSION DeviceExt,
1050 IN PVFATFCB pFcb,
1051 OUT PVFAT_MOVE_CONTEXT MoveContext)
1052 {
1053 ULONG CurrentCluster = 0, NextCluster;
1054 PVOID Context = NULL;
1055 LARGE_INTEGER Offset;
1056 PFATX_DIR_ENTRY pDirEntry;
1057 ULONG StartIndex;
1058 NTSTATUS Status;
1059
1060 ASSERT(pFcb);
1061 ASSERT(pFcb->parentFcb);
1062 ASSERT(vfatVolumeIsFatX(DeviceExt));
1063
1064 StartIndex = pFcb->startIndex;
1065
1066 Status = vfatFCBInitializeCacheFromVolume(DeviceExt, pFcb->parentFcb);
1067 if (!NT_SUCCESS(Status))
1068 {
1069 return Status;
1070 }
1071
1072 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb->PathNameU);
1073 DPRINT("delete entry: %u\n", StartIndex);
1074 Offset.u.HighPart = 0;
1075 Offset.u.LowPart = (StartIndex * sizeof(FATX_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
1076 _SEH2_TRY
1077 {
1078 CcPinRead(pFcb->parentFcb->FileObject, &Offset, PAGE_SIZE, PIN_WAIT, &Context, (PVOID*)&pDirEntry);
1079 }
1080 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1081 {
1082 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset.u.HighPart, Offset.u.LowPart, PAGE_SIZE);
1083 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1084 }
1085 _SEH2_END;
1086 pDirEntry = &pDirEntry[StartIndex % (PAGE_SIZE / sizeof(FATX_DIR_ENTRY))];
1087 pDirEntry->FilenameLength = 0xe5;
1088 CurrentCluster = vfatDirEntryGetFirstCluster(DeviceExt,
1089 (PDIR_ENTRY)pDirEntry);
1090
1091 /* In case of moving, save properties */
1092 if (MoveContext != NULL)
1093 {
1094 MoveContext->FirstCluster = CurrentCluster;
1095 MoveContext->FileSize = pDirEntry->FileSize;
1096 MoveContext->CreationTime = pDirEntry->CreationTime;
1097 MoveContext->CreationDate = pDirEntry->CreationDate;
1098 }
1099
1100 CcSetDirtyPinnedData(Context, NULL);
1101 CcUnpinData(Context);
1102
1103 /* In case of moving, don't delete data */
1104 if (MoveContext == NULL)
1105 {
1106 while (CurrentCluster && CurrentCluster != 0xffffffff)
1107 {
1108 GetNextCluster(DeviceExt, CurrentCluster, &NextCluster);
1109 /* FIXME: check status */
1110 WriteCluster(DeviceExt, CurrentCluster, 0);
1111 CurrentCluster = NextCluster;
1112 }
1113 }
1114
1115 return STATUS_SUCCESS;
1116 }
1117
1118 /*
1119 * move an existing FAT entry
1120 */
1121 NTSTATUS
VfatMoveEntry(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB pFcb,IN PUNICODE_STRING FileName,IN PVFATFCB ParentFcb)1122 VfatMoveEntry(
1123 IN PDEVICE_EXTENSION DeviceExt,
1124 IN PVFATFCB pFcb,
1125 IN PUNICODE_STRING FileName,
1126 IN PVFATFCB ParentFcb)
1127 {
1128 NTSTATUS Status;
1129 PVFATFCB OldParent;
1130 VFAT_MOVE_CONTEXT MoveContext;
1131
1132 DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt, pFcb, FileName, ParentFcb);
1133
1134 /* Delete old entry while keeping data */
1135 Status = VfatDelEntry(DeviceExt, pFcb, &MoveContext);
1136 if (!NT_SUCCESS(Status))
1137 {
1138 return Status;
1139 }
1140
1141 OldParent = pFcb->parentFcb;
1142 CcFlushCache(&OldParent->SectionObjectPointers, NULL, 0, NULL);
1143 MoveContext.InPlace = (OldParent == ParentFcb);
1144
1145 /* Add our new entry with our cluster */
1146 Status = VfatAddEntry(DeviceExt,
1147 FileName,
1148 &pFcb,
1149 ParentFcb,
1150 (vfatFCBIsDirectory(pFcb) ? FILE_DIRECTORY_FILE : 0),
1151 *pFcb->Attributes,
1152 &MoveContext);
1153
1154 CcFlushCache(&pFcb->parentFcb->SectionObjectPointers, NULL, 0, NULL);
1155
1156 return Status;
1157 }
1158
1159 extern BOOLEAN FATXIsDirectoryEmpty(PDEVICE_EXTENSION DeviceExt, PVFATFCB Fcb);
1160 extern BOOLEAN FATIsDirectoryEmpty(PDEVICE_EXTENSION DeviceExt, PVFATFCB Fcb);
1161 extern NTSTATUS FATGetNextDirEntry(PVOID *pContext, PVOID *pPage, PVFATFCB pDirFcb, PVFAT_DIRENTRY_CONTEXT DirContext, BOOLEAN First);
1162 extern NTSTATUS FATXGetNextDirEntry(PVOID *pContext, PVOID *pPage, PVFATFCB pDirFcb, PVFAT_DIRENTRY_CONTEXT DirContext, BOOLEAN First);
1163
1164 VFAT_DISPATCH FatXDispatch = {
1165 FATXIsDirectoryEmpty, // .IsDirectoryEmpty
1166 FATXAddEntry, // .AddEntry
1167 FATXDelEntry, // .DelEntry
1168 FATXGetNextDirEntry, // .GetNextDirEntry
1169 };
1170
1171 VFAT_DISPATCH FatDispatch = {
1172 FATIsDirectoryEmpty, // .IsDirectoryEmpty
1173 FATAddEntry, // .AddEntry
1174 FATDelEntry, // .DelEntry
1175 FATGetNextDirEntry, // .GetNextDirEntry
1176 };
1177
1178 /* EOF */
1179