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;
45 DPRINT ("updEntry PathFileName \'%S\'\n",
46 ((PVFATCCB)(pFileObject->FsContext2))->pFcb->PathName);
48 pFcb = ((PVFATCCB)(pFileObject->FsContext2))->pFcb;
50 pDirFcb = pFcb->parentFcb;
53 Offset.u.HighPart = 0;
54 Offset.u.LowPart = pFcb->dirIndex * sizeof(FATDirEntry);
55 if (CcMapData (pDirFcb->FileObject, &Offset, sizeof(FATDirEntry),
56 TRUE, &Context, (PVOID*)&Buffer))
58 memcpy(Buffer, &pFcb->entry, sizeof(FATDirEntry));
59 CcSetDirtyPinnedData(Context, NULL);
63 DPRINT1 ("Failed write to \'%S\'.\n", pDirFcb->PathName);
65 return STATUS_SUCCESS;
69 findDirSpace(PDEVICE_EXTENSION DeviceExt,
75 * try to find contiguous entries frees in directory,
76 * extend a directory if is neccesary
78 LARGE_INTEGER FileOffset;
79 ULONG i, count, size, nbFree = 0;
80 FATDirEntry* pFatEntry;
83 FileOffset.QuadPart = 0;
84 count = pDirFcb->RFCB.FileSize.u.LowPart / sizeof(FATDirEntry);
85 size = DeviceExt->FatInfo.BytesPerCluster / sizeof(FATDirEntry);
86 for (i = 0; i < count; i++, pFatEntry++)
88 if (Context == NULL || (i % size) == 0)
94 // FIXME: check return value
95 CcMapData (pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
96 TRUE, &Context, (PVOID*)&pFatEntry);
97 FileOffset.u.LowPart += DeviceExt->FatInfo.BytesPerCluster;
99 if (vfatIsDirEntryEndMarker(pFatEntry))
103 if (vfatIsDirEntryDeleted(pFatEntry))
111 if (nbFree == nbSlots)
118 CcUnpinData(Context);
121 if (nbFree == nbSlots)
123 // found enough contiguous free slots
124 *start = i - nbSlots + 1;
129 if (*start + nbSlots > count)
131 LARGE_INTEGER AllocationSize;
133 // extend the directory
134 if (vfatFCBIsRoot(pDirFcb) && DeviceExt->FatInfo.FatType != FAT32)
136 // We can't extend a root directory on a FAT12/FAT16 partition
139 AllocationSize.QuadPart = pDirFcb->RFCB.FileSize.u.LowPart + DeviceExt->FatInfo.BytesPerCluster;
140 Status = VfatSetAllocationSizeInformation(pDirFcb->FileObject, pDirFcb,
141 DeviceExt, &AllocationSize);
142 if (!NT_SUCCESS(Status))
146 // clear the new dir cluster
147 FileOffset.u.LowPart = pDirFcb->RFCB.FileSize.QuadPart -
148 DeviceExt->FatInfo.BytesPerCluster;
149 CcMapData (pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
150 TRUE, &Context, (PVOID*)&pFatEntry);
151 RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
153 else if (*start + nbSlots < count)
155 // clear the entry after the last new entry
156 FileOffset.u.LowPart = (*start + nbSlots) * sizeof(FATDirEntry);
157 CcMapData (pDirFcb->FileObject, &FileOffset, sizeof(FATDirEntry),
158 TRUE, &Context, (PVOID*)&pFatEntry);
159 RtlZeroMemory(pFatEntry, sizeof(FATDirEntry));
163 CcSetDirtyPinnedData(Context, NULL);
164 CcUnpinData(Context);
167 DPRINT ("nbSlots %d nbFree %d, entry number %d\n", nbSlots, nbFree, *start);
172 VfatAddEntry (PDEVICE_EXTENSION DeviceExt,
173 PFILE_OBJECT pFileObject,
174 ULONG RequestedOptions,
177 create a new FAT entry
180 WCHAR DirName[MAX_PATH], *FileName, *PathFileName;
182 PVOID Context = NULL;
183 FATDirEntry *pFatEntry, *pEntry;
185 short nbSlots = 0, nbFree = 0, j, posCar, NameLen;
187 BOOLEAN needTilde = FALSE, needLong = FALSE;
188 BOOLEAN lCaseBase, uCaseBase, lCaseExt, uCaseExt;
190 ULONG CurrentCluster;
191 LARGE_INTEGER SystemTime, LocalTime, FileOffset;
192 NTSTATUS Status = STATUS_SUCCESS;
197 PathFileName = pFileObject->FileName.Buffer;
198 DPRINT ("addEntry: Pathname=%S\n", PathFileName);
199 //find last \ in PathFileName
201 for (i = 0; PathFileName[i]; i++)
203 if (PathFileName[i] == L'\\')
210 return STATUS_UNSUCCESSFUL;
212 FileName = &PathFileName[posCar + 1];
213 for (NameLen = 0; FileName[NameLen]; NameLen++);
214 // extract directory name from pathname
223 memcpy (DirName, PathFileName, posCar * sizeof (WCHAR));
226 // open parent directory
227 pDirFcb = vfatGrabFCBFromTable(DeviceExt, DirName);
230 return STATUS_UNSUCCESSFUL;
232 nbSlots = (NameLen + 12) / 13 + 1; //nb of entry needed for long name+normal entry
233 DPRINT ("NameLen= %d, nbSlots =%d\n", NameLen, nbSlots);
234 Buffer = ExAllocatePool (NonPagedPool, nbSlots * sizeof (FATDirEntry));
235 RtlZeroMemory (Buffer, nbSlots * sizeof (FATDirEntry));
236 pEntry = (FATDirEntry *) (Buffer + (nbSlots - 1) * sizeof (FATDirEntry));
237 pSlots = (slot *) Buffer;
240 // find last point in name
242 for (i = 0; FileName[i]; i++)
244 if (FileName[i] == '.')
266 //copy 8 characters max
267 memset (pEntry, ' ', 11);
268 for (i = 0, j = 0; j < 8 && i < posCar; i++)
270 if (vfatIsShortIllegal (FileName[i]))
273 pEntry->Filename[j++] = '_';
277 if (FileName[i] == '.')
283 pEntry->Filename[j++] = toupper ((char) FileName[i]);
288 if (FileName[posCar])
290 for (j = 0, i = posCar + 1; FileName[i] && j < 3; i++)
292 if (vfatIsShortIllegal(FileName[i]))
295 pEntry->Ext[j++] = '_';
299 if (FileName[i] == '.')
305 pEntry->Ext[j++] = toupper ((char) (FileName[i] & 0x7F));
314 //find good value for tilde
318 DPRINT ("searching a good value for tilde\n");
319 for (posCar = 0; posCar < 8 && pEntry->Filename[posCar] != ' '; posCar++);
320 if (posCar == 0) // ??????????????????????
322 pEntry->Filename[posCar++] = '_';
329 pEntry->Filename[posCar - 2] = '~';
330 pEntry->Filename[posCar - 1] = '1';
331 vfat8Dot3ToString (pEntry, DirName);
332 //try first with xxxxxx~y.zzz
333 for (i = 1; i < 10; i++)
335 DirName[posCar-1] = '0' + i;
336 pEntry->Filename[posCar - 1] = '0' + i;
337 Status = FindFile (DeviceExt, &FileFcb, pDirFcb, DirName, NULL, NULL);
338 if (!NT_SUCCESS(Status))
350 pEntry->Filename[posCar - 3] = '~';
351 pEntry->Filename[posCar - 2] = '1';
352 pEntry->Filename[posCar - 1] = '0';
353 vfat8Dot3ToString (pEntry, DirName);
354 //try second with xxxxx~yy.zzz
355 for (i = 10; i < 100; i++)
357 DirName[posCar - 1] = '0' + i % 10;
358 DirName[posCar - 2] = '0' + i / 10;
359 pEntry->Filename[posCar - 1] = '0' + i % 10;
360 pEntry->Filename[posCar - 2] = '0' + i / 10;
361 Status = FindFile (DeviceExt, &FileFcb, pDirFcb, DirName, NULL, NULL);
362 if (!NT_SUCCESS(Status))
367 if (i == 100) //FIXME : what to do after 99 tilde ?
369 vfatReleaseFCB(DeviceExt, pDirFcb);
371 return STATUS_UNSUCCESSFUL;
377 DPRINT ("check if long name entry needed, needlong=%d\n", needLong);
378 lCaseBase = uCaseBase = lCaseExt = uCaseExt = FALSE;
379 for (i = 0; i < posCar; i++)
381 if ((USHORT) tolower(pEntry->Filename[i]) == FileName[i])
383 DPRINT ("i=%d,%d,%d\n", i, pEntry->Filename[i], FileName[i]);
386 else if ((USHORT) pEntry->Filename[i] == FileName[i])
388 DPRINT ("i=%d,%d,%d\n", i, pEntry->Filename[i], FileName[i]);
393 DPRINT ("i=%d,%d,%d\n", i, pEntry->Filename[i], FileName[i]);
399 i++; //jump on point char
400 for (j = 0, i = posCar + 1; FileName[i] && i < posCar + 4; i++, j++)
402 if ((USHORT) tolower(pEntry->Ext[j]) == FileName[i])
404 DPRINT ("i=%d,j=%d,%d,%d\n", i, j, pEntry->Ext[j], FileName[i]);
407 else if ((USHORT) pEntry->Ext[j] == FileName[i])
409 DPRINT ("i=%d,j=%d,%d,%d\n", i, j, pEntry->Ext[j], FileName[i]);
414 DPRINT ("i=%d,j=%d,%d,%d\n", i, j, pEntry->Ext[j], FileName[i]);
419 if ((lCaseBase && uCaseBase) || (lCaseExt && uCaseExt))
425 if (needLong == FALSE)
428 memcpy (Buffer, pEntry, sizeof (FATDirEntry));
429 memset (pEntry, 0, sizeof (FATDirEntry));
430 pEntry = (FATDirEntry *) Buffer;
433 pEntry->lCase |= VFAT_CASE_LOWER_BASE;
437 pEntry->lCase |= VFAT_CASE_LOWER_EXT;
442 memset (DirName, 0xff, sizeof (DirName));
443 memcpy (DirName, FileName, NameLen * sizeof (WCHAR));
444 DirName[NameLen] = 0;
446 DPRINT ("dos name=%11.11s\n", pEntry->Filename);
449 pEntry->Attrib = ReqAttr;
450 if (RequestedOptions & FILE_DIRECTORY_FILE)
452 pEntry->Attrib |= FILE_ATTRIBUTE_DIRECTORY;
454 /* set dates and times */
455 KeQuerySystemTime (&SystemTime);
456 ExSystemTimeToLocalTime (&SystemTime, &LocalTime);
460 RtlTimeToTimeFields (&LocalTime, &tf);
461 DPRINT1("%d.%d.%d %02d:%02d:%02d.%03d '%S'\n",
462 tf.Day, tf.Month, tf.Year, tf.Hour,
463 tf.Minute, tf.Second, tf.Milliseconds,
464 pFileObject->FileName.Buffer);
467 FsdFileTimeToDosDateTime ((TIME *) & LocalTime, &pEntry->CreationDate,
468 &pEntry->CreationTime);
469 pEntry->UpdateDate = pEntry->CreationDate;
470 pEntry->UpdateTime = pEntry->CreationTime;
471 pEntry->AccessDate = pEntry->CreationDate;
475 // calculate checksum for 8.3 name
476 for (pSlots[0].alias_checksum = i = 0; i < 11; i++)
478 pSlots[0].alias_checksum = (((pSlots[0].alias_checksum & 1) << 7
479 | ((pSlots[0].alias_checksum & 0xfe) >> 1))
480 + pEntry->Filename[i]);
482 //construct slots and entry
483 for (i = nbSlots - 2; i >= 0; i--)
485 DPRINT ("construct slot %d\n", i);
486 pSlots[i].attr = 0xf;
489 pSlots[i].id = nbSlots - i - 1;
493 pSlots[i].id = nbSlots - i - 1 + 0x40;
495 pSlots[i].alias_checksum = pSlots[0].alias_checksum;
496 //FIXME pSlots[i].start=;
497 memcpy (pSlots[i].name0_4, DirName + (nbSlots - i - 2) * 13, 10);
498 memcpy (pSlots[i].name5_10, DirName + (nbSlots - i - 2) * 13 + 5, 12);
499 memcpy (pSlots[i].name11_12, DirName + (nbSlots - i - 2) * 13 + 11, 4);
502 //try to find nbSlots contiguous entries frees in directory
503 if (!findDirSpace(DeviceExt, pDirFcb, nbSlots, &start))
505 vfatReleaseFCB(DeviceExt, pDirFcb);
507 return STATUS_DISK_FULL;
510 if (RequestedOptions & FILE_DIRECTORY_FILE)
512 CurrentCluster = 0xffffffff;
513 Status = NextCluster (DeviceExt, NULL, 0, &CurrentCluster, TRUE);
514 if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status))
516 vfatReleaseFCB(DeviceExt, pDirFcb);
518 if (!NT_SUCCESS(Status))
522 return STATUS_DISK_FULL;
524 if (DeviceExt->FatInfo.FatType == FAT32)
526 pEntry->FirstClusterHigh = CurrentCluster >> 16;
528 pEntry->FirstCluster = CurrentCluster;
531 size = DeviceExt->FatInfo.BytesPerCluster / sizeof(FATDirEntry);
532 FileOffset.u.HighPart = 0;
533 FileOffset.u.LowPart = start * sizeof(FATDirEntry);
534 if (start / size == (start + nbSlots - 1) / size)
538 CcMapData (pDirFcb->FileObject, &FileOffset, nbSlots * sizeof(FATDirEntry),
539 TRUE, &Context, (PVOID*)&pFatEntry);
540 memcpy(pFatEntry, Buffer, nbSlots * sizeof(FATDirEntry));
546 size = DeviceExt->FatInfo.BytesPerCluster -
547 (start * sizeof(FATDirEntry)) % DeviceExt->FatInfo.BytesPerCluster;
548 CcMapData (pDirFcb->FileObject, &FileOffset, size, TRUE,
549 &Context, (PVOID*)&pFatEntry);
550 memcpy(pFatEntry, Buffer, size);
551 CcSetDirtyPinnedData(Context, NULL);
552 CcUnpinData(Context);
553 FileOffset.u.LowPart += size;
554 CcMapData (pDirFcb->FileObject, &FileOffset,
555 nbSlots * sizeof(FATDirEntry) - size,
556 TRUE, &Context, (PVOID*)&pFatEntry);
557 memcpy(pFatEntry, (PVOID)Buffer + size, nbSlots * sizeof(FATDirEntry) - size);
559 CcSetDirtyPinnedData(Context, NULL);
560 CcUnpinData(Context);
562 // FEXME: check status
563 vfatMakeFCBFromDirEntry (DeviceExt, pDirFcb, FileName, pEntry,
564 start, start + nbSlots - 1, &newFCB);
565 vfatAttachFCBToFileObject (DeviceExt, newFCB, pFileObject);
567 DPRINT ("new : entry=%11.11s\n", newFCB->entry.Filename);
568 DPRINT ("new : entry=%11.11s\n", pEntry->Filename);
570 if (RequestedOptions & FILE_DIRECTORY_FILE)
572 FileOffset.QuadPart = 0;
573 CcMapData (pFileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, TRUE,
574 &Context, (PVOID*)&pFatEntry);
575 // clear the new directory cluster
576 RtlZeroMemory (pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
577 // create '.' and '..'
578 memcpy (&pFatEntry[0].Attrib, &pEntry->Attrib, sizeof(FATDirEntry) - 11);
579 memcpy (pFatEntry[0].Filename, ". ", 11);
580 memcpy (&pFatEntry[1].Attrib, &pEntry->Attrib, sizeof(FATDirEntry) - 11);
581 memcpy (pFatEntry[1].Filename, ".. ", 11);
582 pFatEntry[1].FirstCluster = pDirFcb->entry.FirstCluster;
583 pFatEntry[1].FirstClusterHigh = pDirFcb->entry.FirstClusterHigh;
584 if (DeviceExt->FatInfo.FatType == FAT32)
586 if (pFatEntry[1].FirstCluster == (DeviceExt->FatInfo.RootCluster & 0xffff) &&
587 pFatEntry[1].FirstClusterHigh == (DeviceExt->FatInfo.RootCluster >> 16))
589 pFatEntry[1].FirstCluster = 0;
590 pFatEntry[1].FirstClusterHigh = 0;
595 if (pFatEntry[1].FirstCluster == 1)
597 pFatEntry[1].FirstCluster = 0;
600 CcSetDirtyPinnedData(Context, NULL);
601 CcUnpinData(Context);
603 vfatReleaseFCB (DeviceExt, pDirFcb);
605 DPRINT ("addentry ok\n");
606 return STATUS_SUCCESS;
610 delEntry (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT pFileObject)
612 * deleting an existing FAT entry
616 PVFATFCB pFcb = NULL, pDirFcb = NULL;
619 ULONG Entry = 0, startEntry, Read, CurrentCluster, NextCluster, i;
620 FATDirEntry DirEntry;
622 DPRINT ("delEntry PathFileName \'%S\'\n", pFileObject->FileName.Buffer);
624 status = vfatGetFCBForFile(DeviceExt, &pDirFcb, &pFcb,
625 pFileObject->FileName.Buffer);
628 vfatReleaseFCB(DeviceExt, pFcb);
630 if (!NT_SUCCESS(status))
634 vfatReleaseFCB(DeviceExt, pDirFcb);
638 pName = ((PVFATCCB)(pFileObject->FsContext2))->pFcb->ObjectName;
643 status = FindFile (DeviceExt, &Fcb, pDirFcb, pName, &Entry, &startEntry);
645 if (NT_SUCCESS(status))
647 PVOID Context = NULL;
648 LARGE_INTEGER Offset;
649 FATDirEntry* pDirEntry;
650 DPRINT ("delete entry: %d to %d\n", startEntry, Entry);
651 Offset.u.HighPart = 0;
652 for (i = startEntry; i <= Entry; i++)
654 if (Context == NULL || ((i * sizeof(FATDirEntry)) % PAGE_SIZE) == 0)
658 CcSetDirtyPinnedData(Context, NULL);
659 CcUnpinData(Context);
661 Offset.u.LowPart = (i * sizeof(FATDirEntry) / PAGE_SIZE) * PAGE_SIZE;
662 CcMapData (pDirFcb->FileObject, &Offset, PAGE_SIZE, TRUE,
663 &Context, (PVOID*)&pDirEntry);
665 pDirEntry[i % (PAGE_SIZE / sizeof(FATDirEntry))].Filename[0] = 0xe5;
669 vfatDirEntryGetFirstCluster (DeviceExt,
670 &pDirEntry[i % (PAGE_SIZE / sizeof(FATDirEntry))]);
675 CcSetDirtyPinnedData(Context, NULL);
676 CcUnpinData(Context);
679 while (CurrentCluster && CurrentCluster != 0xffffffff)
681 GetNextCluster (DeviceExt, CurrentCluster, &NextCluster, FALSE);
682 // FIXME: check status
683 WriteCluster(DeviceExt, CurrentCluster, 0);
684 CurrentCluster = NextCluster;
687 vfatReleaseFCB(DeviceExt, pDirFcb);