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