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