文件对象,文件系统设备,卷设备之间的关系说明

文件对象、文件系统设备、卷设备之间的关系简单说明

http://hi.baidu.com/jonathan2004/item/cb7fd9406301deac60d7b95d


本文档的CopyRight归jonathan所有,可自由转载,转载时请保持文档的完整性。
/*----------------------------------------------------------------------------------------------------------------------------*/

经常看到一些问文件对象、文件系统设备、卷设备之间关系的问题,这里也归纳一下自己的经验,仅供参考。不对之处,请多指教。

0 前沿

   先描述一下DEVICE_OBJECT的结构。

   文件对象,文件系统设备,卷设备之间的关系说明_第1张图片

1 关系说明

1.1 FILE_OBJECT->VPB 指向VPB ; FILE_OBJECT->DeviceObject指向卷设备;通过ObpLookupObjectName初始文件对象相应值;通过IoGetRelatedDeviceObject(FILE_OBJECT)获取设备栈;

1.2 文件系统设备对象->Vcb->VPB 指向VPB; VPB->RealDeviceObject指向卷设备; VPB->DeviceObject指向文件系统设备对象。

1.3 下层文件系统过滤设备的AttachedDevice指向邻接的上层设备;文件系统过滤对象扩展->AttachedTo指向下层设备。
             

2 简单描述

2.1 即插即用管理器遍历存储设备栈,注册卷更改通知接口。

2.2 即插即用管理器建立卷设备栈, 通过通知机制来与存储设备栈建立相互关系.

2.3 文件系统挂载(文件系统以过滤驱动方式挂载), 建立文件系统与卷设备栈之间关系.

这里以FAT文件系统为例说明:

NTSTATUS
FatMountVolume (
    IN PIRP_CONTEXT IrpContext,
    IN PDEVICE_OBJECT TargetDeviceObject,
    IN PVPB Vpb
    )
{
        ...

        if (!NT_SUCCESS(Status = IoCreateDevice( FatData.DriverObject,
                                                 sizeof(VOLUME_DEVICE_OBJECT) - sizeof(DEVICE_OBJECT),
                                                 NULL,
                                                 FILE_DEVICE_DISK_FILE_SYSTEM,
                                                 0,
                                                 FALSE,
                                                 (PDEVICE_OBJECT *)&VolDo))) {

            try_return( Status );
        }

        ...

        FatInitializeVcb( IrpContext, &VolDo->Vcb, TargetDeviceObject, Vpb);

        ...

        /* 建立 卷到文件系统的关系 */
        Vpb->DeviceObject = (PDEVICE_OBJECT)VolDo;       
       
}

VOID
FatInitializeVcb (
    IN PIRP_CONTEXT IrpContext,
    IN OUT PVCB Vcb,
    IN PDEVICE_OBJECT TargetDeviceObject,
    IN PVPB Vpb
    )
{
        ...

        ObReferenceObject( TargetDeviceObject );
        Vcb->TargetDeviceObject = TargetDeviceObject;

        /* 建立文件系统到卷的关系 */
        Vcb->Vpb = Vpb;

        Vcb->CurrentDevice = Vpb->RealDevice;

        ...
}

以上 卷 和 文件系统 建立了相互关系.

2.3 文件对象 与 卷和文件系统的关系

2.3.1 创建文件对象

IO管理器通过ObpLookupObjectName查找相应的文件.

这里以设备对象类型为IoDeviceObjectType来说明.


    RtlInitUnicodeString( &nameString, L"Device" );
    objectTypeInitializer.DefaultNonPagedPoolCharge = sizeof( DEVICE_OBJECT );
    objectTypeInitializer.ParseProcedure = IopParseDevice;
    objectTypeInitializer.CaseInsensitive = TRUE;
    objectTypeInitializer.DeleteProcedure = IopDeleteDevice;
    objectTypeInitializer.SecurityProcedure = IopGetSetSecurityObject;
    objectTypeInitializer.QueryNameProcedure = (OB_QUERYNAME_METHOD)NULL;
    if (!NT_SUCCESS( ObCreateObjectType( &nameString,
                                      &objectTypeInitializer,
                                      (PSECURITY_DESCRIPTOR) NULL,
                                      &IoDeviceObjectType ))) {
        return FALSE;
    }


/* 这是个重要函数接口,所有有关创建文件的地方都要走这里 */

