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