86f8716de58bb1f514682dd26e0988e0898af35d
[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 (NtfsFCBIsCompressed(Fcb))
302 {
303 DPRINT1("Compressed directory!\n");
304 UNIMPLEMENTED;
305 return STATUS_NOT_IMPLEMENTED;
306 }
307
308 if (!ExAcquireResourceSharedLite(&Fcb->MainResource,
309 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
310 {
311 return STATUS_PENDING;
312 }
313
314 if (SearchPattern != NULL)
315 {
316 if (!Ccb->DirectorySearchPattern)
317 {
318 First = TRUE;
319 Pattern.Length = 0;
320 Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
321 Ccb->DirectorySearchPattern = Pattern.Buffer =
322 ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
323 if (!Ccb->DirectorySearchPattern)
324 {
325 ExReleaseResourceLite(&Fcb->MainResource);
326 return STATUS_INSUFFICIENT_RESOURCES;
327 }
328
329 memcpy(Ccb->DirectorySearchPattern, SearchPattern->Buffer, SearchPattern->Length);
330 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
331 }
332 }
333 else if (!Ccb->DirectorySearchPattern)
334 {
335 First = TRUE;
336 Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
337 if (!Ccb->DirectorySearchPattern)
338 {
339 ExReleaseResourceLite(&Fcb->MainResource);
340 return STATUS_INSUFFICIENT_RESOURCES;
341 }
342
343 Ccb->DirectorySearchPattern[0] = L'*';
344 Ccb->DirectorySearchPattern[1] = 0;
345 }
346
347 RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
348 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
349 DPRINT("In: '%S'\n", Fcb->PathName);
350
351 /* Determine directory index */
352 if (Stack->Flags & SL_INDEX_SPECIFIED)
353 {
354 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
355 }
356 else if (First || (Stack->Flags & SL_RESTART_SCAN))
357 {
358 Ccb->Entry = 0;
359 }
360
361 /* Get Buffer for result */
362 Buffer = NtfsGetUserBuffer(Irp, FALSE);
363
364 DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
365
366 while (Status == STATUS_SUCCESS && BufferLength > 0)
367 {
368 Status = NtfsFindFileAt(DeviceExtension,
369 &Pattern,
370 &Ccb->Entry,
371 &FileRecord,
372 &MFTRecord,
373 Fcb->MFTIndex);
374
375 if (NT_SUCCESS(Status))
376 {
377 /* HACK: files with both a short name and a long name are present twice in the index.
378 * Ignore the second entry, if it is immediately following the first one.
379 */
380 if (MFTRecord == OldMFTRecord)
381 {
382 DPRINT1("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
383 Ccb->Entry++;
384 ExFreePoolWithTag(FileRecord, TAG_NTFS);
385 continue;
386 }
387 OldMFTRecord = MFTRecord;
388
389 switch (FileInformationClass)
390 {
391 case FileNameInformation:
392 Status = NtfsGetNameInformation(DeviceExtension,
393 FileRecord,
394 MFTRecord,
395 (PFILE_NAMES_INFORMATION)Buffer,
396 BufferLength);
397 break;
398
399 case FileDirectoryInformation:
400 Status = NtfsGetDirectoryInformation(DeviceExtension,
401 FileRecord,
402 MFTRecord,
403 (PFILE_DIRECTORY_INFORMATION)Buffer,
404 BufferLength);
405 break;
406
407 case FileFullDirectoryInformation:
408 Status = NtfsGetFullDirectoryInformation(DeviceExtension,
409 FileRecord,
410 MFTRecord,
411 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
412 BufferLength);
413 break;
414
415 case FileBothDirectoryInformation:
416 Status = NtfsGetBothDirectoryInformation(DeviceExtension,
417 FileRecord,
418 MFTRecord,
419 (PFILE_BOTH_DIR_INFORMATION)Buffer,
420 BufferLength);
421 break;
422
423 default:
424 Status = STATUS_INVALID_INFO_CLASS;
425 }
426
427 if (Status == STATUS_BUFFER_OVERFLOW)
428 {
429 if (Buffer0)
430 {
431 Buffer0->NextEntryOffset = 0;
432 }
433 break;
434 }
435 }
436 else
437 {
438 if (Buffer0)
439 {
440 Buffer0->NextEntryOffset = 0;
441 }
442
443 if (First)
444 {
445 Status = STATUS_NO_SUCH_FILE;
446 }
447 else
448 {
449 Status = STATUS_NO_MORE_FILES;
450 }
451 break;
452 }
453
454 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
455 Buffer0->FileIndex = FileIndex++;
456 Ccb->Entry++;
457
458 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
459 {
460 break;
461 }
462 BufferLength -= Buffer0->NextEntryOffset;
463 Buffer += Buffer0->NextEntryOffset;
464 ExFreePoolWithTag(FileRecord, TAG_NTFS);
465 }
466
467 if (Buffer0)
468 {
469 Buffer0->NextEntryOffset = 0;
470 }
471
472 ExReleaseResourceLite(&Fcb->MainResource);
473
474 if (FileIndex > 0)
475 {
476 Status = STATUS_SUCCESS;
477 }
478
479 return Status;
480 }
481
482
483 NTSTATUS
484 NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext)
485 {
486 NTSTATUS Status = STATUS_UNSUCCESSFUL;
487
488 DPRINT1("NtfsDirectoryControl() called\n");
489
490 switch (IrpContext->MinorFunction)
491 {
492 case IRP_MN_QUERY_DIRECTORY:
493 Status = NtfsQueryDirectory(IrpContext);
494 break;
495
496 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
497 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
498 Status = STATUS_NOT_IMPLEMENTED;
499 break;
500
501 default:
502 Status = STATUS_INVALID_DEVICE_REQUEST;
503 break;
504 }
505
506 IrpContext->Irp->IoStatus.Information = 0;
507
508 return Status;
509 }
510
511 /* EOF */