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