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
42 PVFATFCB pDirFcb, pFcb;
46 DPRINT ("updEntry PathFileName \'%S\'\n",
47 ((PVFATCCB)(pFileObject->FsContext2))->pFcb->PathName);
50 pFcb = (PVFATFCB)pFileObject->FsContext;
52 pDirFcb = pFcb->parentFcb;
55 Offset.u.HighPart = 0;
56 Offset.u.LowPart = pFcb->dirIndex * sizeof(FATDirEntry);
57 if (CcMapData (pDirFcb->FileObject, &Offset, sizeof(FATDirEntry),
58 TRUE, &Context, (PVOID*)&Buffer))
60 memcpy(Buffer, &pFcb->entry, sizeof(FATDirEntry));
61 CcSetDirtyPinnedData(Context, NULL);
65 DPRINT1 ("Failed write to \'%S\'.\n", pDirFcb->PathName);
67 return STATUS_SUCCESS;
71 findDirSpace(PDEVICE_EXTENSION DeviceExt,
77 * try to find contiguous entries frees in directory,
78 * extend a directory if is neccesary
80 LARGE_INTEGER FileOffset;
81 ULONG i, count, size, nbFree = 0;
82 FATDirEntry* pFatEntry;
85 FileOffset.QuadPart = 0;
86 count = pDirFcb->RFCB.FileSize.u.LowPart / sizeof(FATDirEntry);
87 size = DeviceExt->FatInfo.BytesPerCluster / sizeof(FATDirEntry);
88 for (i = 0; i < count; i++, pFatEntry++)
90 if (Context == NULL || (i % size) == 0)
96 // FIXME: check return value
97 CcMapData (pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
98 TRUE, &Context, (PVOID*)&pFatEntry);
99 FileOffset.u.LowPart += DeviceExt->FatInfo.BytesPerCluster;
101 if (vfatIsDirEntryEndMarker(pFatEntry))
105 if (vfatIsDirEntryDeleted(pFatEntry))
113 if (nbFree == nbSlots)
120 CcUnpinData(Context);
123 if (nbFree == nbSlots)
125 // found enough contiguous free slots
126 *start = i - nbSlots + 1;
131 if (*start + nbSlots > count)
133 LARGE_INTEGER AllocationSize;
135 // extend the directory
136 if (vfatFCBIsRoot(pDirFcb) && DeviceExt->FatInfo.FatType != FAT32)
138 // We can't extend a root directory on a FAT12/FAT16 partition
141 AllocationSize.QuadPart = pDirFcb->RFCB.FileSize.u.LowPart + DeviceExt->FatInfo.BytesPerCluster;
142 Status = VfatSetAllocationSizeInformation(pDirFcb->FileObject, pDirFcb,
143 DeviceExt, &AllocationSize);
144 if (!NT_SUCCESS(Status))
148 // clear the new dir cluster
149 FileOffset.u.LowPart = (DWORD)(pDirFcb->RFCB.FileSize.QuadPart -
150 DeviceExt->FatInfo.BytesPerCluster);
151 CcMapData (pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
152 TRUE, &Context, (PVOID*)&pFatEntry);
153 RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
155 else if (*start + nbSlots < count)
157 // clear the entry after the last new entry
158 FileOffset.u.LowPart = (*start + nbSlots) * sizeof(FATDirEntry);
159 CcMapData (pDirFcb->FileObject, &FileOffset, sizeof(FATDirEntry),
160 TRUE, &Context, (PVOID*)&pFatEntry);
161 RtlZeroMemory(pFatEntry, sizeof(FATDirEntry));
165 CcSetDirtyPinnedData(Context, NULL);
166 CcUnpinData(Context);
169 DPRINT ("nbSlots %d nbFree %d, entry number %d\n", nbSlots, nbFree, *start);
174 VfatAddEntry (PDEVICE_EXTENSION DeviceExt,
175 PFILE_OBJECT pFileObject,
176 ULONG RequestedOptions,
179 create a new FAT entry
182 WCHAR DirName[MAX_PATH], *FileName, *PathFileName;
184 PVOID Context = NULL;
185 FATDirEntry *pFatEntry, *pEntry;
187 short nbSlots = 0, nbFree = 0, j, posCar, NameLen;
189 BOOLEAN needTilde = FALSE, needLong = FALSE;
190 BOOLEAN lCaseBase, uCaseBase, lCaseExt, uCaseExt;
192 ULONG CurrentCluster;
193 LARGE_INTEGER SystemTime, LocalTime, FileOffset;
194 NTSTATUS Status = STATUS_SUCCESS;
199 PathFileName = pFileObject->FileName.Buffer;
200 DPRINT ("addEntry: Pathname=%S\n", PathFileName);
201 //find last \ in PathFileName
203 for (i = 0; PathFileName[i]; i++)
205 if (PathFileName[i] == L'\\')
212 return STATUS_UNSUCCESSFUL;
214 FileName = &PathFileName[posCar + 1];
215 for (NameLen = 0; FileName[NameLen]; NameLen++);
216 // extract directory name from pathname
225 memcpy (DirName, PathFileName, posCar * sizeof (WCHAR));
228 // open parent directory
229 pDirFcb = vfatGrabFCBFromTable(DeviceExt, DirName);
232 return STATUS_UNSUCCESSFUL;
234 nbSlots = (NameLen + 12) / 13 + 1; //nb of entry needed for long name+normal entry
235 DPRINT ("NameLen= %d, nbSlots =%d\n", NameLen, nbSlots);
236 Buffer = ExAllocatePool (NonPagedPool, nbSlots * sizeof (FATDirEntry));
237 RtlZeroMemory (Buffer, nbSlots * sizeof (FATDirEntry));
238 pEntry = (FATDirEntry *) (Buffer + (nbSlots - 1) * sizeof (FATDirEntry));
239 pSlots = (slot *) Buffer;
242 // find last point in name
244 for (i = 0; FileName[i]; i++)
246 if (FileName[i] == '.')
268 //copy 8 characters max
269 memset (pEntry, ' ', 11);
270 for (i = 0, j = 0; j < 8 && i < posCar; i++)
272 if (vfatIsShortIllegal (FileName[i]))
275 pEntry->Filename[j++] = '_';
279 if (FileName[i] == '.')
285 pEntry->Filename[j++] = toupper ((char) FileName[i]);
290 if (FileName[posCar])
292 for (j = 0, i = posCar + 1; FileName[i] && j < 3; i++)
294 if (vfatIsShortIllegal(FileName[i]))
297 pEntry->Ext[j++] = '_';
301 if (FileName[i] == '.')
307 pEntry->Ext[j++] = toupper ((char) (FileName[i] & 0x7F));
316 //find good value for tilde
320 DPRINT ("searching a good value for tilde\n");
321 for (posCar = 0; posCar < 8 && pEntry->Filename[posCar] != ' '; posCar++);
322 if (posCar == 0) // ??????????????????????
324 pEntry->Filename[posCar++] = '_';
331 pEntry->Filename[posCar - 2] = '~';
332 pEntry->Filename[posCar - 1] = '1';
333 vfat8Dot3ToString (pEntry, DirName);
334 //try first with xxxxxx~y.zzz
335 for (i = 1; i < 10; i++)
337 DirName[posCar-1] = (WCHAR)('0' + i);
338 pEntry->Filename[posCar - 1] = (unsigned char)('0' + i);
339 Status = FindFile (DeviceExt, &FileFcb, pDirFcb, DirName, NULL, NULL);
340 if (!NT_SUCCESS(Status))
352 pEntry->Filename[posCar - 3] = '~';
353 pEntry->Filename[posCar - 2] = '1';
354 pEntry->Filename[posCar - 1] = '0';
355 vfat8Dot3ToString (pEntry, DirName);
356 //try second with xxxxx~yy.zzz
357 for (i = 10; i < 100; i++)
359 DirName[posCar - 1] = '0' + i % 10;
360 DirName[posCar - 2] = '0' + i / 10;
361 pEntry->Filename[posCar - 1] = '0' + i % 10;
362 pEntry->Filename[posCar - 2] = '0' + i / 10;
363 Status = FindFile (DeviceExt, &FileFcb, pDirFcb, DirName, NULL, NULL);
364 if (!NT_SUCCESS(Status))
369 if (i == 100) //FIXME : what to do after 99 tilde ?
371 vfatReleaseFCB(DeviceExt, pDirFcb);
373 return STATUS_UNSUCCESSFUL;
379 DPRINT ("check if long name entry needed, needlong=%d\n", needLong);
380 lCaseBase = uCaseBase = lCaseExt = uCaseExt = FALSE;
381 for (i = 0; i < posCar; i++)
383 if ((USHORT) tolower(pEntry->Filename[i]) == FileName[i])
385 DPRINT ("i=%d,%d,%d\n", i, pEntry->Filename[i], FileName[i]);
388 else if ((USHORT) pEntry->Filename[i] == FileName[i])
390 DPRINT ("i=%d,%d,%d\n", i, pEntry->Filename[i], FileName[i]);
395 DPRINT ("i=%d,%d,%d\n", i, pEntry->Filename[i], FileName[i]);
401 i++; //jump on point char
402 for (j = 0, i = posCar + 1; FileName[i] && i < posCar + 4; i++, j++)
404 if ((USHORT) tolower(pEntry->Ext[j]) == FileName[i])
406 DPRINT ("i=%d,j=%d,%d,%d\n", i, j, pEntry->Ext[j], FileName[i]);
409 else if ((USHORT) pEntry->Ext[j] == FileName[i])
411 DPRINT ("i=%d,j=%d,%d,%d\n", i, j, pEntry->Ext[j], FileName[i]);
416 DPRINT ("i=%d,j=%d,%d,%d\n", i, j, pEntry->Ext[j], FileName[i]);
421 if ((lCaseBase && uCaseBase) || (lCaseExt && uCaseExt))
427 if (needLong == FALSE)
430 memcpy (Buffer, pEntry, sizeof (FATDirEntry));
431 memset (pEntry, 0, sizeof (FATDirEntry));
432 pEntry = (FATDirEntry *) Buffer;
435 pEntry->lCase |= VFAT_CASE_LOWER_BASE;
439 pEntry->lCase |= VFAT_CASE_LOWER_EXT;
444 memset (DirName, 0xff, sizeof (DirName));
445 memcpy (DirName, FileName, NameLen * sizeof (WCHAR));
446 DirName[NameLen] = 0;
448 DPRINT ("dos name=%11.11s\n", pEntry->Filename);
451 pEntry->Attrib = ReqAttr;
452 if (RequestedOptions & FILE_DIRECTORY_FILE)
454 pEntry->Attrib |= FILE_ATTRIBUTE_DIRECTORY;
456 /* set dates and times */
457 KeQuerySystemTime (&SystemTime);
458 ExSystemTimeToLocalTime (&SystemTime, &LocalTime);
462 RtlTimeToTimeFields (&LocalTime, &tf);
463 DPRINT1("%d.%d.%d %02d:%02d:%02d.%03d '%S'\n",
464 tf.Day, tf.Month, tf.Year, tf.Hour,
465 tf.Minute, tf.Second, tf.Milliseconds,
466 pFileObject->FileName.Buffer);
469 FsdFileTimeToDosDateTime ((TIME *) & LocalTime, &pEntry->CreationDate,
470 &pEntry->CreationTime);
471 pEntry->UpdateDate = pEntry->CreationDate;
472 pEntry->UpdateTime = pEntry->CreationTime;
473 pEntry->AccessDate = pEntry->CreationDate;
477 // calculate checksum for 8.3 name
478 for (pSlots[0].alias_checksum = 0, i = 0; i < 11; i++)
480 pSlots[0].alias_checksum = (((pSlots[0].alias_checksum & 1) << 7
481 | ((pSlots[0].alias_checksum & 0xfe) >> 1))
482 + pEntry->Filename[i]);
484 //construct slots and entry
485 for (i = nbSlots - 2; i >= 0; i--)
487 DPRINT ("construct slot %d\n", i);
488 pSlots[i].attr = 0xf;
491 pSlots[i].id = nbSlots - i - 1;
495 pSlots[i].id = nbSlots - i - 1 + 0x40;
497 pSlots[i].alias_checksum = pSlots[0].alias_checksum;
498 //FIXME pSlots[i].start=;
499 memcpy (pSlots[i].name0_4, DirName + (nbSlots - i - 2) * 13, 10);
500 memcpy (pSlots[i].name5_10, DirName + (nbSlots - i - 2) * 13 + 5, 12);
501 memcpy (pSlots[i].name11_12, DirName + (nbSlots - i - 2) * 13 + 11, 4);
504 //try to find nbSlots contiguous entries frees in directory
505 if (!findDirSpace(DeviceExt, pDirFcb, nbSlots, &start))
507 vfatReleaseFCB(DeviceExt, pDirFcb);
509 return STATUS_DISK_FULL;
512 if (RequestedOptions & FILE_DIRECTORY_FILE)
514 CurrentCluster = 0xffffffff;
515 Status = NextCluster (DeviceExt, 0, &CurrentCluster, TRUE);
516 if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status))
518 vfatReleaseFCB(DeviceExt, pDirFcb);
520 if (!NT_SUCCESS(Status))
524 return STATUS_DISK_FULL;
526 if (DeviceExt->FatInfo.FatType == FAT32)
528 pEntry->FirstClusterHigh = (unsigned short)(CurrentCluster >> 16);
530 pEntry->FirstCluster = (unsigned short)CurrentCluster;
533 size = DeviceExt->FatInfo.BytesPerCluster / sizeof(FATDirEntry);
534 FileOffset.u.HighPart = 0;
535 FileOffset.u.LowPart = start * sizeof(FATDirEntry);
536 if (start / size == (start + nbSlots - 1) / size)
540 CcMapData (pDirFcb->FileObject, &FileOffset, nbSlots * sizeof(FATDirEntry),
541 TRUE, &Context, (PVOID*)&pFatEntry);
542 memcpy(pFatEntry, Buffer, nbSlots * sizeof(FATDirEntry));
548 size = DeviceExt->FatInfo.BytesPerCluster -
549 (start * sizeof(FATDirEntry)) % DeviceExt->FatInfo.BytesPerCluster;
550 CcMapData (pDirFcb->FileObject, &FileOffset, size, TRUE,
551 &Context, (PVOID*)&pFatEntry);
552 memcpy(pFatEntry, Buffer, size);
553 CcSetDirtyPinnedData(Context, NULL);
554 CcUnpinData(Context);
555 FileOffset.u.LowPart += size;
556 CcMapData (pDirFcb->FileObject, &FileOffset,
557 nbSlots * sizeof(FATDirEntry) - size,
558 TRUE, &Context, (PVOID*)&pFatEntry);
559 memcpy(pFatEntry, (PVOID)(Buffer + size), nbSlots * sizeof(FATDirEntry) - size);
561 CcSetDirtyPinnedData(Context, NULL);
562 CcUnpinData(Context);
564 // FIXME: check status
565 vfatMakeFCBFromDirEntry (DeviceExt, pDirFcb, FileName, pEntry,
566 start, start + nbSlots - 1, &newFCB);
567 vfatAttachFCBToFileObject (DeviceExt, newFCB, pFileObject);
569 DPRINT ("new : entry=%11.11s\n", newFCB->entry.Filename);
570 DPRINT ("new : entry=%11.11s\n", pEntry->Filename);
572 if (RequestedOptions & FILE_DIRECTORY_FILE)
574 FileOffset.QuadPart = 0;
575 CcMapData (pFileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, TRUE,
576 &Context, (PVOID*)&pFatEntry);
577 // clear the new directory cluster
578 RtlZeroMemory (pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
579 // create '.' and '..'
580 memcpy (&pFatEntry[0].Attrib, &pEntry->Attrib, sizeof(FATDirEntry) - 11);
581 memcpy (pFatEntry[0].Filename, ". ", 11);
582 memcpy (&pFatEntry[1].Attrib, &pEntry->Attrib, sizeof(FATDirEntry) - 11);
583 memcpy (pFatEntry[1].Filename, ".. ", 11);
584 pFatEntry[1].FirstCluster = pDirFcb->entry.FirstCluster;
585 pFatEntry[1].FirstClusterHigh = pDirFcb->entry.FirstClusterHigh;
586 if (DeviceExt->FatInfo.FatType == FAT32)
588 if (pFatEntry[1].FirstCluster == (DeviceExt->FatInfo.RootCluster & 0xffff) &&
589 pFatEntry[1].FirstClusterHigh == (DeviceExt->FatInfo.RootCluster >> 16))
591 pFatEntry[1].FirstCluster = 0;
592 pFatEntry[1].FirstClusterHigh = 0;
597 if (pFatEntry[1].FirstCluster == 1)
599 pFatEntry[1].FirstCluster = 0;
602 CcSetDirtyPinnedData(Context, NULL);
603 CcUnpinData(Context);
605 vfatReleaseFCB (DeviceExt, pDirFcb);
607 DPRINT ("addentry ok\n");
608 return STATUS_SUCCESS;
612 delEntry (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT pFileObject)
614 * deleting an existing FAT entry
618 PVFATFCB pFcb = NULL, pDirFcb = NULL;
621 ULONG Entry = 0, startEntry, CurrentCluster, NextCluster, i;
623 DPRINT ("delEntry PathFileName \'%S\'\n", pFileObject->FileName.Buffer);
625 status = vfatGetFCBForFile(DeviceExt, &pDirFcb, &pFcb,
626 pFileObject->FileName.Buffer);
629 vfatReleaseFCB(DeviceExt, pFcb);
631 if (!NT_SUCCESS(status))
635 vfatReleaseFCB(DeviceExt, pDirFcb);
639 pName = ((PVFATFCB)pFileObject->FsContext)->ObjectName;
644 status = FindFile (DeviceExt, &Fcb, pDirFcb, pName, &Entry, &startEntry);
646 if (NT_SUCCESS(status))
648 PVOID Context = NULL;
649 LARGE_INTEGER Offset;
650 FATDirEntry* pDirEntry;
651 DPRINT ("delete entry: %d to %d\n", startEntry, Entry);
652 Offset.u.HighPart = 0;
653 for (i = startEntry; i <= Entry; i++)
655 if (Context == NULL || ((i * sizeof(FATDirEntry)) % PAGE_SIZE) == 0)
659 CcSetDirtyPinnedData(Context, NULL);
660 CcUnpinData(Context);
662 Offset.u.LowPart = (i * sizeof(FATDirEntry) / PAGE_SIZE) * PAGE_SIZE;
663 CcMapData (pDirFcb->FileObject, &Offset, PAGE_SIZE, TRUE,
664 &Context, (PVOID*)&pDirEntry);
666 pDirEntry[i % (PAGE_SIZE / sizeof(FATDirEntry))].Filename[0] = 0xe5;
670 vfatDirEntryGetFirstCluster (DeviceExt,
671 &pDirEntry[i % (PAGE_SIZE / sizeof(FATDirEntry))]);
676 CcSetDirtyPinnedData(Context, NULL);
677 CcUnpinData(Context);
680 while (CurrentCluster && CurrentCluster != 0xffffffff)
682 GetNextCluster (DeviceExt, CurrentCluster, &NextCluster, FALSE);
683 // FIXME: check status
684 WriteCluster(DeviceExt, CurrentCluster, 0);
685 CurrentCluster = NextCluster;
688 vfatReleaseFCB(DeviceExt, pDirFcb);