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