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