[NTFS] Apply fix from 52f0726: allow partial info copy on dir enumeration on first...
[reactos.git] / 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 ULONGLONG
38 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
39 PFILE_RECORD_HEADER FileRecord,
40 PCWSTR Stream,
41 ULONG StreamLength,
42 PULONGLONG AllocatedSize)
43 {
44 ULONGLONG Size = 0ULL;
45 ULONGLONG Allocated = 0ULL;
46 NTSTATUS Status;
47 PNTFS_ATTR_CONTEXT DataContext;
48
49 Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext, NULL);
50 if (NT_SUCCESS(Status))
51 {
52 Size = AttributeDataLength(DataContext->pRecord);
53 Allocated = AttributeAllocatedLength(DataContext->pRecord);
54 ReleaseAttributeContext(DataContext);
55 }
56
57 if (AllocatedSize != NULL) *AllocatedSize = Allocated;
58
59 return Size;
60 }
61
62
63 #define ULONG_ROUND_UP(x) ROUND_UP((x), (sizeof(ULONG)))
64
65
66 static NTSTATUS
67 NtfsGetNamesInformation(PDEVICE_EXTENSION DeviceExt,
68 PFILE_RECORD_HEADER FileRecord,
69 ULONGLONG MFTIndex,
70 PFILE_NAMES_INFORMATION Info,
71 ULONG BufferLength,
72 PULONG Written,
73 BOOLEAN First)
74 {
75 ULONG Length;
76 NTSTATUS Status;
77 ULONG BytesToCopy = 0;
78 PFILENAME_ATTRIBUTE FileName;
79
80 DPRINT("NtfsGetNamesInformation() called\n");
81
82 *Written = 0;
83 Status = STATUS_BUFFER_OVERFLOW;
84 if (FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) > BufferLength)
85 {
86 return Status;
87 }
88
89 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
90 if (FileName == NULL)
91 {
92 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
93 NtfsDumpFileAttributes(DeviceExt, FileRecord);
94 return STATUS_OBJECT_NAME_NOT_FOUND;
95 }
96
97 Length = FileName->NameLength * sizeof (WCHAR);
98 if (First || (BufferLength >= FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) + Length))
99 {
100 Info->FileNameLength = Length;
101
102 *Written = FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName);
103 Info->NextEntryOffset = 0;
104 if (BufferLength > FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName))
105 {
106 BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName));
107 RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
108 *Written += BytesToCopy;
109
110 if (BytesToCopy == Length)
111 {
112 Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_NAMES_INFORMATION) +
113 BytesToCopy);
114 Status = STATUS_SUCCESS;
115 }
116 }
117 }
118
119 return Status;
120 }
121
122
123 static NTSTATUS
124 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
125 PFILE_RECORD_HEADER FileRecord,
126 ULONGLONG MFTIndex,
127 PFILE_DIRECTORY_INFORMATION Info,
128 ULONG BufferLength,
129 PULONG Written,
130 BOOLEAN First)
131 {
132 ULONG Length;
133 NTSTATUS Status;
134 ULONG BytesToCopy = 0;
135 PFILENAME_ATTRIBUTE FileName;
136 PSTANDARD_INFORMATION StdInfo;
137
138 DPRINT("NtfsGetDirectoryInformation() called\n");
139
140 *Written = 0;
141 Status = STATUS_BUFFER_OVERFLOW;
142 if (FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) > BufferLength)
143 {
144 return Status;
145 }
146
147 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
148 if (FileName == NULL)
149 {
150 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
151 NtfsDumpFileAttributes(DeviceExt, FileRecord);
152 return STATUS_OBJECT_NAME_NOT_FOUND;
153 }
154
155 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
156 ASSERT(StdInfo != NULL);
157
158 Length = FileName->NameLength * sizeof (WCHAR);
159 if (First || (BufferLength >= FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) + Length))
160 {
161 Info->FileNameLength = Length;
162
163 *Written = FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName);
164 Info->NextEntryOffset = 0;
165 if (BufferLength > FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName))
166 {
167 BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName));
168 RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
169 *Written += BytesToCopy;
170
171 if (BytesToCopy == Length)
172 {
173 Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) +
174 BytesToCopy);
175 Status = STATUS_SUCCESS;
176 }
177 }
178
179 Info->CreationTime.QuadPart = FileName->CreationTime;
180 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
181 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
182 Info->ChangeTime.QuadPart = FileName->ChangeTime;
183
184 /* Convert file flags */
185 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
186
187 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
188
189 Info->FileIndex = MFTIndex;
190 }
191
192 return Status;
193 }
194
195
196 static NTSTATUS
197 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
198 PFILE_RECORD_HEADER FileRecord,
199 ULONGLONG MFTIndex,
200 PFILE_FULL_DIRECTORY_INFORMATION Info,
201 ULONG BufferLength,
202 PULONG Written,
203 BOOLEAN First)
204 {
205 ULONG Length;
206 NTSTATUS Status;
207 ULONG BytesToCopy = 0;
208 PFILENAME_ATTRIBUTE FileName;
209 PSTANDARD_INFORMATION StdInfo;
210
211 DPRINT("NtfsGetFullDirectoryInformation() called\n");
212
213 *Written = 0;
214 Status = STATUS_BUFFER_OVERFLOW;
215 if (FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) > BufferLength)
216 {
217 return Status;
218 }
219
220 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
221 if (FileName == NULL)
222 {
223 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
224 NtfsDumpFileAttributes(DeviceExt, FileRecord);
225 return STATUS_OBJECT_NAME_NOT_FOUND;
226 }
227
228 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
229 ASSERT(StdInfo != NULL);
230
231 Length = FileName->NameLength * sizeof (WCHAR);
232 if (First || (BufferLength >= FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) + Length))
233 {
234 Info->FileNameLength = Length;
235
236 *Written = FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName);
237 Info->NextEntryOffset = 0;
238 if (BufferLength > FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName))
239 {
240 BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName));
241 RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
242 *Written += BytesToCopy;
243
244 if (BytesToCopy == Length)
245 {
246 Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_FULL_DIR_INFORMATION) +
247 BytesToCopy);
248 Status = STATUS_SUCCESS;
249 }
250 }
251
252 Info->CreationTime.QuadPart = FileName->CreationTime;
253 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
254 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
255 Info->ChangeTime.QuadPart = FileName->ChangeTime;
256
257 /* Convert file flags */
258 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
259
260 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
261
262 Info->FileIndex = MFTIndex;
263 Info->EaSize = 0;
264 }
265
266 return Status;
267 }
268
269
270 static NTSTATUS
271 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
272 PFILE_RECORD_HEADER FileRecord,
273 ULONGLONG MFTIndex,
274 PFILE_BOTH_DIR_INFORMATION Info,
275 ULONG BufferLength,
276 PULONG Written,
277 BOOLEAN First)
278 {
279 ULONG Length;
280 NTSTATUS Status;
281 ULONG BytesToCopy = 0;
282 PFILENAME_ATTRIBUTE FileName, ShortFileName;
283 PSTANDARD_INFORMATION StdInfo;
284
285 DPRINT("NtfsGetBothDirectoryInformation() called\n");
286
287 *Written = 0;
288 Status = STATUS_BUFFER_OVERFLOW;
289 if (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) > BufferLength)
290 {
291 return Status;
292 }
293
294 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
295 if (FileName == NULL)
296 {
297 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
298 NtfsDumpFileAttributes(DeviceExt, FileRecord);
299 return STATUS_OBJECT_NAME_NOT_FOUND;
300 }
301 ShortFileName = GetFileNameFromRecord(DeviceExt, FileRecord, NTFS_FILE_NAME_DOS);
302
303 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
304 ASSERT(StdInfo != NULL);
305
306 Length = FileName->NameLength * sizeof (WCHAR);
307 if (First || (BufferLength >= FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) + Length))
308 {
309 Info->FileNameLength = Length;
310
311 *Written = FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName);
312 Info->NextEntryOffset = 0;
313 if (BufferLength > FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName))
314 {
315 BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName));
316 RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
317 *Written += BytesToCopy;
318
319 if (BytesToCopy == Length)
320 {
321 Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) +
322 BytesToCopy);
323 Status = STATUS_SUCCESS;
324 }
325 }
326
327 if (ShortFileName)
328 {
329 /* Should we upcase the filename? */
330 ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName));
331 Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR);
332 RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength);
333 }
334 else
335 {
336 Info->ShortName[0] = 0;
337 Info->ShortNameLength = 0;
338 }
339
340 Info->CreationTime.QuadPart = FileName->CreationTime;
341 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
342 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
343 Info->ChangeTime.QuadPart = FileName->ChangeTime;
344
345 /* Convert file flags */
346 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
347
348 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
349
350 Info->FileIndex = MFTIndex;
351 Info->EaSize = 0;
352 }
353
354 return Status;
355 }
356
357
358 NTSTATUS
359 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
360 {
361 PIRP Irp;
362 PDEVICE_OBJECT DeviceObject;
363 PDEVICE_EXTENSION DeviceExtension;
364 LONG BufferLength = 0;
365 PUNICODE_STRING SearchPattern = NULL;
366 FILE_INFORMATION_CLASS FileInformationClass;
367 ULONG FileIndex = 0;
368 PUCHAR Buffer = NULL;
369 PFILE_NAMES_INFORMATION Buffer0 = NULL;
370 PNTFS_FCB Fcb;
371 PNTFS_CCB Ccb;
372 BOOLEAN First = FALSE;
373 PIO_STACK_LOCATION Stack;
374 PFILE_OBJECT FileObject;
375 NTSTATUS Status = STATUS_SUCCESS;
376 PFILE_RECORD_HEADER FileRecord;
377 ULONGLONG MFTRecord, OldMFTRecord = 0;
378 UNICODE_STRING Pattern;
379 ULONG Written;
380
381 DPRINT1("NtfsQueryDirectory() called\n");
382
383 ASSERT(IrpContext);
384 Irp = IrpContext->Irp;
385 DeviceObject = IrpContext->DeviceObject;
386
387 DeviceExtension = DeviceObject->DeviceExtension;
388 Stack = IoGetCurrentIrpStackLocation(Irp);
389 FileObject = Stack->FileObject;
390
391 Ccb = (PNTFS_CCB)FileObject->FsContext2;
392 Fcb = (PNTFS_FCB)FileObject->FsContext;
393
394 /* Obtain the callers parameters */
395 BufferLength = Stack->Parameters.QueryDirectory.Length;
396 SearchPattern = Stack->Parameters.QueryDirectory.FileName;
397 FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
398 FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
399
400 if (NtfsFCBIsCompressed(Fcb))
401 {
402 DPRINT1("Compressed directory!\n");
403 UNIMPLEMENTED;
404 return STATUS_NOT_IMPLEMENTED;
405 }
406
407 if (!ExAcquireResourceSharedLite(&Fcb->MainResource,
408 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
409 {
410 return STATUS_PENDING;
411 }
412
413 if (SearchPattern != NULL)
414 {
415 if (!Ccb->DirectorySearchPattern)
416 {
417 First = TRUE;
418 Pattern.Length = 0;
419 Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
420 Ccb->DirectorySearchPattern = Pattern.Buffer =
421 ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
422 if (!Ccb->DirectorySearchPattern)
423 {
424 ExReleaseResourceLite(&Fcb->MainResource);
425 return STATUS_INSUFFICIENT_RESOURCES;
426 }
427
428 memcpy(Ccb->DirectorySearchPattern, SearchPattern->Buffer, SearchPattern->Length);
429 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
430 }
431 }
432 else if (!Ccb->DirectorySearchPattern)
433 {
434 First = TRUE;
435 Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
436 if (!Ccb->DirectorySearchPattern)
437 {
438 ExReleaseResourceLite(&Fcb->MainResource);
439 return STATUS_INSUFFICIENT_RESOURCES;
440 }
441
442 Ccb->DirectorySearchPattern[0] = L'*';
443 Ccb->DirectorySearchPattern[1] = 0;
444 }
445
446 RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
447 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
448 DPRINT("In: '%S'\n", Fcb->PathName);
449
450 /* Determine directory index */
451 if (Stack->Flags & SL_INDEX_SPECIFIED)
452 {
453 Ccb->Entry = FileIndex;
454 }
455 else if (First || (Stack->Flags & SL_RESTART_SCAN))
456 {
457 Ccb->Entry = 0;
458 }
459
460 /* Get Buffer for result */
461 Buffer = NtfsGetUserBuffer(Irp, FALSE);
462
463 DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
464
465 if (!ExAcquireResourceExclusiveLite(&DeviceExtension->DirResource,
466 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
467 {
468 ExReleaseResourceLite(&Fcb->MainResource);
469 return STATUS_PENDING;
470 }
471
472 Written = 0;
473 while (Status == STATUS_SUCCESS && BufferLength > 0)
474 {
475 Status = NtfsFindFileAt(DeviceExtension,
476 &Pattern,
477 &Ccb->Entry,
478 &FileRecord,
479 &MFTRecord,
480 Fcb->MFTIndex,
481 BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE));
482
483 if (NT_SUCCESS(Status))
484 {
485 /* HACK: files with both a short name and a long name are present twice in the index.
486 * Ignore the second entry, if it is immediately following the first one.
487 */
488 if (MFTRecord == OldMFTRecord)
489 {
490 DPRINT1("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
491 Ccb->Entry++;
492 ExFreeToNPagedLookasideList(&DeviceExtension->FileRecLookasideList, FileRecord);
493 continue;
494 }
495 OldMFTRecord = MFTRecord;
496
497 switch (FileInformationClass)
498 {
499 case FileNamesInformation:
500 Status = NtfsGetNamesInformation(DeviceExtension,
501 FileRecord,
502 MFTRecord,
503 (PFILE_NAMES_INFORMATION)Buffer,
504 BufferLength,
505 &Written,
506 Buffer0 == NULL);
507 break;
508
509 case FileDirectoryInformation:
510 Status = NtfsGetDirectoryInformation(DeviceExtension,
511 FileRecord,
512 MFTRecord,
513 (PFILE_DIRECTORY_INFORMATION)Buffer,
514 BufferLength,
515 &Written,
516 Buffer0 == NULL);
517 break;
518
519 case FileFullDirectoryInformation:
520 Status = NtfsGetFullDirectoryInformation(DeviceExtension,
521 FileRecord,
522 MFTRecord,
523 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
524 BufferLength,
525 &Written,
526 Buffer0 == NULL);
527 break;
528
529 case FileBothDirectoryInformation:
530 Status = NtfsGetBothDirectoryInformation(DeviceExtension,
531 FileRecord,
532 MFTRecord,
533 (PFILE_BOTH_DIR_INFORMATION)Buffer,
534 BufferLength,
535 &Written,
536 Buffer0 == NULL);
537 break;
538
539 default:
540 Status = STATUS_INVALID_INFO_CLASS;
541 }
542
543 if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_INVALID_INFO_CLASS)
544 {
545 break;
546 }
547 }
548 else
549 {
550 Status = (First ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES);
551 break;
552 }
553
554 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
555 Buffer0->FileIndex = FileIndex++;
556 Ccb->Entry++;
557 BufferLength -= Buffer0->NextEntryOffset;
558
559 ExFreeToNPagedLookasideList(&DeviceExtension->FileRecLookasideList, FileRecord);
560
561 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
562 {
563 break;
564 }
565
566 Buffer += Buffer0->NextEntryOffset;
567 }
568
569 if (Buffer0)
570 {
571 Buffer0->NextEntryOffset = 0;
572 Status = STATUS_SUCCESS;
573 IrpContext->Irp->IoStatus.Information = Stack->Parameters.QueryDirectory.Length - BufferLength;
574 }
575 else
576 {
577 ASSERT(Status != STATUS_SUCCESS || BufferLength == 0);
578 ASSERT(Written <= Stack->Parameters.QueryDirectory.Length);
579 IrpContext->Irp->IoStatus.Information = Written;
580 }
581
582 ExReleaseResourceLite(&DeviceExtension->DirResource);
583 ExReleaseResourceLite(&Fcb->MainResource);
584
585 return Status;
586 }
587
588
589 NTSTATUS
590 NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext)
591 {
592 NTSTATUS Status = STATUS_UNSUCCESSFUL;
593
594 DPRINT1("NtfsDirectoryControl() called\n");
595
596 switch (IrpContext->MinorFunction)
597 {
598 case IRP_MN_QUERY_DIRECTORY:
599 Status = NtfsQueryDirectory(IrpContext);
600 break;
601
602 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
603 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
604 Status = STATUS_NOT_IMPLEMENTED;
605 break;
606
607 default:
608 Status = STATUS_INVALID_DEVICE_REQUEST;
609 break;
610 }
611
612 if (Status == STATUS_PENDING && IrpContext->Flags & IRPCONTEXT_COMPLETE)
613 {
614 return NtfsMarkIrpContextForQueue(IrpContext);
615 }
616
617 IrpContext->Irp->IoStatus.Information = 0;
618
619 return Status;
620 }
621
622 /* EOF */