3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: services/fs/vfat/dirwr.c
6 * PURPOSE: VFAT Filesystem : write in directory
10 /* INCLUDES *****************************************************************/
12 #include <ddk/ntddk.h>
22 const char *short_illegals=" ;+=[]',\"*\\<>/?:|";
25 vfatIsShortIllegal(char c)
28 for (i = 0; short_illegals[i]; i++)
29 if (c == short_illegals[i])
35 VfatUpdateEntry (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT pFileObject)
37 * update an existing FAT entry
43 PVFATFCB pDirFcb = NULL, pFcb = NULL;
46 DPRINT ("updEntry PathFileName \'%S\'\n",
47 ((PVFATCCB)(pFileObject->FsContext2))->pFcb->PathName);
48 status = vfatGetFCBForFile(DeviceExt, &pDirFcb, &pFcb,
49 ((PVFATCCB)(pFileObject->FsContext2))->pFcb->PathName);
50 if (!NT_SUCCESS(status))
54 vfatReleaseFCB(DeviceExt, pDirFcb);
59 Offset.u.HighPart = 0;
60 Offset.u.LowPart = pFcb->dirIndex * sizeof(FATDirEntry);
61 if (CcMapData (pDirFcb->FileObject, &Offset, sizeof(FATDirEntry),
62 TRUE, &Context, (PVOID*)&Buffer))
64 memcpy(Buffer, &pFcb->entry, sizeof(FATDirEntry));
65 CcSetDirtyPinnedData(Context, NULL);
69 DPRINT1 ("Failed write to \'%S\'.\n", pDirFcb->PathName);
70 vfatReleaseFCB(DeviceExt, pDirFcb);
71 vfatReleaseFCB(DeviceExt, pFcb);
72 return STATUS_SUCCESS;
76 findDirSpace(PDEVICE_EXTENSION DeviceExt,
82 * try to find contiguous entries frees in directory,
83 * extend a directory if is neccesary
85 LARGE_INTEGER FileOffset;
86 ULONG i, count, size, nbFree = 0;
87 FATDirEntry* pFatEntry;
90 FileOffset.QuadPart = 0;
91 count = pDirFcb->RFCB.FileSize.u.LowPart / sizeof(FATDirEntry);
92 size = DeviceExt->FatInfo.BytesPerCluster / sizeof(FATDirEntry);
93 for (i = 0; i < count; i++, pFatEntry++)
95 if (Context == NULL || (i % size) == 0)
101 // FIXME: check return value
102 CcMapData (pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
103 TRUE, &Context, (PVOID*)&pFatEntry);
104 FileOffset.u.LowPart += DeviceExt->FatInfo.BytesPerCluster;
106 if (vfatIsDirEntryEndMarker(pFatEntry))
110 if (vfatIsDirEntryDeleted(pFatEntry))
118 if (nbFree == nbSlots)
125 CcUnpinData(Context);
128 if (nbFree == nbSlots)
130 // found enough contiguous free slots
131 *start = i - nbSlots + 1;
136 if (*start + nbSlots > count)
138 LARGE_INTEGER AllocationSize;
140 // extend the directory
141 if (vfatFCBIsRoot(pDirFcb) && DeviceExt->FatInfo.FatType != FAT32)
143 // We can't extend a root directory on a FAT12/FAT16 partition
146 AllocationSize.QuadPart = pDirFcb->RFCB.FileSize.u.LowPart + DeviceExt->FatInfo.BytesPerCluster;
147 Status = VfatSetAllocationSizeInformation(pDirFcb->FileObject, pDirFcb,
148 DeviceExt, &AllocationSize);
149 if (!NT_SUCCESS(Status))
153 // clear the new dir cluster
154 FileOffset.u.LowPart = pDirFcb->RFCB.FileSize.QuadPart -
155 DeviceExt->FatInfo.BytesPerCluster;
156 CcMapData (pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
157 TRUE, &Context, (PVOID*)&pFatEntry);
158 RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
160 else if (*start + nbSlots < count)
162 // clear the entry after the last new entry
163 FileOffset.u.LowPart = (*start + nbSlots) * sizeof(FATDirEntry);
164 CcMapData (pDirFcb->FileObject, &FileOffset, sizeof(FATDirEntry),
165 TRUE, &Context, (PVOID*)&pFatEntry);
166 RtlZeroMemory(pFatEntry, sizeof(FATDirEntry));
170 CcSetDirtyPinnedData(Context, NULL);
171 CcUnpinData(Context);
174 DPRINT ("nbSlots %d nbFree %d, entry number %d\n", nbSlots, nbFree, *start);
179 VfatAddEntry (PDEVICE_EXTENSION DeviceExt,
180 PFILE_OBJECT pFileObject,
181 ULONG RequestedOptions,
184 create a new FAT entry
187 WCHAR DirName[MAX_PATH], *FileName, *PathFileName;
189 PVOID Context = NULL;
190 FATDirEntry *pFatEntry, *pEntry;
192 short nbSlots = 0, nbFree = 0, j, posCar, NameLen;
194 BOOLEAN needTilde = FALSE, needLong = FALSE;
196 ULONG CurrentCluster;
197 LARGE_INTEGER SystemTime, LocalTime, FileOffset;
198 NTSTATUS Status = STATUS_SUCCESS;
203 PathFileName = pFileObject->FileName.Buffer;
204 DPRINT ("addEntry: Pathname=%S\n", PathFileName);
205 //find last \ in PathFileName
207 for (i = 0; PathFileName[i]; i++)
209 if (PathFileName[i] == L'\\')
216 return STATUS_UNSUCCESSFUL;
218 FileName = &PathFileName[posCar + 1];
219 for (NameLen = 0; FileName[NameLen]; NameLen++);
220 // extract directory name from pathname
229 memcpy (DirName, PathFileName, posCar * sizeof (WCHAR));
232 // open parent directory
233 pDirFcb = vfatGrabFCBFromTable(DeviceExt, DirName);
236 return STATUS_UNSUCCESSFUL;
238 nbSlots = (NameLen + 12) / 13 + 1; //nb of entry needed for long name+normal entry
239 DPRINT ("NameLen= %d, nbSlots =%d\n", NameLen, nbSlots);
240 Buffer = ExAllocatePool (NonPagedPool, nbSlots * sizeof (FATDirEntry));
241 RtlZeroMemory (Buffer, nbSlots * sizeof (FATDirEntry));
242 pEntry = (FATDirEntry *) (Buffer + (nbSlots - 1) * sizeof (FATDirEntry));
243 pSlots = (slot *) Buffer;
246 // find last point in name
248 for (i = 0; FileName[i]; i++)
250 if (FileName[i] == '.')
272 //copy 8 characters max
273 memset (pEntry, ' ', 11);
274 for (i = 0, j = 0; j < 8 && i < posCar; i++)
276 if (vfatIsShortIllegal (FileName[i]))
279 pEntry->Filename[j++] = '_';
283 if (FileName[i] == '.')
289 pEntry->Filename[j++] = toupper ((char) FileName[i]);
294 if (FileName[posCar])
296 for (j = 0, i = posCar + 1; FileName[i] && j < 3; i++)
298 if (vfatIsShortIllegal(FileName[i]))
301 pEntry->Ext[j++] = '_';
305 if (FileName[i] == '.')
311 pEntry->Ext[j++] = toupper ((char) (FileName[i] & 0x7F));
320 //find good value for tilde
324 DPRINT ("searching a good value for tilde\n");
325 for (posCar = 0; posCar < 8 && pEntry->Filename[posCar] != ' '; posCar++);
326 if (posCar == 0) // ??????????????????????
328 pEntry->Filename[posCar++] = '_';
335 pEntry->Filename[posCar - 2] = '~';
336 pEntry->Filename[posCar - 1] = '1';
337 vfat8Dot3ToString (pEntry->Filename, pEntry->Ext, DirName);
338 //try first with xxxxxx~y.zzz
339 for (i = 1; i < 10; i++)
341 DirName[posCar-1] = '0' + i;
342 pEntry->Filename[posCar - 1] = '0' + i;
343 Status = FindFile (DeviceExt, &FileFcb, pDirFcb, DirName, NULL, NULL);
344 if (!NT_SUCCESS(Status))
356 pEntry->Filename[posCar - 3] = '~';
357 pEntry->Filename[posCar - 2] = '1';
358 pEntry->Filename[posCar - 1] = '0';
359 vfat8Dot3ToString (pEntry->Filename, pEntry->Ext, DirName);
360 //try second with xxxxx~yy.zzz
361 for (i = 10; i < 100; i++)
363 DirName[posCar - 1] = '0' + i % 10;
364 DirName[posCar - 2] = '0' + i / 10;
365 pEntry->Filename[posCar - 1] = '0' + i % 10;
366 pEntry->Filename[posCar - 2] = '0' + i / 10;
367 Status = FindFile (DeviceExt, &FileFcb, pDirFcb, DirName, NULL, NULL);
368 if (!NT_SUCCESS(Status))
373 if (i == 100) //FIXME : what to do after 99 tilde ?
375 vfatReleaseFCB(DeviceExt, pDirFcb);
377 return STATUS_UNSUCCESSFUL;
383 DPRINT ("check if long name entry needed, needlong=%d\n", needLong);
384 for (i = 0; i < posCar; i++)
386 if ((USHORT) pEntry->Filename[i] != FileName[i])
388 DPRINT ("i=%d,%d,%d\n", i, pEntry->Filename[i], FileName[i]);
394 i++; //jump on point char
395 for (j = 0, i = posCar + 1; FileName[i] && i < posCar + 4; i++)
397 if ((USHORT) pEntry->Ext[j++] != FileName[i])
399 DPRINT ("i=%d,j=%d,%d,%d\n", i, j, pEntry->Filename[i],
406 if (needLong == FALSE)
409 memcpy (Buffer, pEntry, sizeof (FATDirEntry));
410 memset (pEntry, 0, sizeof (FATDirEntry));
411 pEntry = (FATDirEntry *) Buffer;
415 memset (DirName, 0xff, sizeof (DirName));
416 memcpy (DirName, FileName, NameLen * sizeof (WCHAR));
417 DirName[NameLen] = 0;
419 DPRINT ("dos name=%11.11s\n", pEntry->Filename);
422 pEntry->Attrib = ReqAttr;
423 if (RequestedOptions & FILE_DIRECTORY_FILE)
425 pEntry->Attrib |= FILE_ATTRIBUTE_DIRECTORY;
427 /* set dates and times */
428 KeQuerySystemTime (&SystemTime);
429 ExSystemTimeToLocalTime (&SystemTime, &LocalTime);
430 FsdFileTimeToDosDateTime ((TIME *) & LocalTime, &pEntry->CreationDate,
431 &pEntry->CreationTime);
432 pEntry->UpdateDate = pEntry->CreationDate;
433 pEntry->UpdateTime = pEntry->CreationTime;
434 pEntry->AccessDate = pEntry->CreationDate;
436 // calculate checksum for 8.3 name
437 for (pSlots[0].alias_checksum = i = 0; i < 11; i++)
439 pSlots[0].alias_checksum = (((pSlots[0].alias_checksum & 1) << 7
440 | ((pSlots[0].alias_checksum & 0xfe) >> 1))
441 + pEntry->Filename[i]);
443 //construct slots and entry
444 for (i = nbSlots - 2; i >= 0; i--)
446 DPRINT ("construct slot %d\n", i);
447 pSlots[i].attr = 0xf;
450 pSlots[i].id = nbSlots - i - 1;
454 pSlots[i].id = nbSlots - i - 1 + 0x40;
456 pSlots[i].alias_checksum = pSlots[0].alias_checksum;
457 //FIXME pSlots[i].start=;
458 memcpy (pSlots[i].name0_4, DirName + (nbSlots - i - 2) * 13, 10);
459 memcpy (pSlots[i].name5_10, DirName + (nbSlots - i - 2) * 13 + 5, 12);
460 memcpy (pSlots[i].name11_12, DirName + (nbSlots - i - 2) * 13 + 11, 4);
463 //try to find nbSlots contiguous entries frees in directory
464 if (!findDirSpace(DeviceExt, pDirFcb, nbSlots, &start))
466 vfatReleaseFCB(DeviceExt, pDirFcb);
468 return STATUS_DISK_FULL;
471 if (RequestedOptions & FILE_DIRECTORY_FILE)
473 CurrentCluster = 0xffffffff;
474 Status = NextCluster (DeviceExt, NULL, 0, &CurrentCluster, TRUE);
475 if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status))
477 vfatReleaseFCB(DeviceExt, pDirFcb);
479 if (!NT_SUCCESS(Status))
483 return STATUS_DISK_FULL;
485 if (DeviceExt->FatInfo.FatType == FAT32)
487 pEntry->FirstClusterHigh = CurrentCluster >> 16;
489 pEntry->FirstCluster = CurrentCluster;
492 size = DeviceExt->FatInfo.BytesPerCluster / sizeof(FATDirEntry);
493 FileOffset.u.HighPart = 0;
494 FileOffset.u.LowPart = start * sizeof(FATDirEntry);
495 if (start / size == (start + nbSlots - 1) / size)
499 CcMapData (pDirFcb->FileObject, &FileOffset, nbSlots * sizeof(FATDirEntry),
500 TRUE, &Context, (PVOID*)&pFatEntry);
501 memcpy(pFatEntry, Buffer, nbSlots * sizeof(FATDirEntry));
507 size = DeviceExt->FatInfo.BytesPerCluster -
508 (start * sizeof(FATDirEntry)) % DeviceExt->FatInfo.BytesPerCluster;
509 CcMapData (pDirFcb->FileObject, &FileOffset, size, TRUE,
510 &Context, (PVOID*)&pFatEntry);
511 memcpy(pFatEntry, Buffer, size);
512 CcSetDirtyPinnedData(Context, NULL);
513 CcUnpinData(Context);
514 FileOffset.u.LowPart += size;
515 CcMapData (pDirFcb->FileObject, &FileOffset,
516 nbSlots * sizeof(FATDirEntry) - size,
517 TRUE, &Context, (PVOID*)&pFatEntry);
518 memcpy(pFatEntry, (PVOID)Buffer + size, nbSlots * sizeof(FATDirEntry) - size);
520 CcSetDirtyPinnedData(Context, NULL);
521 CcUnpinData(Context);
523 // FEXME: check status
524 vfatMakeFCBFromDirEntry (DeviceExt, pDirFcb, FileName, pEntry,
525 start + nbSlots - 1, &newFCB);
526 vfatAttachFCBToFileObject (DeviceExt, newFCB, pFileObject);
528 DPRINT ("new : entry=%11.11s\n", newFCB->entry.Filename);
529 DPRINT ("new : entry=%11.11s\n", pEntry->Filename);
531 if (RequestedOptions & FILE_DIRECTORY_FILE)
533 FileOffset.QuadPart = 0;
534 CcMapData (pFileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, TRUE,
535 &Context, (PVOID*)&pFatEntry);
536 // clear the new directory cluster
537 RtlZeroMemory (pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
538 // create '.' and '..'
539 memcpy (&pFatEntry[0].Attrib, &pEntry->Attrib, sizeof(FATDirEntry) - 11);
540 memcpy (pFatEntry[0].Filename, ". ", 11);
541 memcpy (&pFatEntry[1].Attrib, &pEntry->Attrib, sizeof(FATDirEntry) - 11);
542 memcpy (pFatEntry[1].Filename, ".. ", 11);
543 pFatEntry[1].FirstCluster = pDirFcb->entry.FirstCluster;
544 pFatEntry[1].FirstClusterHigh = pDirFcb->entry.FirstClusterHigh;
545 if (DeviceExt->FatInfo.FatType == FAT32)
547 if (pFatEntry[1].FirstCluster == (DeviceExt->FatInfo.RootCluster & 0xffff) &&
548 pFatEntry[1].FirstClusterHigh == (DeviceExt->FatInfo.RootCluster >> 16))
550 pFatEntry[1].FirstCluster = 0;
551 pFatEntry[1].FirstClusterHigh = 0;
556 if (pFatEntry[1].FirstCluster == 1)
558 pFatEntry[1].FirstCluster = 0;
561 CcSetDirtyPinnedData(Context, NULL);
562 CcUnpinData(Context);
564 vfatReleaseFCB (DeviceExt, pDirFcb);
566 DPRINT ("addentry ok\n");
567 return STATUS_SUCCESS;
571 delEntry (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT pFileObject)
573 * deleting an existing FAT entry
577 PVFATFCB pFcb = NULL, pDirFcb = NULL;
580 ULONG Entry = 0, startEntry, Read, CurrentCluster, NextCluster, i;
581 FATDirEntry DirEntry;
583 DPRINT ("delEntry PathFileName \'%S\'\n", pFileObject->FileName.Buffer);
585 status = vfatGetFCBForFile(DeviceExt, &pDirFcb, &pFcb,
586 pFileObject->FileName.Buffer);
589 vfatReleaseFCB(DeviceExt, pFcb);
591 if (!NT_SUCCESS(status))
595 vfatReleaseFCB(DeviceExt, pDirFcb);
599 pName = ((PVFATCCB)(pFileObject->FsContext2))->pFcb->ObjectName;
604 status = FindFile (DeviceExt, &Fcb, pDirFcb, pName, &Entry, &startEntry);
606 if (NT_SUCCESS(status))
608 PVOID Context = NULL;
609 LARGE_INTEGER Offset;
610 FATDirEntry* pDirEntry;
611 DPRINT ("delete entry: %d to %d\n", startEntry, Entry);
612 Offset.u.HighPart = 0;
613 for (i = startEntry; i <= Entry; i++)
615 if (Context == NULL || ((i * sizeof(FATDirEntry)) % PAGE_SIZE) == 0)
619 CcSetDirtyPinnedData(Context, NULL);
620 CcUnpinData(Context);
622 Offset.u.LowPart = (i * sizeof(FATDirEntry) / PAGE_SIZE) * PAGE_SIZE;
623 CcMapData (pDirFcb->FileObject, &Offset, PAGE_SIZE, TRUE,
624 &Context, (PVOID*)&pDirEntry);
626 pDirEntry[i % (PAGE_SIZE / sizeof(FATDirEntry))].Filename[0] = 0xe5;
630 vfatDirEntryGetFirstCluster (DeviceExt,
631 &pDirEntry[i % (PAGE_SIZE / sizeof(FATDirEntry))]);
636 CcSetDirtyPinnedData(Context, NULL);
637 CcUnpinData(Context);
640 while (CurrentCluster && CurrentCluster != 0xffffffff)
642 GetNextCluster (DeviceExt, CurrentCluster, &NextCluster, FALSE);
643 // FIXME: check status
644 WriteCluster(DeviceExt, CurrentCluster, 0);
645 CurrentCluster = NextCluster;
648 vfatReleaseFCB(DeviceExt, pDirFcb);