vfs — controllo del filesystem virtuale

Il modulo vfs contiene funzioni per creare oggetti filesystem e montarli/smontarli nel Filesystem Virtuale.

Montaggio del filesystem

Alcune porte forniscono un Filesystem Virtuale (VFS) e la possibilità di montare più filesystem «reali» all’interno di questo VFS. Gli oggetti filesystem possono essere montati sia alla radice del VFS, sia in una sottodirectory che risiede nella radice. Questo consente una configurazione dinamica e flessibile del filesystem visto dai programmi Python. Le porte che dispongono di questa funzionalità forniscono le funzioni mount() e umount(), ed eventualmente varie implementazioni di filesystem rappresentate dalle classi VFS.

vfs.mount(fsobj: Any, mount_point: str, *, readonly: bool = False) None

Monta l’oggetto filesystem fsobj nella posizione del VFS indicata dalla stringa mount_point. fsobj può essere un oggetto VFS dotato di un metodo mount(), oppure un block device. Se si tratta di un block device, il tipo di filesystem viene rilevato automaticamente (viene sollevata un’eccezione se non viene riconosciuto alcun filesystem). mount_point può essere '/' per montare fsobj alla radice, oppure '/<name>' per montarlo in una sottodirectory sotto la radice.

Se readonly è True, il filesystem viene montato in sola lettura.

Durante il processo di montaggio viene chiamato il metodo mount() sull’oggetto filesystem.

Solleva OSError(EPERM) se mount_point è già montato.

vfs.mount() List[Tuple[Any, str]]

Senza argomenti, mount() restituisce una lista di tuple che rappresentano tutti i punti di montaggio attivi.

La lista restituita ha la forma [(fsobj, mount_point), …].

vfs.umount(mount_point: str | Any) None

Smonta un filesystem. mount_point può essere una stringa che indica la posizione di montaggio, oppure un oggetto filesystem precedentemente montato. Durante il processo di smontaggio viene chiamato il metodo umount() sull’oggetto filesystem.

Solleva OSError(EINVAL) se mount_point non viene trovato.

class vfs.VfsFat(block_dev: AbstractBlockDev)

Crea un oggetto filesystem che utilizza il formato di filesystem FAT. L’archiviazione del filesystem FAT è fornita da block_dev. Gli oggetti creati da questo costruttore possono essere montati usando mount().

static mkfs(block_dev: AbstractBlockDev) None

Costruisce un filesystem FAT su block_dev.

class vfs.VfsRom(buffer: bytes | bytearray | memoryview)

Crea un oggetto filesystem che utilizza il formato di filesystem in sola lettura ROMFS. buffer deve essere un oggetto che supporta il protocollo buffer (bytes, bytearray o memoryview) e che contiene un’immagine ROMFS valida.

Gli oggetti creati da questo costruttore possono essere montati usando mount().

Vedi Lavorare con ROMFS per i dettagli completi, incluso come costruire e distribuire immagini ROMFS con mpremote.

vfs.rom_ioctl(op: int, *args: Any) Any

Interfaccia di basso livello per accedere alle partizioni di memoria in sola lettura (ROM) del dispositivo. Le operazioni supportate sono:

Chiamata

Comportamento

rom_ioctl(1)

Restituisce il numero di partizioni ROM disponibili.

rom_ioctl(2, id)

Restituisce la partizione id come memoryview.

rom_ioctl(3, id, length)

Cancella i primi length byte della partizione id in preparazione alla scrittura. Restituisce l’allineamento minimo di scrittura in byte.

rom_ioctl(4, id, offset, buf)

Scrive buf nella partizione id al byte offset.

rom_ioctl(5, id)

Finalizza una sequenza di scrittura sulla partizione id (svuota le cache, ecc.).

Queste operazioni vengono normalmente invocate indirettamente da mpremote durante la distribuzione di un’immagine ROMFS; la maggior parte delle applicazioni non ha bisogno di chiamarle direttamente.

class vfs.VfsPosix(root: str | None = None)

Crea un oggetto filesystem che accede al filesystem POSIX dell’host. Se root è specificato, deve essere un percorso nel filesystem dell’host da utilizzare come radice dell’oggetto VfsPosix. In caso contrario viene usata la directory corrente del filesystem dell’host.

Nota

VfsPosix è disponibile solo sulla porta Unix di MicroPython; non è presente nel firmware delle OpenMV Cam.

Block device

Un block device è un oggetto che implementa il protocollo a blocchi. Questo consente a un dispositivo di supportare i filesystem MicroPython. L’hardware fisico è rappresentato da una classe definita dall’utente. La classe AbstractBlockDev è un modello per la progettazione di una tale classe: MicroPython non fornisce effettivamente quella classe, ma una vera classe di block device deve implementare i metodi descritti di seguito.

