文件系统控制处理

处理 IRP_MJ_FILE_SYSTEM_CONTROL 操作不同于文件系统中其他操作所需的数据缓冲区处理。 这是因为每个操作都通过 CTL_CODE 宏为其控制代码的一部分为 I/O 管理器建立其特定的数据传输机制。 此外,控制代码指定调用方所需的文件访问。 定义控制代码时,文件系统应特别认识到此问题,因为此访问由 I/O 管理器强制执行。 某些 I/O 控制代码 (FSCTL_MOVE_FILE ,例如,) 指定FILE_SPECIAL_ACCESS,这是一种允许文件系统指示文件系统将直接检查操作安全性的机制。 FILE_SPECIAL_ACCESS在数字上等效于FILE_ANY_ACCESS,因此 I/O 管理器不提供任何特定的安全检查,而是会延迟到文件系统。 FILE_SPECIAL_ACCESS主要提供文件系统将进行其他检查的文档。

多个文件系统操作指定FILE_SPECIAL_ACCESS。 FSCTL_MOVE_FILE操作用作文件系统的碎片整理接口的一部分,并指定FILE_SPECIAL_ACCESS。 由于你希望能够对正在主动读取和写入的打开的文件进行碎片整理,因此要使用的句柄仅FILE_READ_ATTRIBUTES授予访问权限,以避免共享访问冲突。 但是,此操作需要是特权操作,因为磁盘正在低级别修改。 解决方法是验证用于发出FSCTL_MOVE_FILE的句柄是否是直接访问存储设备, (DASD) 用户卷打开,这是特权句柄。 FastFAT 文件系统代码可确保对打开的用户卷执行此操作,该代码位于 FatMoveFile 函数中, (查看 WDK 包含的 fastfat 示例中的 fsctrl.c 源文件) :

    //
    //  extract and decode the file object and check for type of open
    //

    if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb ) != UserVolumeOpen) {

        FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );

        DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
        return STATUS_INVALID_PARAMETER;
    }

FSCTL_MOVE_FILE 操作使用的 结构指定要移动的文件:

typedef struct {
    HANDLE FileHandle;
    LARGE_INTEGER StartingVcn;
    LARGE_INTEGER StartingLcn;
    ULONG ClusterCount;
} MOVE_FILE_DATA, *PMOVE_FILE_DATA;

如前所述,用于发出FSCTL_MOVE_FILE的句柄是整个卷的“打开”操作,而该操作实际上适用于MOVE_FILE_DATA输入缓冲区中指定的文件句柄。 这使得此操作的安全检查有点复杂。 例如,此接口必须将文件句柄转换为表示要移动的文件的文件对象。 这需要对任何驱动程序进行仔细考虑。 FASTFAT 使用 ObReferenceObject 以受保护的方式在 fsctrl.c 源文件中的 fsctrl.c 源文件中,WDK 包含的 fastfat 示例中使用 ObReferenceObject 执行此操作:

    //
    //  Try to get a pointer to the file object from the handle passed in.
    //

    Status = ObReferenceObjectByHandle( InputBuffer->FileHandle,
                                        0,
                                        *IoFileObjectType,
                                        Irp->RequestorMode,
                                        &FileObject,
                                        NULL );

    if (!NT_SUCCESS(Status)) {

        FatCompleteRequest( IrpContext, Irp, Status );

        DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", Status);
        return Status;
    }
    //  Complete the following steps to ensure that this is not an invalid attempt
    //
    //    - check that the file object is opened on the same volume as the
    //      DASD handle used to call this routine.
    //
    //    - extract and decode the file object and check for type of open.
    //
    //    - if this is a directory, verify that it's not the root and that
    //      you are not trying to move the first cluster.  You cannot move the
    //      first cluster because sub-directories have this cluster number
    //      in them and there is no safe way to simultaneously update them
    //      all.
    //
    //  Allow movefile on the root directory if it's FAT32, since the root dir
    //  is a real chained file.
    //    //

请注意,使用 Irp-RequestorMode> 来确保如果调用方是用户模式应用程序,则句柄不能是内核句柄。 所需的访问权限为 0,以便在文件被主动访问时可以移动该文件。 最后请注意,如果调用源自用户模式,则必须在正确的进程上下文中进行此调用。 FASTFAT 文件系统的源代码在 fsctrl.c 的 FatMoveFile 函数中也强制实施此操作:

    //
    //  Force WAIT to true. There is a handle in the input buffer that can only
    //  be referenced within the originating process.
    //

    SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );

对于通过句柄的任何操作,FAT 文件系统执行这些语义安全检查是典型的。 此外,FAT 文件系统还必须执行特定于操作的健全性检查。 这些健全性检查是为了确保不同的参数兼容, (所移动的文件位于打开的卷上,例如) ,以防止调用方在不应允许的情况下执行特权操作。

对于任何文件系统,正确的安全性是文件系统控制操作的重要组成部分,其中包括:

  • 验证用户句柄是否适当。

  • 保护用户缓冲区访问。

  • 验证特定操作的语义。

在许多情况下,执行适当验证和安全性所需的代码可能构成给定函数中代码的很大一部分。