[NTFS] Hack fix duplicate directory entries for files having a short name and a long...
[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, 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, OldMFTRecord = 0;
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 Pattern.Length = 0;
341 Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
342 Ccb->DirectorySearchPattern = Pattern.Buffer =
343 ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
344 if (!Ccb->DirectorySearchPattern)
345 {
346 return STATUS_INSUFFICIENT_RESOURCES;
347 }
348
349 Status = RtlUpcaseUnicodeString(&Pattern, SearchPattern, FALSE);
350 if (!NT_SUCCESS(Status))
351 {
352 DPRINT1("RtlUpcaseUnicodeString('%wZ') failed with status 0x%08lx\n", &Pattern, Status);
353 ExFreePoolWithTag(Ccb->DirectorySearchPattern, TAG_NTFS);
354 Ccb->DirectorySearchPattern = NULL;
355 return Status;
356 }
357 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
358 }
359 }
360 else if (!Ccb->DirectorySearchPattern)
361 {
362 First = TRUE;
363 Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
364 if (!Ccb->DirectorySearchPattern)
365 {
366 return STATUS_INSUFFICIENT_RESOURCES;
367 }
368
369 Ccb->DirectorySearchPattern[0] = L'*';
370 Ccb->DirectorySearchPattern[1] = 0;
371 }
372
373 RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
374 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
375 DPRINT("In: '%S'\n", Fcb->PathName);
376
377 /* Determine directory index */
378 if (Stack->Flags & SL_INDEX_SPECIFIED)
379 {
380 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
381 }
382 else if (First || (Stack->Flags & SL_RESTART_SCAN))
383 {
384 Ccb->Entry = 0;
385 }
386
387 /* Determine Buffer for result */
388 if (Irp->MdlAddress)
389 {
390 Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
391 }
392 else
393 {
394 Buffer = Irp->UserBuffer;
395 }
396
397 DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
398
399 while (Status == STATUS_SUCCESS && BufferLength > 0)
400 {
401 Status = NtfsFindFileAt(DeviceExtension,
402 &Pattern,
403 &Ccb->Entry,
404 &FileRecord,
405 &DataContext,
406 &MFTRecord,
407 Fcb->MFTIndex);
408
409 if (NT_SUCCESS(Status))
410 {
411 /* HACK: files with both a short name and a long name are present twice in the index.
412 * Ignore the second entry, if it is immediately following the first one.
413 */
414 if (MFTRecord == OldMFTRecord)
415 {
416 DPRINT("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
417 Ccb->Entry++;
418 ExFreePoolWithTag(FileRecord, TAG_NTFS);
419 continue;
420 }
421 OldMFTRecord = MFTRecord;
422
423 switch (FileInformationClass)
424 {
425 case FileNameInformation:
426 Status = NtfsGetNameInformation(DeviceExtension,
427 FileRecord,
428 DataContext,
429 (PFILE_NAMES_INFORMATION)Buffer,
430 BufferLength);
431 break;
432
433 case FileDirectoryInformation:
434 Status = NtfsGetDirectoryInformation(DeviceExtension,
435 FileRecord,
436 DataContext,
437 (PFILE_DIRECTORY_INFORMATION)Buffer,
438 BufferLength);
439 break;
440
441 case FileFullDirectoryInformation:
442 Status = NtfsGetFullDirectoryInformation(DeviceExtension,
443 FileRecord,
444 DataContext,
445 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
446 BufferLength);
447 break;
448
449 case FileBothDirectoryInformation:
450 Status = NtfsGetBothDirectoryInformation(DeviceExtension,
451 FileRecord,
452 DataContext,
453 (PFILE_BOTH_DIR_INFORMATION)Buffer,
454 BufferLength);
455 break;
456
457 default:
458 Status = STATUS_INVALID_INFO_CLASS;
459 }
460
461 if (Status == STATUS_BUFFER_OVERFLOW)
462 {
463 if (Buffer0)
464 {
465 Buffer0->NextEntryOffset = 0;
466 }
467 break;
468 }
469 }
470 else
471 {
472 if (Buffer0)
473 {
474 Buffer0->NextEntryOffset = 0;
475 }
476
477 if (First)
478 {
479 Status = STATUS_NO_SUCH_FILE;
480 }
481 else
482 {
483 Status = STATUS_NO_MORE_FILES;
484 }
485 break;
486 }
487
488 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
489 Buffer0->FileIndex = FileIndex++;
490 Ccb->Entry++;
491
492 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
493 {
494 break;
495 }
496 BufferLength -= Buffer0->NextEntryOffset;
497 Buffer += Buffer0->NextEntryOffset;
498 ExFreePoolWithTag(FileRecord, TAG_NTFS);
499 }
500
501 if (Buffer0)
502 {
503 Buffer0->NextEntryOffset = 0;
504 }
505
506 if (FileIndex > 0)
507 {
508 Status = STATUS_SUCCESS;
509 }
510
511 return Status;
512 }
513
514
515 NTSTATUS
516 NTAPI
517 NtfsFsdDirectoryControl(PDEVICE_OBJECT DeviceObject,
518 PIRP Irp)
519 {
520 PNTFS_IRP_CONTEXT IrpContext = NULL;
521 NTSTATUS Status = STATUS_UNSUCCESSFUL;
522
523 DPRINT1("NtfsDirectoryControl() called\n");
524
525 FsRtlEnterFileSystem();
526 ASSERT(DeviceObject);
527 ASSERT(Irp);
528
529 NtfsIsIrpTopLevel(Irp);
530
531 IrpContext = NtfsAllocateIrpContext(DeviceObject, Irp);
532 if (IrpContext)
533 {
534 switch (IrpContext->MinorFunction)
535 {
536 case IRP_MN_QUERY_DIRECTORY:
537 Status = NtfsQueryDirectory(IrpContext);
538 break;
539
540 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
541 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
542 Status = STATUS_NOT_IMPLEMENTED;
543 break;
544
545 default:
546 Status = STATUS_INVALID_DEVICE_REQUEST;
547 break;
548 }
549 }
550 else
551 Status = STATUS_INSUFFICIENT_RESOURCES;
552
553 Irp->IoStatus.Status = Status;
554 Irp->IoStatus.Information = 0;
555 IoCompleteRequest(Irp, IO_NO_INCREMENT);
556
557 if (IrpContext)
558 ExFreePoolWithTag(IrpContext, 'PRIN');
559
560 IoSetTopLevelIrp(NULL);
561 FsRtlExitFileSystem();
562 return Status;
563 }
564
565 /* EOF */