Un’implementazione concreta di questa classe consente solitamente di accedere alle funzionalità di tipo memoria di un componente hardware (come la memoria flash). Un block device può essere formattato con qualsiasi filesystem supportato e montato usando i metodi di os.

Vedi Lavorare con i filesystem per esempi di implementazioni di block device che usano le due varianti del protocollo a blocchi descritte di seguito.

Interfaccia semplice ed estesa

Esistono due firme compatibili per i metodi readblocks e writeblocks (vedi sotto), al fine di supportare una varietà di casi d’uso. Un dato block device può implementare una forma o l’altra, oppure entrambe contemporaneamente. La seconda forma (con il parametro offset) è denominata «interfaccia estesa».

Alcuni filesystem richiedono un maggiore controllo sulle operazioni di scrittura – ad esempio, la scrittura su regioni di sotto-blocco senza cancellazione – e necessitano che il block device supporti l’interfaccia estesa.

class vfs.AbstractBlockDev

Modello di documentazione per il protocollo dei block device. MicroPython non espone effettivamente questa classe — è mostrata qui solo per documentare i metodi che una classe di block device definita dall’utente deve implementare. Gli argomenti del costruttore sono interamente a discrezione dell’implementazione (tipicamente cose come il bus della flash, il pin di chip-select, la dimensione del settore, ecc.).

readblocks(block_num: int, buf: bytearray) None
readblocks(block_num: int, buf: bytearray, offset: int) None

Legge byte dal dispositivo in buf. Due overload espongono le interfacce semplice ed estesa.

Forma semplice (readblocks(block_num, buf)): legge interi blocchi a partire dall’indice di blocco block_num. len(buf) deve essere un multiplo della dimensione del blocco, e il numero di blocchi letti è len(buf) // block_size.

Forma estesa (readblocks(block_num, buf, offset)): legge len(buf) byte – non necessariamente un numero intero di blocchi – a partire dal byte offset all’interno del blocco block_num. Usa questa forma quando il filesystem necessita di accesso in lettura a livello di sotto-blocco.

writeblocks(block_num: int, buf: bytes) None
writeblocks(block_num: int, buf: bytes, offset: int) None

Scrive byte da buf sul dispositivo.

Forma semplice (writeblocks(block_num, buf)): scrive interi blocchi a partire dall’indice di blocco block_num. len(buf) deve essere un multiplo della dimensione del blocco, e il numero di blocchi scritti è len(buf) // block_size. L’implementazione è responsabile di cancellare prima ciascun blocco di destinazione se l’hardware sottostante lo richiede.

Forma estesa (writeblocks(block_num, buf, offset)): scrive len(buf) byte – non necessariamente un numero intero di blocchi – a partire dal byte offset all’interno del blocco block_num. Possono cambiare solo i byte effettivamente scritti; il chiamante è responsabile di assicurarsi che i blocchi interessati siano stati cancellati tramite una precedente chiamata ioctl(6, block_num). Le implementazioni di questa forma non devono mai cancellare implicitamente un blocco, neppure quando offset è zero.

ioctl(op: int, arg: int) int | None

Controlla il block device e interroga i suoi parametri. L’operazione da eseguire è indicata da op, che è uno dei seguenti interi:

  • 1 – inizializza il dispositivo (arg non è usato)

  • 2 – arresta il dispositivo (arg non è usato)

  • 3 – sincronizza il dispositivo (arg non è usato)

  • 4 – ottiene il conteggio del numero di blocchi, deve restituire un intero (arg non è usato)

  • 5 – ottiene il numero di byte in un blocco, deve restituire un intero, oppure None nel qual caso viene usato il valore predefinito di 512 (arg non è usato)

  • 6 – cancella un blocco, arg è il numero del blocco da cancellare

Come minimo deve essere intercettato ioctl(4, ...); i filesystem che usano l’interfaccia estesa richiedono inoltre ioctl(6, ...). La necessità delle altre operazioni dipende dall’hardware.

Prima di qualsiasi chiamata a writeblocks(block, ...), un filesystem che usa l’interfaccia estesa emette ioctl(6, block) in modo che il driver possa cancellare prima il blocco se l’hardware lo richiede. Un driver può invece intercettare ioctl(6, block) e restituire 0 (successo), assumendosi la responsabilità di rilevare autonomamente quando è necessaria la cancellazione.

Salvo indicazione contraria, ioctl(op, arg) può restituire None. Di conseguenza un’implementazione può ignorare i valori di op non utilizzati. Quando op viene intercettato, i valori restituiti per le operazioni 4 e 5 sono quelli dettagliati sopra. Le altre operazioni dovrebbero restituire 0 in caso di successo e un valore diverso da zero in caso di fallimento, con il valore restituito che è un codice errno di OSError.