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