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