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