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