[NTFS] Fix directory listing with search pattern
[reactos.git] / reactos / drivers / filesystems / ntfs / dirctl.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2002,2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/ntfs/dirctl.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMER: Eric Kohl
24 */
25
26 /* INCLUDES *****************************************************************/
27
28 #include "ntfs.h"
29
30 #define NDEBUG
31 #include <debug.h>
32
33 /* FUNCTIONS ****************************************************************/
34
35 #if 0
36 static NTSTATUS
37 CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt,
38 PVOID *Context,
39 PVOID *Block,
40 PLARGE_INTEGER StreamOffset,
41 ULONG DirLength,
42 PVOID *Ptr,
43 PWSTR Name,
44 PULONG pIndex,
45 PULONG pIndex2)
46 /*
47 * FUNCTION: Retrieves the file name, be it in short or long file name format
48 */
49 {
50 PDIR_RECORD Record;
51 NTSTATUS Status;
52 ULONG Index = 0;
53 ULONG Offset = 0;
54 ULONG BlockOffset = 0;
55
56 Record = (PDIR_RECORD)*Block;
57 while(Index < *pIndex)
58 {
59 BlockOffset += Record->RecordLength;
60 Offset += Record->RecordLength;
61
62 Record = (PDIR_RECORD)(*Block + BlockOffset);
63 if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
64 {
65 DPRINT("Map next sector\n");
66 CcUnpinData(*Context);
67 StreamOffset->QuadPart += BLOCKSIZE;
68 Offset = ROUND_UP(Offset, BLOCKSIZE);
69 BlockOffset = 0;
70
71 if (!CcMapData(DeviceExt->StreamFileObject,
72 StreamOffset,
73 BLOCKSIZE, TRUE,
74 Context, Block))
75 {
76 DPRINT("CcMapData() failed\n");
77 return(STATUS_UNSUCCESSFUL);
78 }
79 Record = (PDIR_RECORD)(*Block + BlockOffset);
80 }
81
82 if (Offset >= DirLength)
83 return(STATUS_NO_MORE_ENTRIES);
84
85 Index++;
86 }
87
88 DPRINT("Index %lu RecordLength %lu Offset %lu\n",
89 Index, Record->RecordLength, Offset);
90
91 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
92 {
93 wcscpy(Name, L".");
94 }
95 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
96 {
97 wcscpy(Name, L"..");
98 }
99 else
100 {
101 if (DeviceExt->CdInfo.JolietLevel == 0)
102 {
103 ULONG i;
104
105 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
106 Name[i] = (WCHAR)Record->FileId[i];
107 Name[i] = 0;
108 }
109 else
110 {
111 CdfsSwapString(Name, Record->FileId, Record->FileIdLength);
112 }
113 }
114
115 DPRINT("Name '%S'\n", Name);
116
117 *Ptr = Record;
118
119 *pIndex = Index;
120
121 return(STATUS_SUCCESS);
122 }
123 #endif
124
125
126 static NTSTATUS
127 NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt,
128 PFILE_RECORD_HEADER FileRecord,
129 PNTFS_ATTR_CONTEXT DataContext,
130 PFILE_NAMES_INFORMATION Info,
131 ULONG BufferLength)
132 {
133 ULONG Length;
134 PFILENAME_ATTRIBUTE FileName;
135
136 DPRINT("NtfsGetNameInformation() called\n");
137
138 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
139 ASSERT(FileName != NULL);
140
141 Length = FileName->NameLength * sizeof (WCHAR);
142 if ((sizeof(FILE_NAMES_INFORMATION) + Length) > BufferLength)
143 return(STATUS_BUFFER_OVERFLOW);
144
145 Info->FileNameLength = Length;
146 Info->NextEntryOffset =
147 ROUND_UP(sizeof(FILE_NAMES_INFORMATION) + Length, sizeof(ULONG));
148 RtlCopyMemory(Info->FileName, FileName->Name, Length);
149
150 return(STATUS_SUCCESS);
151 }
152
153
154 static NTSTATUS
155 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
156 PFILE_RECORD_HEADER FileRecord,
157 PNTFS_ATTR_CONTEXT DataContext,
158 PFILE_DIRECTORY_INFORMATION Info,
159 ULONG BufferLength)
160 {
161 ULONG Length;
162 PFILENAME_ATTRIBUTE FileName;
163
164 DPRINT("NtfsGetDirectoryInformation() called\n");
165
166 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
167 ASSERT(FileName != NULL);
168
169 Length = FileName->NameLength * sizeof (WCHAR);
170 if ((sizeof(FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
171 return(STATUS_BUFFER_OVERFLOW);
172
173 Info->FileNameLength = Length;
174 Info->NextEntryOffset =
175 ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
176 RtlCopyMemory(Info->FileName, FileName->Name, Length);
177
178 Info->CreationTime.QuadPart = FileName->CreationTime;
179 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
180 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
181 Info->ChangeTime.QuadPart = FileName->ChangeTime;
182
183 /* Convert file flags */
184 NtfsFileFlagsToAttributes(FileName->FileAttributes, &Info->FileAttributes);
185
186 Info->EndOfFile.QuadPart = FileName->AllocatedSize;
187 Info->AllocationSize.QuadPart = ROUND_UP(FileName->AllocatedSize, DeviceExt->NtfsInfo.BytesPerCluster);
188
189 // Info->FileIndex=;
190
191 return STATUS_SUCCESS;
192 }
193
194
195 static NTSTATUS
196 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
197 PFILE_RECORD_HEADER FileRecord,
198 PNTFS_ATTR_CONTEXT DataContext,
199 PFILE_FULL_DIRECTORY_INFORMATION Info,
200 ULONG BufferLength)
201 {
202 ULONG Length;
203 PFILENAME_ATTRIBUTE FileName;
204
205 DPRINT("NtfsGetFullDirectoryInformation() called\n");
206
207 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
208 ASSERT(FileName != NULL);
209
210 Length = FileName->NameLength * sizeof (WCHAR);
211 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length) > BufferLength)
212 return(STATUS_BUFFER_OVERFLOW);
213
214 Info->FileNameLength = Length;
215 Info->NextEntryOffset =
216 ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
217 RtlCopyMemory(Info->FileName, FileName->Name, Length);
218
219 Info->CreationTime.QuadPart = FileName->CreationTime;
220 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
221 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
222 Info->ChangeTime.QuadPart = FileName->ChangeTime;
223
224 /* Convert file flags */
225 NtfsFileFlagsToAttributes(FileName->FileAttributes, &Info->FileAttributes);
226
227 Info->EndOfFile.QuadPart = FileName->AllocatedSize;
228 Info->AllocationSize.QuadPart = ROUND_UP(FileName->AllocatedSize, DeviceExt->NtfsInfo.BytesPerCluster);
229
230 // Info->FileIndex=;
231 Info->EaSize = 0;
232
233 return STATUS_SUCCESS;
234 }
235
236
237 static NTSTATUS
238 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
239 PFILE_RECORD_HEADER FileRecord,
240 PNTFS_ATTR_CONTEXT DataContext,
241 PFILE_BOTH_DIR_INFORMATION Info,
242 ULONG BufferLength)
243 {
244 ULONG Length;
245 PFILENAME_ATTRIBUTE FileName, ShortFileName;
246
247 DPRINT("NtfsGetBothDirectoryInformation() called\n");
248
249 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
250 ASSERT(FileName != NULL);
251 ShortFileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_DOS);
252
253 Length = FileName->NameLength * sizeof (WCHAR);
254 if ((sizeof(FILE_BOTH_DIR_INFORMATION) + Length) > BufferLength)
255 return(STATUS_BUFFER_OVERFLOW);
256
257 Info->FileNameLength = Length;
258 Info->NextEntryOffset =
259 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) + Length, sizeof(ULONG));
260 RtlCopyMemory(Info->FileName, FileName->Name, Length);
261
262 if (ShortFileName)
263 {
264 /* Should we upcase the filename? */
265 ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName));
266 Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR);
267 RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength);
268 }
269 else
270 {
271 Info->ShortName[0] = 0;
272 Info->ShortNameLength = 0;
273 }
274
275 Info->CreationTime.QuadPart = FileName->CreationTime;
276 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
277 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
278 Info->ChangeTime.QuadPart = FileName->ChangeTime;
279
280 /* Convert file flags */
281 NtfsFileFlagsToAttributes(FileName->FileAttributes, &Info->FileAttributes);
282
283 Info->EndOfFile.QuadPart = FileName->AllocatedSize;
284 Info->AllocationSize.QuadPart = ROUND_UP(FileName->AllocatedSize, DeviceExt->NtfsInfo.BytesPerCluster);
285
286 // Info->FileIndex=;
287 Info->EaSize = 0;
288
289 return STATUS_SUCCESS;
290 }
291
292
293 NTSTATUS
294 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
295 {
296 PIRP Irp;
297 PDEVICE_OBJECT DeviceObject;
298 PDEVICE_EXTENSION DeviceExtension;
299 LONG BufferLength = 0;
300 PUNICODE_STRING SearchPattern = NULL;
301 FILE_INFORMATION_CLASS FileInformationClass;
302 ULONG FileIndex = 0;
303 PUCHAR Buffer = NULL;
304 PFILE_NAMES_INFORMATION Buffer0 = NULL;
305 PNTFS_FCB Fcb;
306 PNTFS_CCB Ccb;
307 BOOLEAN First = FALSE;
308 BOOLEAN WildCard;
309 PIO_STACK_LOCATION Stack;
310 PFILE_OBJECT FileObject;
311 NTSTATUS Status = STATUS_SUCCESS;
312 PFILE_RECORD_HEADER FileRecord;
313 PNTFS_ATTR_CONTEXT DataContext;
314 ULONGLONG MFTRecord;
315 UNICODE_STRING Pattern;
316
317 DPRINT1("NtfsQueryDirectory() called\n");
318
319 ASSERT(IrpContext);
320 Irp = IrpContext->Irp;
321 DeviceObject = IrpContext->DeviceObject;
322
323 DeviceExtension = DeviceObject->DeviceExtension;
324 Stack = IoGetCurrentIrpStackLocation(Irp);
325 FileObject = Stack->FileObject;
326
327 Ccb = (PNTFS_CCB)FileObject->FsContext2;
328 Fcb = (PNTFS_FCB)FileObject->FsContext;
329
330 /* Obtain the callers parameters */
331 BufferLength = Stack->Parameters.QueryDirectory.Length;
332 SearchPattern = Stack->Parameters.QueryDirectory.FileName;
333 FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
334 FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
335
336 if (SearchPattern != NULL)
337 {
338 if (!Ccb->DirectorySearchPattern)
339 {
340 First = TRUE;
341 Ccb->DirectorySearchPattern =
342 ExAllocatePoolWithTag(NonPagedPool, SearchPattern->Length + sizeof(WCHAR), TAG_NTFS);
343 if (!Ccb->DirectorySearchPattern)
344 {
345 return STATUS_INSUFFICIENT_RESOURCES;
346 }
347
348 memcpy(Ccb->DirectorySearchPattern,
349 SearchPattern->Buffer,
350 SearchPattern->Length);
351 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
352 }
353 }
354 else if (!Ccb->DirectorySearchPattern)
355 {
356 First = TRUE;
357 Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
358 if (!Ccb->DirectorySearchPattern)
359 {
360 return STATUS_INSUFFICIENT_RESOURCES;
361 }
362
363 Ccb->DirectorySearchPattern[0] = L'*';
364 Ccb->DirectorySearchPattern[1] = 0;
365 }
366
367 RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
368 WildCard = FsRtlDoesNameContainWildCards(&Pattern);
369 if (WildCard)
370 {
371 Status = RtlUpcaseUnicodeString(&Pattern, &Pattern, FALSE);
372 if (!NT_SUCCESS(Status))
373 {
374 DPRINT1("RtlUpcaseUnicodeString('%wZ') failed with status 0x%08lx\n", &Pattern, Status);
375 return Status;
376 }
377 }
378
379 DPRINT1("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
380 DPRINT1("In: '%S'\n", Fcb->PathName);
381
382 /* Determine directory index */
383 if (Stack->Flags & SL_INDEX_SPECIFIED)
384 {
385 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
386 }
387 else if (First || (Stack->Flags & SL_RESTART_SCAN))
388 {
389 Ccb->Entry = 0;
390 }
391
392 /* Determine Buffer for result */
393 if (Irp->MdlAddress)
394 {
395 Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
396 }
397 else
398 {
399 Buffer = Irp->UserBuffer;
400 }
401
402 DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
403
404 while (Status == STATUS_SUCCESS && BufferLength > 0)
405 {
406 Status = NtfsFindFileAt(DeviceExtension,
407 &Pattern,
408 &Ccb->Entry,
409 &FileRecord,
410 &DataContext,
411 &MFTRecord,
412 Fcb->MFTIndex);
413 //DPRINT("Found %S, Status=%x, entry %x\n", TempFcb.ObjectName, Status, Ccb->Entry);
414
415 if (NT_SUCCESS(Status))
416 {
417 switch (FileInformationClass)
418 {
419 case FileNameInformation:
420 Status = NtfsGetNameInformation(DeviceExtension,
421 FileRecord,
422 DataContext,
423 (PFILE_NAMES_INFORMATION)Buffer,
424 BufferLength);
425 break;
426
427 case FileDirectoryInformation:
428 Status = NtfsGetDirectoryInformation(DeviceExtension,
429 FileRecord,
430 DataContext,
431 (PFILE_DIRECTORY_INFORMATION)Buffer,
432 BufferLength);
433 break;
434
435 case FileFullDirectoryInformation:
436 Status = NtfsGetFullDirectoryInformation(DeviceExtension,
437 FileRecord,
438 DataContext,
439 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
440 BufferLength);
441 break;
442
443 case FileBothDirectoryInformation:
444 Status = NtfsGetBothDirectoryInformation(DeviceExtension,
445 FileRecord,
446 DataContext,
447 (PFILE_BOTH_DIR_INFORMATION)Buffer,
448 BufferLength);
449 break;
450
451 default:
452 Status = STATUS_INVALID_INFO_CLASS;
453 }
454
455 if (Status == STATUS_BUFFER_OVERFLOW)
456 {
457 if (Buffer0)
458 {
459 Buffer0->NextEntryOffset = 0;
460 }
461 break;
462 }
463 }
464 else
465 {
466 if (Buffer0)
467 {
468 Buffer0->NextEntryOffset = 0;
469 }
470
471 if (First)
472 {
473 Status = STATUS_NO_SUCH_FILE;
474 }
475 else
476 {
477 Status = STATUS_NO_MORE_FILES;
478 }
479 break;
480 }
481
482 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
483 Buffer0->FileIndex = FileIndex++;
484 Ccb->Entry++;
485
486 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
487 {
488 break;
489 }
490 BufferLength -= Buffer0->NextEntryOffset;
491 Buffer += Buffer0->NextEntryOffset;
492 ExFreePoolWithTag(FileRecord, TAG_NTFS);
493 }
494
495 if (Buffer0)
496 {
497 Buffer0->NextEntryOffset = 0;
498 }
499
500 if (FileIndex > 0)
501 {
502 Status = STATUS_SUCCESS;
503 }
504
505 return Status;
506 }
507
508
509 NTSTATUS
510 NTAPI
511 NtfsFsdDirectoryControl(PDEVICE_OBJECT DeviceObject,
512 PIRP Irp)
513 {
514 PNTFS_IRP_CONTEXT IrpContext = NULL;
515 NTSTATUS Status = STATUS_UNSUCCESSFUL;
516
517 DPRINT1("NtfsDirectoryControl() called\n");
518
519 FsRtlEnterFileSystem();
520 ASSERT(DeviceObject);
521 ASSERT(Irp);
522
523 NtfsIsIrpTopLevel(Irp);
524
525 IrpContext = NtfsAllocateIrpContext(DeviceObject, Irp);
526 if (IrpContext)
527 {
528 switch (IrpContext->MinorFunction)
529 {
530 case IRP_MN_QUERY_DIRECTORY:
531 Status = NtfsQueryDirectory(IrpContext);
532 break;
533
534 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
535 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
536 Status = STATUS_NOT_IMPLEMENTED;
537 break;
538
539 default:
540 Status = STATUS_INVALID_DEVICE_REQUEST;
541 break;
542 }
543 }
544 else
545 Status = STATUS_INSUFFICIENT_RESOURCES;
546
547 Irp->IoStatus.Status = Status;
548 Irp->IoStatus.Information = 0;
549 IoCompleteRequest(Irp, IO_NO_INCREMENT);
550
551 if (IrpContext)
552 ExFreePoolWithTag(IrpContext, 'PRIN');
553
554 IoSetTopLevelIrp(NULL);
555 FsRtlExitFileSystem();
556 return Status;
557 }
558
559 /* EOF */