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