088b69fe6ef58acd77cb5d85073895852c27751b
[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_POSIX);
141 if (FileName == NULL)
142 {
143 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
144 if (FileName == NULL)
145 {
146 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_DOS);
147 }
148 }
149 ASSERT(FileName != NULL);
150
151 Length = FileName->NameLength * sizeof (WCHAR);
152 if ((sizeof(FILE_NAMES_INFORMATION) + Length) > BufferLength)
153 return(STATUS_BUFFER_OVERFLOW);
154
155 Info->FileNameLength = Length;
156 Info->NextEntryOffset =
157 ROUND_UP(sizeof(FILE_NAMES_INFORMATION) + Length, sizeof(ULONG));
158 RtlCopyMemory(Info->FileName, FileName->Name, Length);
159
160 return(STATUS_SUCCESS);
161 }
162
163
164 static NTSTATUS
165 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
166 PFILE_RECORD_HEADER FileRecord,
167 PNTFS_ATTR_CONTEXT DataContext,
168 PFILE_DIRECTORY_INFORMATION Info,
169 ULONG BufferLength)
170 {
171 ULONG Length;
172 PFILENAME_ATTRIBUTE FileName;
173
174 DPRINT("NtfsGetDirectoryInformation() called\n");
175
176 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_POSIX);
177 if (FileName == NULL)
178 {
179 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
180 if (FileName == NULL)
181 {
182 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_DOS);
183 }
184 }
185 ASSERT(FileName != NULL);
186
187 Length = FileName->NameLength * sizeof (WCHAR);
188 if ((sizeof(FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
189 return(STATUS_BUFFER_OVERFLOW);
190
191 Info->FileNameLength = Length;
192 Info->NextEntryOffset =
193 ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
194 RtlCopyMemory(Info->FileName, FileName->Name, Length);
195
196 Info->CreationTime.QuadPart = FileName->CreationTime;
197 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
198 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
199 Info->ChangeTime.QuadPart = FileName->ChangeTime;
200
201 /* Convert file flags */
202 NtfsFileFlagsToAttributes(FileName->FileAttributes, &Info->FileAttributes);
203
204 Info->EndOfFile.QuadPart = FileName->AllocatedSize;
205 Info->AllocationSize.QuadPart = ROUND_UP(FileName->AllocatedSize, DeviceExt->NtfsInfo.BytesPerCluster);
206
207 // Info->FileIndex=;
208
209 return STATUS_SUCCESS;
210 }
211
212
213 static NTSTATUS
214 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
215 PFILE_RECORD_HEADER FileRecord,
216 PNTFS_ATTR_CONTEXT DataContext,
217 PFILE_FULL_DIRECTORY_INFORMATION Info,
218 ULONG BufferLength)
219 {
220 ULONG Length;
221 PFILENAME_ATTRIBUTE FileName;
222
223 DPRINT("NtfsGetFullDirectoryInformation() called\n");
224
225 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_POSIX);
226 if (FileName == NULL)
227 {
228 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
229 if (FileName == NULL)
230 {
231 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_DOS);
232 }
233 }
234 ASSERT(FileName != NULL);
235
236 Length = FileName->NameLength * sizeof (WCHAR);
237 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length) > BufferLength)
238 return(STATUS_BUFFER_OVERFLOW);
239
240 Info->FileNameLength = Length;
241 Info->NextEntryOffset =
242 ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
243 RtlCopyMemory(Info->FileName, FileName->Name, Length);
244
245 Info->CreationTime.QuadPart = FileName->CreationTime;
246 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
247 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
248 Info->ChangeTime.QuadPart = FileName->ChangeTime;
249
250 /* Convert file flags */
251 NtfsFileFlagsToAttributes(FileName->FileAttributes, &Info->FileAttributes);
252
253 Info->EndOfFile.QuadPart = FileName->AllocatedSize;
254 Info->AllocationSize.QuadPart = ROUND_UP(FileName->AllocatedSize, DeviceExt->NtfsInfo.BytesPerCluster);
255
256 // Info->FileIndex=;
257 Info->EaSize = 0;
258
259 return STATUS_SUCCESS;
260 }
261
262
263 static NTSTATUS
264 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
265 PFILE_RECORD_HEADER FileRecord,
266 PNTFS_ATTR_CONTEXT DataContext,
267 PFILE_BOTH_DIR_INFORMATION Info,
268 ULONG BufferLength)
269 {
270 ULONG Length;
271 PFILENAME_ATTRIBUTE FileName, ShortFileName;
272
273 DPRINT("NtfsGetBothDirectoryInformation() called\n");
274
275 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_POSIX);
276 if (FileName == NULL)
277 {
278 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
279 if (FileName == NULL)
280 {
281 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_DOS);
282 }
283 }
284 ASSERT(FileName != NULL);
285 ShortFileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_DOS);
286
287 Length = FileName->NameLength * sizeof (WCHAR);
288 if ((sizeof(FILE_BOTH_DIR_INFORMATION) + Length) > BufferLength)
289 return(STATUS_BUFFER_OVERFLOW);
290
291 Info->FileNameLength = Length;
292 Info->NextEntryOffset =
293 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) + Length, sizeof(ULONG));
294 RtlCopyMemory(Info->FileName, FileName->Name, Length);
295
296 if (ShortFileName)
297 {
298 /* Should we upcase the filename? */
299 ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName));
300 Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR);
301 RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength);
302 }
303 else
304 {
305 Info->ShortName[0] = 0;
306 Info->ShortNameLength = 0;
307 }
308
309 Info->CreationTime.QuadPart = FileName->CreationTime;
310 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
311 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
312 Info->ChangeTime.QuadPart = FileName->ChangeTime;
313
314 /* Convert file flags */
315 NtfsFileFlagsToAttributes(FileName->FileAttributes, &Info->FileAttributes);
316
317 Info->EndOfFile.QuadPart = FileName->AllocatedSize;
318 Info->AllocationSize.QuadPart = ROUND_UP(FileName->AllocatedSize, DeviceExt->NtfsInfo.BytesPerCluster);
319
320 // Info->FileIndex=;
321 Info->EaSize = 0;
322
323 return STATUS_SUCCESS;
324 }
325
326
327 NTSTATUS
328 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
329 {
330 PIRP Irp;
331 PDEVICE_OBJECT DeviceObject;
332 PDEVICE_EXTENSION DeviceExtension;
333 LONG BufferLength = 0;
334 PUNICODE_STRING SearchPattern = NULL;
335 FILE_INFORMATION_CLASS FileInformationClass;
336 ULONG FileIndex = 0;
337 PUCHAR Buffer = NULL;
338 PFILE_NAMES_INFORMATION Buffer0 = NULL;
339 PNTFS_FCB Fcb;
340 PNTFS_CCB Ccb;
341 BOOLEAN First = FALSE;
342 PIO_STACK_LOCATION Stack;
343 PFILE_OBJECT FileObject;
344 NTSTATUS Status = STATUS_SUCCESS;
345 PFILE_RECORD_HEADER FileRecord;
346 PNTFS_ATTR_CONTEXT DataContext;
347 ULONGLONG MFTRecord, OldMFTRecord = 0;
348 UNICODE_STRING Pattern;
349
350 DPRINT1("NtfsQueryDirectory() called\n");
351
352 ASSERT(IrpContext);
353 Irp = IrpContext->Irp;
354 DeviceObject = IrpContext->DeviceObject;
355
356 DeviceExtension = DeviceObject->DeviceExtension;
357 Stack = IoGetCurrentIrpStackLocation(Irp);
358 FileObject = Stack->FileObject;
359
360 Ccb = (PNTFS_CCB)FileObject->FsContext2;
361 Fcb = (PNTFS_FCB)FileObject->FsContext;
362
363 /* Obtain the callers parameters */
364 BufferLength = Stack->Parameters.QueryDirectory.Length;
365 SearchPattern = Stack->Parameters.QueryDirectory.FileName;
366 FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
367 FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
368
369 if (SearchPattern != NULL)
370 {
371 if (!Ccb->DirectorySearchPattern)
372 {
373 First = TRUE;
374 Pattern.Length = 0;
375 Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
376 Ccb->DirectorySearchPattern = Pattern.Buffer =
377 ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
378 if (!Ccb->DirectorySearchPattern)
379 {
380 return STATUS_INSUFFICIENT_RESOURCES;
381 }
382
383 Status = RtlUpcaseUnicodeString(&Pattern, SearchPattern, FALSE);
384 if (!NT_SUCCESS(Status))
385 {
386 DPRINT1("RtlUpcaseUnicodeString('%wZ') failed with status 0x%08lx\n", &Pattern, Status);
387 ExFreePoolWithTag(Ccb->DirectorySearchPattern, TAG_NTFS);
388 Ccb->DirectorySearchPattern = NULL;
389 return Status;
390 }
391 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
392 }
393 }
394 else if (!Ccb->DirectorySearchPattern)
395 {
396 First = TRUE;
397 Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
398 if (!Ccb->DirectorySearchPattern)
399 {
400 return STATUS_INSUFFICIENT_RESOURCES;
401 }
402
403 Ccb->DirectorySearchPattern[0] = L'*';
404 Ccb->DirectorySearchPattern[1] = 0;
405 }
406
407 RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
408 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
409 DPRINT("In: '%S'\n", Fcb->PathName);
410
411 /* Determine directory index */
412 if (Stack->Flags & SL_INDEX_SPECIFIED)
413 {
414 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
415 }
416 else if (First || (Stack->Flags & SL_RESTART_SCAN))
417 {
418 Ccb->Entry = 0;
419 }
420
421 /* Determine Buffer for result */
422 if (Irp->MdlAddress)
423 {
424 Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
425 }
426 else
427 {
428 Buffer = Irp->UserBuffer;
429 }
430
431 DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
432
433 while (Status == STATUS_SUCCESS && BufferLength > 0)
434 {
435 Status = NtfsFindFileAt(DeviceExtension,
436 &Pattern,
437 &Ccb->Entry,
438 &FileRecord,
439 &DataContext,
440 &MFTRecord,
441 Fcb->MFTIndex);
442
443 if (NT_SUCCESS(Status))
444 {
445 /* HACK: files with both a short name and a long name are present twice in the index.
446 * Ignore the second entry, if it is immediately following the first one.
447 */
448 if (MFTRecord == OldMFTRecord)
449 {
450 DPRINT("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
451 Ccb->Entry++;
452 ExFreePoolWithTag(FileRecord, TAG_NTFS);
453 continue;
454 }
455 OldMFTRecord = MFTRecord;
456
457 switch (FileInformationClass)
458 {
459 case FileNameInformation:
460 Status = NtfsGetNameInformation(DeviceExtension,
461 FileRecord,
462 DataContext,
463 (PFILE_NAMES_INFORMATION)Buffer,
464 BufferLength);
465 break;
466
467 case FileDirectoryInformation:
468 Status = NtfsGetDirectoryInformation(DeviceExtension,
469 FileRecord,
470 DataContext,
471 (PFILE_DIRECTORY_INFORMATION)Buffer,
472 BufferLength);
473 break;
474
475 case FileFullDirectoryInformation:
476 Status = NtfsGetFullDirectoryInformation(DeviceExtension,
477 FileRecord,
478 DataContext,
479 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
480 BufferLength);
481 break;
482
483 case FileBothDirectoryInformation:
484 Status = NtfsGetBothDirectoryInformation(DeviceExtension,
485 FileRecord,
486 DataContext,
487 (PFILE_BOTH_DIR_INFORMATION)Buffer,
488 BufferLength);
489 break;
490
491 default:
492 Status = STATUS_INVALID_INFO_CLASS;
493 }
494
495 if (Status == STATUS_BUFFER_OVERFLOW)
496 {
497 if (Buffer0)
498 {
499 Buffer0->NextEntryOffset = 0;
500 }
501 break;
502 }
503 }
504 else
505 {
506 if (Buffer0)
507 {
508 Buffer0->NextEntryOffset = 0;
509 }
510
511 if (First)
512 {
513 Status = STATUS_NO_SUCH_FILE;
514 }
515 else
516 {
517 Status = STATUS_NO_MORE_FILES;
518 }
519 break;
520 }
521
522 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
523 Buffer0->FileIndex = FileIndex++;
524 Ccb->Entry++;
525
526 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
527 {
528 break;
529 }
530 BufferLength -= Buffer0->NextEntryOffset;
531 Buffer += Buffer0->NextEntryOffset;
532 ExFreePoolWithTag(FileRecord, TAG_NTFS);
533 }
534
535 if (Buffer0)
536 {
537 Buffer0->NextEntryOffset = 0;
538 }
539
540 if (FileIndex > 0)
541 {
542 Status = STATUS_SUCCESS;
543 }
544
545 return Status;
546 }
547
548
549 NTSTATUS
550 NTAPI
551 NtfsFsdDirectoryControl(PDEVICE_OBJECT DeviceObject,
552 PIRP Irp)
553 {
554 PNTFS_IRP_CONTEXT IrpContext = NULL;
555 NTSTATUS Status = STATUS_UNSUCCESSFUL;
556
557 DPRINT1("NtfsDirectoryControl() called\n");
558
559 FsRtlEnterFileSystem();
560 ASSERT(DeviceObject);
561 ASSERT(Irp);
562
563 NtfsIsIrpTopLevel(Irp);
564
565 IrpContext = NtfsAllocateIrpContext(DeviceObject, Irp);
566 if (IrpContext)
567 {
568 switch (IrpContext->MinorFunction)
569 {
570 case IRP_MN_QUERY_DIRECTORY:
571 Status = NtfsQueryDirectory(IrpContext);
572 break;
573
574 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
575 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
576 Status = STATUS_NOT_IMPLEMENTED;
577 break;
578
579 default:
580 Status = STATUS_INVALID_DEVICE_REQUEST;
581 break;
582 }
583 }
584 else
585 Status = STATUS_INSUFFICIENT_RESOURCES;
586
587 Irp->IoStatus.Status = Status;
588 Irp->IoStatus.Information = 0;
589 IoCompleteRequest(Irp, IO_NO_INCREMENT);
590
591 if (IrpContext)
592 ExFreePoolWithTag(IrpContext, 'PRIN');
593
594 IoSetTopLevelIrp(NULL);
595 FsRtlExitFileSystem();
596 return Status;
597 }
598
599 /* EOF */