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