632d58f1b405ae4779589dd24b07f910b76f9a1b
[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
38 static ULONGLONG
39 NtfsGetFileSize(PFILE_RECORD_HEADER FileRecord,
40 PFILENAME_ATTRIBUTE FileName)
41 {
42 ULONGLONG Size;
43 PNTFS_ATTR_RECORD Attribute;
44
45 Size = FileName->AllocatedSize;
46 Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
47 while (Attribute < (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse) &&
48 Attribute->Type != AttributeEnd)
49 {
50 if (Attribute->Type == AttributeData && Attribute->NameLength == 0)
51 {
52 Size = AttributeDataLength(Attribute);
53 break;
54 }
55
56 Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Attribute + Attribute->Length);
57 }
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(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(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(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(FileRecord, FileName);
138 Info->AllocationSize.QuadPart = ROUND_UP(Info->EndOfFile.QuadPart, DeviceExt->NtfsInfo.BytesPerCluster);
139
140 Info->FileIndex = MFTIndex;
141
142 return STATUS_SUCCESS;
143 }
144
145
146 static NTSTATUS
147 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
148 PFILE_RECORD_HEADER FileRecord,
149 ULONGLONG MFTIndex,
150 PFILE_FULL_DIRECTORY_INFORMATION Info,
151 ULONG BufferLength)
152 {
153 ULONG Length;
154 PFILENAME_ATTRIBUTE FileName;
155 PSTANDARD_INFORMATION StdInfo;
156
157 DPRINT("NtfsGetFullDirectoryInformation() called\n");
158
159 FileName = GetBestFileNameFromRecord(FileRecord);
160 if (FileName == NULL)
161 {
162 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
163 NtfsDumpFileAttributes(DeviceExt, FileRecord);
164 return STATUS_OBJECT_NAME_NOT_FOUND;
165 }
166
167 StdInfo = GetStandardInformationFromRecord(FileRecord);
168 ASSERT(StdInfo != NULL);
169
170 Length = FileName->NameLength * sizeof (WCHAR);
171 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length) > BufferLength)
172 return(STATUS_BUFFER_OVERFLOW);
173
174 Info->FileNameLength = Length;
175 Info->NextEntryOffset =
176 ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
177 RtlCopyMemory(Info->FileName, FileName->Name, Length);
178
179 Info->CreationTime.QuadPart = FileName->CreationTime;
180 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
181 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
182 Info->ChangeTime.QuadPart = FileName->ChangeTime;
183
184 /* Convert file flags */
185 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
186
187 Info->EndOfFile.QuadPart = NtfsGetFileSize(FileRecord, FileName);
188 Info->AllocationSize.QuadPart = ROUND_UP(Info->EndOfFile.QuadPart, DeviceExt->NtfsInfo.BytesPerCluster);
189
190 Info->FileIndex = MFTIndex;
191 Info->EaSize = 0;
192
193 return STATUS_SUCCESS;
194 }
195
196
197 static NTSTATUS
198 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
199 PFILE_RECORD_HEADER FileRecord,
200 ULONGLONG MFTIndex,
201 PFILE_BOTH_DIR_INFORMATION Info,
202 ULONG BufferLength)
203 {
204 ULONG Length;
205 PFILENAME_ATTRIBUTE FileName, ShortFileName;
206 PSTANDARD_INFORMATION StdInfo;
207
208 DPRINT("NtfsGetBothDirectoryInformation() called\n");
209
210 FileName = GetBestFileNameFromRecord(FileRecord);
211 if (FileName == NULL)
212 {
213 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
214 NtfsDumpFileAttributes(DeviceExt, FileRecord);
215 return STATUS_OBJECT_NAME_NOT_FOUND;
216 }
217 ShortFileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_DOS);
218
219 StdInfo = GetStandardInformationFromRecord(FileRecord);
220 ASSERT(StdInfo != NULL);
221
222 Length = FileName->NameLength * sizeof (WCHAR);
223 if ((sizeof(FILE_BOTH_DIR_INFORMATION) + Length) > BufferLength)
224 return(STATUS_BUFFER_OVERFLOW);
225
226 Info->FileNameLength = Length;
227 Info->NextEntryOffset =
228 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) + Length, sizeof(ULONG));
229 RtlCopyMemory(Info->FileName, FileName->Name, Length);
230
231 if (ShortFileName)
232 {
233 /* Should we upcase the filename? */
234 ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName));
235 Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR);
236 RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength);
237 }
238 else
239 {
240 Info->ShortName[0] = 0;
241 Info->ShortNameLength = 0;
242 }
243
244 Info->CreationTime.QuadPart = FileName->CreationTime;
245 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
246 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
247 Info->ChangeTime.QuadPart = FileName->ChangeTime;
248
249 /* Convert file flags */
250 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
251
252 Info->EndOfFile.QuadPart = NtfsGetFileSize(FileRecord, FileName);
253 Info->AllocationSize.QuadPart = ROUND_UP(Info->EndOfFile.QuadPart, DeviceExt->NtfsInfo.BytesPerCluster);
254
255 Info->FileIndex = MFTIndex;
256 Info->EaSize = 0;
257
258 return STATUS_SUCCESS;
259 }
260
261
262 NTSTATUS
263 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
264 {
265 PIRP Irp;
266 PDEVICE_OBJECT DeviceObject;
267 PDEVICE_EXTENSION DeviceExtension;
268 LONG BufferLength = 0;
269 PUNICODE_STRING SearchPattern = NULL;
270 FILE_INFORMATION_CLASS FileInformationClass;
271 ULONG FileIndex = 0;
272 PUCHAR Buffer = NULL;
273 PFILE_NAMES_INFORMATION Buffer0 = NULL;
274 PNTFS_FCB Fcb;
275 PNTFS_CCB Ccb;
276 BOOLEAN First = FALSE;
277 PIO_STACK_LOCATION Stack;
278 PFILE_OBJECT FileObject;
279 NTSTATUS Status = STATUS_SUCCESS;
280 PFILE_RECORD_HEADER FileRecord;
281 ULONGLONG MFTRecord, OldMFTRecord = 0;
282 UNICODE_STRING Pattern;
283
284 DPRINT1("NtfsQueryDirectory() called\n");
285
286 ASSERT(IrpContext);
287 Irp = IrpContext->Irp;
288 DeviceObject = IrpContext->DeviceObject;
289
290 DeviceExtension = DeviceObject->DeviceExtension;
291 Stack = IoGetCurrentIrpStackLocation(Irp);
292 FileObject = Stack->FileObject;
293
294 Ccb = (PNTFS_CCB)FileObject->FsContext2;
295 Fcb = (PNTFS_FCB)FileObject->FsContext;
296
297 /* Obtain the callers parameters */
298 BufferLength = Stack->Parameters.QueryDirectory.Length;
299 SearchPattern = Stack->Parameters.QueryDirectory.FileName;
300 FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
301 FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
302
303 if (SearchPattern != NULL)
304 {
305 if (!Ccb->DirectorySearchPattern)
306 {
307 First = TRUE;
308 Pattern.Length = 0;
309 Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
310 Ccb->DirectorySearchPattern = Pattern.Buffer =
311 ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
312 if (!Ccb->DirectorySearchPattern)
313 {
314 return STATUS_INSUFFICIENT_RESOURCES;
315 }
316
317 memcpy(Ccb->DirectorySearchPattern, SearchPattern->Buffer, SearchPattern->Length);
318 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
319 }
320 }
321 else if (!Ccb->DirectorySearchPattern)
322 {
323 First = TRUE;
324 Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
325 if (!Ccb->DirectorySearchPattern)
326 {
327 return STATUS_INSUFFICIENT_RESOURCES;
328 }
329
330 Ccb->DirectorySearchPattern[0] = L'*';
331 Ccb->DirectorySearchPattern[1] = 0;
332 }
333
334 RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
335 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
336 DPRINT("In: '%S'\n", Fcb->PathName);
337
338 /* Determine directory index */
339 if (Stack->Flags & SL_INDEX_SPECIFIED)
340 {
341 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
342 }
343 else if (First || (Stack->Flags & SL_RESTART_SCAN))
344 {
345 Ccb->Entry = 0;
346 }
347
348 /* Get Buffer for result */
349 Buffer = NtfsGetUserBuffer(Irp, FALSE);
350
351 DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
352
353 while (Status == STATUS_SUCCESS && BufferLength > 0)
354 {
355 Status = NtfsFindFileAt(DeviceExtension,
356 &Pattern,
357 &Ccb->Entry,
358 &FileRecord,
359 &MFTRecord,
360 Fcb->MFTIndex);
361
362 if (NT_SUCCESS(Status))
363 {
364 /* HACK: files with both a short name and a long name are present twice in the index.
365 * Ignore the second entry, if it is immediately following the first one.
366 */
367 if (MFTRecord == OldMFTRecord)
368 {
369 DPRINT("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
370 Ccb->Entry++;
371 ExFreePoolWithTag(FileRecord, TAG_NTFS);
372 continue;
373 }
374 OldMFTRecord = MFTRecord;
375
376 switch (FileInformationClass)
377 {
378 case FileNameInformation:
379 Status = NtfsGetNameInformation(DeviceExtension,
380 FileRecord,
381 MFTRecord,
382 (PFILE_NAMES_INFORMATION)Buffer,
383 BufferLength);
384 break;
385
386 case FileDirectoryInformation:
387 Status = NtfsGetDirectoryInformation(DeviceExtension,
388 FileRecord,
389 MFTRecord,
390 (PFILE_DIRECTORY_INFORMATION)Buffer,
391 BufferLength);
392 break;
393
394 case FileFullDirectoryInformation:
395 Status = NtfsGetFullDirectoryInformation(DeviceExtension,
396 FileRecord,
397 MFTRecord,
398 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
399 BufferLength);
400 break;
401
402 case FileBothDirectoryInformation:
403 Status = NtfsGetBothDirectoryInformation(DeviceExtension,
404 FileRecord,
405 MFTRecord,
406 (PFILE_BOTH_DIR_INFORMATION)Buffer,
407 BufferLength);
408 break;
409
410 default:
411 Status = STATUS_INVALID_INFO_CLASS;
412 }
413
414 if (Status == STATUS_BUFFER_OVERFLOW)
415 {
416 if (Buffer0)
417 {
418 Buffer0->NextEntryOffset = 0;
419 }
420 break;
421 }
422 }
423 else
424 {
425 if (Buffer0)
426 {
427 Buffer0->NextEntryOffset = 0;
428 }
429
430 if (First)
431 {
432 Status = STATUS_NO_SUCH_FILE;
433 }
434 else
435 {
436 Status = STATUS_NO_MORE_FILES;
437 }
438 break;
439 }
440
441 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
442 Buffer0->FileIndex = FileIndex++;
443 Ccb->Entry++;
444
445 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
446 {
447 break;
448 }
449 BufferLength -= Buffer0->NextEntryOffset;
450 Buffer += Buffer0->NextEntryOffset;
451 ExFreePoolWithTag(FileRecord, TAG_NTFS);
452 }
453
454 if (Buffer0)
455 {
456 Buffer0->NextEntryOffset = 0;
457 }
458
459 if (FileIndex > 0)
460 {
461 Status = STATUS_SUCCESS;
462 }
463
464 return Status;
465 }
466
467
468 NTSTATUS
469 NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext)
470 {
471 NTSTATUS Status = STATUS_UNSUCCESSFUL;
472
473 DPRINT1("NtfsDirectoryControl() called\n");
474
475 switch (IrpContext->MinorFunction)
476 {
477 case IRP_MN_QUERY_DIRECTORY:
478 Status = NtfsQueryDirectory(IrpContext);
479 break;
480
481 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
482 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
483 Status = STATUS_NOT_IMPLEMENTED;
484 break;
485
486 default:
487 Status = STATUS_INVALID_DEVICE_REQUEST;
488 break;
489 }
490
491 IrpContext->Irp->IoStatus.Information = 0;
492
493 return Status;
494 }
495
496 /* EOF */