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