| |
| |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| |
| |
| |
| |
| <title>Internals of nffs — Apache Mynewt latest documentation</title> |
| |
| |
| |
| |
| <link rel="shortcut icon" href="../../../../_static/mynewt-logo-only-newt32x32.png"/> |
| |
| |
| |
| |
| <link rel="stylesheet" href="../../../../_static/css/theme.css" type="text/css" /> |
| |
| |
| <link rel="stylesheet" href="../../../../_static/css/sphinx_theme.css" type="text/css" /> |
| |
| <link rel="stylesheet" href="../../../../_static/css/bootstrap-3.0.3.min.css" type="text/css" /> |
| |
| <link rel="stylesheet" href="../../../../_static/css/v2.css" type="text/css" /> |
| |
| <link rel="stylesheet" href="../../../../_static/css/custom.css" type="text/css" /> |
| |
| <link rel="stylesheet" href="../../../../_static/css/restructuredtext.css" type="text/css" /> |
| |
| |
| |
| |
| <link rel="stylesheet" href="../../../../_static/css/overrides.css" type="text/css" /> |
| <link rel="index" title="Index" |
| href="../../../../genindex.html"/> |
| <link rel="search" title="Search" href="../../../../search.html"/> |
| <link rel="top" title="Apache Mynewt latest documentation" href="../../../../index.html"/> |
| |
| |
| <script src="../../../../_static/js/modernizr.min.js"></script> |
| |
| |
| <script> |
| (function(i, s, o, g, r, a, m) { |
| i["GoogleAnalyticsObject"] = r; |
| (i[r] = |
| i[r] || |
| function() { |
| (i[r].q = i[r].q || []).push(arguments); |
| }), |
| (i[r].l = 1 * new Date()); |
| (a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]); |
| a.async = 1; |
| a.src = g; |
| m.parentNode.insertBefore(a, m); |
| })(window, document, "script", "//www.google-analytics.com/analytics.js", "ga"); |
| |
| ga("create", "UA-72162311-1", "auto"); |
| ga("send", "pageview"); |
| </script> |
| |
| |
| </head> |
| |
| <body class="not-front page-documentation" role="document" > |
| <div id="wrapper"> |
| <div class="container"> |
| <div id="banner" class="row v2-main-banner"> |
| <a class="logo-cell" href="/"> |
| <img class="logo" src="../../../../_static/img/logo.png"> |
| </a> |
| <div class="tagline-cell"> |
| <h4 class="tagline">An OS to build, deploy and securely manage billions of devices</h4> |
| </div> |
| <div class="news-cell"> |
| <div class="well"> |
| <h4>Latest News:</h4> <a href="/download">Apache Mynewt 1.12.0, Apache NimBLE 1.7.0 </a> released April 4, 2024) |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <header> |
| <nav id="navbar" class="navbar navbar-inverse" role="navigation"> |
| <div class="container"> |
| <!-- Collapsed navigation --> |
| <div class="navbar-header"> |
| <!-- Expander button --> |
| <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> |
| <span class="sr-only">Toggle navigation</span> |
| <span class="icon-bar"></span> |
| <span class="icon-bar"></span> |
| <span class="icon-bar"></span> |
| </button> |
| |
| </div> |
| |
| <!-- Expanded navigation --> |
| <div class="navbar-collapse collapse"> |
| <!-- Main navigation --> |
| <ul class="nav navbar-nav navbar-right"> |
| <li> |
| <a href="/"><i class="fa fa-home" style="font-size: larger;"></i></a> |
| </li> |
| <li class="important"> |
| <a href="/quick-start/">Quick Start</a> |
| </li> |
| <li> |
| <a href="/about/">About</a> |
| </li> |
| <li> |
| <a href="/talks/">Talks</a> |
| </li> |
| <li class="active"> |
| <a href="/documentation/">Documentation</a> |
| </li> |
| <li> |
| <a href="/download/">Download</a> |
| </li> |
| <li> |
| <a href="/community/">Community</a> |
| </li> |
| <li> |
| <a href="/events/">Events</a> |
| </li> |
| </ul> |
| |
| <!-- Search, Navigation and Repo links --> |
| <ul class="nav navbar-nav navbar-right"> |
| |
| </ul> |
| </div> |
| </div> |
| </nav> |
| </header> |
| <!-- STARTS MAIN CONTENT --> |
| <div id="main-content"> |
| |
| |
| |
| |
| |
| |
| <div id="breadcrumb"> |
| <div class="container"> |
| <a href="/documentation/">Docs</a> / |
| |
| Internals of nffs |
| |
| <div class="sourcelink"> |
| <a href="https://github.com/apache/mynewt-core/edit/master/docs/os/modules/fs/nffs/nffs_internals.rst" class="icon icon-github" |
| rel="nofollow"> Edit on GitHub</a> |
| </div> |
| </div> |
| </div> |
| <!-- STARTS CONTAINER --> |
| <div class="container"> |
| <!-- STARTS .content --> |
| <div id="content" class="row"> |
| |
| <!-- STARTS .container-sidebar --> |
| <div class="container-sidebar col-xs-12 col-sm-3"> |
| <div id="docSidebar" class="sticky-container"> |
| <div role="search" class="sphinx-search"> |
| <form id="rtd-search-form" class="wy-form" action="../../../../search.html" method="get"> |
| <input type="text" name="q" placeholder="Search documentation" class="search-documentation" /> |
| <input type="hidden" name="check_keywords" value="yes" /> |
| <input type="hidden" name="area" value="default" /> |
| </form> |
| </div> |
| <!-- Note: only works when deployed --> |
| <select class="form-control" onchange="if (this.value) window.location.href=this.value"> |
| <option value="/latest" selected> |
| Version: latest |
| </option> |
| <option value="/v1_12_0" > |
| Version: 1.12.0 |
| </option> |
| <option value="/v1_11_0" > |
| Version: 1.11.0 |
| </option> |
| <option value="/v1_10_0" > |
| Version: 1.10.0 |
| </option> |
| <option value="/v1_9_0" > |
| Version: 1.9.0 |
| </option> |
| <option value="/v1_8_0" > |
| Version: 1.8.0 |
| </option> |
| <option value="/v1_7_0" > |
| Version: 1.7.0 |
| </option> |
| <option value="/v1_6_0" > |
| Version: 1.6.0 |
| </option> |
| <option value="/v1_5_0" > |
| Version: 1.5.0 |
| </option> |
| <option value="/v1_4_0" selected="selected" > |
| Version: 1.4.0 |
| </option> |
| <option value="/v1_3_0/os/introduction" > |
| Version: 1.3.0 |
| </option> |
| <option value="/v1_2_0/os/introduction" > |
| Version: 1.2.0 |
| </option> |
| <option value="/v1_1_0/os/introduction" > |
| Version: 1.1.0 |
| </option> |
| <option value="/v1_0_0/os/introduction" > |
| Version: 1.0.0 |
| </option> |
| <option value="/v0_9_0/os/introduction" > |
| Version: 0.9.0 |
| </option> |
| </select> |
| <div class="region region-sidebar"> |
| <div class="docs-menu"> |
| |
| |
| |
| <ul> |
| <li class="toctree-l1"><a class="reference internal" href="../../../../index.html">Introduction</a></li> |
| <li class="toctree-l1"><a class="reference internal" href="../../../../get_started/index.html">Setup & Get Started</a></li> |
| <li class="toctree-l1"><a class="reference internal" href="../../../../concepts.html">Concepts</a></li> |
| <li class="toctree-l1"><a class="reference internal" href="../../../../tutorials/tutorials.html">Tutorials</a></li> |
| <li class="toctree-l1"><a class="reference internal" href="../../../../external_links.html">Third-party Resources</a></li> |
| <li class="toctree-l1"><a class="reference internal" href="../../../os_user_guide.html">OS User Guide</a></li> |
| <li class="toctree-l1"><a class="reference internal" href="../../../../network/index.html">BLE User Guide</a></li> |
| <li class="toctree-l1"><a class="reference internal" href="../../../../newt/index.html">Newt Tool Guide</a></li> |
| <li class="toctree-l1"><a class="reference internal" href="../../../../newtmgr/index.html">Newt Manager Guide</a></li> |
| <li class="toctree-l1"><a class="reference internal" href="../../../../mynewt_faq/index.html">Mynewt FAQ</a></li> |
| <li class="toctree-l1"><a class="reference internal" href="../../../../misc/index.html">Appendix</a></li> |
| </ul> |
| |
| |
| |
| </div> |
| </div> |
| </div> |
| <!-- ENDS STICKY CONTAINER --> |
| </div> |
| <!-- ENDS .container-sidebar --> |
| |
| <div class="col-xs-12 col-sm-9"> |
| |
| <div class="alert alert-warning"> |
| <p> |
| Version 1.4.0 is not the most recent version of the |
| Apache Mynewt documentation. Click <a href="/latest">here</a> to |
| read the latest version. |
| </p> |
| </div> |
| |
| |
| |
| <div class=""> |
| <div class="rst-content"> |
| <div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article"> |
| <div itemprop="articleBody"> |
| |
| <div class="section" id="internals-of-nffs"> |
| <h1>Internals of nffs<a class="headerlink" href="#internals-of-nffs" title="Permalink to this headline">¶</a></h1> |
| <div class="section" id="disk-structure"> |
| <h2>Disk structure<a class="headerlink" href="#disk-structure" title="Permalink to this headline">¶</a></h2> |
| <p>On disk, each area is prefixed with the following header:</p> |
| <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cm">/** On-disk representation of an area header. */</span> |
| <span class="k">struct</span><span class="w"> </span><span class="nc">nffs_disk_area</span><span class="w"> </span><span class="p">{</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">nda_magic</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span><span class="w"> </span><span class="cm">/* NFFS_AREA_MAGIC{0,1,2,3} */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">nda_length</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Total size of area, in bytes. */</span> |
| <span class="w"> </span><span class="kt">uint8_t</span><span class="w"> </span><span class="n">nda_ver</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Current nffs version: 0 */</span> |
| <span class="w"> </span><span class="kt">uint8_t</span><span class="w"> </span><span class="n">nda_gc_seq</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Garbage collection count. */</span> |
| <span class="w"> </span><span class="kt">uint8_t</span><span class="w"> </span><span class="n">reserved8</span><span class="p">;</span> |
| <span class="w"> </span><span class="kt">uint8_t</span><span class="w"> </span><span class="n">nda_id</span><span class="p">;</span><span class="w"> </span><span class="cm">/* 0xff if scratch area. */</span> |
| <span class="p">};</span> |
| </pre></div> |
| </div> |
| <p>Beyond its header, an area contains a sequence of disk objects, |
| representing the contents of the file system. There are two types of |
| objects: <em>inodes</em> and <em>data blocks</em>. An inode represents a file or |
| directory; a data block represents part of a file’s contents.</p> |
| <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cm">/** On-disk representation of an inode (file or directory). */</span> |
| <span class="k">struct</span><span class="w"> </span><span class="nc">nffs_disk_inode</span><span class="w"> </span><span class="p">{</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">ndi_magic</span><span class="p">;</span><span class="w"> </span><span class="cm">/* NFFS_INODE_MAGIC */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">ndi_id</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Unique object ID. */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">ndi_seq</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Sequence number; greater supersedes</span> |
| <span class="cm"> lesser. */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">ndi_parent_id</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Object ID of parent directory inode. */</span> |
| <span class="w"> </span><span class="kt">uint8_t</span><span class="w"> </span><span class="n">reserved8</span><span class="p">;</span> |
| <span class="w"> </span><span class="kt">uint8_t</span><span class="w"> </span><span class="n">ndi_filename_len</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Length of filename, in bytes. */</span> |
| <span class="w"> </span><span class="kt">uint16_t</span><span class="w"> </span><span class="n">ndi_crc16</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Covers rest of header and filename. */</span> |
| <span class="w"> </span><span class="cm">/* Followed by filename. */</span> |
| <span class="p">};</span> |
| </pre></div> |
| </div> |
| <p>An inode filename’s length cannot exceed 256 bytes. The filename is not |
| null-terminated. The following ASCII characters are not allowed in a |
| filename:</p> |
| <ul class="simple"> |
| <li><p>/ (slash character)</p></li> |
| <li><p><a href="#id1"><span class="problematic" id="id2">:raw-latex:`\0`</span></a> (NUL character)</p></li> |
| </ul> |
| <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cm">/** On-disk representation of a data block. */</span> |
| <span class="k">struct</span><span class="w"> </span><span class="nc">nffs_disk_block</span><span class="w"> </span><span class="p">{</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">ndb_magic</span><span class="p">;</span><span class="w"> </span><span class="cm">/* NFFS_BLOCK_MAGIC */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">ndb_id</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Unique object ID. */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">ndb_seq</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Sequence number; greater supersedes lesser. */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">ndb_inode_id</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Object ID of owning inode. */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">ndb_prev_id</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Object ID of previous block in file;</span> |
| <span class="cm"> NFFS_ID_NONE if this is the first block. */</span> |
| <span class="w"> </span><span class="kt">uint16_t</span><span class="w"> </span><span class="n">ndb_data_len</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Length of data contents, in bytes. */</span> |
| <span class="w"> </span><span class="kt">uint16_t</span><span class="w"> </span><span class="n">ndb_crc16</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Covers rest of header and data. */</span> |
| <span class="w"> </span><span class="cm">/* Followed by 'ndb_data_len' bytes of data. */</span> |
| <span class="p">};</span> |
| </pre></div> |
| </div> |
| <p>Each data block contains the ID of the previous data block in the file. |
| Together, the set of blocks in a file form a reverse singly-linked list.</p> |
| <p>The maximum number of data bytes that a block can contain is determined |
| at initialization-time. The result is the greatest number which |
| satisfies all of the following restrictions:</p> |
| <ul class="simple"> |
| <li><p>No more than 2048.</p></li> |
| <li><p>At least two maximum-sized blocks can fit in the smallest area.</p></li> |
| </ul> |
| <p>The 2048 number was chosen somewhat arbitrarily, and may change in the |
| future.</p> |
| </div> |
| <div class="section" id="id-space"> |
| <h2>ID space<a class="headerlink" href="#id-space" title="Permalink to this headline">¶</a></h2> |
| <p>All disk objects have a unique 32-bit ID. The ID space is partitioned as |
| follows:</p> |
| <table class="docutils align-default"> |
| <colgroup> |
| <col style="width: 48%" /> |
| <col style="width: 52%" /> |
| </colgroup> |
| <thead> |
| <tr class="row-odd"><th class="head"><p>ID range</p></th> |
| <th class="head"><p>Node type</p></th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr class="row-even"><td><p>0x00000000 - 0x0fffffff</p></td> |
| <td><p>Directory inodes</p></td> |
| </tr> |
| <tr class="row-odd"><td><p>0x10000000 - 0x7fffffff</p></td> |
| <td><p>File inodes</p></td> |
| </tr> |
| <tr class="row-even"><td><p>0x80000000 - 0xfffffffe</p></td> |
| <td><p>Data blocks</p></td> |
| </tr> |
| <tr class="row-odd"><td><p>0xffffffff</p></td> |
| <td><p>Reserved (NFFS_ID_NONE)</p></td> |
| </tr> |
| </tbody> |
| </table> |
| </div> |
| <div class="section" id="scratch-area"> |
| <h2>Scratch area<a class="headerlink" href="#scratch-area" title="Permalink to this headline">¶</a></h2> |
| <p>A valid nffs file system must contain a single “scratch area.” The |
| scratch area does not contain any objects of its own, and is only used |
| during garbage collection. The scratch area must have a size greater |
| than or equal to each of the other areas in flash.</p> |
| </div> |
| <div class="section" id="ram-representation"> |
| <h2>RAM representation<a class="headerlink" href="#ram-representation" title="Permalink to this headline">¶</a></h2> |
| <p>Every object in the file system is stored in a 256-entry hash table. An |
| object’s hash key is derived from its 32-bit ID. Each list in the hash |
| table is sorted by time of use; most-recently-used is at the front of |
| the list. All objects are represented by the following structure:</p> |
| <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cm">/**</span> |
| <span class="cm"> * What gets stored in the hash table. Each entry represents a data block or</span> |
| <span class="cm"> * an inode.</span> |
| <span class="cm"> */</span> |
| <span class="k">struct</span><span class="w"> </span><span class="nc">nffs_hash_entry</span><span class="w"> </span><span class="p">{</span> |
| <span class="w"> </span><span class="n">SLIST_ENTRY</span><span class="p">(</span><span class="n">nffs_hash_entry</span><span class="p">)</span><span class="w"> </span><span class="n">nhe_next</span><span class="p">;</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">nhe_id</span><span class="p">;</span><span class="w"> </span><span class="cm">/* 0 - 0x7fffffff if inode; else if block. */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">nhe_flash_loc</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Upper-byte = area idx; rest = area offset. */</span> |
| <span class="p">};</span> |
| </pre></div> |
| </div> |
| <p>For each data block, the above structure is all that is stored in RAM. |
| To acquire more information about a data block, the block header must be |
| read from flash.</p> |
| <p>Inodes require a fuller RAM representation to capture the structure of |
| the file system. There are two types of inodes: <em>files</em> and |
| <em>directories</em>. Each inode hash entry is actually an instance of the |
| following structure:</p> |
| <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cm">/** Each inode hash entry is actually one of these. */</span> |
| <span class="k">struct</span><span class="w"> </span><span class="nc">nffs_inode_entry</span><span class="w"> </span><span class="p">{</span> |
| <span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">nffs_hash_entry</span><span class="w"> </span><span class="n">nie_hash_entry</span><span class="p">;</span> |
| <span class="w"> </span><span class="n">SLIST_ENTRY</span><span class="p">(</span><span class="n">nffs_inode_entry</span><span class="p">)</span><span class="w"> </span><span class="n">nie_sibling_next</span><span class="p">;</span> |
| <span class="w"> </span><span class="k">union</span><span class="w"> </span><span class="p">{</span> |
| <span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">nffs_inode_list</span><span class="w"> </span><span class="n">nie_child_list</span><span class="p">;</span><span class="w"> </span><span class="cm">/* If directory */</span> |
| <span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">nffs_hash_entry</span><span class="w"> </span><span class="o">*</span><span class="n">nie_last_block_entry</span><span class="p">;</span><span class="w"> </span><span class="cm">/* If file */</span> |
| <span class="w"> </span><span class="p">};</span> |
| <span class="w"> </span><span class="kt">uint8_t</span><span class="w"> </span><span class="n">nie_refcnt</span><span class="p">;</span> |
| <span class="p">};</span> |
| </pre></div> |
| </div> |
| <p>A directory inode contains a list of its child files and directories |
| (<em>fie_child_list</em>). These entries are sorted alphabetically using the |
| ASCII character set.</p> |
| <p>A file inode contains a pointer to the last data block in the file |
| (<em>nie_last_block_entry</em>). For most file operations, the reversed |
| block list must be walked backwards. This introduces a number of speed |
| inefficiencies:</p> |
| <ul class="simple"> |
| <li><p>All data blocks must be read to determine the length of the file.</p></li> |
| <li><p>Data blocks often need to be processed sequentially. The reversed |
| nature of the block list transforms this from linear time to an |
| O(n^2) operation.</p></li> |
| </ul> |
| <p>Furthermore, obtaining information about any constituent data block |
| requires a separate flash read.</p> |
| </div> |
| <div class="section" id="inode-cache-and-data-block-cache"> |
| <h2>Inode cache and Data Block cache<a class="headerlink" href="#inode-cache-and-data-block-cache" title="Permalink to this headline">¶</a></h2> |
| <p>The speed issues are addressed by a pair of caches. Cached inodes |
| entries contain the file length and a much more convenient doubly-linked |
| list of cached data blocks. The benefit of using caches is that the size |
| of the caches need not be proportional to the size of the file system. |
| In other words, caches can address speed efficiency concerns without |
| negatively impacting the file system’s scalability.</p> |
| <p>nffs requires both caches during normal operation, so it is not possible |
| to disable them. However, the cache sizes are configurable, and both |
| caches can be configured with a size of one if RAM usage must be |
| minimized.</p> |
| <p>The following data structures are used in the inode and data block |
| caches.</p> |
| <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cm">/** Full data block representation; not stored permanently in RAM. */</span> |
| <span class="k">struct</span><span class="w"> </span><span class="nc">nffs_block</span><span class="w"> </span><span class="p">{</span> |
| <span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">nffs_hash_entry</span><span class="w"> </span><span class="o">*</span><span class="n">nb_hash_entry</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Points to real block entry. */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">nb_seq</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Sequence number; greater</span> |
| <span class="cm"> supersedes lesser. */</span> |
| <span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">nffs_inode_entry</span><span class="w"> </span><span class="o">*</span><span class="n">nb_inode_entry</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Owning inode. */</span> |
| <span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">nffs_hash_entry</span><span class="w"> </span><span class="o">*</span><span class="n">nb_prev</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Previous block in file. */</span> |
| <span class="w"> </span><span class="kt">uint16_t</span><span class="w"> </span><span class="n">nb_data_len</span><span class="p">;</span><span class="w"> </span><span class="cm">/* # of data bytes in block. */</span> |
| <span class="w"> </span><span class="kt">uint16_t</span><span class="w"> </span><span class="n">reserved16</span><span class="p">;</span> |
| <span class="p">};</span> |
| </pre></div> |
| </div> |
| <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cm">/** Represents a single cached data block. */</span> |
| <span class="k">struct</span><span class="w"> </span><span class="nc">nffs_cache_block</span><span class="w"> </span><span class="p">{</span> |
| <span class="w"> </span><span class="n">TAILQ_ENTRY</span><span class="p">(</span><span class="n">nffs_cache_block</span><span class="p">)</span><span class="w"> </span><span class="n">ncb_link</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Next / prev cached block. */</span> |
| <span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">nffs_block</span><span class="w"> </span><span class="n">ncb_block</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Full data block. */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">ncb_file_offset</span><span class="p">;</span><span class="w"> </span><span class="cm">/* File offset of this block. */</span> |
| <span class="p">};</span> |
| </pre></div> |
| </div> |
| <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cm">/** Full inode representation; not stored permanently in RAM. */</span> |
| <span class="k">struct</span><span class="w"> </span><span class="nc">nffs_inode</span><span class="w"> </span><span class="p">{</span> |
| <span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">nffs_inode_entry</span><span class="w"> </span><span class="o">*</span><span class="n">ni_inode_entry</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Points to real inode entry. */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">ni_seq</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Sequence number; greater</span> |
| <span class="cm"> supersedes lesser. */</span> |
| <span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">nffs_inode_entry</span><span class="w"> </span><span class="o">*</span><span class="n">ni_parent</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Points to parent directory. */</span> |
| <span class="w"> </span><span class="kt">uint8_t</span><span class="w"> </span><span class="n">ni_filename_len</span><span class="p">;</span><span class="w"> </span><span class="cm">/* # chars in filename. */</span> |
| <span class="w"> </span><span class="kt">uint8_t</span><span class="w"> </span><span class="n">ni_filename</span><span class="p">[</span><span class="n">NFFS_SHORT_FILENAME_LEN</span><span class="p">];</span><span class="w"> </span><span class="cm">/* First 3 bytes. */</span> |
| <span class="p">};</span> |
| </pre></div> |
| </div> |
| <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cm">/** Doubly-linked tail queue of cached blocks; contained in cached inodes. */</span> |
| <span class="n">TAILQ_HEAD</span><span class="p">(</span><span class="n">nffs_block_cache_list</span><span class="p">,</span><span class="w"> </span><span class="n">nffs_block_cache_entry</span><span class="p">);</span> |
| |
| <span class="cm">/** Represents a single cached file inode. */</span> |
| <span class="k">struct</span><span class="w"> </span><span class="nc">nffs_cache_inode</span><span class="w"> </span><span class="p">{</span> |
| <span class="w"> </span><span class="n">TAILQ_ENTRY</span><span class="p">(</span><span class="n">nffs_cache_inode</span><span class="p">)</span><span class="w"> </span><span class="n">nci_link</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Sorted; LRU at tail. */</span> |
| <span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">nffs_inode</span><span class="w"> </span><span class="n">nci_inode</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Full inode. */</span> |
| <span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">nffs_cache_block_list</span><span class="w"> </span><span class="n">nci_block_list</span><span class="p">;</span><span class="w"> </span><span class="cm">/* List of cached blocks. */</span> |
| <span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">nci_file_size</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Total file size. */</span> |
| <span class="p">};</span> |
| </pre></div> |
| </div> |
| <p>Only file inodes are cached; directory inodes are never cached.</p> |
| <p>Within a cached inode, all cached data blocks are contiguous. E.g., if |
| the start and end of a file are cached, then the middle must also be |
| cached. A data block is only cached if its owning file is also cached.</p> |
| <p>Internally, cached inodes are stored in a singly-linked list, ordered by |
| time of use. The most-recently-used entry is the first element in the |
| list. If a new inode needs to be cached, but the inode cache is full, |
| the least-recently-used entry is freed to make room for the new one. The |
| following operations cause an inode to be cached:</p> |
| <ul class="simple"> |
| <li><p>Querying a file’s length.</p></li> |
| <li><p>Seeking within a file.</p></li> |
| <li><p>Reading from a file.</p></li> |
| <li><p>Writing to a file.</p></li> |
| </ul> |
| <p>The following operations cause a data block to be cached:</p> |
| <ul class="simple"> |
| <li><p>Reading from the block.</p></li> |
| <li><p>Writing to the block.</p></li> |
| </ul> |
| <p>If one of the above operations is applied to a data block that is not |
| currently cached, nffs uses the following procedure to cache the |
| necessary block:</p> |
| <ol class="arabic simple"> |
| <li><p>If none of the owning inode’s blocks are currently cached, allocate a |
| cached block entry corresponding to the requested block and insert it |
| into the inode’s list.</p></li> |
| <li><p>Else if the requested file offset is less than that of the first |
| cached block, bridge the gap between the inode’s sequence of cached |
| blocks and the block that now needs to be cached. This is |
| accomplished by caching each block in the gap, finishing with the |
| requested block.</p></li> |
| <li><p>Else (the requested offset is beyond the end of the cache),</p> |
| <ol class="arabic simple"> |
| <li><p>If the requested offset belongs to the block that immediately |
| follows the end of the cache, cache the block and append it to the |
| list.</p></li> |
| <li><p>Else, clear the cache, and populate it with the single entry |
| corresponding to the requested block.</p></li> |
| </ol> |
| </li> |
| </ol> |
| <p>If the system is unable to allocate a cached block entry at any point |
| during the above procedure, the system frees up other blocks currently |
| in the cache. This is accomplished as follows:</p> |
| <ul class="simple"> |
| <li><p>Iterate the inode cache in reverse (i.e., start with the |
| least-recently-used entry). For each entry:</p> |
| <ol class="arabic simple"> |
| <li><p>If the entry’s cached block list is empty, advance to the next |
| entry.</p></li> |
| <li><p>Else, free all the cached blocks in the entry’s list.</p></li> |
| </ol> |
| </li> |
| </ul> |
| <p>Because the system imposes a minimum block cache size of one, the above |
| procedure will always reclaim at least one cache block entry. The above |
| procedure may result in the freeing of the block list that belongs to |
| the very inode being operated on. This is OK, as the final block to get |
| cached is always the block being requested.</p> |
| </div> |
| <div class="section" id="detection"> |
| <h2>Detection<a class="headerlink" href="#detection" title="Permalink to this headline">¶</a></h2> |
| <p>The file system detection process consists of scanning a specified set |
| of flash regions for valid nffs areas, and then populating the RAM |
| representation of the file system with the detected objects. Detection |
| is initiated with the <a class="reference external" href="nffs_detect.html">nffs_detect()</a> function.</p> |
| <p>Not every area descriptor passed to <code class="docutils literal notranslate"><span class="pre">nffs_detect()</span></code> needs to reference |
| a valid nffs area. Detection is successful as long as a complete file |
| system is detected somewhere in the specified regions of flash. If an |
| application is unsure where a file system might be located, it can |
| initiate detection across the entire flash region.</p> |
| <p>A detected file system is valid if:</p> |
| <ol class="arabic simple"> |
| <li><p>At least one non-scratch area is present.</p></li> |
| <li><p>At least one scratch area is present (only the first gets used if |
| there is more than one).</p></li> |
| <li><p>The root directory inode is present.</p></li> |
| </ol> |
| <p>During detection, each indicated region of flash is checked for a valid |
| area header. The contents of each valid non-scratch area are then |
| restored into the nffs RAM representation. The following procedure is |
| applied to each object in the area:</p> |
| <ol class="arabic simple"> |
| <li><p>Verify the object’s integrity via a crc16 check. If invalid, the |
| object is discarded and the procedure restarts on the next object in |
| the area.</p></li> |
| <li><p>Convert the disk object into its corresponding RAM representation and |
| insert it into the hash table. If the object is an inode, its |
| reference count is initialized to 1, indicating ownership by its |
| parent directory.</p></li> |
| <li><p>If an object with the same ID is already present, then one supersedes |
| the other. Accept the object with the greater sequence number and |
| discard the other.</p></li> |
| <li><p>If the object references a nonexistent inode (parent directory in the |
| case of an inode; owning file in the case of a data block), insert a |
| temporary “dummy” inode into the hash table so that inter-object |
| links can be maintained until the absent inode is eventually |
| restored. Dummy inodes are identified by a reference count of 0.</p></li> |
| <li><p>If a delete record for an inode is encountered, the inode’s parent |
| pointer is set to null to indicate that it should be removed from |
| RAM.</p></li> |
| </ol> |
| <p>If nffs encounters an object that cannot be identified (i.e., its magic |
| number is not valid), it scans the remainder of the flash area for the |
| next valid magic number. Upon encountering a valid object, nffs resumes |
| the procedure described above.</p> |
| <p>After all areas have been restored, a sweep is performed across the |
| entire RAM representation so that invalid inodes can be deleted from |
| memory.</p> |
| <p>For each directory inode:</p> |
| <ul class="simple"> |
| <li><p>If its reference count is 0 (i.e., it is a dummy), migrate its |
| children to the <em>/lost+found</em> directory, and delete it from the RAM |
| representation. This should only happen in the case of file system |
| corruption.</p></li> |
| <li><p>If its parent reference is null (i.e., it was deleted), delete it and |
| all its children from the RAM representation.</p></li> |
| </ul> |
| <p>For each file inode:</p> |
| <ul class="simple"> |
| <li><p>If its reference count is 0 (i.e., it is a dummy), delete it from the |
| RAM representation. This should only happen in the case of file |
| system corruption. (We should try to migrate the file to the |
| lost+found directory in this case, as mentioned in the todo section).</p></li> |
| </ul> |
| <p>When an object is deleted during this sweep, it is only deleted from the |
| RAM representation; nothing is written to disk.</p> |
| <p>When objects are migrated to the lost+found directory, their parent |
| inode reference is permanently updated on the disk.</p> |
| <p>In addition, a single scratch area is identified during the detection |
| process. The first area whose <em>fda_id</em> value is set to 0xff is |
| designated as the file system scratch area. If no valid scratch area is |
| found, the cause could be that the system was restarted while a garbage |
| collection cycle was in progress. Such a condition is identified by the |
| presence of two areas with the same ID. In such a case, the shorter of |
| the two areas is erased and designated as the scratch area.</p> |
| </div> |
| <div class="section" id="formatting"> |
| <h2>Formatting<a class="headerlink" href="#formatting" title="Permalink to this headline">¶</a></h2> |
| <p>A new nffs file system is created via formatting. Formatting is achieved |
| via the <a class="reference external" href="nffs_format.html">nffs_format()</a> function.</p> |
| <p>During a successful format, an area header is written to each of the |
| specified locations. One of the areas in the set is designated as the |
| initial scratch area.</p> |
| </div> |
| <div class="section" id="flash-writes"> |
| <h2>Flash writes<a class="headerlink" href="#flash-writes" title="Permalink to this headline">¶</a></h2> |
| <p>The nffs implementation always writes in a strictly sequential fashion |
| within an area. For each area, the system keeps track of the current |
| offset. Whenever an object gets written to an area, it gets written to |
| that area’s current offset, and the offset is increased by the object’s |
| disk size.</p> |
| <p>When a write needs to be performed, the nffs implementation selects the |
| appropriate destination area by iterating though each area until one |
| with sufficient free space is encountered.</p> |
| <p>There is no write buffering. Each call to a write function results in a |
| write operation being sent to the flash hardware.</p> |
| </div> |
| <div class="section" id="new-objects"> |
| <h2>New objects<a class="headerlink" href="#new-objects" title="Permalink to this headline">¶</a></h2> |
| <p>Whenever a new object is written to disk, it is assigned the following |
| properties:</p> |
| <ul class="simple"> |
| <li><p><em>ID:</em> A unique value is selected from the 32-bit ID space, as |
| appropriate for the object’s type.</p></li> |
| <li><p><em>Sequence number:</em> 0</p></li> |
| </ul> |
| <p>When a new file or directory is created, a corresponding inode is |
| written to flash. Likewise, a new data block also results in the writing |
| of a corresponding disk object.</p> |
| </div> |
| <div class="section" id="moving-renaming-files-and-directories"> |
| <h2>Moving/Renaming files and directories<a class="headerlink" href="#moving-renaming-files-and-directories" title="Permalink to this headline">¶</a></h2> |
| <p>When a file or directory is moved or renamed, its corresponding inode is |
| rewritten to flash with the following properties:</p> |
| <ul class="simple"> |
| <li><p><em>ID:</em> Unchanged</p></li> |
| <li><p><em>Sequence number:</em> Previous value plus one.</p></li> |
| <li><p><em>Parent inode:</em> As specified by the move / rename operation.</p></li> |
| <li><p><em>Filename:</em> As specified by the move / rename operation.</p></li> |
| </ul> |
| <p>Because the inode’s ID is unchanged, all dependent objects remain valid.</p> |
| </div> |
| <div class="section" id="unlinking-files-and-directories"> |
| <h2>Unlinking files and directories<a class="headerlink" href="#unlinking-files-and-directories" title="Permalink to this headline">¶</a></h2> |
| <p>When a file or directory is unlinked from its parent directory, a |
| deletion record for the unlinked inode gets written to flash. The |
| deletion record is an inode with the following properties:</p> |
| <ul class="simple"> |
| <li><p><em>ID:</em> Unchanged</p></li> |
| <li><p><em>Sequence number:</em> Previous value plus one.</p></li> |
| <li><p><em>Parent inode ID:</em> NFFS_ID_NONE</p></li> |
| </ul> |
| <p>When an inode is unlinked, no deletion records need to be written for |
| the inode’s dependent objects (constituent data blocks or child inodes). |
| During the next file system detection, it is recognized that the objects |
| belong to a deleted inode, so they are not restored into the RAM |
| representation.</p> |
| <p>If a file has an open handle at the time it gets unlinked, application |
| code can continued to use the file handle to read and write data. All |
| files retain a reference count, and a file isn’t deleted from the RAM |
| representation until its reference code drops to 0. Any attempt to open |
| an unlinked file fails, even if the file is referenced by other file |
| handles.</p> |
| </div> |
| <div class="section" id="writing-to-a-file"> |
| <h2>Writing to a file<a class="headerlink" href="#writing-to-a-file" title="Permalink to this headline">¶</a></h2> |
| <p>The following procedure is used whenever the application code writes to |
| a file. First, if the write operation specifies too much data to fit |
| into a single block, the operation is split into several separate write |
| operations. Then, for each write operation:</p> |
| <ol class="arabic simple"> |
| <li><p>Determine which existing blocks the write operation overlaps (n = |
| number of overwritten blocks).</p></li> |
| <li><p>If <em>n = 0</em>, this is an append operation. Write a data block with the |
| following properties:</p> |
| <ul class="simple"> |
| <li><p><em>ID:</em> New unique value.</p></li> |
| <li><p><em>Sequence number:</em> 0.</p></li> |
| </ul> |
| </li> |
| <li><p>Else <em>(n > 1)</em>, this write overlaps existing data.</p> |
| <ol class="arabic simple"> |
| <li><p>For each block in <em>[1, 2, … n-1]</em>, write a new block containing |
| the updated contents. Each new block supersedes the block it |
| overwrites. That is, each block has the following properties:</p> |
| <ul class="simple"> |
| <li><p><em>ID:</em> Unchanged</p></li> |
| <li><p><em>Sequence number:</em> Previous value plus one.</p></li> |
| </ul> |
| </li> |
| <li><p>Write the nth block. The nth block includes all appended data, if |
| any. As with the other blocks, its ID is unchanged and its |
| sequence number is incremented.</p></li> |
| </ol> |
| </li> |
| </ol> |
| <p>Appended data can only be written to the end of the file. That is, |
| “holes” are not supported.</p> |
| </div> |
| <div class="section" id="garbage-collection"> |
| <h2>Garbage collection<a class="headerlink" href="#garbage-collection" title="Permalink to this headline">¶</a></h2> |
| <p>When the file system is too full to accommodate a write operation, the |
| system must perform garbage collection to make room. The garbage |
| collection procedure is described below:</p> |
| <ul class="simple"> |
| <li><p>The non-scratch area with the lowest garbage collection sequence |
| number is selected as the “source area.” If there are other areas |
| with the same sequence number, the one with the smallest flash offset |
| is selected.</p></li> |
| <li><p>The source area’s ID is written to the scratch area’s header, |
| transforming it into a non-scratch ID. This former scratch area is |
| now known as the “destination area.”</p></li> |
| <li><p>The RAM representation is exhaustively searched for collectible |
| objects. The following procedure is applied to each inode in the |
| system:</p> |
| <ul> |
| <li><p>If the inode is resident in the source area, copy the inode record |
| to the destination area.</p></li> |
| <li><p>If the inode is a file inode, walk the inode’s list of data |
| blocks, starting with the last block in the file. Each block that |
| is resident in the source area is copied to the destination area. |
| If there is a run of two or more blocks that are resident in the |
| source area, they are consolidated and copied to the destination |
| area as a single new block (subject to the maximum block size |
| restriction).</p></li> |
| </ul> |
| </li> |
| <li><p>The source area is reformatted as a scratch sector (i.e., is is fully |
| erased, and its header is rewritten with an ID of 0xff). The area’s |
| garbage collection sequence number is incremented prior to rewriting |
| the header. This area is now the new scratch sector.</p></li> |
| </ul> |
| </div> |
| </div> |
| |
| |
| </div> |
| </div> |
| |
| </div> |
| </div> |
| </div> |
| <!-- ENDS CONTENT SECTION --> |
| </div> |
| <!-- ENDS .content --> |
| </div> |
| </div> |
| <footer> |
| <div class="container"> |
| <div class="row"> |
| <div class="col-xs-12"> |
| |
| <p class="copyright">Apache Mynewt is available under Apache License, version 2.0.</p> |
| |
| </div> |
| <div class="col-xs-12"> |
| <div class="logos"> |
| <img src="../../../../_static/img/asf_logo_wide_small.png" alt="Apache" title="Apache"> |
| <small class="footnote"> |
| Apache Mynewt, Mynewt, Apache, the Apache feather logo, and the Apache Mynewt project logo are either |
| registered trademarks or trademarks of the Apache Software Foundation in the United States and other countries. |
| </small> |
| <a href=""> |
| <img src="../../../../_static/img/add_to_slack.png" alt="Slack Icon" title="Join our Slack Community" /> |
| </a> |
| </div> |
| </div> |
| </div> |
| </div> |
| </footer> |
| </div> |
| <!-- ENDS #wrapper --> |
| |
| |
| |
| <script type="text/javascript"> |
| var DOCUMENTATION_OPTIONS = { |
| URL_ROOT:'../../../../', |
| VERSION:'latest', |
| COLLAPSE_INDEX:false, |
| FILE_SUFFIX:'.html', |
| HAS_SOURCE: true, |
| SOURCELINK_SUFFIX: '.txt', |
| LINK_SUFFIX: '.html' |
| }; |
| </script> |
| <script type="text/javascript" src="../../../../_static/jquery.js"></script> |
| <script type="text/javascript" src="../../../../_static/underscore.js"></script> |
| <script type="text/javascript" src="../../../../_static/doctools.js"></script> |
| <script type="text/javascript" src="../../../../_static/js/bootstrap-3.0.3.min.js"></script> |
| <script type="text/javascript" src="../../../../_static/js/affix.js"></script> |
| <script type="text/javascript" src="../../../../_static/js/main.js"></script> |
| |
| |
| |
| </body> |
| </html> |