EVFS library API¶
The EVFS library has a number of sub-components that are described in the following sections:
Basic usage¶
Your first task is to initialize EVFS with the set of filesystems and shims you want to use. This is described below in more detail but we’ll start with this skeleton:
#include "evfs.h"
#include "evfs/stdio_fs.h"
int main() {
evfs_init();
evfs_register_stdio(/*default_vfs*/ true);
...
From this point you have access to the C stdio filesystem using EVFS. You can perform filesystem level operations using the top level filesystem access functions. Among them are evfs_open_ex()
and evfs_open_dir_ex()
which create EvfsFile
and EvfsDir
objects respectively. These objects each have a set of methods for operating on the underlying file or directory.
Most of the library functions return an integer error code. Success is indicated by EVFS_OK
which is 0. Error conditions are always negative. Some functions will have additional positive values for non-error conditions. You can convert error codes into a string using evfs_err_name()
:
EvfsFile *fh;
int status = evfs_open("foobar/test.txt", &fh, EVFS_READ);
printf("evfs_open() returned '%s'\n", evfs_err_name(status));
Access mode flags¶
When you open a file you must supply a set of flags to identify what mode you want to open the file in. EVFS provides the following set of flags:
EVFS_READ |
Open in read mode |
EVFS_WRITE |
Open in write mode |
EVFS_RDWR |
Combination of EVFS_READ and EVFS_WRITE |
EVFS_OPEN_OR_NEW |
Open an existing file or create a new one |
EVFS_NO_EXIST |
Do not open if the file exists |
EVFS_OVERWRITE |
Truncate all existing file content |
EVFS_APPEND |
Append writes to the end of file |
These can be merged together in combination to achieve different results.
// Open a new file to write into
status = evfs_open("file.txt", &fh, EVFS_WRITE | EVFS_NO_EXIST);
// Open file to append data and read back its content
status = evfs_open("file.txt", &fh, EVFS_RDWR | EVFS_APPEND);
Files are always opened in binary mode for every filesystem. There is no special handling of newline characters.
To read and write from a file you need to have a buffer to hold the data going in or out. Reads and writes may be partial so you should be prepared to repeat an operation if necessary.
status = evfs_open("file.txt", &fh, EVFS_RDWR);
char buf[100];
int read = evfs_file_read(fh, buf, sizeof(buf));
if(read >= 0)
printf("Read %d bytes from file\n", read);
evfs_file_rewind(fh); // Go back to start of the file
int wrote = evfs_file_write(fh, buf, sizeof(buf));
if(wrote >= 0)
printf("Wrote %d bytes to file\n", wrote);
evfs_file_close(fh);
File metadata¶
EVFS uses a common EvfsInfo
struct to work with file and directory metadata.
-
struct
EvfsInfo
¶ Metadata for files and directories
char *name - Name of a file or directory
time_t mtime - Modification time
evfs_off_t size - File size
uint8_t type - Object type (file or directory)
These info structs are returned by evfs_stat_ex()
and evfs_dir_read()
. Not all fields will be populated by these functions. Some data may be unavailable depending on the underlying filesystem. You can check which fields are valid by calling evfs_vfs_ctrl_ex()
with the EVFS_CMD_GET_STAT_FIELDS
and EVFS_CMD_GET_DIR_FIELDS
commands. This will return a bitfield value where set bits correspond to the members of EvfsInfo
that are valid for the current VFS. Unused fields will always be 0.
// Query for supported stat fields
unsigned stat_fields;
evfs_vfs_ctrl_ex(EVFS_CMD_GET_STAT_FIELDS, &stat_fields, "stdio");
// Get stat
EvfsInfo info;
evfs_stat_ex("/file.txt", &info, "stdio");
// Check each supported field before access
// Name is always present so this particular check isn't strictly required
if(stat_fields & EVFS_INFO_NAME) printf("File name is: %s\n", info.name);
// Mtime is missing on filesystems without time metadata and systems that don't
// report time in directory listings.
if(stat_fields & EVFS_INFO_MTIME) {
char mtime[30];
ctime_r(info.mtime, mtime);
printf("Last modified: %s\n", mtime);
}
Directory listing¶
Directories can be listed by opening a EvfsDir
with evfs_open_dir_ex()
. The directory access methods are then used to scan through the contents of a directory. evfs_dir_read()
will return EVFS_DONE
when there are no more entries to read in a directory.
EvfsDir *dh;
evfs_open_dir("path/to/dir", &dh);
EvfsInfo info;
int status = evfs_dir_read(dh, &info);
while(status != EVFS_DONE) {
// Work with this entry
...
status = evfs_dir_read(dh, &info);
}
evfs_dir_close(dh);
Directory entries are returned in whatever order the underlying filesystem produces them. Some filesystems may add entries for the current (“.”) and parent (“..”) directories. These can be suppressed by configuring the VFS with an EVFS_CMD_SET_NO_DIR_DOTS
command:
unsigned no_dots = 1;
evfs_vfs_ctrl(EVFS_CMD_SET_NO_DIR_DOTS, &no_dots);
EVFS architecture¶
EVFS uses a design that takes inspiration from the SQLite VFS driver mechanism. You can register multiple filesystem interfaces and stack optional shims on top of them to alter behavior.
Every VFS interface or shim is assigned a name used to refer to the VFS object at a later time. File and directory handles are opened against one of these named VFS objects. Shims must be registered on top of an existing interface or another shim. One of the VFS objects is designated as the default. All API functions with the “_ex” suffix will target the default if the vfs_name
argument is NULL.
EVFS does not directly interact with storage media. You need to have the filesystem driver required to access your storage. The necessary driver code is provided by your operating system or an auxilliary library outside of EVFS.
Mounting image files¶
EVFS is capable of mounting filesystem images stored on a host filesystem and then accessing them as an independent VFS. To accomplish this you need to incorporate a filesystem driver into your application. This driver layer normally expects to interact with the storage device. You need to add an I/O adapter that translates low level storage operations into EVFS API calls on the opened image file. An EVFS filesystem interface is registered to the new filesystem driver. The EVFS API can then be used to access the contents of the image file as if it were a normal filesystem.
EVFS includes code that manages image files for the FatFs and littlefs filesystems.
Library management¶
The evfs_init()
function must be called before anything can be done with EVFS. This ensures initialization of internal data structures has been performed. When threading is enabled it also creates a library-wide lock to protect accesss to the data structures. If EVFS_USE_ATEXIT
is enabled in ‘evfs_config.h’, an atexit()
handler will also be installed. If this isn’t enabled you must call evfs_unregister_all()
before the process exits to ensure that all VFSs have flushed their data and released any resources.
The mechanism for adding a new VFS or shim is to use evfs_register()
. Normally you will not call this directly but instead call a wrapper function provided by each VFS or shim driver. This function performs a secondary purpose of changing the default status of a VFS. If the VFS already exists, the make_default
argument will change the VFS to the new default if true. If it is set false, another VFS will be found to serve as the new default.
evfs_register_stdio(/*default_vfs*/ true); // Add a filesystem
// stdio is now the default
evfs_register_trace("t_stdio", "stdio", report, stderr, /*default_vfs*/ true); // Add a debug tracing shim
// Default is now the trace shim on top of stdio
// Bypass the shim and make stdio the default again
Evfs *stdio_vfs = evfs_find_vfs("stdio");
evfs_register(stdio_vfs, /*make_default*/ true);
// Remove default from stdio. "t_stdio" will become the new default.
evfs_register(stdio_vfs, /*make_default*/ false);
You can unregister a VFS or shim at runtime using evfs_unregister()
. You must ensure that no open file or directory handles exist that reference back to a VFS being unregistered. The library does not track this for you.
-
void
evfs_init
(void)¶ Initialize the EVFS library.
-
Evfs *
evfs_find_vfs
(const char *vfs_name)¶ Search for a VFS by name.
- Parameters
vfs_name – VFS to search for
- Returns
The matching VFS object or NULL
-
const char *
evfs_vfs_name
(Evfs *vfs)¶ Get the name of a VFS object.
- Parameters
vfs – The VFS to get name from
- Returns
The name this VFS is registered under
-
const char *
evfs_default_vfs_name
(void)¶ Get the name of the default VFS object.
- Returns
The name of the default VFS
-
int
evfs_register
(Evfs *vfs, bool make_default)¶ Register a new VFS or change the default status of an existing VFS.
If the VFS has already been registered the make_default argument will update the status of this VFS. If there is only one registered VFS, make_default is ignored.
This will fail if
evfs_init()
hasn’t been called.- Parameters
vfs – The new VFS to register
make_default – Set this VFS to be default or not
- Returns
EVFS_OK on success
-
int
evfs_unregister
(Evfs *vfs)¶ Unregister a VFS object.
- Parameters
vfs – The VFS to remove
- Returns
EVFS_OK on success
-
void
evfs_unregister_all
(void)¶ Unregister all registered VFS objects.
Filesystem access¶
The top level of the EVFS API involves functions that directly interact with a VFS to perform operations. You can open, delete, rename, copy, and query for the status on files and directories. The evfs_open_ex()
and evfs_open_dir_ex()
functions return a handle that is used to perform further operations on those entities.
Most of these functions have an “_ex” suffix indicating that their last parameter is an optional VFS name. All of these forms have a macro defined without the suffix that uses the default VFS. This is the same as passing NULL for the vfs_name
argument to the “_ex” forms.
EvfsFile *fh;
evfs_open_ex(path, &fh, EVFS_READ, "stdio"); // Open on named VFS
evfs_open_ex(path, &fh, EVFS_READ, NULL); // Open on default VFS
evfs_open(path, &fh, EVFS_READ); // Same as above using macro
There is a general purpose interface for passing named operations to the VFS via evfs_vfs_ctrl_ex()
. It takes a command ID and a command specific argument to pass and return values that can alter or query internal VFS behavior. For example, you can use the EVFS_CMD_SET_READONLY
command to write protect a VFS from any EVFS functions. See the listing of commands in ‘evfs.h’ for what is available and the types they take as an argument.
unsigned readonly = 1;
evfs_vfs_ctrl(EVFS_CMD_SET_READONLY, &readonly); // Make the default VFS readonly
unsigned no_dots = 1;
evfs_vfs_ctrl(EVFS_CMD_SET_NO_DIR_DOTS, &no_dots); // Strip "." and ".." from the output of evfs_dir_read()
-
int
evfs_open_ex
(const char *path, EvfsFile **fh, int flags, const char *vfs_name)¶ Open a file.
- Parameters
path – Filesystem path to the file
fh – Handle for successfully opened file
flags – Open mode flags
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
EVFS_OK on success
-
int
evfs_stat_ex
(const char *path, EvfsInfo *info, const char *vfs_name)¶ Get file or directory status.
Different VFS backends may only support a partial set of
EvfsInfo
fields. Use theEVFS_CMD_GET_STAT_FIELDS
command withevfs_vfs_ctrl_ex()
to query which fields are valid on a particular VFS. Unsupported fields will be 0.- Parameters
path – Filesystem path to the file
info – Information reported on the file
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
EVFS_OK on success
-
bool
evfs_existing_file_ex
(const char *path, const char *vfs_name)¶ Test if a file exists.
- Parameters
path – Filesystem path to the file
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
true if the file exists
-
bool
evfs_existing_dir_ex
(const char *path, const char *vfs_name)¶ Test if a directory exists.
- Parameters
path – Filesystem path to the directory
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
true if the file exists
-
int
evfs_delete_ex
(const char *path, const char *vfs_name)¶ Delete a file or directory.
- Parameters
path – Filesystem path to the file
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
EVFS_OK on success
-
int
evfs_rename_ex
(const char *old_path, const char *new_path, const char *vfs_name)¶ Rename a file or directory.
No validation or transformation is made to the path arguments. Absolute paths should match the same parent directory or this is likely to fail.
- Parameters
old_path – Filesystem path to existing file
new_path – New path to the file
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
EVFS_OK on success
-
int
evfs_make_dir_ex
(const char *path, const char *vfs_name)¶ Make a new directory.
- Parameters
path – Filesystem path to the directory
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
EVFS_OK on success
-
int
evfs_make_path_ex
(const char *path, const char *vfs_name)¶ Make a complete path to a nested directory.
Any missing directories in the path will be created.
- Parameters
path – Filesystem path to a directory
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
EVFS_OK on success
-
int
evfs_make_path_range_ex
(StringRange *path, const char *vfs_name)¶ Make a complete path to a nested directory.
Any missing directories in the path will be created.
This variant takes a
StringRange
object as the path. This allows the directory portion of a file path as generated byevfs_path_dirname_ex()
to be referenced as a substring without making a copy.- Parameters
path – Filesystem path to a directory
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
EVFS_OK on success
-
int
evfs_open_dir_ex
(const char *path, EvfsDir **dh, const char *vfs_name)¶ Open a directory.
- Parameters
path – Filesystem path to the directory
dh – Handle for successfully opened directory
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
EVFS_OK on success
-
int
evfs_vfs_open_dir
(Evfs *vfs, const char *path, EvfsDir **dh)¶ Open a directory from a VFS object.
- Parameters
vfs – The VFS to open on
path – Filesystem path to the directory
dh – Handle for successfully opened directory
- Returns
EVFS_OK on success
-
int
evfs_get_cur_dir_ex
(StringRange *cur_dir, const char *vfs_name)¶ Get the current working directory for a VFS.
Note that EVFS does not have any mechanism for handling DOS/Windows-style drive volumes. The reported working directory will be for the active volume.
The returned
StringRange
should not be modified as it may point into internal EVFS data structures.- Parameters
cur_dir – Reference to the current working directory
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
EVFS_OK on success
-
int
evfs_set_cur_dir_ex
(const char *path, const char *vfs_name)¶ Set the current working directory for a VFS.
Relative paths will be based in this directory until it is changed.
- Parameters
path – Path to the new working directory
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
EVFS_OK on success
-
int
evfs_vfs_ctrl_ex
(int cmd, void *arg, const char *vfs_name)¶ Generic configuration control for a VFS.
See the definition of commands in evfs.h for the expected type to pass as arg.
- Parameters
cmd – Command number for the operation to perform
arg – Variable argument data to write or read associated with the command
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
EVFS_OK on success
-
int
evfs_copy_to_file_ex
(const char *dest_path, EvfsFile *fh, char *buf, size_t buf_size, const char *vfs_name)¶ Copy contents of an open file to a new file.
This allows you to transfer files across different VFSs.
- Parameters
dest_path – Path to the new copy
fh – Open file to copy from
buf – Buffer to use for transfers. Use NULL to malloc a temp buffer.
buf_size – Size of buf array. When buf is NULL this is the size to allocate.
vfs_name – VFS to work on. Use default VFS if NULL
- Returns
EVFS_OK on success
File object methods¶
All of the functions with an “evfs_file” prefix are methods of an EvfsFile
object created by evfs_open_ex()
.
-
int
evfs_file_close
(EvfsFile *fh)¶ Close a file.
- Parameters
fh – The file to close
- Returns
EVFS_OK on success
-
int
evfs_file_ctrl
(EvfsFile *fh, int cmd, void *arg)¶ Generic configuration control for a file object.
See the definition of commands in evfs.h for the expected type to pass as arg.
- Parameters
fh – The file to receive the command
cmd – Command number for the operation to perform
arg – Variable argument data to write or read associated with the command
- Returns
EVFS_OK on success
-
size_t
evfs_file_read
(EvfsFile *fh, void *buf, size_t size)¶ Read data from a file.
- Parameters
fh – The file to read
buf – Buffer for read data
size – Size of buf
- Returns
Number of bytes read on success or negative error code on failure
-
size_t
evfs_file_write
(EvfsFile *fh, const void *buf, size_t size)¶ Write data to a file.
- Parameters
fh – The file to write
buf – Buffer for write data
size – Size of buf
- Returns
Number of bytes written on success or negative error code on failure
-
int
evfs_file_truncate
(EvfsFile *fh, evfs_off_t size)¶ Truncate the length of a file.
- Parameters
fh – The file to truncate
size – New truncated size
- Returns
EVFS_OK on success
-
int
evfs_file_sync
(EvfsFile *fh)¶ Sync a file to the underlying filesystem.
- Parameters
fh – The file to sync
- Returns
EVFS_OK on success
-
evfs_off_t
evfs_file_size
(EvfsFile *fh)¶ Get the size of a file.
This will perform a sync to guarantee that intermediate write buffers are emptied before checking the size.
- Parameters
fh – The file to size up
- Returns
Size of the open file
-
int
evfs_file_seek
(EvfsFile *fh, evfs_off_t offset, EvfsSeekDir origin)¶ Seek to a new offset in a file.
Origin is one of the following:
EVFS_SEEK_TO - Absolute position from the start of the file
EVFS_SEEK_REL - Position relative to current file offset
EVFS_SEEK_REV - Position from the end of the file
EVFS_SEEK_REL uses negative values to seek backward and positive to go forward. The other origin types use positive offset values.
Use the
evfs_file_rewind
macro to seek back to offset 0.- Parameters
fh – The file to seek on
offset – New position relative to the origin
origin – Start position for the seek
- Returns
EVFS_OK on success
-
evfs_off_t
evfs_file_tell
(EvfsFile *fh)¶ Get the current position within a file.
- Parameters
fh – The file to report on
- Returns
Current offset into the file from the start
-
bool
evfs_file_eof
(EvfsFile *fh)¶ Identify end of file.
- Parameters
fh – The file to report on
- Returns
true if file is at the end
Directory object methods¶
All of the functions with an “evfs_dir” prefix are methods of an EvfsDir
object created by evfs_open_dir_ex()
.
-
int
evfs_dir_close
(EvfsDir *dh)¶ Close a directory.
- Parameters
dh – The directory to close
- Returns
EVFS_OK on success
-
int
evfs_dir_read
(EvfsDir *dh, EvfsInfo *info)¶ Read the next directory entry.
Different VFS backends may only support a partial set of
EvfsInfo
fields. Use theEVFS_CMD_GET_DIR_FIELDS
command withevfs_vfs_ctrl_ex()
to query which fields are valid on a particular VFS. Unsupported fields will be 0.- Parameters
dh – The directory to read from
info – Information reported on the file
- Returns
EVFS_OK on success. EVFS_DONE when iteration is complete.
-
int
evfs_dir_rewind
(EvfsDir *dh)¶ Rewind a directory iterator to the beginning.
- Parameters
dh – The directory to rewind
- Returns
EVFS_OK on success
-
int
evfs_dir_find
(EvfsDir *dh, const char *pattern, EvfsInfo *info)¶ Find a file matching a glob pattern.
This scans a directory for a file until a match to pattern is found. Repeated calls will find the next file that matches the pattern.
- Parameters
dh – The directory to search
pattern – Glob pattern to be matched
info – Information reported on the file
- Returns
EVFS_OK on success. EVFS_DONE when iteration is complete.
Miscellaneous¶
-
const char *
evfs_err_name
(int err)¶ Translate an error code into a string.
- Parameters
err – EVFS error code
- Returns
The corresponding string or “<unknown>”
-
const char *
evfs_cmd_name
(int cmd)¶ Translate a command code into a string.
- Parameters
cmd – EVFS command code
- Returns
The corresponding string or “<unknown>”
-
int
evfs_file_printf
(EvfsFile *fh, const char *fmt, ...)¶ Print a formatted string to a file..
- Parameters
fh – Open file to print into
fmt – Format string
args – Variable argument list
- Returns
Number of bytes written on success or negative error code on failure
-
int
evfs_file_puts
(EvfsFile *fh, const char *str)¶ Write a string to a file.
- Parameters
fh – Open file to print into
str – String to write
- Returns
Number of bytes written on success or negative error code on failure