blob: 0062e607e613983964479703696af0f6c3f8a475 [file] [log] [blame]
File System Abstraction
=======================
.. toctree::
:hidden:
nffs
fatfs
otherfs
fs_add
Mynewt provides a file system abstraction layer (``fs/fs``) to allow
client code to be file system agnostic. By accessing the file system via
the ``fs/fs`` API, client code can perform file system operations
without being tied to a particular implementation. When possible,
library code should use the ``fs/fs`` API rather than accessing the
underlying file system directly.
.. contents::
:local:
:depth: 2
Description
~~~~~~~~~~~
Applications should aim to minimize the amount of code which depends on
a particular file system implementation. When possible, only depend on
the ``fs/fs`` package. In terms of the Mynewt hierarchy, an **app**
package must depend on a specific file system package, while **library**
packages should only depend on ``fs/fs``.
Applications wanting to access a filesystem are required to include the
necessary packages in their applications pkg.yml file. In the following
example, the :doc:`Newtron Flash File System <nffs>` is
used.
.. code-block:: console
# repos/apache-mynewt-core/apps/slinky/pkg.yml
pkg.name: repos/apache-mynewt-core/apps/slinky
pkg.deps:
- "@apache-mynewt-core/fs/fs" # include the file operations interfaces
- "@apache-mynewt-core/fs/nffs" # include the NFFS filesystem implementation
::
# repos/apache-mynewt-core/apps/slinky/syscfg.yml
# [...]
# Package: apps/<example app>
# [...]
CONFIG_NFFS: 1 # initialize and configure NFFS into the system
# NFFS_DETECT_FAIL: 1 # Ignore NFFS detection issues
# NFFS_DETECT_FAIL: 2 # Format a new NFFS file system on failure to detect
# [...]
Consult the ``nffs`` :doc:`documentation <nffs>` for a more
detailed explanation of NFFS\_DETECT\_FAIL
Code which uses the file system after the system has been initialized
need only depend on ``fs/fs``. For example, the ``libs/imgmgr`` package
is a library which provides firmware upload and download functionality
via the use of a file system. This library is only used after the system
has been initialized, and therefore only depends on the ``fs/fs``
package.
.. code-block:: console
# repos/apache-mynewt-core/libs/imgmgr/pkg.yml
pkg.name: libs/imgmgr
pkg.deps:
- "@apache-mynewt-core/fs/fs"
# [...]
The ``libs/imgmgr`` package uses the ``fs/fs`` API for all file system
operations.
Support for multiple filesystems
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When using a single filesystem/disk, it is valid to provide paths in the
standard unix way, eg, ``/<dir-name>/<file-name>``. When trying to run
more than one filesystem or a single filesystem in multiple devices
simultaneosly, an extra name has to be given to the disk that is being
used. The abstraction for that was added as the ``fs/disk`` package
which is a dependency of ``fs/fs``. It adds the following extra user
function:
.. code:: c
int disk_register(const char *disk_name, const char *fs_name, struct disk_ops *dops)
As an example os usage:
.. code:: c
disk_register("mmc0", "fatfs", &mmc_ops);
disk_register("flash0", "nffs", NULL);
This registers the name ``mmc0`` to use ``fatfs`` as the filesystem and
``mmc_ops`` for the low-level disk driver and also registers ``flash0``
to use ``nffs``. ``nffs`` is currently strongly bound to the
``hal_flash`` interface, ignoring any other possible ``disk_ops`` given.
struct disk\_ops
^^^^^^^^^^^^^^^^
To support a new low-level disk interface, the ``struct disk_ops``
interface must be implemented by the low-level driver. Currently only
``read`` and ``write`` are effectively used (by ``fatfs``).
.. code:: c
struct disk_ops {
int (*read)(uint8_t, uint32_t, void *, uint32_t);
int (*write)(uint8_t, uint32_t, const void *, uint32_t);
int (*ioctl)(uint8_t, uint32_t, void *);
SLIST_ENTRY(disk_ops) sc_next;
}
Thread Safety
~~~~~~~~~~~~~
All ``fs/fs`` functions are thread safe.
Header Files
~~~~~~~~~~~~
All code which uses the ``fs/fs`` package needs to include the following
header:
.. code:: c
#include "fs/fs.h"
Data Structures
~~~~~~~~~~~~~~~
All ``fs/fs`` data structures are opaque to client code.
.. code:: c
struct fs_file;
struct fs_dir;
struct fs_dirent;
Examples
~~~~~~~~
Example 1 below opens the file /settings/config.txt for reading, reads some data, and then closes the file.
.. code:: c
int
read_config(void)
{
struct fs_file *file;
uint32_t bytes_read;
uint8_t buf[16];
int rc;
/* Open the file for reading. */
rc = fs_open("/settings/config.txt", FS_ACCESS_READ, &file);
if (rc != 0) {
return -1;
}
/* Read up to 16 bytes from the file. */
rc = fs_read(file, sizeof buf, buf, &bytes_read);
if (rc == 0) {
/* buf now contains up to 16 bytes of file data. */
console_printf("read %u bytes\n", bytes_read)
}
/* Close the file. */
fs_close(file);
return rc == 0 ? 0 : -1;
}
Example 2 below iterates through the contents of a directory, printing the name of each child node. When the traversal is complete, the code closes the directory handle.
.. code:: c
int
traverse_dir(const char *dirname)
{
struct fs_dirent *dirent;
struct fs_dir *dir;
char buf[64];
uint8_t name_len;
int rc;
rc = fs_opendir(dirname, &dir);
if (rc != 0) {
return -1;
}
/* Iterate through the parent directory, printing the name of each child
* entry. The loop only terminates via a function return.
*/
while (1) {
/* Retrieve the next child node. */
rc = fs_readdir(dir, &dirent);
if (rc == FS_ENOENT) {
/* Traversal complete. */
return 0;
} else if (rc != 0) {
/* Unexpected error. */
return -1;
}
/* Read the child node's name from the file system. */
rc = fs_dirent_name(dirent, sizeof buf, buf, &name_len);
if (rc != 0) {
return -1;
}
/* Print the child node's name to the console. */
if (fs_dirent_is_dir(dirent)) {
console_printf(" dir: ");
} else {
console_printf("file: ");
}
console_printf("%s\n", buf);
}
}
Example 3 below demonstrates creating a series of nested directories.
.. code:: c
int
create_path(void)
{
int rc;
rc = fs_mkdir("/data");
if (rc != 0) goto err;
rc = fs_mkdir("/data/logs");
if (rc != 0) goto err;
rc = fs_mkdir("/data/logs/temperature");
if (rc != 0) goto err;
rc = fs_mkdir("/data/logs/temperature/current");
if (rc != 0) goto err;
return 0;
err:
/* Clean up the incomplete directory tree, if any. */
fs_unlink("/data");
return -1;
}
Example 4 below demonstrates reading a small text file in its entirety and printing its contents to the console.
.. code:: c
int
print_status(void)
{
uint32_t bytes_read;
uint8_t buf[16];
int rc;
/* Read up to 15 bytes from the start of the file. */
rc = fsutil_read_file("/cfg/status.txt", 0, sizeof buf - 1, buf,
&bytes_read);
if (rc != 0) return -1;
/* Null-terminate the string just read. */
buf[bytes_read] = '\0';
/* Print the file contents to the console. */
console_printf("%s\n", buf);
return 0;
}
Example 5 creates a 4-byte file.
.. code:: c
int
write_id(void)
{
int rc;
/* Create the parent directory. */
rc = fs_mkdir("/cfg");
if (rc != 0 && rc != FS_EALREADY) {
return -1;
}
/* Create a file and write four bytes to it. */
rc = fsutil_write_file("/cfg/id.txt", "1234", 4);
if (rc != 0) {
return -1;
}
return 0;
}
API
~~~
.. doxygenfile:: fs/fs/include/fs/fs.h
.. doxygenfile:: fs/fs/include/fs/fsutil.h