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