Create the AHCI branch for Aman's work
[reactos.git] / drivers / filesystems / reiserfs / src / dirctl.c
1 /*
2 * COPYRIGHT: GNU GENERAL PUBLIC LICENSE VERSION 2
3 * PROJECT: ReiserFs file system driver for Windows NT/2000/XP/Vista.
4 * FILE: dirctl.c
5 * PURPOSE:
6 * PROGRAMMER: Mark Piper, Matt Wu, Bo Brantén.
7 * HOMEPAGE:
8 * UPDATE HISTORY:
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include "rfsd.h"
14
15 /* GLOBALS ***************************************************************/
16
17 extern PRFSD_GLOBAL RfsdGlobal;
18
19 /* DEFINITIONS *************************************************************/
20
21 NTSTATUS
22 RfsdDirectoryCallback(
23 ULONG BlockNumber,
24 PVOID pContext);
25
26 typedef struct _RFSD_CALLBACK_CONTEXT {
27
28 PRFSD_VCB Vcb;
29 PRFSD_CCB Ccb;
30
31 PRFSD_KEY_IN_MEMORY pDirectoryKey;
32 ULONG idxStartingDentry; // The dentry at which the callback should beging triggering output to the Buffer
33 ULONG idxCurrentDentry; // The current dentry (relative to entire set of dentrys, across all spans)
34
35 // These parameters are forwarded to ProcessDirectoryEntry
36 FILE_INFORMATION_CLASS FileInformationClass; // [s]
37 PVOID Buffer; // [s]
38 ULONG BufferLength; // [s]
39 BOOLEAN ReturnSingleEntry; // [s]
40
41 PULONG pUsedLength;
42 PVOID pPreviousEntry;
43
44 } RFSD_CALLBACK_CONTEXT, *PRFSD_CALLBACK_CONTEXT;
45
46 #ifdef ALLOC_PRAGMA
47 #pragma alloc_text(PAGE, RfsdGetInfoLength)
48 #pragma alloc_text(PAGE, RfsdProcessDirEntry)
49 #pragma alloc_text(PAGE, RfsdQueryDirectory)
50 #pragma alloc_text(PAGE, RfsdNotifyChangeDirectory)
51 #pragma alloc_text(PAGE, RfsdDirectoryControl)
52 #pragma alloc_text(PAGE, RfsdIsDirectoryEmpty)
53 #pragma alloc_text(PAGE, RfsdDirectoryCallback)
54 #endif
55
56 ULONG
57 RfsdGetInfoLength(IN FILE_INFORMATION_CLASS FileInformationClass)
58 {
59 PAGED_CODE();
60
61 switch (FileInformationClass) {
62
63 case FileDirectoryInformation:
64 return sizeof(FILE_DIRECTORY_INFORMATION);
65 break;
66
67 case FileFullDirectoryInformation:
68 return sizeof(FILE_FULL_DIR_INFORMATION);
69 break;
70
71 case FileBothDirectoryInformation:
72 return sizeof(FILE_BOTH_DIR_INFORMATION);
73 break;
74
75 case FileNamesInformation:
76 return sizeof(FILE_NAMES_INFORMATION);
77 break;
78
79 default:
80 break;
81 }
82
83 return 0;
84 }
85
86 ULONG // Returns 0 on error, or InfoLength + NameLength (the amount of the buffer used for the entry given)
87 RfsdProcessDirEntry(
88 IN PRFSD_VCB Vcb,
89 IN FILE_INFORMATION_CLASS FileInformationClass, // Identifier indicating the type of file information this function should report
90 IN __u32 Key_ParentDirectoryID,
91 IN __u32 Key_ObjectID,
92 IN PVOID Buffer, // The user's buffer, as obtained from the IRP context (it is already gauranteed to be valid)
93 IN ULONG UsedLength, // Length of Buffer used so far
94 IN ULONG Length, // Length of Buffer remaining, beyond UsedLength
95 IN ULONG FileIndex, // Byte offset of the dentry?? (This will just be placed into the file info structure of the same name)
96 IN PUNICODE_STRING pName, // Filled unicode equivalent of the name (as pulled out from the dentry)
97 IN BOOLEAN Single, // Whether or not QueryDirectory is only supposed to return a single entry
98 IN PVOID pPreviousEntry ) // A pointer to the previous dir entry in Buffer, which will be linked into the newly added entry if one is created
99 {
100 RFSD_INODE inode;
101 PFILE_DIRECTORY_INFORMATION FDI;
102 PFILE_FULL_DIR_INFORMATION FFI;
103 PFILE_BOTH_DIR_INFORMATION FBI;
104 PFILE_NAMES_INFORMATION FNI;
105
106 ULONG InfoLength = 0;
107 ULONG NameLength = 0;
108 ULONG dwBytes = 0;
109 LONGLONG FileSize;
110 LONGLONG AllocationSize;
111
112 PAGED_CODE();
113
114 // Calculate the size of the entry
115 NameLength = pName->Length;
116 InfoLength = RfsdGetInfoLength(FileInformationClass);
117
118 if (!InfoLength || InfoLength + NameLength - sizeof(WCHAR) > Length) {
119 RfsdPrint((DBG_INFO, "RfsdPricessDirEntry: Buffer is not enough.\n"));
120 return 0;
121 }
122
123 // Given the incoming key for this dentry, load the corresponding stat data.
124 {
125 RFSD_KEY_IN_MEMORY key;
126 key.k_dir_id = Key_ParentDirectoryID;
127 key.k_objectid = Key_ObjectID;
128
129 if(!RfsdLoadInode(Vcb, &key, &inode)) {
130 RfsdPrint((DBG_ERROR, "RfsdPricessDirEntry: Loading stat data %xh, %xh error.\n", Key_ParentDirectoryID, Key_ObjectID));
131 DbgBreak();
132 return 0;
133 }
134 }
135
136 FileSize = (LONGLONG) inode.i_size;
137 AllocationSize = CEILING_ALIGNED(FileSize, (ULONGLONG)Vcb->BlockSize); // TODO: THIS ISN'T QUITE RIGHT
138
139 // Link the previous entry into this entry
140 if (pPreviousEntry) {
141 // NOTE: All entries begin with NextEntryOffset, so it doesn't matter what type I cast to.
142 ((PFILE_NAMES_INFORMATION) (pPreviousEntry))->NextEntryOffset =
143 (ULONG) ((PUCHAR) Buffer + UsedLength - (PUCHAR) (pPreviousEntry));
144 }
145
146 switch(FileInformationClass) {
147
148 case FileDirectoryInformation:
149 FDI = (PFILE_DIRECTORY_INFORMATION) ((PUCHAR)Buffer + UsedLength);
150 FDI->NextEntryOffset = 0;
151
152 FDI->FileIndex = FileIndex;
153 FDI->CreationTime = RfsdSysTime(inode.i_ctime);
154 FDI->LastAccessTime = RfsdSysTime(inode.i_atime);
155 FDI->LastWriteTime = RfsdSysTime(inode.i_mtime);
156 FDI->ChangeTime = RfsdSysTime(inode.i_mtime);
157 FDI->EndOfFile.QuadPart = FileSize;
158 FDI->AllocationSize.QuadPart = AllocationSize;
159 FDI->FileAttributes = FILE_ATTRIBUTE_NORMAL;
160
161 if (FlagOn(Vcb->Flags, VCB_READ_ONLY) || RfsdIsReadOnly(inode.i_mode)) {
162 SetFlag(FDI->FileAttributes, FILE_ATTRIBUTE_READONLY);
163 }
164
165 if (S_ISDIR(inode.i_mode))
166 FDI->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
167
168 FDI->FileNameLength = NameLength;
169 RtlCopyMemory(FDI->FileName, pName->Buffer, NameLength);
170 dwBytes = InfoLength + NameLength - sizeof(WCHAR);
171 break;
172
173 case FileFullDirectoryInformation:
174
175 FFI = (PFILE_FULL_DIR_INFORMATION) ((PUCHAR)Buffer + UsedLength);
176 FFI->NextEntryOffset = 0;
177
178 FFI->FileIndex = FileIndex;
179 FFI->CreationTime = RfsdSysTime(inode.i_ctime);
180 FFI->LastAccessTime = RfsdSysTime(inode.i_atime);
181 FFI->LastWriteTime = RfsdSysTime(inode.i_mtime);
182 FFI->ChangeTime = RfsdSysTime(inode.i_mtime);
183 FFI->EndOfFile.QuadPart = FileSize;
184 FFI->AllocationSize.QuadPart = AllocationSize;
185 FFI->FileAttributes = FILE_ATTRIBUTE_NORMAL;
186
187 if (IsFlagOn(Vcb->Flags, VCB_READ_ONLY) || RfsdIsReadOnly(inode.i_mode)) {
188 SetFlag(FFI->FileAttributes, FILE_ATTRIBUTE_READONLY);
189 }
190
191 if (S_ISDIR(inode.i_mode))
192 FFI->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
193
194 FFI->FileNameLength = NameLength;
195 RtlCopyMemory(FFI->FileName, pName->Buffer, NameLength);
196 dwBytes = InfoLength + NameLength - sizeof(WCHAR);
197
198 break;
199
200 case FileBothDirectoryInformation:
201
202 FBI = (PFILE_BOTH_DIR_INFORMATION) ((PUCHAR)Buffer + UsedLength);
203 FBI->NextEntryOffset = 0;
204
205 FBI->CreationTime = RfsdSysTime(inode.i_ctime);
206 FBI->LastAccessTime = RfsdSysTime(inode.i_atime);
207 FBI->LastWriteTime = RfsdSysTime(inode.i_mtime);
208 FBI->ChangeTime = RfsdSysTime(inode.i_mtime);
209
210 FBI->FileIndex = FileIndex;
211 FBI->EndOfFile.QuadPart = FileSize;
212 FBI->AllocationSize.QuadPart = AllocationSize;
213 FBI->FileAttributes = FILE_ATTRIBUTE_NORMAL;
214
215 if (FlagOn(Vcb->Flags, VCB_READ_ONLY) || RfsdIsReadOnly(inode.i_mode)) {
216 SetFlag(FBI->FileAttributes, FILE_ATTRIBUTE_READONLY);
217 }
218
219 if (S_ISDIR(inode.i_mode))
220 FBI->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
221
222 FBI->FileNameLength = NameLength;
223 RtlCopyMemory(FBI->FileName, pName->Buffer, NameLength);
224 dwBytes = InfoLength + NameLength - sizeof(WCHAR);
225
226 break;
227
228 case FileNamesInformation:
229
230 FNI = (PFILE_NAMES_INFORMATION) ((PUCHAR)Buffer + UsedLength);
231 FNI->NextEntryOffset = 0;
232
233 FNI->FileNameLength = NameLength;
234 RtlCopyMemory(FNI->FileName, pName->Buffer, NameLength);
235 dwBytes = InfoLength + NameLength - sizeof(WCHAR);
236
237 break;
238
239 default:
240 break;
241 }
242
243 return dwBytes;
244 }
245
246 /**
247 caller suplies a ptr to a file obj for an open target dir, a search pattern to use when listing, and a spec of the info requested.
248 FSD expected to search and return matching info [503]
249
250 The Fcb->RfsdMcb->Key will determine which directory to list the contents of.
251 */
252 __drv_mustHoldCriticalRegion
253 NTSTATUS
254 RfsdQueryDirectory (IN PRFSD_IRP_CONTEXT IrpContext)
255 {
256 PDEVICE_OBJECT DeviceObject;
257 NTSTATUS Status = STATUS_UNSUCCESSFUL;
258 PRFSD_VCB Vcb;
259 PFILE_OBJECT FileObject;
260 PRFSD_FCB Fcb = 0;
261 PRFSD_CCB Ccb;
262 PIRP Irp;
263 PIO_STACK_LOCATION IoStackLocation;
264 FILE_INFORMATION_CLASS FileInformationClass;
265 ULONG Length;
266 PUNICODE_STRING FileName;
267 ULONG FileIndex;
268 BOOLEAN RestartScan;
269 BOOLEAN ReturnSingleEntry;
270 BOOLEAN IndexSpecified;
271 PUCHAR Buffer;
272 BOOLEAN FirstQuery;
273 PRFSD_KEY_IN_MEMORY pQueryKey; // The key of the directory item that is being retrieved
274 BOOLEAN FcbResourceAcquired = FALSE;
275 ULONG UsedLength = 0;
276
277 PAGED_CODE();
278
279 _SEH2_TRY {
280
281 ASSERT(IrpContext);
282
283 ASSERT((IrpContext->Identifier.Type == RFSDICX) &&
284 (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT)));
285
286 DeviceObject = IrpContext->DeviceObject;
287
288 //
289 // This request is not allowed on the main device object
290 //
291 if (DeviceObject == RfsdGlobal->DeviceObject) {
292 Status = STATUS_INVALID_DEVICE_REQUEST;
293 _SEH2_LEAVE;
294 }
295
296 Vcb = (PRFSD_VCB) DeviceObject->DeviceExtension;
297
298 ASSERT(Vcb != NULL);
299
300 ASSERT((Vcb->Identifier.Type == RFSDVCB) &&
301 (Vcb->Identifier.Size == sizeof(RFSD_VCB)));
302
303 ASSERT(IsMounted(Vcb));
304
305 FileObject = IrpContext->FileObject;
306
307 Fcb = (PRFSD_FCB) FileObject->FsContext;
308 pQueryKey = &(Fcb->RfsdMcb->Key);
309
310 ASSERT(Fcb);
311
312 KdPrint(("QueryDirectory on Key {%x,%x,%x,%x}\n",
313 pQueryKey->k_dir_id, pQueryKey->k_objectid, pQueryKey->k_offset, pQueryKey->k_type));
314
315 //
316 // This request is not allowed on volumes
317 //
318 if (Fcb->Identifier.Type == RFSDVCB) {
319 Status = STATUS_INVALID_PARAMETER;
320 _SEH2_LEAVE;
321 }
322
323 ASSERT((Fcb->Identifier.Type == RFSDFCB) &&
324 (Fcb->Identifier.Size == sizeof(RFSD_FCB)));
325
326 if (!IsDirectory(Fcb)) {
327 Status = STATUS_INVALID_PARAMETER;
328 _SEH2_LEAVE;
329 }
330
331 Ccb = (PRFSD_CCB) FileObject->FsContext2;
332
333 ASSERT(Ccb);
334
335 ASSERT((Ccb->Identifier.Type == RFSDCCB) &&
336 (Ccb->Identifier.Size == sizeof(RFSD_CCB)));
337
338 Irp = IrpContext->Irp;
339
340 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
341
342 #ifndef _GNU_NTIFS_
343
344 FileInformationClass =
345 IoStackLocation->Parameters.QueryDirectory.FileInformationClass;
346
347 Length = IoStackLocation->Parameters.QueryDirectory.Length;
348
349 FileName = IoStackLocation->Parameters.QueryDirectory.FileName;
350
351 FileIndex = IoStackLocation->Parameters.QueryDirectory.FileIndex;
352
353 RestartScan = FlagOn(IoStackLocation->Flags, SL_RESTART_SCAN);
354
355 ReturnSingleEntry = FlagOn(IoStackLocation->Flags, SL_RETURN_SINGLE_ENTRY);
356
357 IndexSpecified = FlagOn(IoStackLocation->Flags, SL_INDEX_SPECIFIED);
358
359 #else // _GNU_NTIFS_
360
361 FileInformationClass = ((PEXTENDED_IO_STACK_LOCATION)
362 IoStackLocation)->Parameters.QueryDirectory.FileInformationClass;
363
364 Length = ((PEXTENDED_IO_STACK_LOCATION)
365 IoStackLocation)->Parameters.QueryDirectory.Length;
366
367 FileName = ((PEXTENDED_IO_STACK_LOCATION)
368 IoStackLocation)->Parameters.QueryDirectory.FileName;
369
370 FileIndex = ((PEXTENDED_IO_STACK_LOCATION)
371 IoStackLocation)->Parameters.QueryDirectory.FileIndex;
372
373 RestartScan = FlagOn(((PEXTENDED_IO_STACK_LOCATION)
374 IoStackLocation)->Flags, SL_RESTART_SCAN);
375
376 ReturnSingleEntry = FlagOn(((PEXTENDED_IO_STACK_LOCATION)
377 IoStackLocation)->Flags, SL_RETURN_SINGLE_ENTRY);
378
379 IndexSpecified = FlagOn(((PEXTENDED_IO_STACK_LOCATION)
380 IoStackLocation)->Flags, SL_INDEX_SPECIFIED);
381
382 #endif // _GNU_NTIFS_
383
384 /*
385 if (!Irp->MdlAddress && Irp->UserBuffer) {
386 ProbeForWrite(Irp->UserBuffer, Length, 1);
387 }
388 */
389
390 // Check that the user's buffer for the output is valid..
391 Buffer = RfsdGetUserBuffer(Irp);
392
393 if (Buffer == NULL) {
394 DbgBreak();
395 Status = STATUS_INVALID_USER_BUFFER;
396 _SEH2_LEAVE;
397 }
398
399 // Check if we have a synchronous or asynchronous request...
400 if (!IrpContext->IsSynchronous) {
401 Status = STATUS_PENDING;
402 _SEH2_LEAVE;
403 }
404
405 if (!ExAcquireResourceSharedLite(
406 &Fcb->MainResource,
407 IrpContext->IsSynchronous )) {
408 Status = STATUS_PENDING;
409 _SEH2_LEAVE;
410 }
411
412 FcbResourceAcquired = TRUE;
413
414 if (FileName != NULL) {
415 // The caller provided a FileName to search on...
416
417 if (Ccb->DirectorySearchPattern.Buffer != NULL) {
418 FirstQuery = FALSE;
419 } else {
420 FirstQuery = TRUE;
421
422 // Set up the DirectorySearchPattern to simply be an uppercase copy of the FileName.
423 Ccb->DirectorySearchPattern.Length =
424 Ccb->DirectorySearchPattern.MaximumLength =
425 FileName->Length;
426
427 Ccb->DirectorySearchPattern.Buffer =
428 ExAllocatePoolWithTag(PagedPool, FileName->Length, RFSD_POOL_TAG);
429
430 if (Ccb->DirectorySearchPattern.Buffer == NULL) {
431 Status = STATUS_INSUFFICIENT_RESOURCES;
432 _SEH2_LEAVE;
433 }
434
435 Status = RtlUpcaseUnicodeString(
436 &(Ccb->DirectorySearchPattern),
437 FileName,
438 FALSE);
439
440 if (!NT_SUCCESS(Status))
441 _SEH2_LEAVE;
442 }
443 } else if (Ccb->DirectorySearchPattern.Buffer != NULL) {
444 FirstQuery = FALSE;
445 FileName = &Ccb->DirectorySearchPattern;
446 } else {
447 // (The FileName and CCB's DirectorySearchPattern were null)
448
449 FirstQuery = TRUE;
450
451 Ccb->DirectorySearchPattern.Length =
452 Ccb->DirectorySearchPattern.MaximumLength = 2;
453
454 Ccb->DirectorySearchPattern.Buffer =
455 ExAllocatePoolWithTag(PagedPool, 2, RFSD_POOL_TAG);
456
457 if (Ccb->DirectorySearchPattern.Buffer == NULL) {
458 Status = STATUS_INSUFFICIENT_RESOURCES;
459 _SEH2_LEAVE;
460 }
461
462 RtlCopyMemory(
463 Ccb->DirectorySearchPattern.Buffer,
464 L"*\0", 2);
465 }
466
467 if (!IndexSpecified) {
468 if (RestartScan || FirstQuery) {
469 FileIndex = Fcb->RfsdMcb->DeOffset = 0;
470 } else {
471 // If we are not starting/restaring a scan, then we must have already started.
472 // Retrieve the byte offset (in the directory . file) where we left off.
473 FileIndex = Ccb->CurrentByteOffset;
474 }
475 }
476
477
478 RtlZeroMemory(Buffer, Length);
479
480 // Leave if a previous query has already read the entire contents of the directory
481 if (Fcb->Inode->i_size <= FileIndex) {
482 Status = STATUS_NO_MORE_FILES;
483 _SEH2_LEAVE;
484 }
485
486 ////////
487
488 // Construct a context for the call, and call to parse the entire file system tree.
489 // A callback will be triggered on any direntry span belonging to DirectoryKey.
490 // This callback will fill the requested section of the user buffer.
491 {
492 ULONG CurrentFileIndex;
493
494 RFSD_CALLBACK_CONTEXT CallbackContext;
495 CallbackContext.Vcb = Vcb;
496 CallbackContext.Ccb = Ccb;
497 CallbackContext.idxStartingDentry = FileIndex / sizeof(RFSD_DENTRY_HEAD);
498 CallbackContext.idxCurrentDentry = 0;
499 CallbackContext.pDirectoryKey = pQueryKey;
500 CallbackContext.FileInformationClass = FileInformationClass;
501 CallbackContext.Buffer = Buffer;
502 CallbackContext.BufferLength = Length;
503 CallbackContext.ReturnSingleEntry = ReturnSingleEntry;
504 CallbackContext.pUsedLength = &UsedLength;
505 CallbackContext.pPreviousEntry = NULL;
506
507 RfsdPrint((DBG_TRACE, "Calculated idxCurrentDentry to be %i\n", CallbackContext.idxStartingDentry));
508
509 RfsdParseFilesystemTree(Vcb, pQueryKey, Vcb->SuperBlock->s_root_block, &RfsdDirectoryCallback, &CallbackContext);
510 }
511
512 //================================================================
513
514 if (!UsedLength) {
515 // No amount of the dsetination buffer has been used (meaning there were no results)...
516 if (FirstQuery) {
517 Status = STATUS_NO_SUCH_FILE;
518 } else {
519 Status = STATUS_NO_MORE_FILES;
520 }
521 } else {
522 Status = STATUS_SUCCESS;
523 }
524
525 } _SEH2_FINALLY {
526
527 if (FcbResourceAcquired) {
528 ExReleaseResourceForThreadLite(
529 &Fcb->MainResource,
530 ExGetCurrentResourceThread() );
531 }
532
533 if (!IrpContext->ExceptionInProgress) {
534 if (Status == STATUS_PENDING) {
535 Status = RfsdLockUserBuffer(
536 IrpContext->Irp,
537 Length,
538 IoWriteAccess );
539
540 if (NT_SUCCESS(Status)) {
541 Status = RfsdQueueRequest(IrpContext);
542 } else {
543 RfsdCompleteIrpContext(IrpContext, Status);
544 }
545 } else {
546 IrpContext->Irp->IoStatus.Information = UsedLength;
547 RfsdCompleteIrpContext(IrpContext, Status);
548 }
549 }
550 } _SEH2_END;
551
552 return Status;
553 }
554
555 __drv_mustHoldCriticalRegion
556 NTSTATUS
557 RfsdNotifyChangeDirectory (
558 IN PRFSD_IRP_CONTEXT IrpContext
559 )
560 {
561 PDEVICE_OBJECT DeviceObject;
562 BOOLEAN CompleteRequest;
563 NTSTATUS Status = STATUS_UNSUCCESSFUL;
564 PRFSD_VCB Vcb;
565 PFILE_OBJECT FileObject;
566 PRFSD_FCB Fcb = 0;
567 PIRP Irp;
568 PIO_STACK_LOCATION IrpSp;
569 ULONG CompletionFilter;
570 BOOLEAN WatchTree;
571 BOOLEAN bFcbAcquired = FALSE;
572 PUNICODE_STRING FullName;
573
574 PAGED_CODE();
575
576 _SEH2_TRY {
577
578 ASSERT(IrpContext);
579
580 ASSERT((IrpContext->Identifier.Type == RFSDICX) &&
581 (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT)));
582
583 //
584 // Always set the wait flag in the Irp context for the original request.
585 //
586
587 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
588
589 DeviceObject = IrpContext->DeviceObject;
590
591 if (DeviceObject == RfsdGlobal->DeviceObject) {
592 CompleteRequest = TRUE;
593 Status = STATUS_INVALID_DEVICE_REQUEST;
594 _SEH2_LEAVE;
595 }
596
597 Vcb = (PRFSD_VCB) DeviceObject->DeviceExtension;
598
599 ASSERT(Vcb != NULL);
600
601 ASSERT((Vcb->Identifier.Type == RFSDVCB) &&
602 (Vcb->Identifier.Size == sizeof(RFSD_VCB)));
603
604 ASSERT(IsMounted(Vcb));
605
606 FileObject = IrpContext->FileObject;
607
608 Fcb = (PRFSD_FCB) FileObject->FsContext;
609
610 ASSERT(Fcb);
611
612 if (Fcb->Identifier.Type == RFSDVCB) {
613 DbgBreak();
614 CompleteRequest = TRUE;
615 Status = STATUS_INVALID_PARAMETER;
616 _SEH2_LEAVE;
617 }
618
619 ASSERT((Fcb->Identifier.Type == RFSDFCB) &&
620 (Fcb->Identifier.Size == sizeof(RFSD_FCB)));
621
622 if (!IsDirectory(Fcb)) {
623 //- DbgBreak(); // NOTE: Windows (at least I've noticed it with the image previewer), will send this request oftentimes on a file!
624 CompleteRequest = TRUE;
625 Status = STATUS_INVALID_PARAMETER;
626 _SEH2_LEAVE;
627 }
628
629 if (ExAcquireResourceExclusiveLite(
630 &Fcb->MainResource,
631 TRUE )) {
632 bFcbAcquired = TRUE;
633 } else {
634 Status = STATUS_PENDING;
635 _SEH2_LEAVE;
636 }
637
638 Irp = IrpContext->Irp;
639
640 IrpSp = IoGetCurrentIrpStackLocation(Irp);
641
642 #ifndef _GNU_NTIFS_
643
644 CompletionFilter =
645 IrpSp->Parameters.NotifyDirectory.CompletionFilter;
646
647 #else // _GNU_NTIFS_
648
649 CompletionFilter = ((PEXTENDED_IO_STACK_LOCATION)
650 IrpSp)->Parameters.NotifyDirectory.CompletionFilter;
651
652 #endif // _GNU_NTIFS_
653
654 WatchTree = IsFlagOn(IrpSp->Flags, SL_WATCH_TREE);
655
656 if (FlagOn(Fcb->Flags, FCB_DELETE_PENDING)) {
657 Status = STATUS_DELETE_PENDING;
658 _SEH2_LEAVE;
659 }
660
661 FullName = &Fcb->LongName;
662
663 if (FullName->Buffer == NULL) {
664 if (!RfsdGetFullFileName(Fcb->RfsdMcb, FullName)) {
665 Status = STATUS_INSUFFICIENT_RESOURCES;
666 _SEH2_LEAVE;
667 }
668 }
669
670 FsRtlNotifyFullChangeDirectory( Vcb->NotifySync,
671 &Vcb->NotifyList,
672 FileObject->FsContext2,
673 (PSTRING)FullName,
674 WatchTree,
675 FALSE,
676 CompletionFilter,
677 Irp,
678 NULL,
679 NULL );
680
681 CompleteRequest = FALSE;
682
683 Status = STATUS_PENDING;
684
685 /*
686 Currently the driver is read-only but here is an example on how to use the
687 FsRtl-functions to report a change:
688
689 ANSI_STRING TestString;
690 USHORT FileNamePartLength;
691
692 RtlInitAnsiString(&TestString, "\\ntifs.h");
693
694 FileNamePartLength = 7;
695
696 FsRtlNotifyReportChange(
697 Vcb->NotifySync, // PNOTIFY_SYNC NotifySync
698 &Vcb->NotifyList, // PLIST_ENTRY NotifyList
699 &TestString, // PSTRING FullTargetName
700 &FileNamePartLength, // PUSHORT FileNamePartLength
701 FILE_NOTIFY_CHANGE_NAME // ULONG FilterMatch
702 );
703
704 or
705
706 ANSI_STRING TestString;
707
708 RtlInitAnsiString(&TestString, "\\ntifs.h");
709
710 FsRtlNotifyFullReportChange(
711 Vcb->NotifySync, // PNOTIFY_SYNC NotifySync
712 &Vcb->NotifyList, // PLIST_ENTRY NotifyList
713 &TestString, // PSTRING FullTargetName
714 1, // USHORT TargetNameOffset
715 NULL, // PSTRING StreamName OPTIONAL
716 NULL, // PSTRING NormalizedParentName OPTIONAL
717 FILE_NOTIFY_CHANGE_NAME, // ULONG FilterMatch
718 0, // ULONG Action
719 NULL // PVOID TargetContext
720 );
721 */
722
723 } _SEH2_FINALLY {
724
725 if (bFcbAcquired) {
726 ExReleaseResourceForThreadLite(
727 &Fcb->MainResource,
728 ExGetCurrentResourceThread());
729 }
730
731 if (!IrpContext->ExceptionInProgress) {
732
733 if (!CompleteRequest) {
734 IrpContext->Irp = NULL;
735 }
736
737 RfsdCompleteIrpContext(IrpContext, Status);
738 }
739 } _SEH2_END;
740
741 return Status;
742 }
743
744 VOID
745 RfsdNotifyReportChange (
746 IN PRFSD_IRP_CONTEXT IrpContext,
747 IN PRFSD_VCB Vcb,
748 IN PRFSD_FCB Fcb,
749 IN ULONG Filter,
750 IN ULONG Action )
751 {
752 PUNICODE_STRING FullName;
753 USHORT Offset;
754
755 FullName = &Fcb->LongName;
756
757 // ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
758
759 if (FullName->Buffer == NULL) {
760 if (!RfsdGetFullFileName(Fcb->RfsdMcb, FullName)) {
761 /*Status = STATUS_INSUFFICIENT_RESOURCES;*/
762 return;
763 }
764 }
765
766 Offset = (USHORT) ( FullName->Length -
767 Fcb->RfsdMcb->ShortName.Length);
768
769 FsRtlNotifyFullReportChange( Vcb->NotifySync,
770 &(Vcb->NotifyList),
771 (PSTRING) (FullName),
772 (USHORT) Offset,
773 (PSTRING)NULL,
774 (PSTRING) NULL,
775 (ULONG) Filter,
776 (ULONG) Action,
777 (PVOID) NULL );
778
779 // ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
780 }
781
782 __drv_mustHoldCriticalRegion
783 NTSTATUS
784 RfsdDirectoryControl (IN PRFSD_IRP_CONTEXT IrpContext)
785 {
786 NTSTATUS Status;
787
788 PAGED_CODE();
789
790 ASSERT(IrpContext);
791
792 ASSERT((IrpContext->Identifier.Type == RFSDICX) &&
793 (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT)));
794
795 switch (IrpContext->MinorFunction) {
796
797 case IRP_MN_QUERY_DIRECTORY:
798 Status = RfsdQueryDirectory(IrpContext);
799 break;
800
801 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
802 Status = RfsdNotifyChangeDirectory(IrpContext);
803 break;
804
805 default:
806 Status = STATUS_INVALID_DEVICE_REQUEST;
807 RfsdCompleteIrpContext(IrpContext, Status);
808 }
809
810 return Status;
811 }
812
813 #if !RFSD_READ_ONLY
814
815 BOOLEAN RfsdIsDirectoryEmpty (
816 PRFSD_VCB Vcb,
817 PRFSD_FCB Dcb )
818 {
819 NTSTATUS Status = STATUS_UNSUCCESSFUL;
820
821 //PRFSD_DIR_ENTRY2 pTarget = NULL;
822
823 ULONG dwBytes = 0;
824 ULONG dwRet;
825
826 BOOLEAN bRet = FALSE;
827
828 PAGED_CODE();
829 #if 0
830 if (!IsFlagOn(Dcb->RfsdMcb->FileAttr, FILE_ATTRIBUTE_DIRECTORY)) {
831 return TRUE;
832 }
833
834 _SEH2_TRY {
835
836 pTarget = (PRFSD_DIR_ENTRY2) ExAllocatePoolWithTag(PagedPool,
837 RFSD_DIR_REC_LEN(RFSD_NAME_LEN), RFSD_POOL_TAG);
838 if (!pTarget) {
839 Status = STATUS_INSUFFICIENT_RESOURCES;
840 _SEH2_LEAVE;
841 }
842
843 dwBytes = 0;
844
845 bRet = TRUE;
846
847 while ((LONGLONG)dwBytes < Dcb->Header.AllocationSize.QuadPart) {
848
849 RtlZeroMemory(pTarget, RFSD_DIR_REC_LEN(RFSD_NAME_LEN));
850
851 Status = RfsdReadInode(
852 NULL,
853 Vcb,
854 &(Dcb->RfsdMcb->Key),
855 Dcb->Inode,
856 dwBytes,
857 (PVOID)pTarget,
858 RFSD_DIR_REC_LEN(RFSD_NAME_LEN),
859 &dwRet);
860
861 if (!NT_SUCCESS(Status)) {
862 RfsdPrint((DBG_ERROR, "RfsdRemoveEntry: Reading Directory Content error.\n"));
863 bRet = FALSE;
864 _SEH2_LEAVE;
865 }
866
867 if (pTarget->inode) {
868 if (pTarget->name_len == 1 && pTarget->name[0] == '.') {
869 } else if (pTarget->name_len == 2 && pTarget->name[0] == '.' &&
870 pTarget->name[1] == '.') {
871 } else {
872 bRet = FALSE;
873 break;
874 }
875 } else {
876 break;
877 }
878
879 dwBytes += pTarget->rec_len;
880 }
881
882 } _SEH2_FINALLY {
883
884 if (pTarget != NULL) {
885 ExFreePool(pTarget);
886 }
887 } _SEH2_END;
888 #endif // 0
889 return bRet;
890 }
891
892 #endif // !RFSD_READ_ONLY
893
894 /**
895 This callback is triggered when the FS tree parser hits a leaf node that may contain a directory item.
896 The function then reads each dentry in the item, and is reponsible for sending them into to ProcessDirEntry.
897 The callback is doing work on behalf of QueryDir -- the context given is from there.
898 */
899 // NOTE: This signature has to be changed here, at the top decleration, and in the RFSD_CALLBACK macro definition
900 NTSTATUS
901 RfsdDirectoryCallback(
902 ULONG BlockNumber,
903 PVOID pContext)
904 {
905 PRFSD_CALLBACK_CONTEXT pCallbackContext = (PRFSD_CALLBACK_CONTEXT) pContext;
906 RFSD_KEY_IN_MEMORY DirectoryKey;
907 PUCHAR pBlockBuffer = NULL;
908 PRFSD_ITEM_HEAD pDirectoryItemHeader = NULL;
909 PUCHAR pDirectoryItemBuffer = NULL;
910 NTSTATUS Status;
911 UNICODE_STRING InodeFileName;
912
913 PAGED_CODE();
914
915 InodeFileName.Buffer = NULL;
916
917 RfsdPrint((DBG_FUNC, /*__FUNCTION__*/ " invoked on block %i\n", BlockNumber));
918
919
920 // Load the block
921 pBlockBuffer = RfsdAllocateAndLoadBlock(pCallbackContext->Vcb, BlockNumber);
922 if (!pBlockBuffer) { Status = STATUS_INSUFFICIENT_RESOURCES; goto out; }
923
924 // Construct the item key to search for
925 DirectoryKey = *(pCallbackContext->pDirectoryKey);
926 DirectoryKey.k_type = RFSD_KEY_TYPE_v2_DIRENTRY;
927
928 // Get the item header and its information
929 Status = RfsdFindItemHeaderInBlock(
930 pCallbackContext->Vcb, &DirectoryKey, pBlockBuffer,
931 ( &pDirectoryItemHeader ), //<
932 &CompareKeysWithoutOffset
933 );
934
935 // If this block doesn't happen to contain a directory item, skip it.
936 if ( (Status == STATUS_NO_SUCH_MEMBER) || !pDirectoryItemHeader )
937 {
938 KdPrint(("Block %i did not contain the appropriate diritem header\n", BlockNumber));
939 Status = STATUS_SUCCESS; goto out;
940 }
941
942 RfsdPrint((DBG_INFO, "Found %i dentries in block\n", pDirectoryItemHeader->u.ih_entry_count));
943
944 // Calculate if the requested result will be from this dentry span
945 // If the end of this span is not greater than the requested start, it can be skipped.
946 if ( !( (pCallbackContext->idxCurrentDentry + (USHORT)(pDirectoryItemHeader->u.ih_entry_count)) > pCallbackContext->idxStartingDentry ) )
947 {
948 RfsdPrint((DBG_TRACE, "SKIPPING block\n"));
949
950 pCallbackContext->idxCurrentDentry += pDirectoryItemHeader->u.ih_entry_count;
951 Status = STATUS_SUCCESS;
952 goto out;
953 }
954
955
956
957 // Otherwise, Read out each dentry, and call ProcessDirEntry on it.
958 {
959 BOOLEAN bRun = TRUE;
960 PRFSD_DENTRY_HEAD pPrevDentry = NULL;
961 ULONG offsetDentry_toSequentialSpan; // The byte offset of this dentry, relative to the dentry spans, as though only their DENTRY_HEADs were stitched together sequentially
962
963 // Skip ahead to the starting dentry in this span.
964 ULONG idxDentryInSpan = pCallbackContext->idxStartingDentry - pCallbackContext->idxCurrentDentry;
965 pCallbackContext->idxCurrentDentry += idxDentryInSpan;
966
967 RfsdPrint((DBG_TRACE, "Sarting dentry: %i. skipped to %i dentry in span\n", pCallbackContext->idxStartingDentry, idxDentryInSpan));
968
969 offsetDentry_toSequentialSpan = pCallbackContext->idxCurrentDentry * sizeof(RFSD_DENTRY_HEAD);
970
971 // Setup the item buffer
972 pDirectoryItemBuffer = (PUCHAR) pBlockBuffer + pDirectoryItemHeader->ih_item_location;
973
974
975 while (bRun
976 && ( *(pCallbackContext->pUsedLength) < (pCallbackContext->BufferLength) )
977 && (idxDentryInSpan < (pDirectoryItemHeader->u.ih_entry_count)) )
978 {
979
980 STRING OemName; // FUTURE: does this support codepages?
981 PRFSD_DENTRY_HEAD pCurrentDentry;
982 USHORT InodeFileNameLength = 0;
983
984 // Read a directory entry from the buffered directory item (from the file associated with the filled inode)
985 pCurrentDentry = (PRFSD_DENTRY_HEAD) (pDirectoryItemBuffer + (idxDentryInSpan * sizeof(RFSD_DENTRY_HEAD) ));
986
987 // Skip the directory entry for the parent of the root directory (because it should not be shown, and has no stat data)
988 // (NOTE: Any change made here should also be mirrored in RfsdScanDirCallback)
989 if (pCurrentDentry->deh_dir_id == 0 /*&& pCurrentDentry->deh_objectid == 1*/)
990 { goto ProcessNextEntry; }
991
992 // Pull the name of the file out from the buffer.
993 // NOTE: The filename is not gauranteed to be null-terminated, and so the end may implicitly be the start of the previous entry.
994 OemName.Buffer = (PUCHAR) pDirectoryItemBuffer + pCurrentDentry->deh_location;
995 OemName.MaximumLength = (pPrevDentry ? pPrevDentry->deh_location : // The end of this entry is the start of the previous
996 pDirectoryItemHeader->ih_item_len // Otherwise this is the first entry, the end of which is the end of the item.
997 ) - pCurrentDentry->deh_location;
998 if (!pPrevDentry && pCallbackContext->idxStartingDentry > 1 && pCallbackContext->Ccb->deh_location)
999 {
1000 if (OemName.MaximumLength != pCallbackContext->Ccb->deh_location - pCurrentDentry->deh_location)
1001 {
1002 //KdPrint(("Changed MaximumLength from %d to %d for {%.*s}\n", OemName.MaximumLength, pCallbackContext->Ccb->deh_location - pCurrentDentry->deh_location, RfsdStringLength(OemName.Buffer, pCallbackContext->Ccb->deh_location - pCurrentDentry->deh_location), OemName.Buffer));
1003 }
1004 OemName.MaximumLength = pCallbackContext->Ccb->deh_location - pCurrentDentry->deh_location;
1005 }
1006 OemName.Length = RfsdStringLength(OemName.Buffer, OemName.MaximumLength);
1007
1008
1009 // Calculate the name's unicode length, allocate memory, and convert the codepaged name to unicode
1010 InodeFileNameLength = (USHORT) RfsdOEMToUnicodeSize(&OemName);
1011 InodeFileName.Length = 0;
1012 InodeFileName.MaximumLength = InodeFileNameLength + 2;
1013
1014 if (InodeFileNameLength <= 0)
1015 { break; }
1016
1017 InodeFileName.Buffer = ExAllocatePoolWithTag(
1018 PagedPool,
1019 InodeFileNameLength + 2, RFSD_POOL_TAG);
1020
1021 if (!InodeFileName.Buffer) {
1022 Status = STATUS_INSUFFICIENT_RESOURCES;
1023 goto out;
1024 }
1025
1026 RtlZeroMemory(InodeFileName.Buffer, InodeFileNameLength + 2);
1027
1028 Status = RfsdOEMToUnicode( &InodeFileName, &OemName );
1029 if (!NT_SUCCESS(Status)) { Status = STATUS_INTERNAL_ERROR; goto out; } // TODO: CHECK IF TIHS OK
1030
1031 ////////////// END OF MY PART
1032 if (FsRtlDoesNameContainWildCards(
1033 &(pCallbackContext->Ccb->DirectorySearchPattern)) ?
1034 FsRtlIsNameInExpression(
1035 &(pCallbackContext->Ccb->DirectorySearchPattern),
1036 &InodeFileName,
1037 TRUE,
1038 NULL) :
1039 !RtlCompareUnicodeString(
1040 &(pCallbackContext->Ccb->DirectorySearchPattern),
1041 &InodeFileName,
1042 TRUE) ) {
1043 // The name either contains wild cards, or matches the directory search pattern...
1044
1045 {
1046 ULONG dwBytesWritten;
1047 dwBytesWritten = RfsdProcessDirEntry(
1048 pCallbackContext->Vcb, pCallbackContext->FileInformationClass,
1049 pCurrentDentry->deh_dir_id,
1050 pCurrentDentry->deh_objectid,
1051 pCallbackContext->Buffer,
1052 *(pCallbackContext->pUsedLength),
1053 pCallbackContext->BufferLength - *(pCallbackContext->pUsedLength), // The remaining length in the user's buffer
1054 offsetDentry_toSequentialSpan,
1055 &InodeFileName,
1056 pCallbackContext->ReturnSingleEntry,
1057 pCallbackContext->pPreviousEntry);
1058
1059 if (dwBytesWritten <= 0) {
1060 Status = STATUS_EVENT_DONE;
1061 bRun = FALSE;
1062 pCallbackContext->Ccb->deh_location = pPrevDentry ? pPrevDentry->deh_location : 0;
1063 } else {
1064 pCallbackContext->Ccb->deh_location = 0;
1065 pCallbackContext->pPreviousEntry = (PUCHAR) (pCallbackContext->Buffer) + *(pCallbackContext->pUsedLength);
1066 *(pCallbackContext->pUsedLength) += dwBytesWritten;
1067 }
1068 }
1069 }
1070
1071 if (InodeFileName.Buffer) {
1072 ExFreePool(InodeFileName.Buffer);
1073 InodeFileName.Buffer = NULL;
1074 }
1075
1076
1077 ProcessNextEntry:
1078
1079 pPrevDentry = pCurrentDentry;
1080
1081 if (bRun)
1082 {
1083 ++idxDentryInSpan;
1084 ++(pCallbackContext->idxCurrentDentry);
1085 ++(pCallbackContext->idxStartingDentry);
1086 offsetDentry_toSequentialSpan += sizeof(RFSD_DENTRY_HEAD);
1087
1088 // Store the current position, so that it will be available for the next call
1089 pCallbackContext->Ccb->CurrentByteOffset = offsetDentry_toSequentialSpan;
1090 }
1091
1092
1093 if ( ( *(pCallbackContext->pUsedLength) > 0) && pCallbackContext->ReturnSingleEntry) {
1094 Status = STATUS_EVENT_DONE;
1095 break;
1096 }
1097
1098 }
1099
1100 }
1101
1102 out:
1103 if (pBlockBuffer) ExFreePool(pBlockBuffer);
1104 if (InodeFileName.Buffer) ExFreePool(InodeFileName.Buffer);
1105
1106 return Status;
1107 }