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