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