branch update for HEAD-2003021201
[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   pCcb = (PVFATCCB) IrpContext->FileObject->FsContext2;
212   pFcb = pCcb->pFcb;
213
214   if (!ExAcquireResourceSharedLite(&pFcb->MainResource, IrpContext->Flags & IRPCONTEXT_CANWAIT))
215   {
216      return STATUS_PENDING;
217   }
218
219   // Obtain the callers parameters
220   BufferLength = IrpContext->Stack->Parameters.QueryDirectory.Length;
221   pSearchPattern = IrpContext->Stack->Parameters.QueryDirectory.FileName;
222   FileInformationClass =
223     IrpContext->Stack->Parameters.QueryDirectory.FileInformationClass;
224   FileIndex = IrpContext->Stack->Parameters.QueryDirectory.FileIndex;
225   if (pSearchPattern)
226   {
227     if (!pCcb->DirectorySearchPattern)
228     {
229       First = TRUE;
230       pCcb->DirectorySearchPattern =
231         ExAllocatePool(NonPagedPool, pSearchPattern->Length + sizeof(WCHAR));
232       if (!pCcb->DirectorySearchPattern)
233       {
234         ExReleaseResourceLite(&pFcb->MainResource);
235         return STATUS_INSUFFICIENT_RESOURCES;
236       }
237       memcpy(pCcb->DirectorySearchPattern, pSearchPattern->Buffer,
238         pSearchPattern->Length);
239       pCcb->DirectorySearchPattern[pSearchPattern->Length / sizeof(WCHAR)] = 0;
240     }
241   }
242   else if (!pCcb->DirectorySearchPattern)
243   {
244     First = TRUE;
245     pCcb->DirectorySearchPattern = ExAllocatePool(NonPagedPool, 2 * sizeof(WCHAR));
246     if (!pCcb->DirectorySearchPattern)
247     {
248       ExReleaseResourceLite(&pFcb->MainResource);
249       return STATUS_INSUFFICIENT_RESOURCES;
250     }
251     pCcb->DirectorySearchPattern[0] = L'*';
252     pCcb->DirectorySearchPattern[1] = 0;
253   }
254
255   if (IrpContext->Stack->Flags & SL_INDEX_SPECIFIED)
256   {
257     pCcb->Entry = pCcb->CurrentByteOffset.u.LowPart;
258   }
259   else if (First || (IrpContext->Stack->Flags & SL_RESTART_SCAN))
260   {
261     pCcb->Entry = 0;
262   }
263   // determine Buffer for result :
264   if (IrpContext->Irp->MdlAddress)
265   {
266     Buffer = MmGetSystemAddressForMdl (IrpContext->Irp->MdlAddress);
267   }
268   else
269   {
270     Buffer = IrpContext->Irp->UserBuffer;
271   }
272   DPRINT ("Buffer=%x tofind=%S\n", Buffer, pCcb->DirectorySearchPattern);
273
274   tmpFcb.ObjectName = tmpFcb.PathName;
275   while (RC == STATUS_SUCCESS && BufferLength > 0)
276   {
277     RC = FindFile (IrpContext->DeviceExt, &tmpFcb, pFcb, 
278            pCcb->DirectorySearchPattern, &pCcb->Entry, NULL);
279     DPRINT ("Found %S, RC=%x, entry %x\n", tmpFcb.ObjectName, RC, pCcb->Entry);
280     if (NT_SUCCESS (RC))
281     {
282       switch (FileInformationClass)
283       {
284         case FileNameInformation:
285           RC = VfatGetFileNameInformation (&tmpFcb,
286             (PFILE_NAMES_INFORMATION) Buffer, BufferLength);
287           break;
288         case FileDirectoryInformation:
289           RC = VfatGetFileDirectoryInformation (&tmpFcb, IrpContext->DeviceExt,
290                  (PFILE_DIRECTORY_INFORMATION) Buffer, BufferLength);
291           break;
292         case FileFullDirectoryInformation:
293           RC = VfatGetFileFullDirectoryInformation (&tmpFcb, IrpContext->DeviceExt,
294                  (PFILE_FULL_DIRECTORY_INFORMATION) Buffer, BufferLength);
295           break;
296         case FileBothDirectoryInformation:
297           RC = VfatGetFileBothInformation (&tmpFcb, IrpContext->DeviceExt,
298                  (PFILE_BOTH_DIRECTORY_INFORMATION) Buffer, BufferLength);
299           break;
300         default:
301           RC = STATUS_INVALID_INFO_CLASS;
302       }
303       if (RC == STATUS_BUFFER_OVERFLOW)
304       {
305         if (Buffer0)
306         {
307           Buffer0->NextEntryOffset = 0;
308         }
309         break;
310       }
311     }
312     else
313     {
314       if (Buffer0)
315       {
316         Buffer0->NextEntryOffset = 0;
317       }
318       if (First)
319       {
320         RC = STATUS_NO_SUCH_FILE;
321       }
322       else
323       {
324         RC = STATUS_NO_MORE_FILES;
325       }
326       break;
327     }
328     Buffer0 = (PFILE_NAMES_INFORMATION) Buffer;
329     Buffer0->FileIndex = FileIndex++;
330     pCcb->Entry++;
331     if (IrpContext->Stack->Flags & SL_RETURN_SINGLE_ENTRY)
332     {
333       break;
334     }
335     BufferLength -= Buffer0->NextEntryOffset;
336     Buffer += Buffer0->NextEntryOffset;
337   }
338   if (Buffer0)
339   {
340     Buffer0->NextEntryOffset = 0;
341   }
342   if (FileIndex > 0)
343   {
344     RC = STATUS_SUCCESS;
345   }
346   ExReleaseResourceLite(&pFcb->MainResource);
347   return RC;
348 }
349
350
351 NTSTATUS VfatDirectoryControl (PVFAT_IRP_CONTEXT IrpContext)
352 /*
353  * FUNCTION: directory control : read/write directory informations
354  */
355 {
356   NTSTATUS RC = STATUS_SUCCESS;
357   CHECKPOINT;
358   switch (IrpContext->MinorFunction)
359     {
360     case IRP_MN_QUERY_DIRECTORY:
361       RC = DoQuery (IrpContext);
362       break;
363     case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
364       DPRINT (" vfat, dir : change\n");
365       RC = STATUS_NOT_IMPLEMENTED;
366       break;
367     default:
368       // error
369       DbgPrint ("unexpected minor function %x in VFAT driver\n",
370                 IrpContext->MinorFunction);
371       RC = STATUS_INVALID_DEVICE_REQUEST;
372       break;
373     }
374   if (RC == STATUS_PENDING)
375   {
376      RC = VfatQueueRequest(IrpContext);
377   }
378   else
379   {
380     IrpContext->Irp->IoStatus.Status = RC;
381     IrpContext->Irp->IoStatus.Information = 0;
382     IoCompleteRequest (IrpContext->Irp, IO_NO_INCREMENT);
383     VfatFreeIrpContext(IrpContext);
384   }
385   return RC;
386 }
387
388