98294b47913f548e3cc2748b57ada3bae74146d0
[reactos.git] / 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 ULONGLONG
38 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
39 PFILE_RECORD_HEADER FileRecord,
40 PCWSTR Stream,
41 ULONG StreamLength,
42 PULONGLONG AllocatedSize)
43 {
44 ULONGLONG Size = 0ULL;
45 ULONGLONG Allocated = 0ULL;
46 NTSTATUS Status;
47 PNTFS_ATTR_CONTEXT DataContext;
48
49 Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext, NULL);
50 if (NT_SUCCESS(Status))
51 {
52 Size = AttributeDataLength(&DataContext->Record);
53 Allocated = AttributeAllocatedLength(&DataContext->Record);
54 ReleaseAttributeContext(DataContext);
55 }
56
57 if (AllocatedSize != NULL) *AllocatedSize = Allocated;
58
59 return Size;
60 }
61
62
63 static NTSTATUS
64 NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt,
65 PFILE_RECORD_HEADER FileRecord,
66 ULONGLONG MFTIndex,
67 PFILE_NAMES_INFORMATION Info,
68 ULONG BufferLength)
69 {
70 ULONG Length;
71 PFILENAME_ATTRIBUTE FileName;
72
73 DPRINT("NtfsGetNameInformation() called\n");
74
75 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
76 if (FileName == NULL)
77 {
78 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
79 NtfsDumpFileAttributes(DeviceExt, FileRecord);
80 return STATUS_OBJECT_NAME_NOT_FOUND;
81 }
82
83 Length = FileName->NameLength * sizeof (WCHAR);
84 if ((sizeof(FILE_NAMES_INFORMATION) + Length) > BufferLength)
85 return(STATUS_BUFFER_OVERFLOW);
86
87 Info->FileNameLength = Length;
88 Info->NextEntryOffset =
89 ROUND_UP(sizeof(FILE_NAMES_INFORMATION) + Length, sizeof(ULONG));
90 RtlCopyMemory(Info->FileName, FileName->Name, Length);
91
92 return(STATUS_SUCCESS);
93 }
94
95
96 static NTSTATUS
97 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
98 PFILE_RECORD_HEADER FileRecord,
99 ULONGLONG MFTIndex,
100 PFILE_DIRECTORY_INFORMATION Info,
101 ULONG BufferLength)
102 {
103 ULONG Length;
104 PFILENAME_ATTRIBUTE FileName;
105 PSTANDARD_INFORMATION StdInfo;
106
107 DPRINT("NtfsGetDirectoryInformation() called\n");
108
109 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
110 if (FileName == NULL)
111 {
112 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
113 NtfsDumpFileAttributes(DeviceExt, FileRecord);
114 return STATUS_OBJECT_NAME_NOT_FOUND;
115 }
116
117 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
118 ASSERT(StdInfo != NULL);
119
120 Length = FileName->NameLength * sizeof (WCHAR);
121 if ((sizeof(FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
122 return(STATUS_BUFFER_OVERFLOW);
123
124 Info->FileNameLength = Length;
125 Info->NextEntryOffset =
126 ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
127 RtlCopyMemory(Info->FileName, FileName->Name, Length);
128
129 Info->CreationTime.QuadPart = FileName->CreationTime;
130 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
131 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
132 Info->ChangeTime.QuadPart = FileName->ChangeTime;
133
134 /* Convert file flags */
135 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
136
137 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
138
139 Info->FileIndex = MFTIndex;
140
141 return STATUS_SUCCESS;
142 }
143
144
145 static NTSTATUS
146 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
147 PFILE_RECORD_HEADER FileRecord,
148 ULONGLONG MFTIndex,
149 PFILE_FULL_DIRECTORY_INFORMATION Info,
150 ULONG BufferLength)
151 {
152 ULONG Length;
153 PFILENAME_ATTRIBUTE FileName;
154 PSTANDARD_INFORMATION StdInfo;
155
156 DPRINT("NtfsGetFullDirectoryInformation() called\n");
157
158 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
159 if (FileName == NULL)
160 {
161 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
162 NtfsDumpFileAttributes(DeviceExt, FileRecord);
163 return STATUS_OBJECT_NAME_NOT_FOUND;
164 }
165
166 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
167 ASSERT(StdInfo != NULL);
168
169 Length = FileName->NameLength * sizeof (WCHAR);
170 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length) > BufferLength)
171 return(STATUS_BUFFER_OVERFLOW);
172
173 Info->FileNameLength = Length;
174 Info->NextEntryOffset =
175 ROUND_UP(sizeof(FILE_FULL_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 | StdInfo->FileAttribute, &Info->FileAttributes);
185
186 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
187
188 Info->FileIndex = MFTIndex;
189 Info->EaSize = 0;
190
191 return STATUS_SUCCESS;
192 }
193
194
195 static NTSTATUS
196 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
197 PFILE_RECORD_HEADER FileRecord,
198 ULONGLONG MFTIndex,
199 PFILE_BOTH_DIR_INFORMATION Info,
200 ULONG BufferLength)
201 {
202 ULONG Length;
203 PFILENAME_ATTRIBUTE FileName, ShortFileName;
204 PSTANDARD_INFORMATION StdInfo;
205
206 DPRINT("NtfsGetBothDirectoryInformation() called\n");
207
208 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
209 if (FileName == NULL)
210 {
211 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
212 NtfsDumpFileAttributes(DeviceExt, FileRecord);
213 return STATUS_OBJECT_NAME_NOT_FOUND;
214 }
215 ShortFileName = GetFileNameFromRecord(DeviceExt, FileRecord, NTFS_FILE_NAME_DOS);
216
217 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
218 ASSERT(StdInfo != NULL);
219
220 Length = FileName->NameLength * sizeof (WCHAR);
221 if ((sizeof(FILE_BOTH_DIR_INFORMATION) + Length) > BufferLength)
222 return(STATUS_BUFFER_OVERFLOW);
223
224 Info->FileNameLength = Length;
225 Info->NextEntryOffset =
226 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) + Length, sizeof(ULONG));
227 RtlCopyMemory(Info->FileName, FileName->Name, Length);
228
229 if (ShortFileName)
230 {
231 /* Should we upcase the filename? */
232 ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName));
233 Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR);
234 RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength);
235 }
236 else
237 {
238 Info->ShortName[0] = 0;
239 Info->ShortNameLength = 0;
240 }
241
242 Info->CreationTime.QuadPart = FileName->CreationTime;
243 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
244 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
245 Info->ChangeTime.QuadPart = FileName->ChangeTime;
246
247 /* Convert file flags */
248 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
249
250 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
251
252 Info->FileIndex = MFTIndex;
253 Info->EaSize = 0;
254
255 return STATUS_SUCCESS;
256 }
257
258
259 NTSTATUS
260 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
261 {
262 PIRP Irp;
263 PDEVICE_OBJECT DeviceObject;
264 PDEVICE_EXTENSION DeviceExtension;
265 LONG BufferLength = 0;
266 PUNICODE_STRING SearchPattern = NULL;
267 FILE_INFORMATION_CLASS FileInformationClass;
268 ULONG FileIndex = 0;
269 PUCHAR Buffer = NULL;
270 PFILE_NAMES_INFORMATION Buffer0 = NULL;
271 PNTFS_FCB Fcb;
272 PNTFS_CCB Ccb;
273 BOOLEAN First = FALSE;
274 PIO_STACK_LOCATION Stack;
275 PFILE_OBJECT FileObject;
276 NTSTATUS Status = STATUS_SUCCESS;
277 PFILE_RECORD_HEADER FileRecord;
278 ULONGLONG MFTRecord, OldMFTRecord = 0;
279 UNICODE_STRING Pattern;
280
281 DPRINT1("NtfsQueryDirectory() called\n");
282
283 ASSERT(IrpContext);
284 Irp = IrpContext->Irp;
285 DeviceObject = IrpContext->DeviceObject;
286
287 DeviceExtension = DeviceObject->DeviceExtension;
288 Stack = IoGetCurrentIrpStackLocation(Irp);
289 FileObject = Stack->FileObject;
290
291 Ccb = (PNTFS_CCB)FileObject->FsContext2;
292 Fcb = (PNTFS_FCB)FileObject->FsContext;
293
294 /* Obtain the callers parameters */
295 BufferLength = Stack->Parameters.QueryDirectory.Length;
296 SearchPattern = Stack->Parameters.QueryDirectory.FileName;
297 FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
298 FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
299
300 if (NtfsFCBIsCompressed(Fcb))
301 {
302 DPRINT1("Compressed directory!\n");
303 UNIMPLEMENTED;
304 return STATUS_NOT_IMPLEMENTED;
305 }
306
307 if (!ExAcquireResourceSharedLite(&Fcb->MainResource,
308 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
309 {
310 return STATUS_PENDING;
311 }
312
313 if (SearchPattern != NULL)
314 {
315 if (!Ccb->DirectorySearchPattern)
316 {
317 First = TRUE;
318 Pattern.Length = 0;
319 Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
320 Ccb->DirectorySearchPattern = Pattern.Buffer =
321 ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
322 if (!Ccb->DirectorySearchPattern)
323 {
324 ExReleaseResourceLite(&Fcb->MainResource);
325 return STATUS_INSUFFICIENT_RESOURCES;
326 }
327
328 memcpy(Ccb->DirectorySearchPattern, SearchPattern->Buffer, SearchPattern->Length);
329 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
330 }
331 }
332 else if (!Ccb->DirectorySearchPattern)
333 {
334 First = TRUE;
335 Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
336 if (!Ccb->DirectorySearchPattern)
337 {
338 ExReleaseResourceLite(&Fcb->MainResource);
339 return STATUS_INSUFFICIENT_RESOURCES;
340 }
341
342 Ccb->DirectorySearchPattern[0] = L'*';
343 Ccb->DirectorySearchPattern[1] = 0;
344 }
345
346 RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
347 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
348 DPRINT("In: '%S'\n", Fcb->PathName);
349
350 /* Determine directory index */
351 if (Stack->Flags & SL_INDEX_SPECIFIED)
352 {
353 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
354 }
355 else if (First || (Stack->Flags & SL_RESTART_SCAN))
356 {
357 Ccb->Entry = 0;
358 }
359
360 /* Get Buffer for result */
361 Buffer = NtfsGetUserBuffer(Irp, FALSE);
362
363 DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
364
365 if (!ExAcquireResourceExclusiveLite(&DeviceExtension->DirResource,
366 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
367 {
368 ExReleaseResourceLite(&Fcb->MainResource);
369 return STATUS_PENDING;
370 }
371
372 while (Status == STATUS_SUCCESS && BufferLength > 0)
373 {
374 Status = NtfsFindFileAt(DeviceExtension,
375 &Pattern,
376 &Ccb->Entry,
377 &FileRecord,
378 &MFTRecord,
379 Fcb->MFTIndex,
380 BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE));
381
382 if (NT_SUCCESS(Status))
383 {
384 /* HACK: files with both a short name and a long name are present twice in the index.
385 * Ignore the second entry, if it is immediately following the first one.
386 */
387 if (MFTRecord == OldMFTRecord)
388 {
389 DPRINT1("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
390 Ccb->Entry++;
391 ExFreePoolWithTag(FileRecord, TAG_NTFS);
392 continue;
393 }
394 OldMFTRecord = MFTRecord;
395
396 switch (FileInformationClass)
397 {
398 case FileNameInformation:
399 Status = NtfsGetNameInformation(DeviceExtension,
400 FileRecord,
401 MFTRecord,
402 (PFILE_NAMES_INFORMATION)Buffer,
403 BufferLength);
404 break;
405
406 case FileDirectoryInformation:
407 Status = NtfsGetDirectoryInformation(DeviceExtension,
408 FileRecord,
409 MFTRecord,
410 (PFILE_DIRECTORY_INFORMATION)Buffer,
411 BufferLength);
412 break;
413
414 case FileFullDirectoryInformation:
415 Status = NtfsGetFullDirectoryInformation(DeviceExtension,
416 FileRecord,
417 MFTRecord,
418 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
419 BufferLength);
420 break;
421
422 case FileBothDirectoryInformation:
423 Status = NtfsGetBothDirectoryInformation(DeviceExtension,
424 FileRecord,
425 MFTRecord,
426 (PFILE_BOTH_DIR_INFORMATION)Buffer,
427 BufferLength);
428 break;
429
430 default:
431 Status = STATUS_INVALID_INFO_CLASS;
432 }
433
434 if (Status == STATUS_BUFFER_OVERFLOW)
435 {
436 if (Buffer0)
437 {
438 Buffer0->NextEntryOffset = 0;
439 }
440 break;
441 }
442 }
443 else
444 {
445 if (Buffer0)
446 {
447 Buffer0->NextEntryOffset = 0;
448 }
449
450 if (First)
451 {
452 Status = STATUS_NO_SUCH_FILE;
453 }
454 else
455 {
456 Status = STATUS_NO_MORE_FILES;
457 }
458 break;
459 }
460
461 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
462 Buffer0->FileIndex = FileIndex++;
463 Ccb->Entry++;
464
465 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
466 {
467 break;
468 }
469 BufferLength -= Buffer0->NextEntryOffset;
470 Buffer += Buffer0->NextEntryOffset;
471 ExFreePoolWithTag(FileRecord, TAG_NTFS);
472 }
473
474 if (Buffer0)
475 {
476 Buffer0->NextEntryOffset = 0;
477 }
478
479 ExReleaseResourceLite(&DeviceExtension->DirResource);
480 ExReleaseResourceLite(&Fcb->MainResource);
481
482 if (FileIndex > 0)
483 {
484 Status = STATUS_SUCCESS;
485 }
486
487 return Status;
488 }
489
490
491 NTSTATUS
492 NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext)
493 {
494 NTSTATUS Status = STATUS_UNSUCCESSFUL;
495
496 DPRINT1("NtfsDirectoryControl() called\n");
497
498 switch (IrpContext->MinorFunction)
499 {
500 case IRP_MN_QUERY_DIRECTORY:
501 Status = NtfsQueryDirectory(IrpContext);
502 break;
503
504 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
505 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
506 Status = STATUS_NOT_IMPLEMENTED;
507 break;
508
509 default:
510 Status = STATUS_INVALID_DEVICE_REQUEST;
511 break;
512 }
513
514 if (Status == STATUS_PENDING && IrpContext->Flags & IRPCONTEXT_COMPLETE)
515 {
516 return NtfsMarkIrpContextForQueue(IrpContext);
517 }
518
519 IrpContext->Irp->IoStatus.Information = 0;
520
521 return Status;
522 }
523
524 /* EOF */