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