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