:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / drivers / fs / ntfs / dirctl.c
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002 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., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /* $Id$
20  *
21  * COPYRIGHT:        See COPYING in the top level directory
22  * PROJECT:          ReactOS kernel
23  * FILE:             services/fs/ntfs/dirctl.c
24  * PURPOSE:          NTFS filesystem driver
25  * PROGRAMMER:       Eric Kohl
26  */
27
28 /* INCLUDES *****************************************************************/
29
30 #include <ddk/ntddk.h>
31
32 //#define NDEBUG
33 #include <debug.h>
34
35 #include "ntfs.h"
36
37
38 /* FUNCTIONS ****************************************************************/
39
40 #if 0
41 static NTSTATUS
42 CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt,
43                  PVOID *Context,
44                  PVOID *Block,
45                  PLARGE_INTEGER StreamOffset,
46                  ULONG DirLength,
47                  PVOID *Ptr,
48                  PWSTR Name,
49                  PULONG pIndex,
50                  PULONG pIndex2)
51 /*
52  * FUNCTION: Retrieves the file name, be it in short or long file name format
53  */
54 {
55   PDIR_RECORD Record;
56   NTSTATUS Status;
57   ULONG Index = 0;
58   ULONG Offset = 0;
59   ULONG BlockOffset = 0;
60
61   Record = (PDIR_RECORD)*Block;
62   while(Index < *pIndex)
63     {
64       BlockOffset += Record->RecordLength;
65       Offset += Record->RecordLength;
66
67       Record = (PDIR_RECORD)(*Block + BlockOffset);
68       if (BlockOffset >= BLOCKSIZE || Record->RecordLength == 0)
69         {
70           DPRINT("Map next sector\n");
71           CcUnpinData(*Context);
72           StreamOffset->QuadPart += BLOCKSIZE;
73           Offset = ROUND_UP(Offset, BLOCKSIZE);
74           BlockOffset = 0;
75
76           if (!CcMapData(DeviceExt->StreamFileObject,
77                          StreamOffset,
78                          BLOCKSIZE, TRUE,
79                          Context, Block))
80             {
81               DPRINT("CcMapData() failed\n");
82               return(STATUS_UNSUCCESSFUL);
83             }
84           Record = (PDIR_RECORD)(*Block + BlockOffset);
85         }
86
87       if (Offset >= DirLength)
88         return(STATUS_NO_MORE_ENTRIES);
89
90       Index++;
91     }
92
93   DPRINT("Index %lu  RecordLength %lu  Offset %lu\n",
94          Index, Record->RecordLength, Offset);
95
96   if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
97     {
98       wcscpy(Name, L".");
99     }
100   else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
101     {
102       wcscpy(Name, L"..");
103     }
104   else
105     {
106       if (DeviceExt->CdInfo.JolietLevel == 0)
107         {
108           ULONG i;
109
110           for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
111             Name[i] = (WCHAR)Record->FileId[i];
112           Name[i] = 0;
113         }
114       else
115         {
116           CdfsSwapString(Name, Record->FileId, Record->FileIdLength);
117         }
118     }
119
120   DPRINT("Name '%S'\n", Name);
121
122   *Ptr = Record;
123
124   *pIndex = Index;
125
126   return(STATUS_SUCCESS);
127 }
128
129
130 static NTSTATUS
131 CdfsFindFile(PDEVICE_EXTENSION DeviceExt,
132              PFCB Fcb,
133              PFCB Parent,
134              PWSTR FileToFind,
135              PULONG pDirIndex,
136              PULONG pDirIndex2)
137 /*
138  * FUNCTION: Find a file
139  */
140 {
141   WCHAR name[256];
142   WCHAR TempStr[2];
143   PVOID Block;
144   NTSTATUS Status;
145   ULONG len;
146   ULONG DirIndex;
147   ULONG Offset;
148   ULONG Read;
149   BOOLEAN IsRoot;
150   PVOID Context = NULL;
151   ULONG DirSize;
152   PUCHAR Ptr;
153   PDIR_RECORD Record;
154   LARGE_INTEGER StreamOffset;
155
156   DPRINT("FindFile(Parent %x, FileToFind '%S', DirIndex: %d)\n",
157          Parent, FileToFind, pDirIndex ? *pDirIndex : 0);
158   DPRINT("FindFile: old Pathname %x, old Objectname %x)\n",
159          Fcb->PathName, Fcb->ObjectName);
160
161   IsRoot = FALSE;
162   DirIndex = 0;
163   if (wcslen (FileToFind) == 0)
164     {
165       CHECKPOINT;
166       TempStr[0] = (WCHAR) '.';
167       TempStr[1] = 0;
168       FileToFind = (PWSTR)&TempStr;
169     }
170
171   if (Parent)
172     {
173       if (Parent->Entry.ExtentLocationL == DeviceExt->CdInfo.RootStart)
174         {
175           IsRoot = TRUE;
176         }
177     }
178   else
179     {
180       IsRoot = TRUE;
181     }
182
183   if (IsRoot == TRUE)
184     {
185       StreamOffset.QuadPart = (LONGLONG)DeviceExt->CdInfo.RootStart * (LONGLONG)BLOCKSIZE;
186       DirSize = DeviceExt->CdInfo.RootSize;
187
188
189       if (FileToFind[0] == 0 || (FileToFind[0] == '\\' && FileToFind[1] == 0)
190           || (FileToFind[0] == '.' && FileToFind[1] == 0))
191         {
192           /* it's root : complete essentials fields then return ok */
193           RtlZeroMemory(Fcb, sizeof(FCB));
194
195           Fcb->PathName[0]='\\';
196           Fcb->ObjectName = &Fcb->PathName[1];
197           Fcb->Entry.ExtentLocationL = DeviceExt->CdInfo.RootStart;
198           Fcb->Entry.DataLengthL = DeviceExt->CdInfo.RootSize;
199           Fcb->Entry.FileFlags = 0x02; //FILE_ATTRIBUTE_DIRECTORY;
200
201           if (pDirIndex)
202             *pDirIndex = 0;
203           if (pDirIndex2)
204             *pDirIndex2 = 0;
205           DPRINT("CdfsFindFile: new Pathname %S, new Objectname %S)\n",Fcb->PathName, Fcb->ObjectName);
206           return (STATUS_SUCCESS);
207         }
208     }
209   else
210     {
211       StreamOffset.QuadPart = (LONGLONG)Parent->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
212       DirSize = Parent->Entry.DataLengthL;
213     }
214
215   DPRINT("StreamOffset %I64u  DirSize %lu\n", StreamOffset.QuadPart, DirSize);
216
217   if (pDirIndex && (*pDirIndex))
218     DirIndex = *pDirIndex;
219
220   if(!CcMapData(DeviceExt->StreamFileObject, &StreamOffset,
221                 BLOCKSIZE, TRUE, &Context, &Block))
222   {
223     DPRINT("CcMapData() failed\n");
224     return(STATUS_UNSUCCESSFUL);
225   }
226
227   Ptr = (PUCHAR)Block;
228   while(TRUE)
229     {
230       Record = (PDIR_RECORD)Ptr;
231       if (Record->RecordLength == 0)
232         {
233           DPRINT1("Stopped!\n");
234           break;
235         }
236         
237       DPRINT("RecordLength %u  ExtAttrRecordLength %u  NameLength %u\n",
238              Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
239
240       Status = CdfsGetEntryName(DeviceExt, &Context, &Block, &StreamOffset,
241                                 DirSize, (PVOID*)&Ptr, name, &DirIndex, pDirIndex2);
242       if (Status == STATUS_NO_MORE_ENTRIES)
243         {
244           break;
245         }
246       else if (Status == STATUS_UNSUCCESSFUL)
247         {
248           /* Note: the directory cache has already been unpinned */
249           return(Status);
250         }
251
252       DPRINT("Name '%S'\n", name);
253
254       if (wstrcmpjoki(name, FileToFind)) /* || wstrcmpjoki (name2, FileToFind)) */
255         {
256           if (Parent && Parent->PathName)
257             {
258               len = wcslen(Parent->PathName);
259               memcpy(Fcb->PathName, Parent->PathName, len*sizeof(WCHAR));
260               Fcb->ObjectName=&Fcb->PathName[len];
261               if (len != 1 || Fcb->PathName[0] != '\\')
262                 {
263                   Fcb->ObjectName[0] = '\\';
264                   Fcb->ObjectName = &Fcb->ObjectName[1];
265                 }
266             }
267           else
268             {
269               Fcb->ObjectName=Fcb->PathName;
270               Fcb->ObjectName[0]='\\';
271               Fcb->ObjectName=&Fcb->ObjectName[1];
272             }
273
274           DPRINT("PathName '%S'  ObjectName '%S'\n", Fcb->PathName, Fcb->ObjectName);
275
276           memcpy(&Fcb->Entry, Ptr, sizeof(DIR_RECORD));
277           wcsncpy(Fcb->ObjectName, name, MAX_PATH);
278           if (pDirIndex)
279             *pDirIndex = DirIndex;
280
281           DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d\n",
282                  Fcb->PathName, Fcb->ObjectName, DirIndex);
283
284           CcUnpinData(Context);
285
286           return(STATUS_SUCCESS);
287         }
288
289
290       Ptr = Ptr + Record->RecordLength;
291       DirIndex++;
292
293       if (((ULONG)Ptr - (ULONG)Block) >= DirSize)
294         {
295           DPRINT("Stopped!\n");
296           break;
297         }
298     }
299
300   CcUnpinData(Context);
301
302   if (pDirIndex)
303     *pDirIndex = DirIndex;
304
305   return(STATUS_UNSUCCESSFUL);
306 }
307
308
309 static NTSTATUS
310 CdfsGetNameInformation(PFCB Fcb,
311                        PDEVICE_EXTENSION DeviceExt,
312                        PFILE_NAMES_INFORMATION Info,
313                        ULONG BufferLength)
314 {
315   ULONG Length;
316
317   DPRINT("CdfsGetNameInformation() called\n");
318
319   Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
320   if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
321     return(STATUS_BUFFER_OVERFLOW);
322
323   Info->FileNameLength = Length;
324   Info->NextEntryOffset =
325     ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
326   memcpy(Info->FileName, Fcb->ObjectName, Length);
327
328   return(STATUS_SUCCESS);
329 }
330
331
332 static NTSTATUS
333 CdfsGetDirectoryInformation(PFCB Fcb,
334                             PDEVICE_EXTENSION DeviceExt,
335                             PFILE_DIRECTORY_INFORMATION Info,
336                             ULONG BufferLength)
337 {
338   ULONG Length;
339
340   DPRINT("CdfsGetDirectoryInformation() called\n");
341
342   Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
343   if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
344     return(STATUS_BUFFER_OVERFLOW);
345
346   Info->FileNameLength = Length;
347   Info->NextEntryOffset =
348     ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
349   memcpy(Info->FileName, Fcb->ObjectName, Length);
350
351   /* Convert file times */
352   CdfsDateTimeToFileTime(Fcb,
353                          &Info->CreationTime);
354   CdfsDateTimeToFileTime(Fcb,
355                          &Info->LastAccessTime);
356   CdfsDateTimeToFileTime(Fcb,
357                          &Info->LastWriteTime);
358   CdfsDateTimeToFileTime(Fcb,
359                          &Info->ChangeTime);
360
361   /* Convert file flags */
362   CdfsFileFlagsToAttributes(Fcb,
363                             &Info->FileAttributes);
364
365   Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
366
367   /* Make AllocSize a rounded up multiple of the sector size */
368   Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
369
370 //  Info->FileIndex=;
371
372   return(STATUS_SUCCESS);
373 }
374
375
376 static NTSTATUS
377 CdfsGetFullDirectoryInformation(PFCB Fcb,
378                                 PDEVICE_EXTENSION DeviceExt,
379                                 PFILE_FULL_DIRECTORY_INFORMATION Info,
380                                 ULONG BufferLength)
381 {
382   ULONG Length;
383
384   DPRINT("CdfsGetFullDirectoryInformation() called\n");
385
386   Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
387   if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
388     return(STATUS_BUFFER_OVERFLOW);
389
390   Info->FileNameLength = Length;
391   Info->NextEntryOffset =
392     ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
393   memcpy(Info->FileName, Fcb->ObjectName, Length);
394
395   /* Convert file times */
396   CdfsDateTimeToFileTime(Fcb,
397                          &Info->CreationTime);
398   CdfsDateTimeToFileTime(Fcb,
399                          &Info->LastAccessTime);
400   CdfsDateTimeToFileTime(Fcb,
401                          &Info->LastWriteTime);
402   CdfsDateTimeToFileTime(Fcb,
403                          &Info->ChangeTime);
404
405   /* Convert file flags */
406   CdfsFileFlagsToAttributes(Fcb,
407                             &Info->FileAttributes);
408
409   Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
410
411   /* Make AllocSize a rounded up multiple of the sector size */
412   Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
413
414 //  Info->FileIndex=;
415   Info->EaSize = 0;
416
417   return(STATUS_SUCCESS);
418 }
419
420
421 static NTSTATUS
422 CdfsGetBothDirectoryInformation(PFCB Fcb,
423                                 PDEVICE_EXTENSION DeviceExt,
424                                 PFILE_BOTH_DIRECTORY_INFORMATION Info,
425                                 ULONG BufferLength)
426 {
427   ULONG Length;
428
429   DPRINT("CdfsGetBothDirectoryInformation() called\n");
430
431   Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
432   if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
433     return(STATUS_BUFFER_OVERFLOW);
434
435   Info->FileNameLength = Length;
436   Info->NextEntryOffset =
437     ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
438   memcpy(Info->FileName, Fcb->ObjectName, Length);
439
440   /* Convert file times */
441   CdfsDateTimeToFileTime(Fcb,
442                          &Info->CreationTime);
443   CdfsDateTimeToFileTime(Fcb,
444                          &Info->LastAccessTime);
445   CdfsDateTimeToFileTime(Fcb,
446                          &Info->LastWriteTime);
447   CdfsDateTimeToFileTime(Fcb,
448                          &Info->ChangeTime);
449
450   /* Convert file flags */
451   CdfsFileFlagsToAttributes(Fcb,
452                             &Info->FileAttributes);
453
454   Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
455
456   /* Make AllocSize a rounded up multiple of the sector size */
457   Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
458
459 //  Info->FileIndex=;
460   Info->EaSize = 0;
461
462   if (DeviceExt->CdInfo.JolietLevel == 0)
463     {
464       /* Standard ISO-9660 format */
465       Info->ShortNameLength = Length;
466       memcpy(Info->ShortName, Fcb->ObjectName, Length);
467     }
468   else
469     {
470       /* Joliet extension */
471
472       /* FIXME: Copy or create a short file name */
473
474       Info->ShortName[0] = 0;
475       Info->ShortNameLength = 0;
476     }
477
478   return(STATUS_SUCCESS);
479 }
480 #endif
481
482 static NTSTATUS
483 NtfsQueryDirectory(PDEVICE_OBJECT DeviceObject,
484                    PIRP Irp)
485 {
486   PDEVICE_EXTENSION DeviceExtension;
487   LONG BufferLength = 0;
488   PUNICODE_STRING SearchPattern = NULL;
489   FILE_INFORMATION_CLASS FileInformationClass;
490   ULONG FileIndex = 0;
491   PUCHAR Buffer = NULL;
492   PFILE_NAMES_INFORMATION Buffer0 = NULL;
493   PFCB Fcb;
494   PCCB Ccb;
495   FCB TempFcb;
496   BOOLEAN First = FALSE;
497   PIO_STACK_LOCATION Stack;
498   PFILE_OBJECT FileObject;
499   NTSTATUS Status = STATUS_SUCCESS;
500
501   DPRINT1("NtfsQueryDirectory() called\n");
502
503   DeviceExtension = DeviceObject->DeviceExtension;
504   Stack = IoGetCurrentIrpStackLocation(Irp);
505   FileObject = Stack->FileObject;
506
507   Ccb = (PCCB)FileObject->FsContext2;
508   Fcb = Ccb->Fcb;
509
510   /* Obtain the callers parameters */
511   BufferLength = Stack->Parameters.QueryDirectory.Length;
512   SearchPattern = Stack->Parameters.QueryDirectory.FileName;
513   FileInformationClass =
514     Stack->Parameters.QueryDirectory.FileInformationClass;
515   FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
516
517
518   if (SearchPattern != NULL)
519     {
520       if (!Ccb->DirectorySearchPattern)
521         {
522           First = TRUE;
523           Ccb->DirectorySearchPattern =
524             ExAllocatePool(NonPagedPool, SearchPattern->Length + sizeof(WCHAR));
525           if (!Ccb->DirectorySearchPattern)
526             {
527               return(STATUS_INSUFFICIENT_RESOURCES);
528             }
529
530           memcpy(Ccb->DirectorySearchPattern,
531                  SearchPattern->Buffer,
532                  SearchPattern->Length);
533           Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
534         }
535     }
536   else if (!Ccb->DirectorySearchPattern)
537     {
538       First = TRUE;
539       Ccb->DirectorySearchPattern = ExAllocatePool(NonPagedPool, 2 * sizeof(WCHAR));
540       if (!Ccb->DirectorySearchPattern)
541         {
542           return(STATUS_INSUFFICIENT_RESOURCES);
543         }
544       Ccb->DirectorySearchPattern[0] = L'*';
545       Ccb->DirectorySearchPattern[1] = 0;
546     }
547   DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
548
549   /* Determine directory index */
550   if (Stack->Flags & SL_INDEX_SPECIFIED)
551     {
552       Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
553     }
554   else if (First || (Stack->Flags & SL_RESTART_SCAN))
555     {
556       Ccb->Entry = 0;
557     }
558
559   /* Determine Buffer for result */
560   if (Irp->MdlAddress)
561     {
562       Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
563     }
564   else
565     {
566       Buffer = Irp->UserBuffer;
567     }
568   DPRINT("Buffer=%x tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
569 #if 0
570   TempFcb.ObjectName = TempFcb.PathName;
571   while (Status == STATUS_SUCCESS && BufferLength > 0)
572     {
573       Status = CdfsFindFile(DeviceExtension,
574                             &TempFcb,
575                             Fcb,
576                             Ccb->DirectorySearchPattern,
577                             &Ccb->Entry,
578                             NULL);
579       DPRINT("Found %S, Status=%x, entry %x\n", TempFcb.ObjectName, Status, Ccb->Entry);
580
581       if (NT_SUCCESS(Status))
582         {
583           switch (FileInformationClass)
584             {
585               case FileNameInformation:
586                 Status = CdfsGetNameInformation(&TempFcb,
587                                                 DeviceExtension,
588                                                 (PFILE_NAMES_INFORMATION)Buffer,
589                                                 BufferLength);
590                 break;
591
592               case FileDirectoryInformation:
593                 Status = CdfsGetDirectoryInformation(&TempFcb,
594                                                      DeviceExtension,
595                                                      (PFILE_DIRECTORY_INFORMATION)Buffer,
596                                                      BufferLength);
597                 break;
598
599               case FileFullDirectoryInformation:
600                 Status = CdfsGetFullDirectoryInformation(&TempFcb,
601                                                          DeviceExtension,
602                                                          (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
603                                                          BufferLength);
604                 break;
605
606               case FileBothDirectoryInformation:
607                 Status = NtfsGetBothDirectoryInformation(&TempFcb,
608                                                          DeviceExtension,
609                                                          (PFILE_BOTH_DIRECTORY_INFORMATION)Buffer,
610                                                          BufferLength);
611                 break;
612
613               default:
614                 Status = STATUS_INVALID_INFO_CLASS;
615             }
616
617           if (Status == STATUS_BUFFER_OVERFLOW)
618             {
619               if (Buffer0)
620                 {
621                   Buffer0->NextEntryOffset = 0;
622                 }
623               break;
624             }
625         }
626       else
627         {
628           if (Buffer0)
629             {
630               Buffer0->NextEntryOffset = 0;
631             }
632
633           if (First)
634             {
635               Status = STATUS_NO_SUCH_FILE;
636             }
637           else
638             {
639               Status = STATUS_NO_MORE_FILES;
640             }
641           break;
642         }
643
644       Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
645       Buffer0->FileIndex = FileIndex++;
646       Ccb->Entry++;
647
648       if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
649         {
650           break;
651         }
652       BufferLength -= Buffer0->NextEntryOffset;
653       Buffer += Buffer0->NextEntryOffset;
654     }
655 #endif
656
657   if (Buffer0)
658     {
659       Buffer0->NextEntryOffset = 0;
660     }
661
662   if (FileIndex > 0)
663     {
664       Status = STATUS_SUCCESS;
665     }
666
667   return(Status);
668 }
669
670
671
672 NTSTATUS STDCALL
673 NtfsDirectoryControl(PDEVICE_OBJECT DeviceObject,
674                      PIRP Irp)
675 {
676   PIO_STACK_LOCATION Stack;
677   NTSTATUS Status;
678
679   DPRINT1("NtfsDirectoryControl() called\n");
680
681   Stack = IoGetCurrentIrpStackLocation(Irp);
682
683   switch (Stack->MinorFunction)
684     {
685       case IRP_MN_QUERY_DIRECTORY:
686         Status = NtfsQueryDirectory(DeviceObject,
687                                     Irp);
688         break;
689
690       case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
691         DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
692         Status = STATUS_NOT_IMPLEMENTED;
693         break;
694
695       default:
696         DPRINT1("NTFS: MinorFunction %d\n", Stack->MinorFunction);
697         Status = STATUS_INVALID_DEVICE_REQUEST;
698         break;
699     }
700
701   Irp->IoStatus.Status = Status;
702   Irp->IoStatus.Information = 0;
703
704   IoCompleteRequest(Irp, IO_NO_INCREMENT);
705
706   return(Status);
707 }
708
709 /* EOF */