Compartilhar via


Processamento de controle do sistema de arquivos

O tratamento da operação de IRP_MJ_FILE_SYSTEM_CONTROL é diferente do tratamento do buffer de dados exigido por outras operações no sistema de arquivos. Isso ocorre porque cada operação estabelece seu mecanismo de transferência de dados específico para o gerenciador de E/S como parte de seu código de controle por meio da macro CTL_CODE. Além disso, o código de controle especifica o acesso ao arquivo exigido pelo chamador. Um sistema de arquivos deve estar particularmente ciente desse problema ao definir o código de controle, pois esse acesso é imposto pelo gerenciador de E/S. Alguns códigos de controle de E/S (FSCTL_MOVE_FILE , por exemplo) especificam FILE_SPECIAL_ACCESS, que é um mecanismo para permitir que o sistema de arquivos indique que a segurança da operação será verificada diretamente pelo sistema de arquivos. FILE_SPECIAL_ACCESS é numericamente equivalente a FILE_ANY_ACCESS, portanto, o gerente de E/S não fornece nenhuma verificação de segurança específica, adiando para o sistema de arquivos. FILE_SPECIAL_ACCESS fornece principalmente a documentação de que verificações adicionais serão feitas pelo sistema de arquivos.

Várias operações do sistema de arquivos especificam FILE_SPECIAL_ACCESS. A operação FSCTL_MOVE_FILE é usada como parte da interface de desfragmentação para sistemas de arquivos e especifica FILE_SPECIAL_ACCESS. Como você deseja poder desfragmentar arquivos abertos que estão sendo lidos e gravados ativamente, o identificador a ser usado tem apenas FILE_READ_ATTRIBUTES acesso concedido para evitar conflitos de acesso de compartilhamento. No entanto, essa operação precisa ser uma operação privilegiada, pois o disco está sendo modificado em um nível baixo. A solução é verificar se o identificador usado para emitir o FSCTL_MOVE_FILE é um volume de usuário do DASD (dispositivo de armazenamento de acesso direto) aberto, que é um identificador privilegiado. O código do sistema de arquivos FASTFAT que garante que essa operação esteja sendo feita em relação a um volume de usuário aberto está na função FatMoveFile (consulte o arquivo de origem fsctrl.c do exemplo de fastfat que o WDK contém):

    //
    //  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;
    }

A estrutura usada pela operação FSCTL_MOVE_FILE especifica o arquivo que está sendo movido:

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

Conforme observado anteriormente, o identificador usado para emitir o FSCTL_MOVE_FILE é uma operação "aberta" de todo o volume, enquanto a operação realmente se aplica ao identificador de arquivo especificado no buffer de entrada MOVE_FILE_DATA. Isso torna as verificações de segurança para essa operação um pouco complexas. Por exemplo, essa interface deve converter o identificador de arquivo em um objeto de arquivo que representa o arquivo que está sendo movido. Isso requer uma consideração cuidadosa por parte de qualquer driver. O FASTFAT faz isso usando ObReferenceObject de forma protegida na função FatMoveFile no arquivo de origem fsctrl.c no exemplo de fastfat que o WDK contém:

    //
    //  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.
    //    //

Observe o uso de Irp-RequestorMode> para garantir que, se o chamador for um aplicativo no modo de usuário, o identificador não poderá ser um identificador de kernel. O acesso necessário é 0 para que um arquivo possa ser movido enquanto está sendo acessado ativamente. Por fim, observe que essa chamada deve ser feita no contexto de processo correto se a chamada se originou no modo de usuário. O código-fonte do sistema de arquivos FASTFAT impõe isso também na função FatMoveFile em fsctrl.c:

    //
    //  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 );

Essas verificações de segurança semânticas executadas pelo sistema de arquivos FAT são típicas daquelas exigidas por um sistema de arquivos para qualquer operação que passe um identificador. Além disso, o sistema de arquivos FAT também deve executar verificações de integridade específicas da operação. Essas verificações de sanidade são para garantir que os parâmetros diferentes sejam compatíveis (o arquivo que está sendo movido está no volume que foi aberto, por exemplo) a fim de impedir que o chamador execute uma operação privilegiada quando não deve ser permitido.

Para qualquer sistema de arquivos, a segurança correta é uma parte essencial das operações de controle do sistema de arquivos, que incluem:

  • Validar os identificadores do usuário adequadamente.

  • Protegendo o acesso ao buffer de usuário.

  • Validando a semântica da operação específica.

Em muitos casos, o código necessário para executar a validação e a segurança adequadas pode constituir uma parte substancial do código dentro da função fornecida.