* Sync up to trunk head (r65147).
[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, NTFS_FILE_NAME_WIN32);
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, NTFS_FILE_NAME_WIN32);
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, NTFS_FILE_NAME_WIN32);
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, ShortFileName;
246
247 DPRINT("NtfsGetBothDirectoryInformation() called\n");
248
249 FileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_WIN32);
250 ASSERT(FileName != NULL);
251 ShortFileName = GetFileNameFromRecord(FileRecord, NTFS_FILE_NAME_DOS);
252
253 Length = FileName->NameLength * sizeof (WCHAR);
254 if ((sizeof(FILE_BOTH_DIR_INFORMATION) + Length) > BufferLength)
255 return(STATUS_BUFFER_OVERFLOW);
256
257 Info->FileNameLength = Length;
258 Info->NextEntryOffset =
259 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) + Length, sizeof(ULONG));
260 RtlCopyMemory(Info->FileName, FileName->Name, Length);
261
262 if (ShortFileName)
263 {
264 /* Should we upcase the filename? */
265 ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName));
266 Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR);
267 RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength);
268 }
269 else
270 {
271 Info->ShortName[0] = 0;
272 Info->ShortNameLength = 0;
273 }
274
275 Info->CreationTime.QuadPart = FileName->CreationTime;
276 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
277 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
278 Info->ChangeTime.QuadPart = FileName->ChangeTime;
279
280 /* Convert file flags */
281 NtfsFileFlagsToAttributes(FileName->FileAttributes, &Info->FileAttributes);
282
283 Info->EndOfFile.QuadPart = FileName->AllocatedSize;
284 Info->AllocationSize.QuadPart = ROUND_UP(FileName->AllocatedSize, DeviceExt->NtfsInfo.BytesPerCluster);
285
286 // Info->FileIndex=;
287 Info->EaSize = 0;
288
289 return STATUS_SUCCESS;
290 }
291
292
293 NTSTATUS
294 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
295 {
296 PIRP Irp;
297 PDEVICE_OBJECT DeviceObject;
298 PDEVICE_EXTENSION DeviceExtension;
299 LONG BufferLength = 0;
300 PUNICODE_STRING SearchPattern = NULL;
301 FILE_INFORMATION_CLASS FileInformationClass;
302 ULONG FileIndex = 0;
303 PUCHAR Buffer = NULL;
304 PFILE_NAMES_INFORMATION Buffer0 = NULL;
305 PNTFS_FCB Fcb;
306 PNTFS_CCB Ccb;
307 BOOLEAN First = FALSE;
308 PIO_STACK_LOCATION Stack;
309 PFILE_OBJECT FileObject;
310 NTSTATUS Status = STATUS_SUCCESS;
311 PFILE_RECORD_HEADER FileRecord;
312 PNTFS_ATTR_CONTEXT DataContext;
313 ULONGLONG MFTRecord;
314 UNICODE_STRING Pattern;
315
316 DPRINT1("NtfsQueryDirectory() called\n");
317
318 ASSERT(IrpContext);
319 Irp = IrpContext->Irp;
320 DeviceObject = IrpContext->DeviceObject;
321
322 DeviceExtension = DeviceObject->DeviceExtension;
323 Stack = IoGetCurrentIrpStackLocation(Irp);
324 FileObject = Stack->FileObject;
325
326 Ccb = (PNTFS_CCB)FileObject->FsContext2;
327 Fcb = (PNTFS_FCB)FileObject->FsContext;
328
329 /* Obtain the callers parameters */
330 BufferLength = Stack->Parameters.QueryDirectory.Length;
331 SearchPattern = Stack->Parameters.QueryDirectory.FileName;
332 FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
333 FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
334
335 if (SearchPattern != NULL)
336 {
337 if (!Ccb->DirectorySearchPattern)
338 {
339 First = TRUE;
340 Ccb->DirectorySearchPattern =
341 ExAllocatePoolWithTag(NonPagedPool, SearchPattern->Length + sizeof(WCHAR), TAG_NTFS);
342 if (!Ccb->DirectorySearchPattern)
343 {
344 return STATUS_INSUFFICIENT_RESOURCES;
345 }
346
347 memcpy(Ccb->DirectorySearchPattern,
348 SearchPattern->Buffer,
349 SearchPattern->Length);
350 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
351 }
352 }
353 else if (!Ccb->DirectorySearchPattern)
354 {
355 First = TRUE;
356 Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
357 if (!Ccb->DirectorySearchPattern)
358 {
359 return STATUS_INSUFFICIENT_RESOURCES;
360 }
361
362 Ccb->DirectorySearchPattern[0] = L'*';
363 Ccb->DirectorySearchPattern[1] = 0;
364 }
365
366 RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
367
368 DPRINT1("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
369 DPRINT1("In: '%S'\n", Fcb->PathName);
370
371 /* Determine directory index */
372 if (Stack->Flags & SL_INDEX_SPECIFIED)
373 {
374 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
375 }
376 else if (First || (Stack->Flags & SL_RESTART_SCAN))
377 {
378 Ccb->Entry = 0;
379 }
380
381 /* Determine Buffer for result */
382 if (Irp->MdlAddress)
383 {
384 Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
385 }
386 else
387 {
388 Buffer = Irp->UserBuffer;
389 }
390
391 DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
392
393 while (Status == STATUS_SUCCESS && BufferLength > 0)
394 {
395 Status = NtfsFindFileAt(DeviceExtension,
396 &Pattern,
397 &Ccb->Entry,
398 &FileRecord,
399 &DataContext,
400 &MFTRecord,
401 Fcb->MFTIndex);
402 //DPRINT("Found %S, Status=%x, entry %x\n", TempFcb.ObjectName, Status, Ccb->Entry);
403
404 if (NT_SUCCESS(Status))
405 {
406 switch (FileInformationClass)
407 {
408 case FileNameInformation:
409 Status = NtfsGetNameInformation(DeviceExtension,
410 FileRecord,
411 DataContext,
412 (PFILE_NAMES_INFORMATION)Buffer,
413 BufferLength);
414 break;
415
416 case FileDirectoryInformation:
417 Status = NtfsGetDirectoryInformation(DeviceExtension,
418 FileRecord,
419 DataContext,
420 (PFILE_DIRECTORY_INFORMATION)Buffer,
421 BufferLength);
422 break;
423
424 case FileFullDirectoryInformation:
425 Status = NtfsGetFullDirectoryInformation(DeviceExtension,
426 FileRecord,
427 DataContext,
428 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
429 BufferLength);
430 break;
431
432 case FileBothDirectoryInformation:
433 Status = NtfsGetBothDirectoryInformation(DeviceExtension,
434 FileRecord,
435 DataContext,
436 (PFILE_BOTH_DIR_INFORMATION)Buffer,
437 BufferLength);
438 break;
439
440 default:
441 Status = STATUS_INVALID_INFO_CLASS;
442 }
443
444 if (Status == STATUS_BUFFER_OVERFLOW)
445 {
446 if (Buffer0)
447 {
448 Buffer0->NextEntryOffset = 0;
449 }
450 break;
451 }
452 }
453 else
454 {
455 if (Buffer0)
456 {
457 Buffer0->NextEntryOffset = 0;
458 }
459
460 if (First)
461 {
462 Status = STATUS_NO_SUCH_FILE;
463 }
464 else
465 {
466 Status = STATUS_NO_MORE_FILES;
467 }
468 break;
469 }
470
471 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
472 Buffer0->FileIndex = FileIndex++;
473 Ccb->Entry++;
474
475 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
476 {
477 break;
478 }
479 BufferLength -= Buffer0->NextEntryOffset;
480 Buffer += Buffer0->NextEntryOffset;
481 ExFreePoolWithTag(FileRecord, TAG_NTFS);
482 }
483
484 if (Buffer0)
485 {
486 Buffer0->NextEntryOffset = 0;
487 }
488
489 if (FileIndex > 0)
490 {
491 Status = STATUS_SUCCESS;
492 }
493
494 return Status;
495 }
496
497
498 NTSTATUS
499 NTAPI
500 NtfsFsdDirectoryControl(PDEVICE_OBJECT DeviceObject,
501 PIRP Irp)
502 {
503 PNTFS_IRP_CONTEXT IrpContext = NULL;
504 NTSTATUS Status = STATUS_UNSUCCESSFUL;
505
506 DPRINT1("NtfsDirectoryControl() called\n");
507
508 FsRtlEnterFileSystem();
509 ASSERT(DeviceObject);
510 ASSERT(Irp);
511
512 NtfsIsIrpTopLevel(Irp);
513
514 IrpContext = NtfsAllocateIrpContext(DeviceObject, Irp);
515 if (IrpContext)
516 {
517 switch (IrpContext->MinorFunction)
518 {
519 case IRP_MN_QUERY_DIRECTORY:
520 Status = NtfsQueryDirectory(IrpContext);
521 break;
522
523 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
524 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
525 Status = STATUS_NOT_IMPLEMENTED;
526 break;
527
528 default:
529 Status = STATUS_INVALID_DEVICE_REQUEST;
530 break;
531 }
532 }
533 else
534 Status = STATUS_INSUFFICIENT_RESOURCES;
535
536 Irp->IoStatus.Status = Status;
537 Irp->IoStatus.Information = 0;
538 IoCompleteRequest(Irp, IO_NO_INCREMENT);
539
540 if (IrpContext)
541 ExFreePoolWithTag(IrpContext, 'PRIN');
542
543 IoSetTopLevelIrp(NULL);
544 FsRtlExitFileSystem();
545 return Status;
546 }
547
548 /* EOF */