:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / drivers / fs / vfat / dir.c
1 /*
2  * $Id$
3  *
4  * COPYRIGHT:        See COPYING in the top level directory
5  * PROJECT:          ReactOS kernel
6  * FILE:             services/fs/vfat/dir.c
7  * PURPOSE:          VFAT Filesystem : directory control
8  * UPDATE HISTORY:
9      19-12-1998 : created
10
11 */
12
13 #include <ddk/ntddk.h>
14 #include <wchar.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 #include "vfat.h"
20
21
22 // function like DosDateTimeToFileTime
23 BOOL FsdDosDateTimeToFileTime (WORD wDosDate, WORD wDosTime, TIME * FileTime)
24 {
25   PDOSTIME pdtime = (PDOSTIME) & wDosTime;
26   PDOSDATE pddate = (PDOSDATE) & wDosDate;
27   TIME_FIELDS TimeFields;
28
29   if (FileTime == NULL)
30     return FALSE;
31
32   TimeFields.Milliseconds = 0;
33   TimeFields.Second = pdtime->Second * 2;
34   TimeFields.Minute = pdtime->Minute;
35   TimeFields.Hour = pdtime->Hour;
36
37   TimeFields.Day = pddate->Day;
38   TimeFields.Month = pddate->Month;
39   TimeFields.Year = 1980 + pddate->Year;
40
41   RtlTimeFieldsToTime (&TimeFields, (PLARGE_INTEGER) FileTime);
42
43   return TRUE;
44 }
45
46
47 // function like FileTimeToDosDateTime
48 BOOL
49 FsdFileTimeToDosDateTime (TIME * FileTime, WORD * pwDosDate, WORD * pwDosTime)
50 {
51   PDOSTIME pdtime = (PDOSTIME) pwDosTime;
52   PDOSDATE pddate = (PDOSDATE) pwDosDate;
53   TIME_FIELDS TimeFields;
54
55   if (FileTime == NULL)
56     return FALSE;
57
58   RtlTimeToTimeFields ((PLARGE_INTEGER) FileTime, &TimeFields);
59
60   if (pdtime)
61     {
62       pdtime->Second = TimeFields.Second / 2;
63       pdtime->Minute = TimeFields.Minute;
64       pdtime->Hour = TimeFields.Hour;
65     }
66
67   if (pddate)
68     {
69       pddate->Day = TimeFields.Day;
70       pddate->Month = TimeFields.Month;
71       pddate->Year = TimeFields.Year - 1980;
72     }
73
74   return TRUE;
75 }
76
77
78
79 unsigned long
80 vfat_wstrlen (PWSTR s)
81 {
82   WCHAR c = ' ';
83   unsigned int len = 0;
84
85   while (c != 0)
86     {
87       c = *s;
88       s++;
89       len++;
90     };
91   s -= len;
92
93   return len - 1;
94 }
95
96 #define DWORD_ROUND_UP(x) ( (((ULONG)(x))%32) ? ((((ULONG)x)&(~0x1f))+0x20) : ((ULONG)x) )
97
98 NTSTATUS
99 VfatGetFileNameInformation (PVFATFCB pFcb,
100                            PFILE_NAMES_INFORMATION pInfo, ULONG BufferLength)
101 {
102   ULONG Length;
103   Length = vfat_wstrlen (pFcb->ObjectName) * sizeof(WCHAR);
104   if ((sizeof (FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
105     return STATUS_BUFFER_OVERFLOW;
106   pInfo->FileNameLength = Length;
107   pInfo->NextEntryOffset =
108     DWORD_ROUND_UP (sizeof (FILE_DIRECTORY_INFORMATION) + Length);
109   memcpy (pInfo->FileName, pFcb->ObjectName, Length);
110   return STATUS_SUCCESS;
111 }
112
113 NTSTATUS
114 VfatGetFileDirectoryInformation (PVFATFCB pFcb,
115                                 PDEVICE_EXTENSION DeviceExt,
116                                 PFILE_DIRECTORY_INFORMATION pInfo,
117                                 ULONG BufferLength)
118 {
119   unsigned long long AllocSize;
120   ULONG Length;
121   Length = vfat_wstrlen (pFcb->ObjectName) * sizeof(WCHAR);
122   if ((sizeof (FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
123     return STATUS_BUFFER_OVERFLOW;
124   pInfo->FileNameLength = Length;
125   pInfo->NextEntryOffset =
126     DWORD_ROUND_UP (sizeof (FILE_DIRECTORY_INFORMATION) + Length);
127   memcpy (pInfo->FileName, pFcb->ObjectName, Length);
128 //      pInfo->FileIndex=;
129   FsdDosDateTimeToFileTime (pFcb->entry.CreationDate,
130                             pFcb->entry.CreationTime, &pInfo->CreationTime);
131   FsdDosDateTimeToFileTime (pFcb->entry.AccessDate, 0,
132                             &pInfo->LastAccessTime);
133   FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
134                             &pInfo->LastWriteTime);
135   FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
136                             &pInfo->ChangeTime);
137   pInfo->EndOfFile = RtlConvertUlongToLargeInteger (pFcb->entry.FileSize);
138   /* Make allocsize a rounded up multiple of BytesPerCluster */
139   AllocSize = ((pFcb->entry.FileSize + DeviceExt->FatInfo.BytesPerCluster - 1) /
140                DeviceExt->FatInfo.BytesPerCluster) * DeviceExt->FatInfo.BytesPerCluster;
141   pInfo->AllocationSize.QuadPart = AllocSize;
142   pInfo->FileAttributes = pFcb->entry.Attrib;
143
144   return STATUS_SUCCESS;
145 }
146
147 NTSTATUS
148 VfatGetFileFullDirectoryInformation (PVFATFCB pFcb,
149                                     PDEVICE_EXTENSION DeviceExt,
150                                     PFILE_FULL_DIRECTORY_INFORMATION pInfo,
151                                     ULONG BufferLength)
152 {
153   unsigned long long AllocSize;
154   ULONG Length;
155   Length = vfat_wstrlen (pFcb->ObjectName) * sizeof(WCHAR);
156   if ((sizeof (FILE_FULL_DIRECTORY_INFORMATION) + Length) > BufferLength)
157     return STATUS_BUFFER_OVERFLOW;
158   pInfo->FileNameLength = Length;
159   pInfo->NextEntryOffset =
160     DWORD_ROUND_UP (sizeof (FILE_FULL_DIRECTORY_INFORMATION) + Length);
161   memcpy (pInfo->FileName, pFcb->ObjectName, Length);
162 //      pInfo->FileIndex=;
163   FsdDosDateTimeToFileTime (pFcb->entry.CreationDate,
164                             pFcb->entry.CreationTime, &pInfo->CreationTime);
165   FsdDosDateTimeToFileTime (pFcb->entry.AccessDate, 0,
166                             &pInfo->LastAccessTime);
167   FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
168                             &pInfo->LastWriteTime);
169   FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
170                             &pInfo->ChangeTime);
171   pInfo->EndOfFile = RtlConvertUlongToLargeInteger (pFcb->entry.FileSize);
172   /* Make allocsize a rounded up multiple of BytesPerCluster */
173   AllocSize = ((pFcb->entry.FileSize + DeviceExt->FatInfo.BytesPerCluster - 1) /
174                DeviceExt->FatInfo.BytesPerCluster) * DeviceExt->FatInfo.BytesPerCluster;
175   pInfo->AllocationSize.QuadPart = AllocSize;
176   pInfo->FileAttributes = pFcb->entry.Attrib;
177 //      pInfo->EaSize=;
178   return STATUS_SUCCESS;
179 }
180
181 NTSTATUS
182 VfatGetFileBothInformation (PVFATFCB pFcb,
183                            PDEVICE_EXTENSION DeviceExt,
184                            PFILE_BOTH_DIRECTORY_INFORMATION pInfo,
185                            ULONG BufferLength)
186 {
187   short i;
188   unsigned long long AllocSize;
189   ULONG Length;
190   Length = vfat_wstrlen (pFcb->ObjectName) * sizeof(WCHAR);
191   if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
192     return STATUS_BUFFER_OVERFLOW;
193   pInfo->FileNameLength = Length;
194   pInfo->NextEntryOffset =
195     DWORD_ROUND_UP (sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length);
196   memcpy (pInfo->FileName, pFcb->ObjectName, Length);
197 //      pInfo->FileIndex=;
198   FsdDosDateTimeToFileTime (pFcb->entry.CreationDate,
199                             pFcb->entry.CreationTime, &pInfo->CreationTime);
200   FsdDosDateTimeToFileTime (pFcb->entry.AccessDate, 0,
201                             &pInfo->LastAccessTime);
202   FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
203                             &pInfo->LastWriteTime);
204   FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
205                             &pInfo->ChangeTime);
206   pInfo->EndOfFile = RtlConvertUlongToLargeInteger (pFcb->entry.FileSize);
207   /* Make allocsize a rounded up multiple of BytesPerCluster */
208   AllocSize = ((pFcb->entry.FileSize + DeviceExt->FatInfo.BytesPerCluster - 1) /
209                DeviceExt->FatInfo.BytesPerCluster) * DeviceExt->FatInfo.BytesPerCluster;
210   pInfo->AllocationSize.QuadPart = AllocSize;
211   pInfo->FileAttributes = pFcb->entry.Attrib;
212 //      pInfo->EaSize=;
213   for (i = 0; i < 8 && (pFcb->entry.Filename[i] != ' '); i++)
214     pInfo->ShortName[i] = pFcb->entry.Filename[i];
215   pInfo->ShortNameLength = i;
216   pInfo->ShortName[i] = '.';
217   for (i = 0; i < 3 && (pFcb->entry.Ext[i] != ' '); i++)
218     pInfo->ShortName[i + 1 + pInfo->ShortNameLength] = pFcb->entry.Ext[i];
219   if (i)
220     pInfo->ShortNameLength += (i + 1);
221   pInfo->ShortNameLength *= sizeof(WCHAR);
222   return STATUS_SUCCESS;
223 }
224
225 NTSTATUS DoQuery (PVFAT_IRP_CONTEXT IrpContext)
226 {
227   NTSTATUS RC = STATUS_SUCCESS;
228   long BufferLength = 0;
229   PUNICODE_STRING pSearchPattern = NULL;
230   FILE_INFORMATION_CLASS FileInformationClass;
231   unsigned long FileIndex = 0;
232   unsigned char *Buffer = NULL;
233   PFILE_NAMES_INFORMATION Buffer0 = NULL;
234   PVFATFCB pFcb;
235   VFATFCB tmpFcb;
236   PVFATCCB pCcb;
237   BOOLEAN First = FALSE;
238
239   pCcb = (PVFATCCB) IrpContext->FileObject->FsContext2;
240   pFcb = pCcb->pFcb;
241
242   if (!ExAcquireResourceSharedLite(&pFcb->MainResource, IrpContext->Flags & IRPCONTEXT_CANWAIT))
243   {
244      return STATUS_PENDING;
245   }
246
247   // Obtain the callers parameters
248   BufferLength = IrpContext->Stack->Parameters.QueryDirectory.Length;
249   pSearchPattern = IrpContext->Stack->Parameters.QueryDirectory.FileName;
250   FileInformationClass =
251     IrpContext->Stack->Parameters.QueryDirectory.FileInformationClass;
252   FileIndex = IrpContext->Stack->Parameters.QueryDirectory.FileIndex;
253   if (pSearchPattern)
254   {
255     if (!pCcb->DirectorySearchPattern)
256     {
257       First = TRUE;
258       pCcb->DirectorySearchPattern =
259         ExAllocatePool(NonPagedPool, pSearchPattern->Length + sizeof(WCHAR));
260       if (!pCcb->DirectorySearchPattern)
261       {
262         ExReleaseResourceLite(&pFcb->MainResource);
263         return STATUS_INSUFFICIENT_RESOURCES;
264       }
265       memcpy(pCcb->DirectorySearchPattern, pSearchPattern->Buffer,
266         pSearchPattern->Length);
267       pCcb->DirectorySearchPattern[pSearchPattern->Length / sizeof(WCHAR)] = 0;
268     }
269   }
270   else if (!pCcb->DirectorySearchPattern)
271   {
272     First = TRUE;
273     pCcb->DirectorySearchPattern = ExAllocatePool(NonPagedPool, 2 * sizeof(WCHAR));
274     if (!pCcb->DirectorySearchPattern)
275     {
276       ExReleaseResourceLite(&pFcb->MainResource);
277       return STATUS_INSUFFICIENT_RESOURCES;
278     }
279     pCcb->DirectorySearchPattern[0] = L'*';
280     pCcb->DirectorySearchPattern[1] = 0;
281   }
282
283   if (IrpContext->Stack->Flags & SL_INDEX_SPECIFIED)
284   {
285     pCcb->Entry = pCcb->CurrentByteOffset.u.LowPart;
286   }
287   else if (First || (IrpContext->Stack->Flags & SL_RESTART_SCAN))
288   {
289     pCcb->Entry = 0;
290   }
291   // determine Buffer for result :
292   if (IrpContext->Irp->MdlAddress)
293   {
294     Buffer = MmGetSystemAddressForMdl (IrpContext->Irp->MdlAddress);
295   }
296   else
297   {
298     Buffer = IrpContext->Irp->UserBuffer;
299   }
300   DPRINT ("Buffer=%x tofind=%S\n", Buffer, pCcb->DirectorySearchPattern);
301
302   tmpFcb.ObjectName = tmpFcb.PathName;
303   while (RC == STATUS_SUCCESS && BufferLength > 0)
304   {
305     RC = FindFile (IrpContext->DeviceExt, &tmpFcb, pFcb, 
306            pCcb->DirectorySearchPattern, &pCcb->Entry, NULL);
307     DPRINT ("Found %S, RC=%x, entry %x\n", tmpFcb.ObjectName, RC, pCcb->Entry);
308     if (NT_SUCCESS (RC))
309     {
310       switch (FileInformationClass)
311       {
312         case FileNameInformation:
313           RC = VfatGetFileNameInformation (&tmpFcb,
314             (PFILE_NAMES_INFORMATION) Buffer, BufferLength);
315           break;
316         case FileDirectoryInformation:
317           RC = VfatGetFileDirectoryInformation (&tmpFcb, IrpContext->DeviceExt,
318                  (PFILE_DIRECTORY_INFORMATION) Buffer, BufferLength);
319           break;
320         case FileFullDirectoryInformation:
321           RC = VfatGetFileFullDirectoryInformation (&tmpFcb, IrpContext->DeviceExt,
322                  (PFILE_FULL_DIRECTORY_INFORMATION) Buffer, BufferLength);
323           break;
324         case FileBothDirectoryInformation:
325           RC = VfatGetFileBothInformation (&tmpFcb, IrpContext->DeviceExt,
326                  (PFILE_BOTH_DIRECTORY_INFORMATION) Buffer, BufferLength);
327           break;
328         default:
329           RC = STATUS_INVALID_INFO_CLASS;
330       }
331       if (RC == STATUS_BUFFER_OVERFLOW)
332       {
333         if (Buffer0)
334         {
335           Buffer0->NextEntryOffset = 0;
336         }
337         break;
338       }
339     }
340     else
341     {
342       if (Buffer0)
343       {
344         Buffer0->NextEntryOffset = 0;
345       }
346       if (First)
347       {
348         RC = STATUS_NO_SUCH_FILE;
349       }
350       else
351       {
352         RC = STATUS_NO_MORE_FILES;
353       }
354       break;
355     }
356     Buffer0 = (PFILE_NAMES_INFORMATION) Buffer;
357     Buffer0->FileIndex = FileIndex++;
358     pCcb->Entry++;
359     if (IrpContext->Stack->Flags & SL_RETURN_SINGLE_ENTRY)
360     {
361       break;
362     }
363     BufferLength -= Buffer0->NextEntryOffset;
364     Buffer += Buffer0->NextEntryOffset;
365   }
366   if (Buffer0)
367   {
368     Buffer0->NextEntryOffset = 0;
369   }
370   if (FileIndex > 0)
371   {
372     RC = STATUS_SUCCESS;
373   }
374   ExReleaseResourceLite(&pFcb->MainResource);
375   return RC;
376 }
377
378
379 NTSTATUS VfatDirectoryControl (PVFAT_IRP_CONTEXT IrpContext)
380 /*
381  * FUNCTION: directory control : read/write directory informations
382  */
383 {
384   NTSTATUS RC = STATUS_SUCCESS;
385   CHECKPOINT;
386   switch (IrpContext->MinorFunction)
387     {
388     case IRP_MN_QUERY_DIRECTORY:
389       RC = DoQuery (IrpContext);
390       break;
391     case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
392       DPRINT (" vfat, dir : change\n");
393       RC = STATUS_NOT_IMPLEMENTED;
394       break;
395     default:
396       // error
397       DbgPrint ("unexpected minor function %x in VFAT driver\n",
398                 IrpContext->MinorFunction);
399       RC = STATUS_INVALID_DEVICE_REQUEST;
400       break;
401     }
402   if (RC == STATUS_PENDING)
403   {
404      RC = VfatQueueRequest(IrpContext);
405   }
406   else
407   {
408     IrpContext->Irp->IoStatus.Status = RC;
409     IrpContext->Irp->IoStatus.Information = 0;
410     IoCompleteRequest (IrpContext->Irp, IO_NO_INCREMENT);
411     VfatFreeIrpContext(IrpContext);
412   }
413   return RC;
414 }
415
416