Partilhar via


Processamento de controle do sistema de arquivos

A manipulação da operação IRP_MJ_FILE_SYSTEM_CONTROL é diferente da manipulação do buffer de dados exigida por outras operações dentro do sistema de arquivos. Isso ocorre porque cada operação estabelece seu mecanismo específico de transferência de dados 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 que é exigido pelo chamador. Um sistema de arquivos deve estar particularmente ciente desse problema ao definir o código de controle, porque 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 gerenciador de E/S não fornece nenhuma verificação de segurança específica, adiando em vez disso para o sistema de arquivos. FILE_SPECIAL_ACCESS fornece principalmente 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 ser capaz de desfragmentar arquivos abertos que estão sendo lidos e gravados ativamente, o identificador a ser usado só tem acesso FILE_READ_ATTRIBUTES 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 de dispositivo de armazenamento de acesso direto (DASD) aberto, que é um identificador privilegiado. O código do sistema de arquivos FASTFAT que garante que essa operação está 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 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;

Como 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 esta 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. Isto requer uma consideração cuidadosa por parte de qualquer condutor. FASTFAT faz isso usando ObReferenceObject de forma protegida na função FatMoveFile no arquivo de origem fsctrl.c no exemplo 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 de 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. E, finalmente, 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ântica realizadas pelo sistema de arquivos FAT são típicas daquelas exigidas por um sistema de arquivos para qualquer operação que passe por um identificador. Além disso, o sistema de arquivos FAT também deve realizar verificações de sanidade específicas para a operação. Essas verificações de sanidade são para garantir que os parâmetros díspares sejam compatíveis (o arquivo que está sendo movido está no volume que foi aberto, por exemplo), a fim de evitar que o chamador execute uma operação privilegiada quando ela não deveria ser permitida.

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

  • Validando manipulações de usuário apropriadamente.

  • Protegendo o acesso ao buffer do usuário.

  • Validação 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 dada.