Condividi tramite


Nessuna delle operazioni di I/O

Un file system deve gestire le operazioni che in genere comportano la modifica diretta dei buffer utente. Tali operazioni sono intrinsecamente rischiose perché l'indirizzo utente potrebbe non essere valido. I file system devono essere particolarmente consapevoli di tali operazioni e assicurarsi che li proteggono in modo appropriato. Le operazioni seguenti si basano sul membro Flag dell'oggetto dispositivo del file system per specificare il modo in cui gestione I/O consiste nel trasferire i dati tra l'utente e lo spazio degli indirizzi del kernel:

In genere, un file system sceglie né I/O in modo implicito impostando né DO_DIRECT_IO né DO_BUFFERED_IO nel membro Flag dell'oggetto dispositivo del volume creato.

L'operazione seguente ignora il membro Flag dell'oggetto dispositivo del file system e non usa né I/O per trasferire i dati tra l'utente e lo spazio degli indirizzi del kernel:

Usando né I/O, il file system è responsabile della gestione delle proprie operazioni di trasferimento dei dati. Ciò consente a un file system di soddisfare un'operazione inserendo direttamente i dati nel buffer dello spazio utente di un'applicazione. Il file system deve quindi assicurarsi che il buffer dell'utente sia valido quando l'operazione inizia e gestisce in modo regolare il buffer che diventa non valido mentre l'operazione è in corso. I/O veloci passano anche puntatori non elaborati. Gli sviluppatori devono tenere presente che il controllo della validità del buffer all'inizio dell'operazione non è sufficiente per garantire che rimanga valido durante l'operazione. Ad esempio, un'applicazione dannosa potrebbe eseguire il mapping di un blocco di memoria (tramite una sezione, ad esempio), eseguire un'operazione di I/O e annullare il mapping del blocco di memoria mentre l'operazione di I/O è in corso.

Esistono diversi modi per gestire questa situazione in un file system. Un meccanismo consiste nel bloccare la memoria fisica corrispondente all'indirizzo dell'utente e creare un secondo mapping nello spazio indirizzi del sistema operativo. Ciò garantisce che il file system usi un indirizzo virtuale che controlla. Pertanto, anche se l'indirizzo utente diventa non valido, l'indirizzo creato dal file system rimarrà valido. Il codice del file system FASTFAT usa due funzioni diverse per ottenere questo risultato. La prima funzione blocca il buffer dell'utente:

VOID
FatLockUserBuffer (
    IN PIRP_CONTEXT IrpContext,
    IN OUT PIRP Irp,
    IN LOCK_OPERATION Operation,
    IN ULONG BufferLength
    )

/*++
Routine Description:

    This routine locks the specified buffer for the specified type of
    access. The file system requires this routine because it does not
    ask the I/O system to lock its buffers for direct I/O. This routine
    can only be called from the file system driver (FSD) while still in the user context.

    Note that this is the *input/output* buffer.

Arguments:
    Irp - Pointer to the Irp for which the buffer will be locked.
    Operation - IoWriteAccess for read operations, or IoReadAccess for
                write operations.
    BufferLength - Length of user buffer.

Return Value:
    None
--*/

{
    PMDL Mdl = NULL;

    if (Irp->MdlAddress == NULL) {
        //
        // Allocate the Mdl and Raise if the allocation fails.
        //
        Mdl = IoAllocateMdl( Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp );
        if (Mdl == NULL) {
            FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
        }

        //
        // now probe the buffer described by the Irp. If there is an exception,
        // deallocate the Mdl and return the appropriate "expected" status.
        //
        try {
            MmProbeAndLockPages( Mdl,
                                 Irp->RequestorMode,
                                 Operation );
        } except(EXCEPTION_EXECUTE_HANDLER) {
            NTSTATUS Status;
            Status = GetExceptionCode();
            IoFreeMdl( Mdl );
            Irp->MdlAddress = NULL;
            FatRaiseStatus( IrpContext,
                            FsRtlIsNtstatusExpected(Status) ? Status : STATUS_INVALID_USER_BUFFER );
        }
    }

    UNREFERENCED_PARAMETER( IrpContext );
}

Questa routine garantisce che la memoria fisica che restituisce l'indirizzo di un utente non verrà riutilizzata per alcun altro scopo mentre l'operazione è in corso. Un file system può eseguire questa operazione per inviare l'operazione di I/O al livello di gestione del volume o della classe disco sottostante per soddisfare un I/O utente non memorizzato nella cache. In tal caso, il file system non ha bisogno del proprio indirizzo virtuale nel buffer. Una seconda funzione crea il mapping del file system nello spazio indirizzi del kernel:

PVOID
FatMapUserBuffer (
    IN PIRP_CONTEXT IrpContext,
    IN OUT PIRP Irp
    )
/*++
Routine Description:
    This routine conditionally maps the user buffer for the current I/O
    request in the specified mode. If the buffer is already mapped, it
    just returns its address.
 
    Note that this is the *input/output* buffer.

Arguments:
    Irp - Pointer to the Irp for the request.

Return Value:
    Mapped address
--*/
{
    UNREFERENCED_PARAMETER( IrpContext );

    //
    // If there is no Mdl, then we must be in  the FSD, and can simply
    // return the UserBuffer field from the Irp.
    //
    if (Irp->MdlAddress == NULL) {
        return Irp->UserBuffer;
    } else {
        PVOID Address = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, NormalPagePriority );
        if (Address == NULL) {
            ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
        }
        return Address;
    }
}

L'implementazione FASTFAT consente anche alla seconda routine di restituire l'indirizzo a livello di utente, che richiede che il file system FAT assicuri che l'indirizzo restituito (utente o kernel) sia valido. Questa operazione viene eseguita usando le parole chiave __try e __except per creare un blocco protetto.

Queste routine si trovano nel file di origine deviosup.c dagli esempi fastfat contenuti nel WDK.

Un altro problema critico si verifica quando la richiesta non è soddisfatta nel contesto del chiamante. Se un file system pubblica la richiesta a un thread di lavoro, il driver deve bloccare il buffer con un MDL per non perderne traccia. La funzione FatPrePostIrp nel file di origine workque.c degli esempi fastfat fornisce un esempio di come questo problema viene gestito dal file system FASTFAT.

Il file system FASTFAT protegge da un'ampia gamma di errori, non semplicemente da buffer utente non validi, usando queste routine. Anche se si tratta di una tecnica molto potente, comporta anche la garanzia che tutti i blocchi di codice protetti rilasciano correttamente tutte le risorse che potrebbero contenere. Le risorse da rilasciare includono memoria, oggetti di sincronizzazione o altre risorse del file system stesso. Un errore a tale scopo darebbe a un utente malintenzionato la possibilità di causare la fame di risorse eseguendo molte chiamate ripetitive nel sistema operativo per esaurire la risorsa.