branch update for HEAD-2003021201
[reactos.git] / drivers / fs / vfat / dirwr.c
1 /* $Id$
2  *
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
7  *
8  */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ddk/ntddk.h>
13 #include <ctype.h>
14 #include <wchar.h>
15 #include <string.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20 #include "vfat.h"
21
22 const char *short_illegals=" ;+=[]',\"*\\<>/?:|";
23
24 static BOOLEAN
25 vfatIsShortIllegal(char c)
26 {
27   int i;
28   for (i = 0; short_illegals[i]; i++)
29     if (c == short_illegals[i])
30       return TRUE;
31   return FALSE;
32 }
33
34 NTSTATUS 
35 VfatUpdateEntry (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT pFileObject)
36 /*
37  * update an existing FAT entry
38  */
39 {
40   PVOID Context;
41   PVOID Buffer;
42   PVFATFCB pDirFcb, pFcb;
43   LARGE_INTEGER Offset;
44
45   DPRINT ("updEntry PathFileName \'%S\'\n", 
46           ((PVFATCCB)(pFileObject->FsContext2))->pFcb->PathName);
47
48   pFcb = ((PVFATCCB)(pFileObject->FsContext2))->pFcb;
49   assert (pFcb);
50   pDirFcb = pFcb->parentFcb;
51   assert (pDirFcb);
52
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))
57   {
58      memcpy(Buffer, &pFcb->entry, sizeof(FATDirEntry));
59      CcSetDirtyPinnedData(Context, NULL);
60      CcUnpinData(Context);
61   }
62   else
63      DPRINT1 ("Failed write to \'%S\'.\n", pDirFcb->PathName);
64
65   return STATUS_SUCCESS;
66 }
67
68 BOOLEAN
69 findDirSpace(PDEVICE_EXTENSION DeviceExt,
70              PVFATFCB pDirFcb,
71              ULONG nbSlots,
72              PULONG start)
73 {
74 /*
75  * try to find contiguous entries frees in directory,
76  * extend a directory if is neccesary
77  */
78   LARGE_INTEGER FileOffset;
79   ULONG i, count, size, nbFree = 0;
80   FATDirEntry* pFatEntry;
81   PVOID Context = NULL;
82   NTSTATUS Status;
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++)
87   {
88     if (Context == NULL || (i % size) == 0)
89     {
90       if (Context)
91       {
92         CcUnpinData(Context);
93       }
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;
98     }
99     if (vfatIsDirEntryEndMarker(pFatEntry))
100     {
101       break;
102     }
103     if (vfatIsDirEntryDeleted(pFatEntry))
104     {
105       nbFree++;
106     }
107     else
108     {
109       nbFree = 0;
110     }
111     if (nbFree == nbSlots)
112     {
113       break;
114     }
115   }
116   if (Context)
117   {
118     CcUnpinData(Context);
119     Context = NULL;
120   }
121   if (nbFree == nbSlots)
122   {
123     // found enough contiguous free slots
124     *start = i - nbSlots + 1;
125   }
126   else
127   {
128     *start = i - nbFree;
129     if (*start + nbSlots > count)
130     {
131       LARGE_INTEGER AllocationSize;
132       CHECKPOINT;
133       // extend the directory
134       if (vfatFCBIsRoot(pDirFcb) && DeviceExt->FatInfo.FatType != FAT32)
135       {
136         // We can't extend a root directory on a FAT12/FAT16 partition
137         return FALSE;
138       }
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))
143       {
144         return FALSE;
145       }
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);
152     }
153     else if (*start + nbSlots < count)
154     {
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));
160     }
161     if (Context)
162     {
163       CcSetDirtyPinnedData(Context, NULL);
164       CcUnpinData(Context);
165     }
166   }
167   DPRINT ("nbSlots %d nbFree %d, entry number %d\n", nbSlots, nbFree, *start);
168   return TRUE;
169 }
170
171 NTSTATUS
172 VfatAddEntry (PDEVICE_EXTENSION DeviceExt,
173               PFILE_OBJECT pFileObject,
174               ULONG RequestedOptions,
175               UCHAR ReqAttr)
176 /*
177   create a new FAT entry
178 */
179 {
180   WCHAR DirName[MAX_PATH], *FileName, *PathFileName;
181   VFATFCB FileFcb;
182   PVOID Context = NULL;
183   FATDirEntry *pFatEntry, *pEntry;
184   slot *pSlots;
185   short nbSlots = 0, nbFree = 0, j, posCar, NameLen;
186   PUCHAR Buffer;
187   BOOLEAN needTilde = FALSE, needLong = FALSE;
188   BOOLEAN lCaseBase, uCaseBase, lCaseExt, uCaseExt;
189   PVFATFCB newFCB;
190   ULONG CurrentCluster;
191   LARGE_INTEGER SystemTime, LocalTime, FileOffset;
192   NTSTATUS Status = STATUS_SUCCESS;
193   PVFATFCB pDirFcb;
194   ULONG start, size;
195   long i;
196
197   PathFileName = pFileObject->FileName.Buffer;
198   DPRINT ("addEntry: Pathname=%S\n", PathFileName);
199   //find last \ in PathFileName
200   posCar = -1;
201   for (i = 0; PathFileName[i]; i++)
202   {
203     if (PathFileName[i] == L'\\')
204     {
205       posCar = i;
206     }
207   }
208   if (posCar == -1)
209   {
210     return STATUS_UNSUCCESSFUL;
211   }
212   FileName = &PathFileName[posCar + 1];
213   for (NameLen = 0; FileName[NameLen]; NameLen++);
214   // extract directory name from pathname
215   if (posCar == 0)
216   {
217     // root dir
218     DirName[0] = L'\\';
219     DirName[1] = 0;
220   }
221   else
222   {
223     memcpy (DirName, PathFileName, posCar * sizeof (WCHAR));
224     DirName[posCar] = 0;
225   }
226   // open parent directory
227   pDirFcb = vfatGrabFCBFromTable(DeviceExt, DirName);
228   if (pDirFcb == NULL)
229   {
230     return STATUS_UNSUCCESSFUL;
231   }
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;
238   // create 8.3 name
239   needTilde = FALSE;
240   // find last point in name
241   posCar = j = 0;
242   for (i = 0; FileName[i]; i++)
243   {
244     if (FileName[i] == '.')
245     {
246       posCar = i;
247       if (i == j)
248       {
249         j++;
250       }
251     }
252   }
253   if (!posCar)
254   {
255     posCar = i;
256   }
257   if (posCar < j)
258   {
259     posCar = i;
260     needTilde = TRUE;
261   }
262   if (posCar > 8)
263   {
264     needTilde = TRUE;
265   }
266   //copy 8 characters max
267   memset (pEntry, ' ', 11);
268   for (i = 0, j = 0; j < 8 && i < posCar; i++)
269   {
270     if (vfatIsShortIllegal (FileName[i]))
271     {
272       needTilde = TRUE;
273       pEntry->Filename[j++] = '_';
274     }
275     else
276     {
277       if (FileName[i] == '.')
278       {
279         needTilde = TRUE;
280       }
281       else
282       {
283         pEntry->Filename[j++] = toupper ((char) FileName[i]);
284       }
285     }
286   }
287   //copy extension
288   if (FileName[posCar])
289   {
290     for (j = 0, i = posCar + 1; FileName[i] && j < 3; i++)
291     {
292       if (vfatIsShortIllegal(FileName[i]))
293       {
294         needTilde = TRUE;
295         pEntry->Ext[j++] = '_';
296       }
297       else
298       {
299         if (FileName[i] == '.')
300         {
301           needTilde = TRUE;
302         }
303         else
304         {
305           pEntry->Ext[j++] = toupper ((char) (FileName[i] & 0x7F));
306         }
307       }
308     }
309   }
310   if (FileName[i])
311   {
312     needTilde = TRUE;
313   }
314   //find good value for tilde
315   if (needTilde)
316   {
317     needLong = TRUE;
318     DPRINT ("searching a good value for tilde\n");
319     for (posCar = 0; posCar < 8 && pEntry->Filename[posCar] != ' '; posCar++);
320     if (posCar == 0) // ??????????????????????
321     {
322       pEntry->Filename[posCar++] = '_';
323     }
324     posCar += 2;
325     if (posCar > 8)
326     {
327       posCar = 8;
328     }
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++)
334     {
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))
339       {
340         break;
341       }
342     }
343     if (i == 10)
344     {
345       posCar++;
346       if (posCar > 8)
347       {
348         posCar = 8;
349       }
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++)
356       {
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))
363         {
364           break;
365         }
366       }
367       if (i == 100) //FIXME : what to do after 99 tilde ?
368       {
369         vfatReleaseFCB(DeviceExt, pDirFcb);
370         ExFreePool (Buffer);
371         return STATUS_UNSUCCESSFUL;
372       }
373     }
374   }
375   else
376   {
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++)
380     {
381       if ((USHORT) tolower(pEntry->Filename[i]) == FileName[i])
382       {
383          DPRINT ("i=%d,%d,%d\n", i, pEntry->Filename[i], FileName[i]);
384          lCaseBase = TRUE;
385       }
386       else if ((USHORT) pEntry->Filename[i] == FileName[i])
387       {
388          DPRINT ("i=%d,%d,%d\n", i, pEntry->Filename[i], FileName[i]);
389          uCaseBase = TRUE;
390       }
391       else
392       {
393         DPRINT ("i=%d,%d,%d\n", i, pEntry->Filename[i], FileName[i]);
394         needLong = TRUE;
395       }
396     }
397     if (FileName[i])
398     {
399       i++;                      //jump on point char
400       for (j = 0, i = posCar + 1; FileName[i] && i < posCar + 4; i++, j++)
401       {
402         if ((USHORT) tolower(pEntry->Ext[j]) == FileName[i])
403         {
404            DPRINT ("i=%d,j=%d,%d,%d\n", i, j, pEntry->Ext[j], FileName[i]);
405            lCaseExt = TRUE;
406         }
407         else if ((USHORT) pEntry->Ext[j] == FileName[i])
408         {
409            DPRINT ("i=%d,j=%d,%d,%d\n", i, j, pEntry->Ext[j], FileName[i]);
410            uCaseExt = TRUE;
411         }
412         else
413         {
414           DPRINT ("i=%d,j=%d,%d,%d\n", i, j, pEntry->Ext[j], FileName[i]);
415           needLong = TRUE;
416         }
417       }
418     }
419     if ((lCaseBase && uCaseBase) || (lCaseExt && uCaseExt))
420     {
421        CHECKPOINT;
422        needLong = TRUE;
423     }
424   }
425   if (needLong == FALSE)
426   {
427     nbSlots = 1;
428     memcpy (Buffer, pEntry, sizeof (FATDirEntry));
429     memset (pEntry, 0, sizeof (FATDirEntry));
430     pEntry = (FATDirEntry *) Buffer;
431     if (lCaseBase)
432     {
433         pEntry->lCase |= VFAT_CASE_LOWER_BASE;
434     }
435     if (lCaseExt)
436     {
437         pEntry->lCase |= VFAT_CASE_LOWER_EXT;
438     }
439   }
440   else
441   {
442     memset (DirName, 0xff, sizeof (DirName));
443     memcpy (DirName, FileName, NameLen * sizeof (WCHAR));
444     DirName[NameLen] = 0;
445   }
446   DPRINT ("dos name=%11.11s\n", pEntry->Filename);
447
448   /* set attributes */
449   pEntry->Attrib = ReqAttr;
450   if (RequestedOptions & FILE_DIRECTORY_FILE)
451   {
452     pEntry->Attrib |= FILE_ATTRIBUTE_DIRECTORY;
453   }
454   /* set dates and times */
455   KeQuerySystemTime (&SystemTime);
456   ExSystemTimeToLocalTime (&SystemTime, &LocalTime);
457 #if 0
458   {
459     TIME_FIELDS tf;
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);
465   }
466 #endif
467   FsdFileTimeToDosDateTime ((TIME *) & LocalTime, &pEntry->CreationDate,
468                             &pEntry->CreationTime);
469   pEntry->UpdateDate = pEntry->CreationDate;
470   pEntry->UpdateTime = pEntry->CreationTime;
471   pEntry->AccessDate = pEntry->CreationDate;
472
473   if (needLong)
474   {
475     // calculate checksum for 8.3 name
476     for (pSlots[0].alias_checksum = i = 0; i < 11; i++)
477     {
478        pSlots[0].alias_checksum = (((pSlots[0].alias_checksum & 1) << 7
479                                   | ((pSlots[0].alias_checksum & 0xfe) >> 1))
480                                   + pEntry->Filename[i]);
481     }
482     //construct slots and entry
483     for (i = nbSlots - 2; i >= 0; i--)
484     {
485        DPRINT ("construct slot %d\n", i);
486        pSlots[i].attr = 0xf;
487        if (i)
488        {
489           pSlots[i].id = nbSlots - i - 1;
490        }
491        else
492        {
493           pSlots[i].id = nbSlots - i - 1 + 0x40;
494        }
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);
500     }
501   }
502   //try to find nbSlots contiguous entries frees in directory
503   if (!findDirSpace(DeviceExt, pDirFcb, nbSlots, &start))
504   {
505     vfatReleaseFCB(DeviceExt, pDirFcb);
506     ExFreePool (Buffer);
507     return STATUS_DISK_FULL;
508   }
509
510   if (RequestedOptions & FILE_DIRECTORY_FILE)
511   {
512     CurrentCluster = 0xffffffff;
513     Status = NextCluster (DeviceExt, NULL, 0, &CurrentCluster, TRUE);
514     if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status))
515     {
516       vfatReleaseFCB(DeviceExt, pDirFcb);
517       ExFreePool (Buffer);
518       if (!NT_SUCCESS(Status))
519       {
520         return Status;
521       }
522       return STATUS_DISK_FULL;
523     }
524     if (DeviceExt->FatInfo.FatType == FAT32)
525     {
526       pEntry->FirstClusterHigh = CurrentCluster >> 16;
527     }
528     pEntry->FirstCluster = CurrentCluster;
529   }
530
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)
535   {
536     // one cluster
537     CHECKPOINT;
538     CcMapData (pDirFcb->FileObject, &FileOffset, nbSlots * sizeof(FATDirEntry),
539                TRUE, &Context, (PVOID*)&pFatEntry);
540     memcpy(pFatEntry, Buffer, nbSlots * sizeof(FATDirEntry));
541   }
542   else
543   {
544     // two clusters
545     CHECKPOINT;
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);
558   }
559   CcSetDirtyPinnedData(Context, NULL);
560   CcUnpinData(Context);
561
562   // FEXME: check status
563   vfatMakeFCBFromDirEntry (DeviceExt, pDirFcb, FileName, pEntry,
564                            start, start + nbSlots - 1, &newFCB);
565   vfatAttachFCBToFileObject (DeviceExt, newFCB, pFileObject);
566
567   DPRINT ("new : entry=%11.11s\n", newFCB->entry.Filename);
568   DPRINT ("new : entry=%11.11s\n", pEntry->Filename);
569
570   if (RequestedOptions & FILE_DIRECTORY_FILE)
571   {
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)
585     {
586       if (pFatEntry[1].FirstCluster == (DeviceExt->FatInfo.RootCluster & 0xffff) &&
587           pFatEntry[1].FirstClusterHigh == (DeviceExt->FatInfo.RootCluster >> 16))
588       {
589         pFatEntry[1].FirstCluster = 0;
590         pFatEntry[1].FirstClusterHigh = 0;
591       }
592     }
593     else
594     {
595       if (pFatEntry[1].FirstCluster == 1)
596       {
597         pFatEntry[1].FirstCluster = 0;
598       }
599     }
600     CcSetDirtyPinnedData(Context, NULL);
601     CcUnpinData(Context);
602   }
603   vfatReleaseFCB (DeviceExt, pDirFcb);
604   ExFreePool (Buffer);
605   DPRINT ("addentry ok\n");
606   return STATUS_SUCCESS;
607 }
608
609 NTSTATUS
610 delEntry (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT pFileObject)
611 /*
612  * deleting an existing FAT entry
613  */
614 {
615   VFATFCB Fcb;
616   PVFATFCB pFcb = NULL, pDirFcb = NULL;
617   NTSTATUS status;
618   PWSTR pName;
619   ULONG Entry = 0, startEntry, Read, CurrentCluster, NextCluster, i;
620   FATDirEntry DirEntry;
621
622   DPRINT ("delEntry PathFileName \'%S\'\n", pFileObject->FileName.Buffer);
623
624   status = vfatGetFCBForFile(DeviceExt, &pDirFcb, &pFcb,
625                              pFileObject->FileName.Buffer);
626   if (pFcb != NULL)
627   {
628     vfatReleaseFCB(DeviceExt, pFcb);
629   }
630   if (!NT_SUCCESS(status))
631   {
632     if (pDirFcb != NULL)
633     {
634       vfatReleaseFCB(DeviceExt, pDirFcb);
635     }
636     return status;
637   }
638   pName = ((PVFATCCB)(pFileObject->FsContext2))->pFcb->ObjectName;
639   if (*pName == L'\\')
640   {
641     pName ++;
642   }
643   status = FindFile (DeviceExt, &Fcb, pDirFcb, pName, &Entry, &startEntry);
644
645   if (NT_SUCCESS(status))
646   {
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++)
653     {
654       if (Context == NULL || ((i * sizeof(FATDirEntry)) % PAGE_SIZE) == 0)
655       {
656         if (Context)
657         {
658           CcSetDirtyPinnedData(Context, NULL);
659           CcUnpinData(Context);
660         }
661         Offset.u.LowPart = (i * sizeof(FATDirEntry) / PAGE_SIZE) * PAGE_SIZE;
662         CcMapData (pDirFcb->FileObject, &Offset, PAGE_SIZE, TRUE,
663                    &Context, (PVOID*)&pDirEntry);
664       }
665       pDirEntry[i % (PAGE_SIZE / sizeof(FATDirEntry))].Filename[0] = 0xe5;
666       if (i == Entry)
667       {
668         CurrentCluster =
669           vfatDirEntryGetFirstCluster (DeviceExt,
670             &pDirEntry[i % (PAGE_SIZE / sizeof(FATDirEntry))]);
671       }
672     }
673     if (Context)
674     {
675       CcSetDirtyPinnedData(Context, NULL);
676       CcUnpinData(Context);
677     }
678
679     while (CurrentCluster && CurrentCluster != 0xffffffff)
680     {
681       GetNextCluster (DeviceExt, CurrentCluster, &NextCluster, FALSE);
682       // FIXME: check status
683       WriteCluster(DeviceExt, CurrentCluster, 0);
684       CurrentCluster = NextCluster;
685     }
686   }
687   vfatReleaseFCB(DeviceExt, pDirFcb);
688   return status;
689 }
690
691 /* EOF */