All:
[reactos.git] / reactos / drivers / fs / vfat / dir.c
1 /*
2 * $Id: dir.c,v 1.24 2002/03/18 22:37:12 hbirr Exp $
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