35a74793f60bc17efe25a362212451a03380b36c
[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 #define DWORD_ROUND_UP(x)   ROUND_UP((x), (sizeof(DWORD)))
79
80 NTSTATUS
81 VfatGetFileNameInformation (PVFATFCB pFcb,
82                            PFILE_NAMES_INFORMATION pInfo, ULONG BufferLength)
83 {
84   ULONG Length;
85   Length = wcslen (pFcb->ObjectName) * sizeof(WCHAR);
86   if ((sizeof (FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
87     return STATUS_BUFFER_OVERFLOW;
88   pInfo->FileNameLength = Length;
89   pInfo->NextEntryOffset =
90     DWORD_ROUND_UP (sizeof (FILE_DIRECTORY_INFORMATION) + Length);
91   memcpy (pInfo->FileName, pFcb->ObjectName, Length);
92   return STATUS_SUCCESS;
93 }
94
95 NTSTATUS
96 VfatGetFileDirectoryInformation (PVFATFCB pFcb,
97                                 PDEVICE_EXTENSION DeviceExt,
98                                 PFILE_DIRECTORY_INFORMATION pInfo,
99                                 ULONG BufferLength)
100 {
101   ULONG Length;
102   Length = wcslen (pFcb->ObjectName) * sizeof(WCHAR);
103   if ((sizeof (FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
104     return STATUS_BUFFER_OVERFLOW;
105   pInfo->FileNameLength = Length;
106   pInfo->NextEntryOffset =
107     DWORD_ROUND_UP (sizeof (FILE_DIRECTORY_INFORMATION) + Length);
108   memcpy (pInfo->FileName, pFcb->ObjectName, Length);
109 //      pInfo->FileIndex=;
110   FsdDosDateTimeToFileTime (pFcb->entry.CreationDate,
111                             pFcb->entry.CreationTime, &pInfo->CreationTime);
112   FsdDosDateTimeToFileTime (pFcb->entry.AccessDate, 0,
113                             &pInfo->LastAccessTime);
114   FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
115                             &pInfo->LastWriteTime);
116   pInfo->ChangeTime = pInfo->LastWriteTime;
117   pInfo->EndOfFile.u.HighPart = 0;
118   pInfo->EndOfFile.u.LowPart = pFcb->entry.FileSize;
119   /* Make allocsize a rounded up multiple of BytesPerCluster */
120   pInfo->AllocationSize.u.HighPart = 0;
121   pInfo->AllocationSize.u.LowPart = ROUND_UP(pFcb->entry.FileSize, DeviceExt->FatInfo.BytesPerCluster);
122   pInfo->FileAttributes = pFcb->entry.Attrib;
123
124   return STATUS_SUCCESS;
125 }
126
127 NTSTATUS
128 VfatGetFileFullDirectoryInformation (PVFATFCB pFcb,
129                                     PDEVICE_EXTENSION DeviceExt,
130                                     PFILE_FULL_DIRECTORY_INFORMATION pInfo,
131                                     ULONG BufferLength)
132 {
133   ULONG Length;
134   Length = wcslen (pFcb->ObjectName) * sizeof(WCHAR);
135   if ((sizeof (FILE_FULL_DIRECTORY_INFORMATION) + Length) > BufferLength)
136     return STATUS_BUFFER_OVERFLOW;
137   pInfo->FileNameLength = Length;
138   pInfo->NextEntryOffset =
139     DWORD_ROUND_UP (sizeof (FILE_FULL_DIRECTORY_INFORMATION) + Length);
140   memcpy (pInfo->FileName, pFcb->ObjectName, Length);
141 //      pInfo->FileIndex=;
142   FsdDosDateTimeToFileTime (pFcb->entry.CreationDate,
143                             pFcb->entry.CreationTime, &pInfo->CreationTime);
144   FsdDosDateTimeToFileTime (pFcb->entry.AccessDate, 0,
145                             &pInfo->LastAccessTime);
146   FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
147                             &pInfo->LastWriteTime);
148   pInfo->ChangeTime = pInfo->LastWriteTime;
149   pInfo->EndOfFile.u.HighPart = 0;
150   pInfo->EndOfFile.u.LowPart = pFcb->entry.FileSize;
151   /* Make allocsize a rounded up multiple of BytesPerCluster */
152   pInfo->AllocationSize.u.HighPart = 0;
153   pInfo->AllocationSize.u.LowPart = ROUND_UP(pFcb->entry.FileSize, DeviceExt->FatInfo.BytesPerCluster);
154   pInfo->FileAttributes = pFcb->entry.Attrib;
155 //      pInfo->EaSize=;
156   return STATUS_SUCCESS;
157 }
158
159 NTSTATUS
160 VfatGetFileBothInformation (PVFATFCB pFcb,
161                            PDEVICE_EXTENSION DeviceExt,
162                            PFILE_BOTH_DIRECTORY_INFORMATION pInfo,
163                            ULONG BufferLength)
164 {
165   ULONG Length;
166   Length = wcslen (pFcb->ObjectName) * sizeof(WCHAR);
167   if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
168     return STATUS_BUFFER_OVERFLOW;
169   pInfo->FileNameLength = Length;
170   pInfo->NextEntryOffset = 
171     DWORD_ROUND_UP (sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length);
172   /* 
173    * vfatGetDirEntryName must be called befor the long name is copyed.
174    * The terminating null will overwrite the first character from long name. 
175    */
176   vfatGetDirEntryName(&pFcb->entry, pInfo->ShortName);
177   pInfo->ShortNameLength = wcslen(pInfo->ShortName) * sizeof(WCHAR);
178   memcpy (pInfo->FileName, pFcb->ObjectName, Length);
179 //      pInfo->FileIndex=;
180   FsdDosDateTimeToFileTime (pFcb->entry.CreationDate,
181                             pFcb->entry.CreationTime, &pInfo->CreationTime);
182   FsdDosDateTimeToFileTime (pFcb->entry.AccessDate, 0,
183                             &pInfo->LastAccessTime);
184   FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
185                             &pInfo->LastWriteTime);
186   pInfo->ChangeTime = pInfo->LastWriteTime;
187   pInfo->EndOfFile.u.HighPart = 0;
188   pInfo->EndOfFile.u.LowPart = pFcb->entry.FileSize;
189   /* Make allocsize a rounded up multiple of BytesPerCluster */
190   pInfo->AllocationSize.u.HighPart = 0;
191   pInfo->AllocationSize.u.LowPart = ROUND_UP(pFcb->entry.FileSize, DeviceExt->FatInfo.BytesPerCluster);
192   pInfo->FileAttributes = pFcb->entry.Attrib;
193 //      pInfo->EaSize=;
194   return STATUS_SUCCESS;
195 }
196
197 NTSTATUS DoQuery (PVFAT_IRP_CONTEXT IrpContext)
198 {
199   NTSTATUS RC = STATUS_SUCCESS;
200   long BufferLength = 0;
201   PUNICODE_STRING pSearchPattern = NULL;
202   FILE_INFORMATION_CLASS FileInformationClass;
203   unsigned long FileIndex = 0;
204   unsigned char *Buffer = NULL;
205   PFILE_NAMES_INFORMATION Buffer0 = NULL;
206   PVFATFCB pFcb;
207   VFATFCB tmpFcb;
208   PVFATCCB pCcb;
209   BOOLEAN First = FALSE;
210   
211   PEXTENDED_IO_STACK_LOCATION Stack = (PEXTENDED_IO_STACK_LOCATION) IrpContext->Stack;
212
213   pCcb = (PVFATCCB) IrpContext->FileObject->FsContext2;
214   pFcb = (PVFATFCB) IrpContext->FileObject->FsContext;
215
216   if (!ExAcquireResourceSharedLite(&pFcb->MainResource,
217                                    (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
218   {
219      return STATUS_PENDING;
220   }
221
222   // Obtain the callers parameters
223   BufferLength = Stack->Parameters.QueryDirectory.Length;
224   pSearchPattern = Stack->Parameters.QueryDirectory.FileName;
225   FileInformationClass =
226     Stack->Parameters.QueryDirectory.FileInformationClass;
227   FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
228   if (pSearchPattern)
229   {
230     if (!pCcb->DirectorySearchPattern)
231     {
232       First = TRUE;
233       pCcb->DirectorySearchPattern =
234         ExAllocatePool(NonPagedPool, pSearchPattern->Length + sizeof(WCHAR));
235       if (!pCcb->DirectorySearchPattern)
236       {
237         ExReleaseResourceLite(&pFcb->MainResource);
238         return STATUS_INSUFFICIENT_RESOURCES;
239       }
240       memcpy(pCcb->DirectorySearchPattern, pSearchPattern->Buffer,
241         pSearchPattern->Length);
242       pCcb->DirectorySearchPattern[pSearchPattern->Length / sizeof(WCHAR)] = 0;
243     }
244   }
245   else if (!pCcb->DirectorySearchPattern)
246   {
247     First = TRUE;
248     pCcb->DirectorySearchPattern = ExAllocatePool(NonPagedPool, 2 * sizeof(WCHAR));
249     if (!pCcb->DirectorySearchPattern)
250     {
251       ExReleaseResourceLite(&pFcb->MainResource);
252       return STATUS_INSUFFICIENT_RESOURCES;
253     }
254     pCcb->DirectorySearchPattern[0] = L'*';
255     pCcb->DirectorySearchPattern[1] = 0;
256   }
257
258   if (IrpContext->Stack->Flags & SL_INDEX_SPECIFIED)
259   {
260     pCcb->Entry = pCcb->CurrentByteOffset.u.LowPart;
261   }
262   else if (First || (IrpContext->Stack->Flags & SL_RESTART_SCAN))
263   {
264     pCcb->Entry = 0;
265   }
266   // determine Buffer for result :
267   if (IrpContext->Irp->MdlAddress)
268   {
269     Buffer = MmGetSystemAddressForMdl (IrpContext->Irp->MdlAddress);
270   }
271   else
272   {
273     Buffer = IrpContext->Irp->UserBuffer;
274   }
275   DPRINT ("Buffer=%x tofind=%S\n", Buffer, pCcb->DirectorySearchPattern);
276
277   tmpFcb.ObjectName = tmpFcb.PathName;
278   while (RC == STATUS_SUCCESS && BufferLength > 0)
279   {
280     RC = FindFile (IrpContext->DeviceExt, &tmpFcb, pFcb, 
281            pCcb->DirectorySearchPattern, &pCcb->Entry, NULL);
282     DPRINT ("Found %S, RC=%x, entry %x\n", tmpFcb.ObjectName, RC, pCcb->Entry);
283     if (NT_SUCCESS (RC))
284     {
285       switch (FileInformationClass)
286       {
287         case FileNameInformation:
288           RC = VfatGetFileNameInformation (&tmpFcb,
289             (PFILE_NAMES_INFORMATION) Buffer, BufferLength);
290           break;
291         case FileDirectoryInformation:
292           RC = VfatGetFileDirectoryInformation (&tmpFcb, IrpContext->DeviceExt,
293                  (PFILE_DIRECTORY_INFORMATION) Buffer, BufferLength);
294           break;
295         case FileFullDirectoryInformation:
296           RC = VfatGetFileFullDirectoryInformation (&tmpFcb, IrpContext->DeviceExt,
297                  (PFILE_FULL_DIRECTORY_INFORMATION) Buffer, BufferLength);
298           break;
299         case FileBothDirectoryInformation:
300           RC = VfatGetFileBothInformation (&tmpFcb, IrpContext->DeviceExt,
301                  (PFILE_BOTH_DIRECTORY_INFORMATION) Buffer, BufferLength);
302           break;
303         default:
304           RC = STATUS_INVALID_INFO_CLASS;
305       }
306       if (RC == STATUS_BUFFER_OVERFLOW)
307       {
308         if (Buffer0)
309         {
310           Buffer0->NextEntryOffset = 0;
311         }
312         break;
313       }
314     }
315     else
316     {
317       if (Buffer0)
318       {
319         Buffer0->NextEntryOffset = 0;
320       }
321       if (First)
322       {
323         RC = STATUS_NO_SUCH_FILE;
324       }
325       else
326       {
327         RC = STATUS_NO_MORE_FILES;
328       }
329       break;
330     }
331     Buffer0 = (PFILE_NAMES_INFORMATION) Buffer;
332     Buffer0->FileIndex = FileIndex++;
333     pCcb->Entry++;
334     if (IrpContext->Stack->Flags & SL_RETURN_SINGLE_ENTRY)
335     {
336       break;
337     }
338     BufferLength -= Buffer0->NextEntryOffset;
339     Buffer += Buffer0->NextEntryOffset;
340   }
341   if (Buffer0)
342   {
343     Buffer0->NextEntryOffset = 0;
344   }
345   if (FileIndex > 0)
346   {
347     RC = STATUS_SUCCESS;
348   }
349   ExReleaseResourceLite(&pFcb->MainResource);
350   return RC;
351 }
352
353
354 NTSTATUS VfatDirectoryControl (PVFAT_IRP_CONTEXT IrpContext)
355 /*
356  * FUNCTION: directory control : read/write directory informations
357  */
358 {
359   NTSTATUS RC = STATUS_SUCCESS;
360   CHECKPOINT;
361   switch (IrpContext->MinorFunction)
362     {
363     case IRP_MN_QUERY_DIRECTORY:
364       RC = DoQuery (IrpContext);
365       break;
366     case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
367       DPRINT (" vfat, dir : change\n");
368       RC = STATUS_NOT_IMPLEMENTED;
369       break;
370     default:
371       // error
372       DbgPrint ("unexpected minor function %x in VFAT driver\n",
373                 IrpContext->MinorFunction);
374       RC = STATUS_INVALID_DEVICE_REQUEST;
375       break;
376     }
377   if (RC == STATUS_PENDING)
378   {
379      RC = VfatQueueRequest(IrpContext);
380   }
381   else
382   {
383     IrpContext->Irp->IoStatus.Status = RC;
384     IrpContext->Irp->IoStatus.Information = 0;
385     IoCompleteRequest (IrpContext->Irp, IO_NO_INCREMENT);
386     VfatFreeIrpContext(IrpContext);
387   }
388   return RC;
389 }
390
391