[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 */
26
27 /* INCLUDES *****************************************************************/
28
29 #include "ntfs.h"
30
31 #define NDEBUG
32 #include <debug.h>
33
34 /* FUNCTIONS ****************************************************************/
35
36 #if 0
37 static NTSTATUS
38 CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt,
39 PVOID *Context,
40 PVOID *Block,
41 PLARGE_INTEGER StreamOffset,
42 ULONG DirLength,
43 PVOID *Ptr,
44 PWSTR Name,
45 PULONG pIndex,
46 PULONG pIndex2)
47 /*
48 * FUNCTION: Retrieves the file name, be it in short or long file name format
49 */
50 {
51 PDIR_RECORD Record;
52 NTSTATUS Status;
53 ULONG Index = 0;
54 ULONG Offset = 0;
55 ULONG BlockOffset = 0;
56
57 Record = (PDIR_RECORD)*Block;
58 while(Index < *pIndex)
59 {
60 BlockOffset += Record->RecordLength;
61 Offset += Record->RecordLength;
62
63 Record = (PDIR_RECORD)(*Block + BlockOffset);
64 if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
65 {
66 DPRINT("Map next sector\n");
67 CcUnpinData(*Context);
68 StreamOffset->QuadPart += BLOCKSIZE;
69 Offset = ROUND_UP(Offset, BLOCKSIZE);
70 BlockOffset = 0;
71
72 if (!CcMapData(DeviceExt->StreamFileObject,
73 StreamOffset,
74 BLOCKSIZE, TRUE,
75 Context, Block))
76 {
77 DPRINT("CcMapData() failed\n");
78 return(STATUS_UNSUCCESSFUL);
79 }
80 Record = (PDIR_RECORD)(*Block + BlockOffset);
81 }
82
83 if (Offset >= DirLength)
84 return(STATUS_NO_MORE_ENTRIES);
85
86 Index++;
87 }
88
89 DPRINT("Index %lu RecordLength %lu Offset %lu\n",
90 Index, Record->RecordLength, Offset);
91
92 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
93 {
94 wcscpy(Name, L".");
95 }
96 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
97 {
98 wcscpy(Name, L"..");
99 }
100 else
101 {
102 if (DeviceExt->CdInfo.JolietLevel == 0)
103 {
104 ULONG i;
105
106 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
107 Name[i] = (WCHAR)Record->FileId[i];
108 Name[i] = 0;
109 }
110 else
111 {
112 CdfsSwapString(Name, Record->FileId, Record->FileIdLength);
113 }
114 }
115
116 DPRINT("Name '%S'\n", Name);
117
118 *Ptr = Record;
119
120 *pIndex = Index;
121
122 return(STATUS_SUCCESS);
123 }
124 #endif
125
126
127 static NTSTATUS
128 NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt,
129 PFILE_RECORD_HEADER FileRecord,
130 PNTFS_ATTR_CONTEXT DataContext,
131 PFILE_NAMES_INFORMATION Info,
132 ULONG BufferLength)
133 {
134 ULONG Length;
135 PFILENAME_ATTRIBUTE FileName;
136
137 DPRINT("NtfsGetNameInformation() called\n");
138
139 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
140 ASSERT(FileName != NULL);
141
142 Length = FileName->NameLength * sizeof (WCHAR);
143 if ((sizeof(FILE_NAMES_INFORMATION) + Length) > BufferLength)
144 return(STATUS_BUFFER_OVERFLOW);
145
146 Info->FileNameLength = Length;
147 Info->NextEntryOffset =
148 ROUND_UP(sizeof(FILE_NAMES_INFORMATION) + Length, sizeof(ULONG));
149 RtlCopyMemory(Info->FileName, FileName->Name, Length);
150
151 return(STATUS_SUCCESS);
152 }
153
154
155 static NTSTATUS
156 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
157 PFILE_RECORD_HEADER FileRecord,
158 PNTFS_ATTR_CONTEXT DataContext,
159 PFILE_DIRECTORY_INFORMATION Info,
160 ULONG BufferLength)
161 {
162 ULONG Length;
163 PFILENAME_ATTRIBUTE FileName;
164
165 DPRINT("NtfsGetDirectoryInformation() called\n");
166
167 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
168 ASSERT(FileName != NULL);
169
170 Length = FileName->NameLength * sizeof (WCHAR);
171 if ((sizeof(FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
172 return(STATUS_BUFFER_OVERFLOW);
173
174 Info->FileNameLength = Length;
175 Info->NextEntryOffset =
176 ROUND_UP(sizeof(FILE_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, &Info->FileAttributes);
186
187 Info->EndOfFile.QuadPart = FileName->AllocatedSize;
188 Info->AllocationSize.QuadPart = ROUND_UP(FileName->AllocatedSize, DeviceExt->NtfsInfo.BytesPerCluster);
189
190 // Info->FileIndex=;
191
192 return STATUS_SUCCESS;
193 }
194
195
196 static NTSTATUS
197 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
198 PFILE_RECORD_HEADER FileRecord,
199 PNTFS_ATTR_CONTEXT DataContext,
200 PFILE_FULL_DIRECTORY_INFORMATION Info,
201 ULONG BufferLength)
202 {
203 ULONG Length;
204 PFILENAME_ATTRIBUTE FileName;
205
206 DPRINT("NtfsGetFullDirectoryInformation() called\n");
207
208 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
209 ASSERT(FileName != NULL);
210
211 Length = FileName->NameLength * sizeof (WCHAR);
212 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length) > BufferLength)
213 return(STATUS_BUFFER_OVERFLOW);
214
215 Info->FileNameLength = Length;
216 Info->NextEntryOffset =
217 ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
218 RtlCopyMemory(Info->FileName, FileName->Name, Length);
219
220 Info->CreationTime.QuadPart = FileName->CreationTime;
221 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
222 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
223 Info->ChangeTime.QuadPart = FileName->ChangeTime;
224
225 /* Convert file flags */
226 NtfsFileFlagsToAttributes(FileName->FileAttributes, &Info->FileAttributes);
227
228 Info->EndOfFile.QuadPart = FileName->AllocatedSize;
229 Info->AllocationSize.QuadPart = ROUND_UP(FileName->AllocatedSize, DeviceExt->NtfsInfo.BytesPerCluster);
230
231 // Info->FileIndex=;
232 Info->EaSize = 0;
233
234 return STATUS_SUCCESS;
235 }
236
237
238 static NTSTATUS
239 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
240 PFILE_RECORD_HEADER FileRecord,
241 PNTFS_ATTR_CONTEXT DataContext,
242 PFILE_BOTH_DIR_INFORMATION Info,
243 ULONG BufferLength)
244 {
245 ULONG Length;
246 PFILENAME_ATTRIBUTE FileName, ShortFileName;
247
248 DPRINT("NtfsGetBothDirectoryInformation() called\n");
249
250 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
251 ASSERT(FileName != NULL);
252 ShortFileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_DOS);
253
254 Length = FileName->NameLength * sizeof (WCHAR);
255 if ((sizeof(FILE_BOTH_DIR_INFORMATION) + Length) > BufferLength)
256 return(STATUS_BUFFER_OVERFLOW);
257
258 Info->FileNameLength = Length;
259 Info->NextEntryOffset =
260 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) + Length, sizeof(ULONG));
261 RtlCopyMemory(Info->FileName, FileName->Name, Length);
262
263 if (ShortFileName)
264 {
265 /* Should we upcase the filename? */
266 ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName));
267 Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR);
268 RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength);
269 }
270 else
271 {
272 Info->ShortName[0] = 0;
273 Info->ShortNameLength = 0;
274 }
275
276 Info->CreationTime.QuadPart = FileName->CreationTime;
277 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
278 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
279 Info->ChangeTime.QuadPart = FileName->ChangeTime;
280
281 /* Convert file flags */
282 NtfsFileFlagsToAttributes(FileName->FileAttributes, &Info->FileAttributes);
283
284 Info->EndOfFile.QuadPart = FileName->AllocatedSize;
285 Info->AllocationSize.QuadPart = ROUND_UP(FileName->AllocatedSize, DeviceExt->NtfsInfo.BytesPerCluster);
286
287 // Info->FileIndex=;
288 Info->EaSize = 0;
289
290 return STATUS_SUCCESS;
291 }
292
293
294 NTSTATUS
295 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
296 {
297 PIRP Irp;
298 PDEVICE_OBJECT DeviceObject;
299 PDEVICE_EXTENSION DeviceExtension;
300 LONG BufferLength = 0;
301 PUNICODE_STRING SearchPattern = NULL;
302 FILE_INFORMATION_CLASS FileInformationClass;
303 ULONG FileIndex = 0;
304 PUCHAR Buffer = NULL;
305 PFILE_NAMES_INFORMATION Buffer0 = NULL;
306 PNTFS_FCB Fcb;
307 PNTFS_CCB Ccb;
308 BOOLEAN First = FALSE;
309 PIO_STACK_LOCATION Stack;
310 PFILE_OBJECT FileObject;
311 NTSTATUS Status = STATUS_SUCCESS;
312 PFILE_RECORD_HEADER FileRecord;
313 PNTFS_ATTR_CONTEXT DataContext;
314 ULONGLONG MFTRecord, OldMFTRecord = 0;
315 UNICODE_STRING Pattern;
316
317 DPRINT1("NtfsQueryDirectory() called\n");
318
319 ASSERT(IrpContext);
320 Irp = IrpContext->Irp;
321 DeviceObject = IrpContext->DeviceObject;
322
323 DeviceExtension = DeviceObject->DeviceExtension;
324 Stack = IoGetCurrentIrpStackLocation(Irp);
325 FileObject = Stack->FileObject;
326
327 Ccb = (PNTFS_CCB)FileObject->FsContext2;
328 Fcb = (PNTFS_FCB)FileObject->FsContext;
329
330 /* Obtain the callers parameters */
331 BufferLength = Stack->Parameters.QueryDirectory.Length;
332 SearchPattern = Stack->Parameters.QueryDirectory.FileName;
333 FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
334 FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
335
336 if (SearchPattern != NULL)
337 {
338 if (!Ccb->DirectorySearchPattern)
339 {
340 First = TRUE;
341 Pattern.Length = 0;
342 Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
343 Ccb->DirectorySearchPattern = Pattern.Buffer =
344 ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
345 if (!Ccb->DirectorySearchPattern)
346 {
347 return STATUS_INSUFFICIENT_RESOURCES;
348 }
349
350 Status = RtlUpcaseUnicodeString(&Pattern, SearchPattern, FALSE);
351 if (!NT_SUCCESS(Status))
352 {
353 DPRINT1("RtlUpcaseUnicodeString('%wZ') failed with status 0x%08lx\n", &Pattern, Status);
354 ExFreePoolWithTag(Ccb->DirectorySearchPattern, TAG_NTFS);
355 Ccb->DirectorySearchPattern = NULL;
356 return Status;
357 }
358 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
359 }
360 }
361 else if (!Ccb->DirectorySearchPattern)
362 {
363 First = TRUE;
364 Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
365 if (!Ccb->DirectorySearchPattern)
366 {
367 return STATUS_INSUFFICIENT_RESOURCES;
368 }
369
370 Ccb->DirectorySearchPattern[0] = L'*';
371 Ccb->DirectorySearchPattern[1] = 0;
372 }
373
374 RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
375 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
376 DPRINT("In: '%S'\n", Fcb->PathName);
377
378 /* Determine directory index */
379 if (Stack->Flags & SL_INDEX_SPECIFIED)
380 {
381 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
382 }
383 else if (First || (Stack->Flags & SL_RESTART_SCAN))
384 {
385 Ccb->Entry = 0;
386 }
387
388 /* Determine Buffer for result */
389 if (Irp->MdlAddress)
390 {
391 Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
392 }
393 else
394 {
395 Buffer = Irp->UserBuffer;
396 }
397
398 DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
399
400 while (Status == STATUS_SUCCESS && BufferLength > 0)
401 {
402 Status = NtfsFindFileAt(DeviceExtension,
403 &Pattern,
404 &Ccb->Entry,
405 &FileRecord,
406 &DataContext,
407 &MFTRecord,
408 Fcb->MFTIndex);
409
410 if (NT_SUCCESS(Status))
411 {
412 /* HACK: files with both a short name and a long name are present twice in the index.
413 * Ignore the second entry, if it is immediately following the first one.
414 */
415 if (MFTRecord == OldMFTRecord)
416 {
417 DPRINT("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
418 Ccb->Entry++;
419 ExFreePoolWithTag(FileRecord, TAG_NTFS);
420 continue;
421 }
422 OldMFTRecord = MFTRecord;
423
424 switch (FileInformationClass)
425 {
426 case FileNameInformation:
427 Status = NtfsGetNameInformation(DeviceExtension,
428 FileRecord,
429 DataContext,
430 (PFILE_NAMES_INFORMATION)Buffer,
431 BufferLength);
432 break;
433
434 case FileDirectoryInformation:
435 Status = NtfsGetDirectoryInformation(DeviceExtension,
436 FileRecord,
437 DataContext,
438 (PFILE_DIRECTORY_INFORMATION)Buffer,
439 BufferLength);
440 break;
441
442 case FileFullDirectoryInformation:
443 Status = NtfsGetFullDirectoryInformation(DeviceExtension,
444 FileRecord,
445 DataContext,
446 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
447 BufferLength);
448 break;
449
450 case FileBothDirectoryInformation:
451 Status = NtfsGetBothDirectoryInformation(DeviceExtension,
452 FileRecord,
453 DataContext,
454 (PFILE_BOTH_DIR_INFORMATION)Buffer,
455 BufferLength);
456 break;
457
458 default:
459 Status = STATUS_INVALID_INFO_CLASS;
460 }
461
462 if (Status == STATUS_BUFFER_OVERFLOW)
463 {
464 if (Buffer0)
465 {
466 Buffer0->NextEntryOffset = 0;
467 }
468 break;
469 }
470 }
471 else
472 {
473 if (Buffer0)
474 {
475 Buffer0->NextEntryOffset = 0;
476 }
477
478 if (First)
479 {
480 Status = STATUS_NO_SUCH_FILE;
481 }
482 else
483 {
484 Status = STATUS_NO_MORE_FILES;
485 }
486 break;
487 }
488
489 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
490 Buffer0->FileIndex = FileIndex++;
491 Ccb->Entry++;
492
493 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
494 {
495 break;
496 }
497 BufferLength -= Buffer0->NextEntryOffset;
498 Buffer += Buffer0->NextEntryOffset;
499 ExFreePoolWithTag(FileRecord, TAG_NTFS);
500 }
501
502 if (Buffer0)
503 {
504 Buffer0->NextEntryOffset = 0;
505 }
506
507 if (FileIndex > 0)
508 {
509 Status = STATUS_SUCCESS;
510 }
511
512 return Status;
513 }
514
515
516 NTSTATUS
517 NTAPI
518 NtfsFsdDirectoryControl(PDEVICE_OBJECT DeviceObject,
519 PIRP Irp)
520 {
521 PNTFS_IRP_CONTEXT IrpContext = NULL;
522 NTSTATUS Status = STATUS_UNSUCCESSFUL;
523
524 DPRINT1("NtfsDirectoryControl() called\n");
525
526 FsRtlEnterFileSystem();
527 ASSERT(DeviceObject);
528 ASSERT(Irp);
529
530 NtfsIsIrpTopLevel(Irp);
531
532 IrpContext = NtfsAllocateIrpContext(DeviceObject, Irp);
533 if (IrpContext)
534 {
535 switch (IrpContext->MinorFunction)
536 {
537 case IRP_MN_QUERY_DIRECTORY:
538 Status = NtfsQueryDirectory(IrpContext);
539 break;
540
541 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
542 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
543 Status = STATUS_NOT_IMPLEMENTED;
544 break;
545
546 default:
547 Status = STATUS_INVALID_DEVICE_REQUEST;
548 break;
549 }
550 }
551 else
552 Status = STATUS_INSUFFICIENT_RESOURCES;
553
554 Irp->IoStatus.Status = Status;
555 Irp->IoStatus.Information = 0;
556 IoCompleteRequest(Irp, IO_NO_INCREMENT);
557
558 if (IrpContext)
559 ExFreePoolWithTag(IrpContext, 'PRIN');
560
561 IoSetTopLevelIrp(NULL);
562 FsRtlExitFileSystem();
563 return Status;
564 }
565
566 /* EOF */