NTSTATUS
IopParseDevice(
    IN PVOID ParseObject,
    IN PVOID ObjectType,
    IN PACCESS_STATE AccessState,
    IN KPROCESSOR_MODE AccessMode,
    IN ULONG Attributes,
    IN OUT PUNICODE_STRING CompleteName,
    IN OUT PUNICODE_STRING RemainingName,
    IN OUT PVOID Context OPTIONAL,
    IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
    OUT PVOID *Object
    )
{
    ...

    vpb = NULL;

    //
    // If the related open was a direct device open then we should go through the full mount
    // path for this open as this may not be a direct device open.
    //
    if (op->RelatedFileObject && (!(op->RelatedFileObject->Flags & FO_DIRECT_DEVICE_OPEN))) {

        deviceObject = (PDEVICE_OBJECT)ParseObject;

        if (op->RelatedFileObject->Vpb) {

            vpb = op->RelatedFileObject->Vpb;

            //
            // Synchronize here with the file system to make sure that
            // volumes don't go away while en route to the FS.
            //

            IopInterlockedIncrementUlong( LockQueueIoVpbLock,
                                          (PLONG) &vpb->ReferenceCount);
        }
        
        if (op->InternalFlags & IOP_CREATE_USE_TOP_DEVICE_OBJECT_HINT) {

            if (vpb) {
                deviceObject = vpb->DeviceObject;
            }

            checkDeviceHint = TRUE;
        }

    } else {

        deviceObject = parseDeviceObject;

        if (parseDeviceObject->Vpb && !directDeviceOpen) {
            vpb = IopCheckVpbMounted( op,
                                      parseDeviceObject,
                                      RemainingName,
                                      &status );
            //
            // Device object reference is decremented in IopCheckVpbMounted.
            //

            if ( !vpb ) {
                return status;
            }

            //
            // Set the address of the device object associated with the VPB.
            //

            /* 获取 卷设备 */
            deviceObject = vpb->DeviceObject;
        }


        //
        // If the top deviceobject hint is set use the hint if possible.
        //

        if (op->InternalFlags & IOP_CREATE_USE_TOP_DEVICE_OBJECT_HINT) {

            checkDeviceHint = TRUE;

        } else {

            //
            // Walk the attached device list.
            //

            if (deviceObject->AttachedDevice) {
                /* 加入栈中 */
                deviceObject = IoGetAttachedDevice( deviceObject );
            }
        }
    }

    ...

    /* 准备打开相应的文件或者设备 */
    irp = IopAllocateIrp( deviceObject->StackSize, FALSE );
    if (!irp) {

        //
        // An IRP could not be allocated. Cleanup and return an appropriate
        // error status code.
        //

        IopDecrementDeviceObjectRef( parseDeviceObject, FALSE, FALSE );

        if (vpb) {
            IopDereferenceVpbAndFree(vpb);
        }
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    irp->Tail.Overlay.Thread = CurrentThread;
    irp->RequestorMode = AccessMode;
    irp->Flags = IRP_CREATE_OPERATION | IRP_SYNCHRONOUS_API | IRP_DEFER_IO_COMPLETION;

    securityContext.SecurityQos = SecurityQos;
    securityContext.AccessState = AccessState;
    securityContext.DesiredAccess = desiredAccess;
    securityContext.FullCreateOptions = op->CreateOptions;

    //
    // Get a pointer to the stack location for the first driver. This is where
    // the original function codes and parameters are passed.
    //

    irpSp = IoGetNextIrpStackLocation( irp );
    irpSp->Control = 0;

    ...

    irp->Overlay.AllocationSize = op->AllocationSize;
    irp->AssociatedIrp.SystemBuffer = op->EaBuffer;
    irpSp->Parameters.Create.Options = (op->Disposition << 24) | (op->CreateOptions & 0x00ffffff);
    irpSp->Parameters.Create.FileAttributes = op->FileAttributes;
    irpSp->Parameters.Create.ShareAccess = op->ShareAccess;
    irpSp->Parameters.Create.SecurityContext = &securityContext;

    //
    // Fill in local parameters so this routine can determine when the I/O is
    // finished, and the normal I/O completion code will not get any errors.
    //

    irp->UserIosb = &ioStatus;
    irp->MdlAddress = (PMDL) NULL;
    irp->PendingReturned = FALSE;
    irp->Cancel = FALSE;
    irp->UserEvent = (PKEVENT) NULL;
    irp->CancelRoutine = (PDRIVER_CANCEL) NULL;
    irp->Tail.Overlay.AuxiliaryBuffer = (PVOID) NULL;
     
    ...

    /* 创建文件对象 */
    status = ObCreateObject( KernelMode,
                                 IoFileObjectType,
                                 &objectAttributes,
                                 AccessMode,
                                 (PVOID) NULL,
                                 fileObjectSize,
                                 0,
                                 0,
                                 (PVOID *) &fileObject );

    /* 设置文件对象相应属性,为打开文件作准备 */
    RtlZeroMemory( fileObject, sizeof( FILE_OBJECT ) );
    if (op->CreateOptions & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) {
        fileObject->Flags = FO_SYNCHRONOUS_IO;
        if (op->CreateOptions & FILE_SYNCHRONOUS_IO_ALERT) {
                fileObject->Flags |= FO_ALERTABLE_IO;
        }
    }

    ...

    fileObject->Type = IO_TYPE_FILE;
    fileObject->Size = sizeof( FILE_OBJECT );
    fileObject->RelatedFileObject = op->RelatedFileObject;
    fileObject->DeviceObject = parseDeviceObject;

    irp->Tail.Overlay.OriginalFileObject = fileObject;
    irpSp->FileObject = fileObject;

    ...

    RtlCopyUnicodeString( &fileObject->FileName, RemainingName );

    ...

    /* 调用fastio接口 */
    if (op->QueryOnly) {
        PFAST_IO_DISPATCH fastIoDispatch = deviceObject->DriverObject->FastIoDispatch;
        BOOLEAN result;

        if (fastIoDispatch &&
            fastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET( FAST_IO_DISPATCH, FastIoQueryOpen ) &&
            fastIoDispatch->FastIoQueryOpen) {

            IoSetNextIrpStackLocation( irp );
            irpSp->DeviceObject = deviceObject;
            result = (fastIoDispatch->FastIoQueryOpen)( irp,
                                                        op->NetworkInformation,
                                                        deviceObject );
            if (result) {
                op->FinalStatus = irp->IoStatus.Status;
                op->Information = irp->IoStatus.Information;

                //
                // The operation worked, so simply dereference and free the
                // resources acquired up to this point.
                //

                if ((op->FinalStatus == STATUS_REPARSE) &&
                    irp->Tail.Overlay.AuxiliaryBuffer) {
                    ASSERT( op->Information > IO_REPARSE_TAG_RESERVED_ONE );
                    ExFreePool( irp->Tail.Overlay.AuxiliaryBuffer );
                    irp->Tail.Overlay.AuxiliaryBuffer = NULL;
                    op->RelatedFileObject = (PFILE_OBJECT) NULL;
                }

                if (fileObject->FileName.Length) {
                    ExFreePool( fileObject->FileName.Buffer );
                }

                IopDecrementDeviceObjectRef( parseDeviceObject, FALSE, FALSE );

                if (vpb) {
                    IopDereferenceVpbAndFree(vpb);
                }

#if DBG
                irp->CurrentLocation = irp->StackCount + 2;
#endif // DBG

                IoFreeIrp( irp );

                //
                // Finally, indicate that the parse routine was actually
                // invoked and that the information returned herein can be
                // used.
                //

                op->ParseCheck = OPEN_PACKET_PATTERN;
                status = STATUS_SUCCESS;

                if (!op->FullAttributes) {
                    try {
                        op->BasicInformation->FileAttributes = op->NetworkInformation->FileAttributes;
                    } except(EXCEPTION_EXECUTE_HANDLER) {
                        status = GetExceptionCode();
                    }
                }

                return status;

            } else {

                //
                // The fast I/O operation did not work, so take the longer
                // route.
                //

                irp->Tail.Overlay.CurrentStackLocation++;
                irp->CurrentLocation++;
            }
        }
    }

    
    //
    // Finally, initialize the file object's event to the Not Signaled state
    // and remember that a file object was created.
    //

    KeInitializeEvent( &fileObject->Event, NotificationEvent, FALSE );
    op->FileObject = fileObject;

    //
    // Insert the packet at the head of the IRP list for the thread.
    //

    IopQueueThreadIrp( irp );

    //
    // Now invoke the driver itself to open the file.
    //

    status = IoCallDriver( deviceObject, irp );

    ...

     if (!NT_SUCCESS( status )) {
             ....
      } else if (status == STATUS_REPARSE) { /* 重新解析文件 */
             ...
             
             RtlCopyUnicodeString( CompleteName, &fileObject->FileName );

             ...
      }else{

           ...

           /* fileobject 中的Vpb可能发生变化,这是由于可能发生了文件定向 */
           deviceObjectThatOpenedFile = IoGetRelatedDeviceObject(fileObject);
           if (deviceObject != deviceObjectThatOpenedFile) {
            PVPB    newVpb;

            //
            // The device that opened the related file is not the one
            // that opened this file. So, readjust the vpb reference
            // counts.
            //

            newVpb = fileObject->Vpb;

            //
            // If the new VPB is not the same as the original VPB
            // then reference the new one before freeing the old one.
            // If a filter was just added to the fileobject stack the VPB will not
            // change. So check for a difference in VPB as well.
            //

            if (newVpb != vpb) {
                if (newVpb) {
                    IopInterlockedIncrementUlong( LockQueueIoVpbLock,
                                                  (PLONG) &newVpb->ReferenceCount);
                }

                if (vpb) {
                    IopDereferenceVpbAndFree(vpb);
                }
            }
        }

         ...
      }
     
}

IopCheckVpbMounted 获取卷设备的VPB。如果当前卷设备还没有挂载,则调用IopMountVolume挂载相应设备。

NTSTATUS
IopMountVolume(
    IN PDEVICE_OBJECT DeviceObject,
    IN BOOLEAN AllowRawMount,
    IN BOOLEAN DeviceLockAlreadyHeld,
    IN BOOLEAN Alertable,
    OUT PVPB    *Vpb
    )
{
        ...

        if (DeviceObject->DeviceType == FILE_DEVICE_DISK ||
            DeviceObject->DeviceType == FILE_DEVICE_VIRTUAL_DISK) {
            queueHeader = &IopDiskFileSystemQueueHead;
        } else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) {
            queueHeader = &IopCdRomFileSystemQueueHead;
        } else {
            queueHeader = &IopTapeFileSystemQueueHead;
        }

        rawMountOnly = (DeviceObject->Vpb->Flags & VPB_RAW_MOUNT);

        //
        // Now loop through each of the file systems which have been loaded in
        // the system to see whether anyone understands the media in the device.
        //

        for (entry = queueHeader->Flink;
             entry != queueHeader && !NT_SUCCESS( status );
             entry = entry->Flink) {
            
            ...

            extraStack = 1;

            ...

            irp = IoAllocateIrp ((CCHAR) (attachedDevice->StackSize + extraStack), FALSE);

            if ( !irp ) {

                status = STATUS_INSUFFICIENT_RESOURCES;
                break;
            }

            irp->Flags = IRP_MOUNT_COMPLETION | IRP_SYNCHRONOUS_PAGING_IO;
            irp->RequestorMode = KernelMode;
            irp->UserEvent = &event;
            irp->UserIosb = &ioStatus;
            irp->Tail.Overlay.Thread = CurrentThread;
            irpSp = IoGetNextIrpStackLocation( irp );
            irpSp->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
            irpSp->MinorFunction = IRP_MN_MOUNT_VOLUME;
            irpSp->Flags = AllowRawMount;
            irpSp->Parameters.MountVolume.Vpb = DeviceObject->Vpb;
            irpSp->Parameters.MountVolume.DeviceObject = attachedDevice;

            ...
            status = IoCallDriver( fsDeviceObject, irp );

       }

       ...

      return status;
}


2.3.2 文件对象中的Vpb

通过上面的分析可以看出, 在上面中并没有给File_Object中的VPB负值,那么文件对象的中这个VPB哪里来的呢?

这就是在文件系统的创建函数接口中来获取的.下面仍然以Fat文件系统为例:

NTSTATUS
FatCommonCreate (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp
    )
{
     ...

    if ( RelatedFileObject != NULL ) {
        FileObject->Vpb = RelatedFileObject->Vpb;
    }

    ...

    if (RelatedFileObject != NULL) {

        ...
        FileObject->Vpb = RelatedFileObject->Vpb;
        ...
    } else {

        ...
    }

    ...

}

2.3.3 使用文件对象

IopReadFile等接口函数通过调用IoGetRelatedDeviceObject(文件对象)来获取相应的设备对象。

你可能感兴趣的:(文件对象,文件系统设备,卷设备之间的关系说明)