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