[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 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 if (!ExAcquireResourceExclusiveLite(&DeviceExtension->DirResource,
367 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
368 {
369 ExReleaseResourceLite(&Fcb->MainResource);
370 return STATUS_PENDING;
371 }
372
373 while (Status == STATUS_SUCCESS && BufferLength > 0)
374 {
375 Status = NtfsFindFileAt(DeviceExtension,
376 &Pattern,
377 &Ccb->Entry,
378 &FileRecord,
379 &MFTRecord,
380 Fcb->MFTIndex);
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 */