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