* Sync up to trunk HEAD (r62502).
[reactos.git] / drivers / filesystems / fastfat / dirwr.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/fs/vfat/dirwr.c
5 * PURPOSE: VFAT Filesystem : write in directory
6 *
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include "vfat.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /*
17 * update an existing FAT entry
18 */
19 NTSTATUS
20 VfatUpdateEntry(
21 IN PVFATFCB pFcb)
22 {
23 PVOID Context;
24 PDIR_ENTRY PinEntry;
25 LARGE_INTEGER Offset;
26 ULONG SizeDirEntry;
27 ULONG dirIndex;
28
29 ASSERT(pFcb);
30
31 if (pFcb->Flags & FCB_IS_FATX_ENTRY)
32 {
33 SizeDirEntry = sizeof(FATX_DIR_ENTRY);
34 dirIndex = pFcb->startIndex;
35 }
36 else
37 {
38 SizeDirEntry = sizeof(FAT_DIR_ENTRY);
39 dirIndex = pFcb->dirIndex;
40 }
41
42 DPRINT("updEntry dirIndex %u, PathName \'%wZ\'\n", dirIndex, &pFcb->PathNameU);
43
44 if (vfatFCBIsRoot(pFcb) || (pFcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)))
45 {
46 return STATUS_SUCCESS;
47 }
48
49 ASSERT(pFcb->parentFcb);
50
51 Offset.u.HighPart = 0;
52 Offset.u.LowPart = dirIndex * SizeDirEntry;
53 if (CcPinRead(pFcb->parentFcb->FileObject, &Offset, SizeDirEntry,
54 TRUE, &Context, (PVOID*)&PinEntry))
55 {
56 pFcb->Flags &= ~FCB_IS_DIRTY;
57 RtlCopyMemory(PinEntry, &pFcb->entry, SizeDirEntry);
58 CcSetDirtyPinnedData(Context, NULL);
59 CcUnpinData(Context);
60 return STATUS_SUCCESS;
61 }
62 else
63 {
64 DPRINT1("Failed write to \'%wZ\'.\n", &pFcb->parentFcb->PathNameU);
65 return STATUS_UNSUCCESSFUL;
66 }
67 }
68
69 /*
70 * try to find contiguous entries frees in directory,
71 * extend a directory if is neccesary
72 */
73 BOOLEAN
74 vfatFindDirSpace(
75 IN PDEVICE_EXTENSION DeviceExt,
76 IN PVFATFCB pDirFcb,
77 IN ULONG nbSlots,
78 OUT PULONG start)
79 {
80 LARGE_INTEGER FileOffset;
81 ULONG i, count, size, nbFree = 0;
82 PDIR_ENTRY pFatEntry = NULL;
83 PVOID Context = NULL;
84 NTSTATUS Status;
85 ULONG SizeDirEntry;
86 FileOffset.QuadPart = 0;
87
88 if (DeviceExt->Flags & VCB_IS_FATX)
89 SizeDirEntry = sizeof(FATX_DIR_ENTRY);
90 else
91 SizeDirEntry = sizeof(FAT_DIR_ENTRY);
92
93 count = pDirFcb->RFCB.FileSize.u.LowPart / SizeDirEntry;
94 size = DeviceExt->FatInfo.BytesPerCluster / SizeDirEntry;
95 for (i = 0; i < count; i++, pFatEntry = (PDIR_ENTRY)((ULONG_PTR)pFatEntry + SizeDirEntry))
96 {
97 if (Context == NULL || (i % size) == 0)
98 {
99 if (Context)
100 {
101 CcUnpinData(Context);
102 }
103 if (!CcPinRead(pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
104 TRUE, &Context, (PVOID*)&pFatEntry))
105 {
106 return FALSE;
107 }
108 FileOffset.u.LowPart += DeviceExt->FatInfo.BytesPerCluster;
109 }
110 if (ENTRY_END(DeviceExt, pFatEntry))
111 {
112 break;
113 }
114 if (ENTRY_DELETED(DeviceExt, pFatEntry))
115 {
116 nbFree++;
117 }
118 else
119 {
120 nbFree = 0;
121 }
122 if (nbFree == nbSlots)
123 {
124 break;
125 }
126 }
127 if (Context)
128 {
129 CcUnpinData(Context);
130 Context = NULL;
131 }
132 if (nbFree == nbSlots)
133 {
134 /* found enough contiguous free slots */
135 *start = i - nbSlots + 1;
136 }
137 else
138 {
139 *start = i - nbFree;
140 if (*start + nbSlots > count)
141 {
142 LARGE_INTEGER AllocationSize;
143 /* extend the directory */
144 if (vfatFCBIsRoot(pDirFcb) && DeviceExt->FatInfo.FatType != FAT32)
145 {
146 /* We can't extend a root directory on a FAT12/FAT16/FATX partition */
147 return FALSE;
148 }
149 AllocationSize.QuadPart = pDirFcb->RFCB.FileSize.u.LowPart + DeviceExt->FatInfo.BytesPerCluster;
150 Status = VfatSetAllocationSizeInformation(pDirFcb->FileObject, pDirFcb,
151 DeviceExt, &AllocationSize);
152 if (!NT_SUCCESS(Status))
153 {
154 return FALSE;
155 }
156 /* clear the new dir cluster */
157 FileOffset.u.LowPart = (ULONG)(pDirFcb->RFCB.FileSize.QuadPart -
158 DeviceExt->FatInfo.BytesPerCluster);
159 if (!CcPinRead(pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
160 TRUE, &Context, (PVOID*)&pFatEntry))
161 {
162 return FALSE;
163 }
164 if (DeviceExt->Flags & VCB_IS_FATX)
165 memset(pFatEntry, 0xff, DeviceExt->FatInfo.BytesPerCluster);
166 else
167 RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
168 }
169 else if (*start + nbSlots < count)
170 {
171 /* clear the entry after the last new entry */
172 FileOffset.u.LowPart = (*start + nbSlots) * SizeDirEntry;
173 if (!CcPinRead(pDirFcb->FileObject, &FileOffset, SizeDirEntry,
174 TRUE, &Context, (PVOID*)&pFatEntry))
175 {
176 return FALSE;
177 }
178 if (DeviceExt->Flags & VCB_IS_FATX)
179 memset(pFatEntry, 0xff, SizeDirEntry);
180 else
181 RtlZeroMemory(pFatEntry, SizeDirEntry);
182 }
183 if (Context)
184 {
185 CcSetDirtyPinnedData(Context, NULL);
186 CcUnpinData(Context);
187 }
188 }
189 DPRINT("nbSlots %u nbFree %u, entry number %u\n", nbSlots, nbFree, *start);
190 return TRUE;
191 }
192
193 /*
194 create a new FAT entry
195 */
196 static NTSTATUS
197 FATAddEntry(
198 IN PDEVICE_EXTENSION DeviceExt,
199 IN PUNICODE_STRING NameU,
200 IN PVFATFCB* Fcb,
201 IN PVFATFCB ParentFcb,
202 IN ULONG RequestedOptions,
203 IN UCHAR ReqAttr)
204 {
205 PVOID Context = NULL;
206 PFAT_DIR_ENTRY pFatEntry;
207 slot *pSlots;
208 USHORT nbSlots = 0, j, posCar;
209 PUCHAR Buffer;
210 BOOLEAN needTilde = FALSE, needLong = FALSE;
211 BOOLEAN lCaseBase = FALSE, uCaseBase, lCaseExt = FALSE, uCaseExt;
212 ULONG CurrentCluster;
213 LARGE_INTEGER SystemTime, FileOffset;
214 NTSTATUS Status = STATUS_SUCCESS;
215 ULONG size;
216 long i;
217
218 OEM_STRING NameA;
219 CHAR aName[13];
220 BOOLEAN IsNameLegal;
221 BOOLEAN SpacesFound;
222
223 VFAT_DIRENTRY_CONTEXT DirContext;
224 WCHAR LongNameBuffer[LONGNAME_MAX_LENGTH + 1];
225 WCHAR ShortNameBuffer[13];
226
227 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU, &ParentFcb->PathNameU);
228
229 DirContext.LongNameU = *NameU;
230
231 /* nb of entry needed for long name+normal entry */
232 nbSlots = (DirContext.LongNameU.Length / sizeof(WCHAR) + 12) / 13 + 1;
233 DPRINT("NameLen= %u, nbSlots =%u\n", DirContext.LongNameU.Length / sizeof(WCHAR), nbSlots);
234 Buffer = ExAllocatePoolWithTag(NonPagedPool, (nbSlots - 1) * sizeof(FAT_DIR_ENTRY), TAG_VFAT);
235 if (Buffer == NULL)
236 {
237 return STATUS_INSUFFICIENT_RESOURCES;
238 }
239 RtlZeroMemory(Buffer, (nbSlots - 1) * sizeof(FAT_DIR_ENTRY));
240 pSlots = (slot *) Buffer;
241
242 NameA.Buffer = aName;
243 NameA.Length = 0;
244 NameA.MaximumLength = sizeof(aName);
245
246 DirContext.ShortNameU.Buffer = ShortNameBuffer;
247 DirContext.ShortNameU.Length = 0;
248 DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer);
249
250 RtlZeroMemory(&DirContext.DirEntry.Fat, sizeof(FAT_DIR_ENTRY));
251
252 IsNameLegal = RtlIsNameLegalDOS8Dot3(&DirContext.LongNameU, &NameA, &SpacesFound);
253
254 if (!IsNameLegal || SpacesFound)
255 {
256 GENERATE_NAME_CONTEXT NameContext;
257 VFAT_DIRENTRY_CONTEXT SearchContext;
258 WCHAR ShortSearchName[13];
259 needTilde = TRUE;
260 needLong = TRUE;
261 RtlZeroMemory(&NameContext, sizeof(GENERATE_NAME_CONTEXT));
262 SearchContext.LongNameU.Buffer = LongNameBuffer;
263 SearchContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
264 SearchContext.ShortNameU.Buffer = ShortSearchName;
265 SearchContext.ShortNameU.MaximumLength = sizeof(ShortSearchName);
266
267 for (i = 0; i < 100; i++)
268 {
269 RtlGenerate8dot3Name(&DirContext.LongNameU, FALSE, &NameContext, &DirContext.ShortNameU);
270 DirContext.ShortNameU.Buffer[DirContext.ShortNameU.Length / sizeof(WCHAR)] = 0;
271 SearchContext.DirIndex = 0;
272 Status = FindFile(DeviceExt, ParentFcb, &DirContext.ShortNameU, &SearchContext, TRUE);
273 if (!NT_SUCCESS(Status))
274 {
275 break;
276 }
277 }
278 if (i == 100) /* FIXME : what to do after this ? */
279 {
280 ExFreePoolWithTag(Buffer, TAG_VFAT);
281 return STATUS_UNSUCCESSFUL;
282 }
283 IsNameLegal = RtlIsNameLegalDOS8Dot3(&DirContext.ShortNameU, &NameA, &SpacesFound);
284 aName[NameA.Length]=0;
285 }
286 else
287 {
288 aName[NameA.Length] = 0;
289 for (posCar = 0; posCar < DirContext.LongNameU.Length / sizeof(WCHAR); posCar++)
290 {
291 if (DirContext.LongNameU.Buffer[posCar] == L'.')
292 {
293 break;
294 }
295 }
296 /* check if the name and the extension contains upper case characters */
297 RtlDowncaseUnicodeString(&DirContext.ShortNameU, &DirContext.LongNameU, FALSE);
298 DirContext.ShortNameU.Buffer[DirContext.ShortNameU.Length / sizeof(WCHAR)] = 0;
299 uCaseBase = wcsncmp(DirContext.LongNameU.Buffer,
300 DirContext.ShortNameU.Buffer, posCar) ? TRUE : FALSE;
301 if (posCar < DirContext.LongNameU.Length/sizeof(WCHAR))
302 {
303 i = DirContext.LongNameU.Length / sizeof(WCHAR) - posCar;
304 uCaseExt = wcsncmp(DirContext.LongNameU.Buffer + posCar,
305 DirContext.ShortNameU.Buffer + posCar, i) ? TRUE : FALSE;
306 }
307 else
308 {
309 uCaseExt = FALSE;
310 }
311 /* check if the name and the extension contains lower case characters */
312 RtlUpcaseUnicodeString(&DirContext.ShortNameU, &DirContext.LongNameU, FALSE);
313 DirContext.ShortNameU.Buffer[DirContext.ShortNameU.Length / sizeof(WCHAR)] = 0;
314 lCaseBase = wcsncmp(DirContext.LongNameU.Buffer,
315 DirContext.ShortNameU.Buffer, posCar) ? TRUE : FALSE;
316 if (posCar < DirContext.LongNameU.Length / sizeof(WCHAR))
317 {
318 i = DirContext.LongNameU.Length / sizeof(WCHAR) - posCar;
319 lCaseExt = wcsncmp(DirContext.LongNameU.Buffer + posCar,
320 DirContext.ShortNameU.Buffer + posCar, i) ? TRUE : FALSE;
321 }
322 else
323 {
324 lCaseExt = FALSE;
325 }
326 if ((lCaseBase && uCaseBase) || (lCaseExt && uCaseExt))
327 {
328 needLong = TRUE;
329 }
330 }
331 DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
332 aName, &DirContext.LongNameU, needTilde, needLong);
333 memset(DirContext.DirEntry.Fat.ShortName, ' ', 11);
334 for (i = 0; i < 8 && aName[i] && aName[i] != '.'; i++)
335 {
336 DirContext.DirEntry.Fat.Filename[i] = aName[i];
337 }
338 if (aName[i] == '.')
339 {
340 i++;
341 for (j = 0; j < 3 && aName[i]; j++, i++)
342 {
343 DirContext.DirEntry.Fat.Ext[j] = aName[i];
344 }
345 }
346 if (DirContext.DirEntry.Fat.Filename[0] == 0xe5)
347 {
348 DirContext.DirEntry.Fat.Filename[0] = 0x05;
349 }
350
351 if (needLong)
352 {
353 RtlCopyMemory(LongNameBuffer, DirContext.LongNameU.Buffer, DirContext.LongNameU.Length);
354 DirContext.LongNameU.Buffer = LongNameBuffer;
355 DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
356 DirContext.LongNameU.Buffer[DirContext.LongNameU.Length / sizeof(WCHAR)] = 0;
357 memset(DirContext.LongNameU.Buffer + DirContext.LongNameU.Length / sizeof(WCHAR) + 1, 0xff,
358 DirContext.LongNameU.MaximumLength - DirContext.LongNameU.Length - sizeof(WCHAR));
359 }
360 else
361 {
362 nbSlots = 1;
363 if (lCaseBase)
364 {
365 DirContext.DirEntry.Fat.lCase |= VFAT_CASE_LOWER_BASE;
366 }
367 if (lCaseExt)
368 {
369 DirContext.DirEntry.Fat.lCase |= VFAT_CASE_LOWER_EXT;
370 }
371 }
372
373 DPRINT ("dos name=%11.11s\n", DirContext.DirEntry.Fat.Filename);
374
375 /* set attributes */
376 DirContext.DirEntry.Fat.Attrib = ReqAttr;
377 if (RequestedOptions & FILE_DIRECTORY_FILE)
378 {
379 DirContext.DirEntry.Fat.Attrib |= FILE_ATTRIBUTE_DIRECTORY;
380 }
381 /* set dates and times */
382 KeQuerySystemTime(&SystemTime);
383 FsdSystemTimeToDosDateTime(DeviceExt, &SystemTime, &DirContext.DirEntry.Fat.CreationDate,
384 &DirContext.DirEntry.Fat.CreationTime);
385 DirContext.DirEntry.Fat.UpdateDate = DirContext.DirEntry.Fat.CreationDate;
386 DirContext.DirEntry.Fat.UpdateTime = DirContext.DirEntry.Fat.CreationTime;
387 DirContext.DirEntry.Fat.AccessDate = DirContext.DirEntry.Fat.CreationDate;
388
389 if (needLong)
390 {
391 /* calculate checksum for 8.3 name */
392 for (pSlots[0].alias_checksum = 0, i = 0; i < 11; i++)
393 {
394 pSlots[0].alias_checksum = (((pSlots[0].alias_checksum & 1) << 7
395 | ((pSlots[0].alias_checksum & 0xfe) >> 1))
396 + DirContext.DirEntry.Fat.ShortName[i]);
397 }
398 /* construct slots and entry */
399 for (i = nbSlots - 2; i >= 0; i--)
400 {
401 DPRINT("construct slot %d\n", i);
402 pSlots[i].attr = 0xf;
403 if (i)
404 {
405 pSlots[i].id = (unsigned char)(nbSlots - i - 1);
406 }
407 else
408 {
409 pSlots[i].id = (unsigned char)(nbSlots - i - 1 + 0x40);
410 }
411 pSlots[i].alias_checksum = pSlots[0].alias_checksum;
412 RtlCopyMemory(pSlots[i].name0_4, DirContext.LongNameU.Buffer + (nbSlots - i - 2) * 13, 10);
413 RtlCopyMemory(pSlots[i].name5_10, DirContext.LongNameU.Buffer + (nbSlots - i - 2) * 13 + 5, 12);
414 RtlCopyMemory(pSlots[i].name11_12, DirContext.LongNameU.Buffer + (nbSlots - i - 2) * 13 + 11, 4);
415 }
416 }
417 /* try to find nbSlots contiguous entries frees in directory */
418 if (!vfatFindDirSpace(DeviceExt, ParentFcb, nbSlots, &DirContext.StartIndex))
419 {
420 ExFreePoolWithTag(Buffer, TAG_VFAT);
421 return STATUS_DISK_FULL;
422 }
423 DirContext.DirIndex = DirContext.StartIndex + nbSlots - 1;
424 if (RequestedOptions & FILE_DIRECTORY_FILE)
425 {
426 CurrentCluster = 0;
427 Status = NextCluster(DeviceExt, 0, &CurrentCluster, TRUE);
428 if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status))
429 {
430 ExFreePoolWithTag(Buffer, TAG_VFAT);
431 if (!NT_SUCCESS(Status))
432 {
433 return Status;
434 }
435 return STATUS_DISK_FULL;
436 }
437 if (DeviceExt->FatInfo.FatType == FAT32)
438 {
439 DirContext.DirEntry.Fat.FirstClusterHigh = (unsigned short)(CurrentCluster >> 16);
440 }
441 DirContext.DirEntry.Fat.FirstCluster = (unsigned short)CurrentCluster;
442 }
443
444 i = DeviceExt->FatInfo.BytesPerCluster / sizeof(FAT_DIR_ENTRY);
445 FileOffset.u.HighPart = 0;
446 FileOffset.u.LowPart = DirContext.StartIndex * sizeof(FAT_DIR_ENTRY);
447 if (DirContext.StartIndex / i == DirContext.DirIndex / i)
448 {
449 /* one cluster */
450 if (!CcPinRead(ParentFcb->FileObject, &FileOffset, nbSlots * sizeof(FAT_DIR_ENTRY),
451 TRUE, &Context, (PVOID*)&pFatEntry))
452 {
453 ExFreePoolWithTag(Buffer, TAG_VFAT);
454 return STATUS_UNSUCCESSFUL;
455 }
456 if (nbSlots > 1)
457 {
458 RtlCopyMemory(pFatEntry, Buffer, (nbSlots - 1) * sizeof(FAT_DIR_ENTRY));
459 }
460 RtlCopyMemory(pFatEntry + (nbSlots - 1), &DirContext.DirEntry.Fat, sizeof(FAT_DIR_ENTRY));
461 }
462 else
463 {
464 /* two clusters */
465 size = DeviceExt->FatInfo.BytesPerCluster -
466 (DirContext.StartIndex * sizeof(FAT_DIR_ENTRY)) % DeviceExt->FatInfo.BytesPerCluster;
467 i = size / sizeof(FAT_DIR_ENTRY);
468 if (!CcPinRead(ParentFcb->FileObject, &FileOffset, size, TRUE,
469 &Context, (PVOID*)&pFatEntry))
470 {
471 ExFreePoolWithTag(Buffer, TAG_VFAT);
472 return STATUS_UNSUCCESSFUL;
473 }
474 RtlCopyMemory(pFatEntry, Buffer, size);
475 CcSetDirtyPinnedData(Context, NULL);
476 CcUnpinData(Context);
477 FileOffset.u.LowPart += size;
478 if (!CcPinRead(ParentFcb->FileObject, &FileOffset,
479 nbSlots * sizeof(FAT_DIR_ENTRY) - size,
480 TRUE, &Context, (PVOID*)&pFatEntry))
481 {
482 ExFreePoolWithTag(Buffer, TAG_VFAT);
483 return STATUS_UNSUCCESSFUL;
484 }
485 if (nbSlots - 1 > i)
486 {
487 RtlCopyMemory(pFatEntry, (PVOID)(Buffer + size), (nbSlots - 1 - i) * sizeof(FAT_DIR_ENTRY));
488 }
489 RtlCopyMemory(pFatEntry + nbSlots - 1 - i, &DirContext.DirEntry.Fat, sizeof(FAT_DIR_ENTRY));
490 }
491 CcSetDirtyPinnedData(Context, NULL);
492 CcUnpinData(Context);
493
494 Status = vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb);
495 if (!NT_SUCCESS(Status))
496 {
497 ExFreePoolWithTag(Buffer, TAG_VFAT);
498 return Status;
499 }
500
501 DPRINT("new : entry=%11.11s\n", (*Fcb)->entry.Fat.Filename);
502 DPRINT("new : entry=%11.11s\n", DirContext.DirEntry.Fat.Filename);
503
504 if (RequestedOptions & FILE_DIRECTORY_FILE)
505 {
506 FileOffset.QuadPart = 0;
507 if (!CcPinRead((*Fcb)->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, TRUE,
508 &Context, (PVOID*)&pFatEntry))
509 {
510 ExFreePoolWithTag(Buffer, TAG_VFAT);
511 return STATUS_UNSUCCESSFUL;
512 }
513 /* clear the new directory cluster */
514 RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
515 /* create '.' and '..' */
516 RtlCopyMemory(&pFatEntry[0].Attrib, &DirContext.DirEntry.Fat.Attrib, sizeof(FAT_DIR_ENTRY) - 11);
517 RtlCopyMemory(pFatEntry[0].ShortName, ". ", 11);
518 RtlCopyMemory(&pFatEntry[1].Attrib, &DirContext.DirEntry.Fat.Attrib, sizeof(FAT_DIR_ENTRY) - 11);
519 RtlCopyMemory(pFatEntry[1].ShortName, ".. ", 11);
520 pFatEntry[1].FirstCluster = ParentFcb->entry.Fat.FirstCluster;
521 pFatEntry[1].FirstClusterHigh = ParentFcb->entry.Fat.FirstClusterHigh;
522 if (vfatFCBIsRoot(ParentFcb))
523 {
524 pFatEntry[1].FirstCluster = 0;
525 pFatEntry[1].FirstClusterHigh = 0;
526 }
527 CcSetDirtyPinnedData(Context, NULL);
528 CcUnpinData(Context);
529 }
530 ExFreePoolWithTag(Buffer, TAG_VFAT);
531 DPRINT("addentry ok\n");
532 return STATUS_SUCCESS;
533 }
534
535 /*
536 create a new FAT entry
537 */
538 static NTSTATUS
539 FATXAddEntry(
540 IN PDEVICE_EXTENSION DeviceExt,
541 IN PUNICODE_STRING NameU,
542 IN PVFATFCB* Fcb,
543 IN PVFATFCB ParentFcb,
544 IN ULONG RequestedOptions,
545 IN UCHAR ReqAttr)
546 {
547 PVOID Context = NULL;
548 LARGE_INTEGER SystemTime, FileOffset;
549 OEM_STRING NameA;
550 VFAT_DIRENTRY_CONTEXT DirContext;
551 PFATX_DIR_ENTRY pFatXDirEntry;
552 ULONG Index;
553
554 DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU, &ParentFcb->PathNameU);
555
556 DirContext.LongNameU = *NameU;
557
558 if (DirContext.LongNameU.Length / sizeof(WCHAR) > 42)
559 {
560 /* name too long */
561 return STATUS_NAME_TOO_LONG;
562 }
563
564 /* try to find 1 entry free in directory */
565 if (!vfatFindDirSpace(DeviceExt, ParentFcb, 1, &DirContext.StartIndex))
566 {
567 return STATUS_DISK_FULL;
568 }
569 Index = DirContext.DirIndex = DirContext.StartIndex;
570 if (!vfatFCBIsRoot(ParentFcb))
571 {
572 DirContext.DirIndex += 2;
573 DirContext.StartIndex += 2;
574 }
575
576 DirContext.ShortNameU.Buffer = 0;
577 DirContext.ShortNameU.Length = 0;
578 DirContext.ShortNameU.MaximumLength = 0;
579 RtlZeroMemory(&DirContext.DirEntry.FatX, sizeof(FATX_DIR_ENTRY));
580 memset(DirContext.DirEntry.FatX.Filename, 0xff, 42);
581 DirContext.DirEntry.FatX.FirstCluster = 0;
582 DirContext.DirEntry.FatX.FileSize = 0;
583
584 /* set file name */
585 NameA.Buffer = (PCHAR)DirContext.DirEntry.FatX.Filename;
586 NameA.Length = 0;
587 NameA.MaximumLength = 42;
588 RtlUnicodeStringToOemString(&NameA, &DirContext.LongNameU, FALSE);
589 DirContext.DirEntry.FatX.FilenameLength = (unsigned char)NameA.Length;
590
591 /* set attributes */
592 DirContext.DirEntry.FatX.Attrib = ReqAttr;
593 if (RequestedOptions & FILE_DIRECTORY_FILE)
594 {
595 DirContext.DirEntry.FatX.Attrib |= FILE_ATTRIBUTE_DIRECTORY;
596 }
597
598 /* set dates and times */
599 KeQuerySystemTime(&SystemTime);
600 FsdSystemTimeToDosDateTime(DeviceExt, &SystemTime, &DirContext.DirEntry.FatX.CreationDate,
601 &DirContext.DirEntry.FatX.CreationTime);
602 DirContext.DirEntry.FatX.UpdateDate = DirContext.DirEntry.FatX.CreationDate;
603 DirContext.DirEntry.FatX.UpdateTime = DirContext.DirEntry.FatX.CreationTime;
604 DirContext.DirEntry.FatX.AccessDate = DirContext.DirEntry.FatX.CreationDate;
605 DirContext.DirEntry.FatX.AccessTime = DirContext.DirEntry.FatX.CreationTime;
606
607 /* add entry into parent directory */
608 FileOffset.u.HighPart = 0;
609 FileOffset.u.LowPart = Index * sizeof(FATX_DIR_ENTRY);
610 if (!CcPinRead(ParentFcb->FileObject, &FileOffset, sizeof(FATX_DIR_ENTRY),
611 TRUE, &Context, (PVOID*)&pFatXDirEntry))
612 {
613 return STATUS_UNSUCCESSFUL;
614 }
615 RtlCopyMemory(pFatXDirEntry, &DirContext.DirEntry.FatX, sizeof(FATX_DIR_ENTRY));
616 CcSetDirtyPinnedData(Context, NULL);
617 CcUnpinData(Context);
618
619 /* FIXME: check status */
620 vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb);
621
622 DPRINT("addentry ok\n");
623 return STATUS_SUCCESS;
624 }
625
626 NTSTATUS
627 VfatAddEntry(
628 IN PDEVICE_EXTENSION DeviceExt,
629 IN PUNICODE_STRING NameU,
630 IN PVFATFCB *Fcb,
631 IN PVFATFCB ParentFcb,
632 IN ULONG RequestedOptions,
633 IN UCHAR ReqAttr)
634 {
635 if (DeviceExt->Flags & VCB_IS_FATX)
636 return FATXAddEntry(DeviceExt, NameU, Fcb, ParentFcb, RequestedOptions, ReqAttr);
637 else
638 return FATAddEntry(DeviceExt, NameU, Fcb, ParentFcb, RequestedOptions, ReqAttr);
639 }
640
641 /*
642 * deleting an existing FAT entry
643 */
644 static NTSTATUS
645 FATDelEntry(
646 IN PDEVICE_EXTENSION DeviceExt,
647 IN PVFATFCB pFcb)
648 {
649 ULONG CurrentCluster = 0, NextCluster, i;
650 PVOID Context = NULL;
651 LARGE_INTEGER Offset;
652 PFAT_DIR_ENTRY pDirEntry = NULL;
653
654 ASSERT(pFcb);
655 ASSERT(pFcb->parentFcb);
656
657 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb->PathNameU);
658 DPRINT("delete entry: %u to %u\n", pFcb->startIndex, pFcb->dirIndex);
659 Offset.u.HighPart = 0;
660 for (i = pFcb->startIndex; i <= pFcb->dirIndex; i++)
661 {
662 if (Context == NULL || ((i * sizeof(FAT_DIR_ENTRY)) % PAGE_SIZE) == 0)
663 {
664 if (Context)
665 {
666 CcSetDirtyPinnedData(Context, NULL);
667 CcUnpinData(Context);
668 }
669 Offset.u.LowPart = (i * sizeof(FAT_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
670 if (!CcPinRead(pFcb->parentFcb->FileObject, &Offset, sizeof(FAT_DIR_ENTRY), TRUE,
671 &Context, (PVOID*)&pDirEntry))
672 {
673 return STATUS_UNSUCCESSFUL;
674 }
675 }
676 pDirEntry[i % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))].Filename[0] = 0xe5;
677 if (i == pFcb->dirIndex)
678 {
679 CurrentCluster =
680 vfatDirEntryGetFirstCluster(DeviceExt,
681 (PDIR_ENTRY)&pDirEntry[i % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))]);
682 }
683 }
684 if (Context)
685 {
686 CcSetDirtyPinnedData(Context, NULL);
687 CcUnpinData(Context);
688 }
689
690 while (CurrentCluster && CurrentCluster != 0xffffffff)
691 {
692 GetNextCluster(DeviceExt, CurrentCluster, &NextCluster);
693 /* FIXME: check status */
694 WriteCluster(DeviceExt, CurrentCluster, 0);
695 CurrentCluster = NextCluster;
696 }
697 return STATUS_SUCCESS;
698 }
699
700 /*
701 * deleting an existing FAT entry
702 */
703 static NTSTATUS
704 FATXDelEntry(
705 IN PDEVICE_EXTENSION DeviceExt,
706 IN PVFATFCB pFcb)
707 {
708 ULONG CurrentCluster = 0, NextCluster;
709 PVOID Context = NULL;
710 LARGE_INTEGER Offset;
711 PFATX_DIR_ENTRY pDirEntry;
712 ULONG StartIndex;
713
714 ASSERT(pFcb);
715 ASSERT(pFcb->parentFcb);
716 ASSERT(pFcb->Flags & FCB_IS_FATX_ENTRY);
717
718 StartIndex = pFcb->startIndex;
719
720 DPRINT("delEntry PathName \'%wZ\'\n", &pFcb->PathNameU);
721 DPRINT("delete entry: %u\n", StartIndex);
722 Offset.u.HighPart = 0;
723 Offset.u.LowPart = (StartIndex * sizeof(FATX_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
724 if (!CcPinRead(pFcb->parentFcb->FileObject, &Offset, sizeof(FATX_DIR_ENTRY), TRUE,
725 &Context, (PVOID*)&pDirEntry))
726 {
727 DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset.u.HighPart, Offset.u.LowPart, PAGE_SIZE);
728 return STATUS_UNSUCCESSFUL;
729 }
730 pDirEntry = &pDirEntry[StartIndex % (PAGE_SIZE / sizeof(FATX_DIR_ENTRY))];
731 pDirEntry->FilenameLength = 0xe5;
732 CurrentCluster = vfatDirEntryGetFirstCluster(DeviceExt,
733 (PDIR_ENTRY)pDirEntry);
734 CcSetDirtyPinnedData(Context, NULL);
735 CcUnpinData(Context);
736
737 while (CurrentCluster && CurrentCluster != 0xffffffff)
738 {
739 GetNextCluster(DeviceExt, CurrentCluster, &NextCluster);
740 /* FIXME: check status */
741 WriteCluster(DeviceExt, CurrentCluster, 0);
742 CurrentCluster = NextCluster;
743 }
744 return STATUS_SUCCESS;
745 }
746
747 NTSTATUS
748 VfatDelEntry(
749 IN PDEVICE_EXTENSION DeviceExt,
750 IN PVFATFCB pFcb)
751 {
752 if (DeviceExt->Flags & VCB_IS_FATX)
753 return FATXDelEntry(DeviceExt, pFcb);
754 else
755 return FATDelEntry(DeviceExt, pFcb);
756 }
757
758 /* EOF */