[FASTFAT]
[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 else if (MoveContext)
349 {
350 ASSERT(*Fcb);
351 if (strncmp((char *)SearchContext.DirEntry.Fat.ShortName, (char *)(*Fcb)->entry.Fat.ShortName, 11) == 0)
352 {
353 if (MoveContext->InPlace)
354 {
355 ASSERT(SearchContext.DirEntry.Fat.FileSize == MoveContext->FileSize);
356 break;
357 }
358 }
359 }
360 }
361 if (i == 100) /* FIXME : what to do after this ? */
362 {
363 ExFreePoolWithTag(Buffer, TAG_VFAT);
364 return STATUS_UNSUCCESSFUL;
365 }
366 IsNameLegal = RtlIsNameLegalDOS8Dot3(&DirContext.ShortNameU, &NameA, &SpacesFound);
367 aName[NameA.Length]=0;
368 }
369 else
370 {
371 aName[NameA.Length] = 0;
372 for (posCar = 0; posCar < DirContext.LongNameU.Length / sizeof(WCHAR); posCar++)
373 {
374 if (DirContext.LongNameU.Buffer[posCar] == L'.')
375 {
376 break;
377 }
378 }
379 /* check if the name and the extension contains upper case characters */
380 RtlDowncaseUnicodeString(&DirContext.ShortNameU, &DirContext.LongNameU, FALSE);
381 DirContext.ShortNameU.Buffer[DirContext.ShortNameU.Length / sizeof(WCHAR)] = 0;
382 uCaseBase = 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 uCaseExt = wcsncmp(DirContext.LongNameU.Buffer + posCar,
388 DirContext.ShortNameU.Buffer + posCar, i) ? TRUE : FALSE;
389 }
390 else
391 {
392 uCaseExt = FALSE;
393 }
394 /* check if the name and the extension contains lower case characters */
395 RtlUpcaseUnicodeString(&DirContext.ShortNameU, &DirContext.LongNameU, FALSE);
396 DirContext.ShortNameU.Buffer[DirContext.ShortNameU.Length / sizeof(WCHAR)] = 0;
397 lCaseBase = wcsncmp(DirContext.LongNameU.Buffer,
398 DirContext.ShortNameU.Buffer, posCar) ? TRUE : FALSE;
399 if (posCar < DirContext.LongNameU.Length / sizeof(WCHAR))
400 {
401 i = DirContext.LongNameU.Length / sizeof(WCHAR) - posCar;
402 lCaseExt = wcsncmp(DirContext.LongNameU.Buffer + posCar,
403 DirContext.ShortNameU.Buffer + posCar, i) ? TRUE : FALSE;
404 }
405 else
406 {
407 lCaseExt = FALSE;
408 }
409 if ((lCaseBase && uCaseBase) || (lCaseExt && uCaseExt))
410 {
411 needLong = TRUE;
412 }
413 }
414 DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
415 aName, &DirContext.LongNameU, needTilde, needLong);
416 memset(DirContext.DirEntry.Fat.ShortName, ' ', 11);
417 for (i = 0; i < 8 && aName[i] && aName[i] != '.'; i++)
418 {
419 DirContext.DirEntry.Fat.Filename[i] = aName[i];
420 }
421 if (aName[i] == '.')
422 {
423 i++;
424 for (j = 0; j < 3 && aName[i]; j++, i++)
425 {
426 DirContext.DirEntry.Fat.Ext[j] = aName[i];
427 }
428 }
429 if (DirContext.DirEntry.Fat.Filename[0] == 0xe5)
430 {
431 DirContext.DirEntry.Fat.Filename[0] = 0x05;
432 }
433
434 if (needLong)
435 {
436 RtlCopyMemory(LongNameBuffer, DirContext.LongNameU.Buffer, DirContext.LongNameU.Length);
437 DirContext.LongNameU.Buffer = LongNameBuffer;
438 DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
439 DirContext.LongNameU.Buffer[DirContext.LongNameU.Length / sizeof(WCHAR)] = 0;
440 memset(DirContext.LongNameU.Buffer + DirContext.LongNameU.Length / sizeof(WCHAR) + 1, 0xff,
441 DirContext.LongNameU.MaximumLength - DirContext.LongNameU.Length - sizeof(WCHAR));
442 }
443 else
444 {
445 nbSlots = 1;
446 if (lCaseBase)
447 {
448 DirContext.DirEntry.Fat.lCase |= VFAT_CASE_LOWER_BASE;
449 }
450 if (lCaseExt)
451 {
452 DirContext.DirEntry.Fat.lCase |= VFAT_CASE_LOWER_EXT;
453 }
454 }
455
456 DPRINT ("dos name=%11.11s\n", DirContext.DirEntry.Fat.Filename);
457
458 /* set attributes */
459 DirContext.DirEntry.Fat.Attrib = ReqAttr;
460 if (RequestedOptions & FILE_DIRECTORY_FILE)
461 {
462 DirContext.DirEntry.Fat.Attrib |= FILE_ATTRIBUTE_DIRECTORY;
463 }
464 /* set dates and times */
465 KeQuerySystemTime(&SystemTime);
466 FsdSystemTimeToDosDateTime(DeviceExt, &SystemTime, &DirContext.DirEntry.Fat.CreationDate,
467 &DirContext.DirEntry.Fat.CreationTime);
468 DirContext.DirEntry.Fat.UpdateDate = DirContext.DirEntry.Fat.CreationDate;
469 DirContext.DirEntry.Fat.UpdateTime = DirContext.DirEntry.Fat.CreationTime;
470 DirContext.DirEntry.Fat.AccessDate = DirContext.DirEntry.Fat.CreationDate;
471 /* If it's moving, preserve creation time and file size */
472 if (MoveContext != NULL)
473 {
474 DirContext.DirEntry.Fat.CreationDate = MoveContext->CreationDate;
475 DirContext.DirEntry.Fat.CreationTime = MoveContext->CreationTime;
476 DirContext.DirEntry.Fat.FileSize = MoveContext->FileSize;
477 }
478
479 if (needLong)
480 {
481 /* calculate checksum for 8.3 name */
482 for (pSlots[0].alias_checksum = 0, i = 0; i < 11; i++)
483 {
484 pSlots[0].alias_checksum = (((pSlots[0].alias_checksum & 1) << 7
485 | ((pSlots[0].alias_checksum & 0xfe) >> 1))
486 + DirContext.DirEntry.Fat.ShortName[i]);
487 }
488 /* construct slots and entry */
489 for (i = nbSlots - 2; i >= 0; i--)
490 {
491 DPRINT("construct slot %d\n", i);
492 pSlots[i].attr = 0xf;
493 if (i)
494 {
495 pSlots[i].id = (unsigned char)(nbSlots - i - 1);
496 }
497 else
498 {
499 pSlots[i].id = (unsigned char)(nbSlots - i - 1 + 0x40);
500 }
501 pSlots[i].alias_checksum = pSlots[0].alias_checksum;
502 RtlCopyMemory(pSlots[i].name0_4, DirContext.LongNameU.Buffer + (nbSlots - i - 2) * 13, 10);
503 RtlCopyMemory(pSlots[i].name5_10, DirContext.LongNameU.Buffer + (nbSlots - i - 2) * 13 + 5, 12);
504 RtlCopyMemory(pSlots[i].name11_12, DirContext.LongNameU.Buffer + (nbSlots - i - 2) * 13 + 11, 4);
505 }
506 }
507 /* try to find nbSlots contiguous entries frees in directory */
508 if (!vfatFindDirSpace(DeviceExt, ParentFcb, nbSlots, &DirContext.StartIndex))
509 {
510 ExFreePoolWithTag(Buffer, TAG_VFAT);
511 return STATUS_DISK_FULL;
512 }
513 DirContext.DirIndex = DirContext.StartIndex + nbSlots - 1;
514 if (RequestedOptions & FILE_DIRECTORY_FILE)
515 {
516 /* If we aren't moving, use next */
517 if (MoveContext == NULL)
518 {
519 CurrentCluster = 0;
520 Status = NextCluster(DeviceExt, 0, &CurrentCluster, TRUE);
521 if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status))
522 {
523 ExFreePoolWithTag(Buffer, TAG_VFAT);
524 if (!NT_SUCCESS(Status))
525 {
526 return Status;
527 }
528 return STATUS_DISK_FULL;
529 }
530 }
531 else
532 {
533 CurrentCluster = MoveContext->FirstCluster;
534 }
535
536 if (DeviceExt->FatInfo.FatType == FAT32)
537 {
538 DirContext.DirEntry.Fat.FirstClusterHigh = (unsigned short)(CurrentCluster >> 16);
539 }
540 DirContext.DirEntry.Fat.FirstCluster = (unsigned short)CurrentCluster;
541 }
542 else if (MoveContext != NULL)
543 {
544 CurrentCluster = MoveContext->FirstCluster;
545
546 if (DeviceExt->FatInfo.FatType == FAT32)
547 {
548 DirContext.DirEntry.Fat.FirstClusterHigh = (unsigned short)(CurrentCluster >> 16);
549 }
550 DirContext.DirEntry.Fat.FirstCluster = (unsigned short)CurrentCluster;
551 }
552
553 i = DeviceExt->FatInfo.BytesPerCluster / sizeof(FAT_DIR_ENTRY);
554 FileOffset.u.HighPart = 0;
555 FileOffset.u.LowPart = DirContext.StartIndex * sizeof(FAT_DIR_ENTRY);
556 if (DirContext.StartIndex / i == DirContext.DirIndex / i)
557 {
558 /* one cluster */
559 if (!CcPinRead(ParentFcb->FileObject, &FileOffset, nbSlots * sizeof(FAT_DIR_ENTRY),
560 TRUE, &Context, (PVOID*)&pFatEntry))
561 {
562 ExFreePoolWithTag(Buffer, TAG_VFAT);
563 return STATUS_UNSUCCESSFUL;
564 }
565 if (nbSlots > 1)
566 {
567 RtlCopyMemory(pFatEntry, Buffer, (nbSlots - 1) * sizeof(FAT_DIR_ENTRY));
568 }
569 RtlCopyMemory(pFatEntry + (nbSlots - 1), &DirContext.DirEntry.Fat, sizeof(FAT_DIR_ENTRY));
570 }
571 else
572 {
573 /* two clusters */
574 size = DeviceExt->FatInfo.BytesPerCluster -
575 (DirContext.StartIndex * sizeof(FAT_DIR_ENTRY)) % DeviceExt->FatInfo.BytesPerCluster;
576 i = size / sizeof(FAT_DIR_ENTRY);
577 if (!CcPinRead(ParentFcb->FileObject, &FileOffset, size, TRUE,
578 &Context, (PVOID*)&pFatEntry))
579 {
580 ExFreePoolWithTag(Buffer, TAG_VFAT);
581 return STATUS_UNSUCCESSFUL;
582 }
583 RtlCopyMemory(pFatEntry, Buffer, size);
584 CcSetDirtyPinnedData(Context, NULL);
585 CcUnpinData(Context);
586 FileOffset.u.LowPart += size;
587 if (!CcPinRead(ParentFcb->FileObject, &FileOffset,
588 nbSlots * sizeof(FAT_DIR_ENTRY) - size,
589 TRUE, &Context, (PVOID*)&pFatEntry))
590 {
591 ExFreePoolWithTag(Buffer, TAG_VFAT);
592 return STATUS_UNSUCCESSFUL;
593 }
594 if (nbSlots - 1 > i)
595 {
596 RtlCopyMemory(pFatEntry, (PVOID)(Buffer + size), (nbSlots - 1 - i) * sizeof(FAT_DIR_ENTRY));
597 }
598 RtlCopyMemory(pFatEntry + nbSlots - 1 - i, &DirContext.DirEntry.Fat, sizeof(FAT_DIR_ENTRY));
599 }
600 CcSetDirtyPinnedData(Context, NULL);
601 CcUnpinData(Context);
602
603 if (MoveContext != NULL)
604 {
605 /* We're modifying an existing FCB - likely rename/move */
606 Status = vfatUpdateFCB(DeviceExt, *Fcb, &DirContext, ParentFcb);
607 (*Fcb)->dirIndex = DirContext.DirIndex;
608 (*Fcb)->startIndex = DirContext.StartIndex;
609 }
610 else
611 {
612 Status = vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb);
613 }
614 if (!NT_SUCCESS(Status))
615 {
616 ExFreePoolWithTag(Buffer, TAG_VFAT);
617 return Status;
618 }
619
620 DPRINT("new : entry=%11.11s\n", (*Fcb)->entry.Fat.Filename);
621 DPRINT("new : entry=%11.11s\n", DirContext.DirEntry.Fat.Filename);
622
623 if (RequestedOptions & FILE_DIRECTORY_FILE)
624 {
625 FileOffset.QuadPart = 0;
626 if (!CcPinRead((*Fcb)->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, TRUE,
627 &Context, (PVOID*)&pFatEntry))
628 {
629 ExFreePoolWithTag(Buffer, TAG_VFAT);
630 return STATUS_UNSUCCESSFUL;
631 }
632 /* clear the new directory cluster if not moving */
633 if (MoveContext == NULL)
634 {
635 RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
636 /* create '.' and '..' */
637 RtlCopyMemory(&pFatEntry[0].Attrib, &DirContext.DirEntry.Fat.Attrib, sizeof(FAT_DIR_ENTRY) - 11);
638 RtlCopyMemory(pFatEntry[0].ShortName, ". ", 11);
639 RtlCopyMemory(&pFatEntry[1].Attrib, &DirContext.DirEntry.Fat.Attrib, sizeof(FAT_DIR_ENTRY) - 11);
640 RtlCopyMemory(pFatEntry[1].ShortName, ".. ", 11);
641 }
642
643 pFatEntry[1].FirstCluster = ParentFcb->entry.Fat.FirstCluster;
644 pFatEntry[1].FirstClusterHigh = ParentFcb->entry.Fat.FirstClusterHigh;
645 if (vfatFCBIsRoot(ParentFcb))
646 {
647 pFatEntry[1].FirstCluster = 0;
648 pFatEntry[1].FirstClusterHigh = 0;
649 }
650 CcSetDirtyPinnedData(Context, NULL);
651 CcUnpinData(Context);
652 }
653 ExFreePoolWithTag(Buffer, TAG_VFAT);
654 DPRINT("addentry ok\n");
655 return STATUS_SUCCESS;
656 }
657
658 /*
659 create a new FAT entry
660 */
661 static NTSTATUS
662 FATXAddEntry(
663 IN PDEVICE_EXTENSION DeviceExt,
664 IN PUNICODE_STRING NameU,
665 IN PVFATFCB* Fcb,
666 IN PVFATFCB ParentFcb,
667 IN ULONG RequestedOptions,
668 IN UCHAR ReqAttr,
669 IN PVFAT_MOVE_CONTEXT MoveContext)
670 {
671 PVOID Context = NULL;
672 LARGE_INTEGER SystemTime, FileOffset;
673 OEM_STRING NameA;
674 VFAT_DIRENTRY_CONTEXT DirContext;
675 PFATX_DIR_ENTRY pFatXDirEntry;
676 ULONG Index;
677
678 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU, &ParentFcb->PathNameU);
679
680 DirContext.LongNameU = *NameU;
681
682 if (DirContext.LongNameU.Length / sizeof(WCHAR) > 42)
683 {
684 /* name too long */
685 return STATUS_NAME_TOO_LONG;
686 }
687
688 /* try to find 1 entry free in directory */
689 if (!vfatFindDirSpace(DeviceExt, ParentFcb, 1, &DirContext.StartIndex))
690 {
691 return STATUS_DISK_FULL;
692 }
693 Index = DirContext.DirIndex = DirContext.StartIndex;
694 if (!vfatFCBIsRoot(ParentFcb))
695 {
696 DirContext.DirIndex += 2;
697 DirContext.StartIndex += 2;
698 }
699
700 DirContext.ShortNameU.Buffer = 0;
701 DirContext.ShortNameU.Length = 0;
702 DirContext.ShortNameU.MaximumLength = 0;
703 RtlZeroMemory(&DirContext.DirEntry.FatX, sizeof(FATX_DIR_ENTRY));
704 memset(DirContext.DirEntry.FatX.Filename, 0xff, 42);
705 /* Use cluster, if moving */
706 if (MoveContext != NULL)
707 {
708 DirContext.DirEntry.FatX.FirstCluster = MoveContext->FirstCluster;
709 }
710 else
711 {
712 DirContext.DirEntry.FatX.FirstCluster = 0;
713 }
714 DirContext.DirEntry.FatX.FileSize = 0;
715
716 /* set file name */
717 NameA.Buffer = (PCHAR)DirContext.DirEntry.FatX.Filename;
718 NameA.Length = 0;
719 NameA.MaximumLength = 42;
720 RtlUnicodeStringToOemString(&NameA, &DirContext.LongNameU, FALSE);
721 DirContext.DirEntry.FatX.FilenameLength = (unsigned char)NameA.Length;
722
723 /* set attributes */
724 DirContext.DirEntry.FatX.Attrib = ReqAttr;
725 if (RequestedOptions & FILE_DIRECTORY_FILE)
726 {
727 DirContext.DirEntry.FatX.Attrib |= FILE_ATTRIBUTE_DIRECTORY;
728 }
729
730 /* set dates and times */
731 KeQuerySystemTime(&SystemTime);
732 FsdSystemTimeToDosDateTime(DeviceExt, &SystemTime, &DirContext.DirEntry.FatX.CreationDate,
733 &DirContext.DirEntry.FatX.CreationTime);
734 DirContext.DirEntry.FatX.UpdateDate = DirContext.DirEntry.FatX.CreationDate;
735 DirContext.DirEntry.FatX.UpdateTime = DirContext.DirEntry.FatX.CreationTime;
736 DirContext.DirEntry.FatX.AccessDate = DirContext.DirEntry.FatX.CreationDate;
737 DirContext.DirEntry.FatX.AccessTime = DirContext.DirEntry.FatX.CreationTime;
738 /* If it's moving, preserve creation time and file size */
739 if (MoveContext != NULL)
740 {
741 DirContext.DirEntry.FatX.CreationDate = MoveContext->CreationDate;
742 DirContext.DirEntry.FatX.CreationTime = MoveContext->CreationTime;
743 DirContext.DirEntry.FatX.FileSize = MoveContext->FileSize;
744 }
745
746 /* add entry into parent directory */
747 FileOffset.u.HighPart = 0;
748 FileOffset.u.LowPart = Index * sizeof(FATX_DIR_ENTRY);
749 if (!CcPinRead(ParentFcb->FileObject, &FileOffset, sizeof(FATX_DIR_ENTRY),
750 TRUE, &Context, (PVOID*)&pFatXDirEntry))
751 {
752 return STATUS_UNSUCCESSFUL;
753 }
754 RtlCopyMemory(pFatXDirEntry, &DirContext.DirEntry.FatX, sizeof(FATX_DIR_ENTRY));
755 CcSetDirtyPinnedData(Context, NULL);
756 CcUnpinData(Context);
757
758 if (MoveContext != NULL)
759 {
760 /* We're modifying an existing FCB - likely rename/move */
761 /* FIXME: check status */
762 vfatUpdateFCB(DeviceExt, *Fcb, &DirContext, ParentFcb);
763 (*Fcb)->dirIndex = DirContext.DirIndex;
764 (*Fcb)->startIndex = DirContext.StartIndex;
765 }
766 else
767 {
768 /* FIXME: check status */
769 vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb);
770 }
771
772 DPRINT("addentry ok\n");
773 return STATUS_SUCCESS;
774 }
775
776 NTSTATUS
777 VfatAddEntry(
778 IN PDEVICE_EXTENSION DeviceExt,
779 IN PUNICODE_STRING NameU,
780 IN PVFATFCB *Fcb,
781 IN PVFATFCB ParentFcb,
782 IN ULONG RequestedOptions,
783 IN UCHAR ReqAttr,
784 IN PVFAT_MOVE_CONTEXT MoveContext)
785 {
786 if (DeviceExt->Flags & VCB_IS_FATX)
787 return FATXAddEntry(DeviceExt, NameU, Fcb, ParentFcb, RequestedOptions, ReqAttr, MoveContext);
788 else
789 return FATAddEntry(DeviceExt, NameU, Fcb, ParentFcb, RequestedOptions, ReqAttr, MoveContext);
790 }
791
792 /*
793 * deleting an existing FAT entry
794 */
795 static NTSTATUS
796 FATDelEntry(
797 IN PDEVICE_EXTENSION DeviceExt,
798 IN PVFATFCB pFcb,
799 OUT PVFAT_MOVE_CONTEXT MoveContext)
800 {
801 ULONG CurrentCluster = 0, NextCluster, i;
802 PVOID Context = NULL;
803 LARGE_INTEGER Offset;
804 PFAT_DIR_ENTRY pDirEntry = NULL;
805
806 ASSERT(pFcb);
807 ASSERT(pFcb->parentFcb);
808
809 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb->PathNameU);
810 DPRINT("delete entry: %u to %u\n", pFcb->startIndex, pFcb->dirIndex);
811 Offset.u.HighPart = 0;
812 for (i = pFcb->startIndex; i <= pFcb->dirIndex; i++)
813 {
814 if (Context == NULL || ((i * sizeof(FAT_DIR_ENTRY)) % PAGE_SIZE) == 0)
815 {
816 if (Context)
817 {
818 CcSetDirtyPinnedData(Context, NULL);
819 CcUnpinData(Context);
820 }
821 Offset.u.LowPart = (i * sizeof(FAT_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
822 if (!CcPinRead(pFcb->parentFcb->FileObject, &Offset, sizeof(FAT_DIR_ENTRY), TRUE,
823 &Context, (PVOID*)&pDirEntry))
824 {
825 return STATUS_UNSUCCESSFUL;
826 }
827 }
828 pDirEntry[i % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))].Filename[0] = 0xe5;
829 if (i == pFcb->dirIndex)
830 {
831 CurrentCluster =
832 vfatDirEntryGetFirstCluster(DeviceExt,
833 (PDIR_ENTRY)&pDirEntry[i % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))]);
834 }
835 }
836 if (Context)
837 {
838 CcSetDirtyPinnedData(Context, NULL);
839 CcUnpinData(Context);
840 }
841
842 /* In case of moving, don't delete data */
843 if (MoveContext != NULL)
844 {
845 pDirEntry = &pDirEntry[pFcb->dirIndex % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))];
846 MoveContext->FirstCluster = CurrentCluster;
847 MoveContext->FileSize = pDirEntry->FileSize;
848 MoveContext->CreationTime = pDirEntry->CreationTime;
849 MoveContext->CreationDate = pDirEntry->CreationDate;
850 }
851 else
852 {
853 while (CurrentCluster && CurrentCluster != 0xffffffff)
854 {
855 GetNextCluster(DeviceExt, CurrentCluster, &NextCluster);
856 /* FIXME: check status */
857 WriteCluster(DeviceExt, CurrentCluster, 0);
858 CurrentCluster = NextCluster;
859 }
860 }
861
862 return STATUS_SUCCESS;
863 }
864
865 /*
866 * deleting an existing FAT entry
867 */
868 static NTSTATUS
869 FATXDelEntry(
870 IN PDEVICE_EXTENSION DeviceExt,
871 IN PVFATFCB pFcb,
872 OUT PVFAT_MOVE_CONTEXT MoveContext)
873 {
874 ULONG CurrentCluster = 0, NextCluster;
875 PVOID Context = NULL;
876 LARGE_INTEGER Offset;
877 PFATX_DIR_ENTRY pDirEntry;
878 ULONG StartIndex;
879
880 ASSERT(pFcb);
881 ASSERT(pFcb->parentFcb);
882 ASSERT(pFcb->Flags & FCB_IS_FATX_ENTRY);
883
884 StartIndex = pFcb->startIndex;
885
886 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb->PathNameU);
887 DPRINT("delete entry: %u\n", StartIndex);
888 Offset.u.HighPart = 0;
889 Offset.u.LowPart = (StartIndex * sizeof(FATX_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
890 if (!CcPinRead(pFcb->parentFcb->FileObject, &Offset, sizeof(FATX_DIR_ENTRY), TRUE,
891 &Context, (PVOID*)&pDirEntry))
892 {
893 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset.u.HighPart, Offset.u.LowPart, PAGE_SIZE);
894 return STATUS_UNSUCCESSFUL;
895 }
896 pDirEntry = &pDirEntry[StartIndex % (PAGE_SIZE / sizeof(FATX_DIR_ENTRY))];
897 pDirEntry->FilenameLength = 0xe5;
898 CurrentCluster = vfatDirEntryGetFirstCluster(DeviceExt,
899 (PDIR_ENTRY)pDirEntry);
900 CcSetDirtyPinnedData(Context, NULL);
901 CcUnpinData(Context);
902
903 /* In case of moving, don't delete data */
904 if (MoveContext != NULL)
905 {
906 MoveContext->FirstCluster = CurrentCluster;
907 MoveContext->FileSize = pDirEntry->FileSize;
908 MoveContext->CreationTime = pDirEntry->CreationTime;
909 MoveContext->CreationDate = pDirEntry->CreationDate;
910 }
911 else
912 {
913 while (CurrentCluster && CurrentCluster != 0xffffffff)
914 {
915 GetNextCluster(DeviceExt, CurrentCluster, &NextCluster);
916 /* FIXME: check status */
917 WriteCluster(DeviceExt, CurrentCluster, 0);
918 CurrentCluster = NextCluster;
919 }
920 }
921
922 return STATUS_SUCCESS;
923 }
924
925 NTSTATUS
926 VfatDelEntry(
927 IN PDEVICE_EXTENSION DeviceExt,
928 IN PVFATFCB pFcb,
929 OUT PVFAT_MOVE_CONTEXT MoveContext)
930 {
931 if (DeviceExt->Flags & VCB_IS_FATX)
932 return FATXDelEntry(DeviceExt, pFcb, MoveContext);
933 else
934 return FATDelEntry(DeviceExt, pFcb, MoveContext);
935 }
936
937 /*
938 * move an existing FAT entry
939 */
940 NTSTATUS
941 VfatMoveEntry(
942 IN PDEVICE_EXTENSION DeviceExt,
943 IN PVFATFCB pFcb,
944 IN PUNICODE_STRING FileName,
945 IN PVFATFCB ParentFcb)
946 {
947 NTSTATUS Status;
948 PVFATFCB OldParent;
949 VFAT_MOVE_CONTEXT MoveContext;
950
951 DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt, pFcb, FileName, ParentFcb);
952
953 /* Delete old entry while keeping data */
954 Status = VfatDelEntry(DeviceExt, pFcb, &MoveContext);
955 if (!NT_SUCCESS(Status))
956 {
957 return Status;
958 }
959
960 OldParent = pFcb->parentFcb;
961 CcPurgeCacheSection(&OldParent->SectionObjectPointers, NULL, 0, FALSE);
962 MoveContext.InPlace = (OldParent == ParentFcb);
963
964 /* Add our new entry with our cluster */
965 Status = VfatAddEntry(DeviceExt,
966 FileName,
967 &pFcb,
968 ParentFcb,
969 (vfatFCBIsDirectory(pFcb) ? FILE_DIRECTORY_FILE : 0),
970 *pFcb->Attributes,
971 &MoveContext);
972
973 CcPurgeCacheSection(&pFcb->parentFcb->SectionObjectPointers, NULL, 0, FALSE);
974
975 return Status;
976 }
977
978 /* EOF */