<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>kyle kneitinger</title>
    <description>The personal homepage of Kyle Kneitinger: programmer, ops engineer, software synthesizer creator, musician &amp; friend of dogs.
</description>
    <link>kneit.in/</link>
    <atom:link href="kneit.in/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 04 Jan 2023 04:25:04 +0000</pubDate>
    <lastBuildDate>Wed, 04 Jan 2023 04:25:04 +0000</lastBuildDate>
    <generator>Jekyll v3.9.2</generator>
    
      <item>
        <title>Perplexing Config Lexeme Conflicts in the i3 Window Manager</title>
        <description>&lt;h2 id=&quot;the-issue&quot;&gt;The Issue&lt;/h2&gt;

&lt;p&gt;In February, 2016, Github user @BobuSumisu filed [a bug report about variable expansion collisions] (https://github.com/i3/i3/issues/2235) when one variable starts with the name of another variable.&lt;/p&gt;

&lt;p&gt;For example, say you had the following variable declarations, and an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt; line
to autolaunch a program at boot:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;set $shoopaloop HOWDY
set $shoop      i heard you have scrumptious seafood

exec $shoopaloop&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;One would expect this to evaluate to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec HOWDY&lt;/code&gt;, however it becomes
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec i heard you have scrumptious seafoodaloop&lt;/code&gt;.  Furthermore, given the configuration below:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;set $shoop      i heard you have scrumptious seafood
set $shoopaloop HOWDY

exec $shoopaloop&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The correct result of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec HOWDY&lt;/code&gt; is achieved.&lt;/p&gt;

&lt;h2 id=&quot;why-is-it-happening&quot;&gt;Why is it happening?&lt;/h2&gt;

&lt;h1 id=&quot;how-variables-are-parsed&quot;&gt;How Variables Are Parsed&lt;/h1&gt;

&lt;p&gt;To understand this bug, lets look at the &lt;a href=&quot;https://github.com/i3/i3/blob/23beac46b74fea66c7680443e384b7ed5c5933e6/src/config_parser.c#L819&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parse_file&lt;/code&gt;
function of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/config_parser.c&lt;/code&gt;&lt;/a&gt; from the commit right before I started working on this bug. This function encapsulates a lot of logic, but the only code that is applicable to this issue is where variables are read in and stored in a list (&lt;a href=&quot;https://github.com/i3/i3/blob/23beac46b74fea66c7680443e384b7ed5c5933e6/src/config_parser.c#L861&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/config_parser.c:861887&lt;/code&gt;&lt;/a&gt;), and when that list is iterated over, replacing all occurrences of the variable with its intended value (&lt;a href=&quot;https://github.com/i3/i3/blob/23beac46b74fea66c7680443e384b7ed5c5933e6/src/config_parser.c#L914&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/config_parser.c:914-944&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The grammar for a variable declaration is essentially:&lt;/p&gt;

&lt;p class=&quot;center&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vardecl -&amp;gt; &quot;set&quot; [ \t]+ \$[\S]+ [ \t]+ [\S]+ [ \t]*&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\$&lt;/code&gt; matches a literal $, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\S&lt;/code&gt; matches any
non-whitespace character, all tokens are space or tab separated, and all configuration lines are newline separated.&lt;/p&gt;

&lt;p&gt;When a variable is found in a config (indicated by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set&lt;/code&gt; keyword), a few
things happen in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parse_file&lt;/code&gt; to verify it is syntactically correct, and then the key and value are copied to a new &lt;code&gt;Variable&lt;/code&gt; struct which is added
&lt;b&gt;to the front of&lt;/b&gt; a list called variables (this is important in diagnosing the
issue)&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;    &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Variable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scalloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sstrdup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sstrdup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SLIST_INSERT_HEAD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DLOG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Got new variable %s = %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The code for substituting the variable key occurrences with their respective
values is shown here:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;cm&quot;&gt;/* Then, allocate a new buffer and copy the file over to the new one,
 * but replace occurrences of our variables */&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destwalk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smalloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stbuf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extra_bytes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;destwalk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stbuf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* Find the next variable */&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SLIST_FOREACH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next_match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strcasestr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;nearest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stbuf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SLIST_FOREACH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next_match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next_match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next_match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;nearest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nearest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* If there are no more variables, we just copy the rest */&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;strncpy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destwalk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stbuf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;destwalk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stbuf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destwalk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'\0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* Copy until the next variable, then copy its value */&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;strncpy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destwalk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;strncpy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destwalk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nearest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nearest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nearest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;destwalk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nearest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There is a lot of logistical stuff going on here that makes this code a little
hard to read, but the gist of this algorithm is to iterate through the list of
variables, hence the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SLIST_FOREACH&lt;/code&gt; macro, &lt;strong&gt;from front to back&lt;/strong&gt;, replacing
the nearest variables with their values.&lt;/p&gt;

&lt;h1 id=&quot;so-what-went-wrong&quot;&gt;So What Went Wrong?&lt;/h1&gt;

&lt;p&gt;It should be clear now that the variables are substituted in an order reversed
from the order in which they were declared.  With that in mind, let’s look at an
example configuration that generates a happy dialog of a fish connesior visiting
Los Angeles, and see how it is processed.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;set $foo  hey la
set $foodies i heard you have scrumptious seafood

exec echo $foo &amp;gt; ~/pete
exec echo $foodies &amp;gt;&amp;gt; ~/pete&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;After the variables are read in, we have a list &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;( $foodies-&amp;gt;i heard you have scrumptious seafood, $foo-&amp;gt;hey la )&lt;/code&gt;. When
the replacement starts, we find the instances of the first variable, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foodies&lt;/code&gt; and
essentially rewrite them in-place &lt;em&gt;(note: this doesn’t actually happen
“in-place”, but for the sake of simplicity in this article, it is a safe abstraction to make)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;At this stage, we have&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;exec echo $foo &amp;gt;  ~/pete
exec echo i heard you have scrumptious seafood &amp;gt;&amp;gt; ~/pete&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then, we move on to the next variable in the list, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foo&lt;/code&gt;, and replace its
occurences, resulting in the final configuration:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;exec echo hey la &amp;gt;  ~/pete
exec echo i heard you have scrumptious seafood &amp;gt;&amp;gt; ~/pete&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Checking the output of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat pete&lt;/code&gt; verifies this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/foodies.png&quot; alt=&quot;Pete the cat reflecting on Los Angeles' fine seafood dining&quot; style=&quot;display:block;margin:auto;width:80%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That seemed to work well, but what if the variables were declared the other way
around?&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;set $foodies i heard you have scrumptious seafood
set $foo  hey la

exec echo $foo &amp;gt; ~/pete
exec echo $foodies &amp;gt;&amp;gt; ~/pete&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This gives us the list of variables containing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;( $foo-&amp;gt;hey la, $foodies-&amp;gt;i heard you have scrumptious seafood )&lt;/code&gt;. Let’s
see what happens when we start to replace the keys with their values, again,
starting with the first variable in the list, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foo&lt;/code&gt;.  There is only one
instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foo&lt;/code&gt;, so there should be no issue.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;exec echo hey la &amp;gt; ~/pete
exec echo hey ladies &amp;gt;&amp;gt; ~/pete&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;Sigh&lt;/em&gt;, what does &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat pete&lt;/code&gt; have to say?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/ladies.png&quot; alt=&quot;Pete the cat is now a cat-calling creep&quot; style=&quot;display:block;margin:auto;width:80%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Woah woah woah…&lt;em&gt;what?&lt;/em&gt;. Our previously sweet Ol’ Pete fled, and became a deviously effete meat head! So yeah, turns out there were &lt;em&gt;two&lt;/em&gt; instances of
“&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foo&lt;/code&gt;”, but one was contained in “&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foodies&lt;/code&gt;”. At this point there’s nothing left to even do, we’ve
overwritten all of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foo&lt;/code&gt; substrings in the occurrences of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foodies&lt;/code&gt;, leaving
just the skeezy “hey ladies” behind.&lt;/p&gt;

&lt;h2 id=&quot;fixing-the-issue&quot;&gt;Fixing the Issue&lt;/h2&gt;

&lt;p&gt;So it seems that there are two ways we could possibly fix this, one based on
whitespace, and another more subtle way. If we looked for variable occurrences
that were complete tokens separated by whitespace, this would certainly solve
the issue…&lt;em&gt;at least in the above example&lt;/em&gt;.  However, it is extremely common,
and sometimes necessary to have variable references that are not separated by
whitespace, such as:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;bindsym $mod+1 workspace 1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Therefore, this option is not feasible in the current context.  So what does that leave
us?&lt;/p&gt;

&lt;h1 id=&quot;longest-lexeme-based-parsing&quot;&gt;Longest Lexeme Based Parsing&lt;/h1&gt;

&lt;p&gt;The concept of matching on longest lexeme means that given a variable reference
in which more than one substring starting at position 0 of the name is equal to a defined variable, then it should be replaced by the one with the longest length. For example, in our running example, a reference to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foodies&lt;/code&gt; should not be matched with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foo&lt;/code&gt;, because there is a longer declared variable that (exactly) matches it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foodies&lt;/code&gt;.  This is a pretty simple concept, and seems perfect for this scenario, but how exactly do we implement it in the context of this codebase?&lt;/p&gt;

&lt;h2 id=&quot;implementing-longest-lexeme-matching&quot;&gt;Implementing Longest Lexeme Matching&lt;/h2&gt;

&lt;p&gt;Recall the example configuration:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;set $foo  hey la
set $foodies i heard you have scrumptious seafood

exec echo $foo &amp;gt; ~/pete
exec echo $foodies &amp;gt;&amp;gt; ~/pete&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The variables in this configuration were correctly replaced, so what is there to
learn from it? Well, remember that the variable key/value pairs are added to the
Variables list at the front, which means that after all variable declarations were
parsed, the list had the longer lexeme at the head.  When variable replacement
started, it substituted all instances of the longest variable, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foodies&lt;/code&gt; leaving
none for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$foo&lt;/code&gt; to eagerly match on.&lt;/p&gt;

&lt;p&gt;In essence, if we can ensure that our complete list of Variables is sorted with
the longest key at the head and the shortest key at the tail, then we can ensure
that if variables are initial substrings of another, the longer variable
reference will have already been replaced, avoiding any conflict.&lt;/p&gt;

&lt;p&gt;The i3 codebase is very elegantly designed, and a lot of decisions are made to
maintain a cohesive style, convention, and design.  The initial implementation
of the Variables list uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;sys/queue.h&amp;gt;&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SLIST&lt;/code&gt; singly-linked list macros
as its structure.  Obviously it seemed like a perfect choice to insert in O(1)
on a tried and true data structure, but unfortunately this would have to be
changed in one of two way: either change the data structure and its &lt;em&gt;many&lt;/em&gt;
interactions to a better suited
sorted list, or use the SLIST and sacrifice O(1) in exchange for minimal codebase changes.  Since variable parsing only ever happens once, is relatively lightweight, and generally will not have a sufficiently large amount of variables to work with, I opted for the latter approach, wherein I compared the length of each new variable key to those of the current values; inserting before any keys that are shorter, or, simply sorting the list by length upon insertion. If the new variable key is longer than all others it becomes the new front of the list.  The new code is shown below, if you compare it to the original Variables insertion code, you will find that consists of actually very few changes.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Variable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scalloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Variable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sstrdup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sstrdup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* ensure that the correct variable is matched in case of one being
 * the prefix of another */&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SLIST_FOREACH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SLIST_INSERT_HEAD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SLIST_INSERT_AFTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;DLOG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Got new variable %s = %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;feel free to contact me with any comments or corrections via the options listed
at the bottom
of the page.&lt;/em&gt;&lt;/p&gt;

</description>
        <pubDate>Sat, 04 Mar 2017 00:00:00 +0000</pubDate>
        <link>kneit.in/2017/03/04/lexeme-conflicts-in-i3-config-parsing.html</link>
        <guid isPermaLink="true">kneit.in/2017/03/04/lexeme-conflicts-in-i3-config-parsing.html</guid>
        
        
      </item>
    
      <item>
        <title>Ensuring Beautiful Commits with rustfmt and Travis-CI</title>
        <description>&lt;h1 id=&quot;when-style-is-standardized-style-can-be-standard&quot;&gt;When Style is Standardized, Style Can Be Standard&lt;/h1&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/rust-lang-nursery&quot;&gt;rust-lang-nursery GitHub
organization&lt;/a&gt; is a fantastic group of
folks building tools for both working in, and working with, the Rust language.
One of these tools, &lt;a href=&quot;https://github.com/rust-lang-nursery/rustfmt&quot;&gt;rustfmt&lt;/a&gt;, is
quite helpful for maintaining a consistent code style throughout a project.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustfmt&lt;/code&gt; is pretty easy to work with.  Its default behavior, when executed on
a file, is to check it for anything that violates the style guide, and if
anything needs to be changed, backup the file with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bk&lt;/code&gt; suffix and silently
replace the original with the new formatting. If passed a valid rust file, it
returns 0 regardless of any changes being made. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustfmt&lt;/code&gt; only operates on a
single file, so if the current directory is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo&lt;/code&gt; project, a handy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo
fmt&lt;/code&gt; wrapper is provided that checks the entire project.&lt;/p&gt;

&lt;p&gt;There are a few nice ways to integrate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustfmt&lt;/code&gt; into your workflow, such as
&lt;a href=&quot;https://github.com/rust-lang/rust.vim#formatting-with-rustfmt&quot;&gt;vim plugins&lt;/a&gt;,
and git commit hooks.  Unfortunately, these depend on the individual developer
to use them, and thus do not enforce any project-wide policy.&lt;/p&gt;

&lt;h1 id=&quot;enter-travis&quot;&gt;Enter Travis&lt;/h1&gt;
&lt;p&gt;A lot of the themes brought up in the previous paragraph are pretty reminiscent
of testing: consistent when used, but largely reliant on the developer to use
them. Since Travis-CI was
already set up to run our tests and report upon failure, why not leverage that build process to handle
formatting?&lt;/p&gt;

&lt;p&gt;The default Rust &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.travis.yml&lt;/code&gt; provided by Travis CI is pretty straightforward:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rust&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;rust&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;stable&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;beta&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nightly&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;allow_failures&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rust&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nightly&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It relies on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo&lt;/code&gt; to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo build --verbose&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo test
--verbose&lt;/code&gt; during the implicit “script” phase of the build.
With just a few slight modifications, we can have format checking be a part of
this process.&lt;/p&gt;

&lt;h1 id=&quot;installing-the-tool&quot;&gt;Installing the Tool&lt;/h1&gt;

&lt;p&gt;Travis CI can has an optional “install” step (see &lt;a href=&quot;https://docs.travis-ci.com/user/customizing-the-build/#The-Build-Lifecycle&quot;&gt;Travis-CI: The Build
Lifecycle&lt;/a&gt;)
where we can have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo&lt;/code&gt; install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustfmt&lt;/code&gt; and then add it to our path. This step can take a very long time to finish due to compilation of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env_logger&lt;/code&gt; crate.  Thankfully Travis CI provides a mechanism for caching dependencies with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cache&lt;/code&gt; key. Putting what we have so far together, we get:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rust&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cargo&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;rust&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;stable&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;beta&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nightly&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;allow_failures&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rust&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nightly&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;(cargo install rustfmt || &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;PATH=$PATH:/home/travis/.cargo/bin&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Recall how I said that any non-zero return status breaks the build?  Well the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo install&lt;/code&gt; command returns non-zero if the desired package is already
installed (which means caching worked!). In order to get around this,
we massage it into returning true upon “failure” and thus continuing the build.&lt;/p&gt;

&lt;h1 id=&quot;checking-the-style&quot;&gt;Checking the Style&lt;/h1&gt;

&lt;p&gt;Now that our build context has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustfmt&lt;/code&gt;, we can have it run along with the
builds and test. It would be nice if we could have Travis-CI automatically and politely run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustfmt&lt;/code&gt; and fix any format-offensive code, but that would have to rewrite git history and is out of the question.  The best we can do is break the build upon a push or pull request, so that the developer knows to run the format command and recommit.&lt;/p&gt;

&lt;p&gt;Since any command with a non-zero return status breaks the build, we just need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo fmt&lt;/code&gt; to break at some point.
Again, the default behavior of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustfmt&lt;/code&gt;, even when wrapped
into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo&lt;/code&gt;, is to silently backup and reformat and offending code. We need a
way to detect when code is malformatted.  If we check the usage with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustfmt -h&lt;/code&gt;, we see the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--write-mode [replace|overwrite|display|diff|coverage|checkstyle]&lt;/code&gt; option.
The default setting is ‘replace’, but after trying them out, it seems the most
helpful mode for this purpose is ‘diff’.  It provides two things that work well
in this context: a non-zero return code upon code that doesn’t meet the style
guide, and output that makes it clear why the build broke. Since we’re using the project-aware &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo fmt&lt;/code&gt; wrapper, we cannot use
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustfmt&lt;/code&gt; flags directly until we insert a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--&lt;/code&gt; argument.&lt;/p&gt;

&lt;p&gt;At this point, we &lt;em&gt;could&lt;/em&gt; just put the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo fmt&lt;/code&gt; line in the “install step”,
it would certainly work as desired.  However, for the sake of clarity and
further modularity, let’s make the “script” step explicit now.  This means
inserting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo fmt&lt;/code&gt; command as well as the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;
commands as elements of the “script” key:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rust&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cargo&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;rust&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;stable&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;beta&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nightly&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;allow_failures&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rust&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nightly&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;(cargo install rustfmt || &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;PATH=$PATH:/home/travis/.cargo/bin&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cargo fmt -- --write-mode=diff&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cargo build --verbose&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cargo test --verbose&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And there you have it! I hope this helps you make the world a cleaner, more stylish place! If you have any feedback or questions, feel free to
comment below or reach out on twitter.&lt;/p&gt;
</description>
        <pubDate>Sat, 26 Nov 2016 00:00:00 +0000</pubDate>
        <link>kneit.in/2016/11/26/rustfmt-in-travisci.html</link>
        <guid isPermaLink="true">kneit.in/2016/11/26/rustfmt-in-travisci.html</guid>
        
        
      </item>
    
      <item>
        <title>SysEx-ploration</title>
        <description>&lt;style&gt;
@import 'https://fonts.googleapis.com/css?family=Press+Start+2P';
&lt;/style&gt;

&lt;h2 id=&quot;motivation&quot;&gt;Motivation&lt;/h2&gt;
&lt;p&gt;I recently purchased an Akai MPD32 MIDI controller from a swell person on
craiglist.  I wanted a relatively small controller that had enough knobs and
sliders to map to various parameters when sketching out ideas in
&lt;a href=&quot;http://chuck.stanford.edu/&quot;&gt;ChucK&lt;/a&gt; and &lt;a href=&quot;http://faust.grame.fr/&quot;&gt;Faust&lt;/a&gt;, and
this seemed like a perfect solution…&lt;em&gt;plus&lt;/em&gt; 4 banks of 16 pads with independent
pressure tracking!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/mpd_hello.gif&quot; alt=&quot;MPD32 waving hello!&quot; style=&quot;display:block;margin:auto;width:50%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I plugged it in, did a little pad tapping, a little knob turning, and a little
fader fading.  It was quite nice.  However, on every preset, each pad was set to
channel aftertouch and not polyphonic pressure sensing, and the pads were
arranged chromatically always starting at C.  No worries I thought, I could just
spend a few minutes changing those to my liking.  I was horribly mistaken.&lt;/p&gt;

&lt;p&gt;After entering edit mode, setting the pressure requires 6 button presses &lt;strong&gt;for each&lt;/strong&gt; pad.
With 64 pads to set, that’s 384 presses plus the bank change presses. And don’t
even get me started on changing the pads to diatonic from chromatic! There had
to be a better way. I noticed that the Akai website had a download for the Vyzex
preset editor, so I decided to give it a shot. I installed it in my Windows 10
VM, and redirected the MPD32 USB to it no problem.  I fired it up and was
greeted with a nice splash screen letting me know that the copyright date was
2007…and it showed.  Apparently in 2007, nobody could fathom that screens
would be above 1024x768, as the window is tiny and nonadjustable on a relatively
modern screen. But, if it would make editing easier, I could live with those. I
was hoping for bulk edit of pads, increment/decrement buttons for values, and
toggle switches for options.  I got text boxes.  Click a pad, then click the note
field then enter value in a text box.  Womp womp, this wasn’t a solution either.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/mpd_stern.gif&quot; alt=&quot;MPD32 arms-crossed, tapping foot&quot; style=&quot;display:block;margin:auto;width:50%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So when faced with only two systems that are difficult to interact with, what should
one chose?  A third!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/mpd_hokay.gif&quot; alt=&quot;MPD32 stroking beard and pondering about there being a better way.&quot; style=&quot;display:block;margin:auto;width:50%;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;idea&quot;&gt;Idea&lt;/h2&gt;
&lt;p&gt;I noticed that in the &lt;span style=&quot;font-family: 'Press Start 2P', cursive; font-size: 11px;padding:2px;&quot;&gt; GLOBAL&lt;/span&gt; menu on the MPD32, there is a &lt;span style=&quot;font-family: 'Press Start 2P', cursive; font-size: 11px;padding:2px;&quot;&gt; SysEx Tx&lt;/span&gt; option to
transmit the &lt;a href=&quot;http://www.indiana.edu/~emusic/etext/MIDI/chapter3_MIDI9.shtml&quot;&gt;MIDI &lt;strong&gt;Sys&lt;/strong&gt;tem
&lt;strong&gt;Ex&lt;/strong&gt;clusive&lt;/a&gt;
representation of a single preset to another device. I had encountered SysEx in
the past when I wanted to back up my Yamaha DX-7 patches to a computer, but
knowing nothing about its data representation, ‘twas a pure mystery to me back then.
Now, equipped with 3 years of CS knowledge, I figured I could certainly grok
the format enough to write my own editor…and that is just what I did!&lt;/p&gt;

&lt;h2 id=&quot;the-system-exclusive-specification&quot;&gt;The System Exclusive Specification&lt;/h2&gt;
&lt;p&gt;Since I was going to be extracting meaning from a SysEx dump, it would probably
be a good idea to see exactly what the SysEx specification is. I referred to a
couple of sites, and learned the entire specification in about 20 seconds.  I’m
not saying that to sound boastful, its just that the entire specification, for
better or for worse is:&lt;/p&gt;

&lt;table style=&quot;padding-left:100px;padding-right:100px;font-size:12px;&quot;&gt;
&lt;tr style=&quot;background: #CCC;&quot;&gt;
&lt;td&gt; 0xF0 &lt;/td&gt;
&lt;td&gt; 0xXX, 0xYY, ... &lt;/td&gt;
&lt;td&gt; 0xF7 &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt; SysEx Begin &lt;/td&gt;
&lt;td&gt; Any amount of data arranged in any way &lt;/td&gt;
&lt;td&gt; SysEx End &lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Luckily, the MIDI Manufacturer’s Association adopted the following convention to
specify which device a message is intended for.&lt;/p&gt;

&lt;table style=&quot;font-size:12px;&quot;&gt;
&lt;tr style=&quot;background: #CCC;&quot;&gt;
&lt;td&gt; 0xF0 &lt;/td&gt;
&lt;td&gt; 0xMM &lt;/td&gt;
&lt;td&gt; 0xMM &lt;/td&gt;
&lt;td&gt; 0xMM &lt;/td&gt;
&lt;td&gt; 0xPP &lt;/td&gt;
&lt;td&gt; 0xXX, 0xYY, ... &lt;/td&gt;
&lt;td&gt; 0xF7 &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt; SysEx Begin &lt;/td&gt;
&lt;td colspan=&quot;3&quot;&gt; Manufacturer ID &lt;/td&gt;
&lt;td&gt; Product ID &lt;/td&gt;
&lt;td&gt; Any amount of data arranged in any way &lt;/td&gt;
&lt;td&gt; SysEx End &lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;It also seems that usually the first data byte will provide some sort of
opcode-like purpose, declaring the intent of the message.  With all this in
mind, lets look at the MPD32 data.&lt;/p&gt;

&lt;h2 id=&quot;dumping-the-data&quot;&gt;Dumping the Data&lt;/h2&gt;

&lt;p&gt;I used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;amidi&lt;/code&gt; from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alsa-utils&lt;/code&gt; suite of programs to accomplish most of this task. First, I needed to find the device ID, which is easily found with the list (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-l&lt;/code&gt;) flag&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;amidi &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;
Dir Device    Name
IO  hw:1,0,0  Akai MPD32 MIDI 1
IO  hw:1,0,1  Akai MPD32 MIDI 2
I   hw:1,0,2  Akai MPD32 MIDI 3&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The MPD32 is quite flexible, so it provides 3 virtual devices, yet as it turns out (from trial and error),
only the first one transmits and receives SysEx messages, so I used that one from there on out.  Note the
“Device” field lists it as “hw:1,0,0”, which is the value to be used in the
upcoming steps when interacting with the device.  Next, I used the port and
receive flags for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;amidi&lt;/code&gt; to store the data to a file called mpd_dump.syx.
First, I navigated to the &lt;span style=&quot;font-family: 'Press Start 2P', cursive; font-size: 11px;padding:2px;&quot;&gt; SysEx Tx&lt;/span&gt; page so as to not clutter the dump with
realtime button press info.  Then after running the following command, I pressed the
enter knob to initiate the transfer, and pressed Ctrl+C to stop receiving:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;$ amidi -p hw:1,0,0 -r mpd_dump.syx
^C
1033 bytes read&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/img/mpd_xfer.gif&quot; alt=&quot;MPD32 transferring its preset data&quot; style=&quot;display:block;margin:auto;width:50%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Wow. So 1033 bytes. That is a fairly hefty amount of data to sift through, so
first I need to verify that it was actually the preset dump.  First, a simple
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hexdump&lt;/code&gt; on the SysEx file, confirms that it starts with an 0xF0, and ends with
an 0xF7 &lt;em&gt;(remember, little endian!)&lt;/em&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;hexdump mpd_dump.syx
47f0 6c00 0810 1e01 6547 656e 6972 2063
7800 0401 3201 013a 0204 323c 0300 0101
0002 0000 0000 0000 0000 0000 0003 0024
0001 0000 0003 0025 0001 0000 0003 0026
&lt;span class=&quot;c&quot;&gt;# many lines omitted for space!&lt;/span&gt;
6f00 0001 0000 0000 0170 0000 0000 7100
0001 0000 0100 7f00 0000 0b01 7f00 0100
0040 0000 4001 0000 00f7               &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Furthermore, the preset was called “Generic”. Assuming that the name is stored
in an ASCII-like format,  the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strings&lt;/code&gt; command would find it.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;strings mpd_dump.syx
Generic&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Bingo! So I knew I was definitely dealing with the correct data, but how could I
know which of the 1033 bytes did what?&lt;/p&gt;

&lt;h2 id=&quot;deciphering-the-data&quot;&gt;Deciphering the Data&lt;/h2&gt;
&lt;p&gt;The most obvious way to determine which byte was associated with which
parameter,  was to change some
parameters and track the deltas between the Generic dump and the new ones. But again, I’m doing this all to
&lt;em&gt;save work&lt;/em&gt; not make more and that sounds like quite an undertaking!  Since I was tracking changes or &lt;strong&gt;diff&lt;/strong&gt;erences,
perhaps there was a standard tool I could use to, such as…&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python&lt;/code&gt;! Oh yeah, it
would probably be helpful to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diff&lt;/code&gt; too.  I wrote a script that encapsulated
the following flow:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Write a program that takes a raw binary SysEx message, and outputs it as hex
values, one per line.
    &lt;ol&gt;
      &lt;li&gt;Run it on the Generic mpd_dump.syx file&lt;/li&gt;
      &lt;li&gt;Run it on the modified mpd_edit.syx file&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diff&lt;/code&gt; the two outputs to calculate any lines that have changed. Because
there is only one byte per line, the line number - 1 is the byte offset in
 the SysEx dump&lt;/li&gt;
  &lt;li&gt;Prompt for a comment on what I changed&lt;/li&gt;
  &lt;li&gt;Append the byte number, the generic value, the new value, and the comment to
a .csv file&lt;/li&gt;
  &lt;li&gt;Assess if a coffee refill is needed&lt;/li&gt;
  &lt;li&gt;Repeat steps 1.2-5 until all desired parameters are mapped.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It became clear pretty quickly that the data was arranged in a &lt;strong&gt;very&lt;/strong&gt;
intuitive manner!  For example, I began with the 1st pad, and changed it’s menu
options one by one, and found that they are arranged in the dump in the same
order they appear on the screen.  Furthermore, the biggest relief was to find
that the data for all pads is stored identically and sequentially!  After
mapping the first pad (8 bytes), and the second pad, my prediction of where the
64th pad should begin was spot on: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PAD_BEGIN + (PAD_WIDTH * padNum)&lt;/code&gt;, where
the respective values are 0x2C, 0x08, and the pad number desired (zero-indexed).
I ended up with the following map of how pad parameters are stored:&lt;/p&gt;

&lt;table style=&quot;text-align:left;font-size:12px;&quot;&gt;
&lt;tr style=&quot;background: #CCC;font-weight: bold;&quot;&gt;
&lt;td&gt;Offset from 0x2C + (0x8 * Pad #)&lt;/td&gt;
&lt;td&gt;Parameter&lt;/td&gt;
&lt;td&gt;Values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x00&lt;/td&gt;
&lt;td&gt;Mode&lt;/td&gt;
&lt;td&gt;3:Note, 4:Program Change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x01&lt;/td&gt;
&lt;td&gt;Channel&lt;/td&gt;
&lt;td&gt;0:Common. 1-3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x02&lt;/td&gt;
&lt;td&gt;Note Number&lt;/td&gt;
&lt;td&gt;0-127&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x03&lt;/td&gt;
&lt;td&gt;Trigger mode&lt;/td&gt;
&lt;td&gt;0:Momentary 1, Toggle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x04&lt;/td&gt;
&lt;td&gt;Pressure Mode&lt;/td&gt;
&lt;td&gt;0:Off, 1:Channel Press, 2: Polyphonic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x05&lt;/td&gt;
&lt;td&gt;Program Num&lt;/td&gt;
&lt;td&gt;0-127&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x06&lt;/td&gt;
&lt;td&gt;Bank MSB&lt;/td&gt;
&lt;td&gt;0-127&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0x07&lt;/td&gt;
&lt;td&gt;Bank LSB&lt;/td&gt;
&lt;td&gt;0-127&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;The layout was similar for the knobs, switches and faders, each of which have 8
physical * 3 banks = 24 virtual units.  They all varied in the different ways
they can be configured, some controls requiring 7 bytes, some only 5.
Additionally there are a few one-off settings such as the note-repeat
(drum-roll) button, tempo, playback control, etc.  The final mapping of all
parameters that were physically modifiable is:&lt;/p&gt;

&lt;table style=&quot;text-align:left;font-size:12px;&quot;&gt;
    &lt;tr style=&quot;background: #CCC; font-weight: bold;&quot;&gt;
        &lt;td&gt;Byte&lt;/td&gt;
        &lt;td&gt;Field&lt;/td&gt;
        &lt;td&gt;Valid Values&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0000&lt;/td&gt;
        &lt;td&gt;SysEx Begin&lt;/td&gt;
        &lt;td&gt;(0xF0)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0001&lt;/td&gt;
        &lt;td&gt;Mfg Id&lt;/td&gt;
        &lt;td&gt;(0x47)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0002&lt;/td&gt;
        &lt;td&gt;Mfg Id&lt;/td&gt;
        &lt;td&gt;(0x00)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0003&lt;/td&gt;
        &lt;td&gt;Mfg Id&lt;/td&gt;
        &lt;td&gt;(0x6C)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0004&lt;/td&gt;
        &lt;td&gt;Prod Id&lt;/td&gt;
        &lt;td&gt;(0x10)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0007&lt;/td&gt;
        &lt;td&gt;Preset number&lt;/td&gt;
        &lt;td&gt;1-30&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0008&lt;/td&gt;
        &lt;td&gt;Preset Name Char 0&lt;/td&gt;
        &lt;td&gt;Pretty much ASCII&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;…&lt;/td&gt;
        &lt;td&gt;…&lt;/td&gt;
        &lt;td&gt;…&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x000F&lt;/td&gt;
        &lt;td&gt;Preset Name Char 7&lt;/td&gt;
        &lt;td&gt;Pretty much ASCII&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0010&lt;/td&gt;
        &lt;td&gt;(Presumed null char for string)&lt;/td&gt;
        &lt;td&gt;(0x00)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0011&lt;/td&gt;
        &lt;td&gt;Tempo&lt;/td&gt;
        &lt;td&gt;30-300&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0012&lt;/td&gt;
        &lt;td&gt;Time Divide Mode&lt;/td&gt;
        &lt;td&gt;0:Momentary 1, Toggle&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0013&lt;/td&gt;
        &lt;td&gt;Time Divide Amount&lt;/td&gt;
        &lt;td&gt;0:1/4-Note-7:1/32T-Note&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0014&lt;/td&gt;
        &lt;td&gt;Note Repeat Mode&lt;/td&gt;
        &lt;td&gt;0:Momentary 1, Toggle&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0015&lt;/td&gt;
        &lt;td&gt;Note Repeat Gate&lt;/td&gt;
        &lt;td&gt;0-99&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0016&lt;/td&gt;
        &lt;td&gt;Note Repeat Swing&lt;/td&gt;
        &lt;td&gt;50-75&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0020&lt;/td&gt;
        &lt;td&gt;Transport Mode&lt;/td&gt;
        &lt;td&gt;0:MMC, 1:MIDI. 2:MMC/MIDI, 3:CTRL&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #9E9;&quot;&gt;
        &lt;td&gt;0x002C&lt;/td&gt;
        &lt;td&gt;Pad Mode&lt;/td&gt;
        &lt;td&gt;3:Note, 4:Program Change&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #9E9;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x01&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Channel&lt;/td&gt;
        &lt;td&gt;0:Common. 1-3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #9E9;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x02&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Note Number&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #9E9;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x03&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Trigger mode&lt;/td&gt;
        &lt;td&gt;0:Momentary 1, Toggle&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #9E9;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x04&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Pressure Mode&lt;/td&gt;
        &lt;td&gt;0:Off, 1:Channel Press, 2: Polyphonic&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #9E9;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x05&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Program Num&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #9E9;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x06&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Bank MSB&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #9E9;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x07&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Bank LSB&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #D8A;&quot;&gt;
        &lt;td&gt;0x022C&lt;/td&gt;
        &lt;td&gt;Knob Mode&lt;/td&gt;
        &lt;td&gt;0:Control Change, 1:Aftertouch. 2: Inc/Dec&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #D8A;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x01&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Channel&lt;/td&gt;
        &lt;td&gt;0:Common. 1-3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #D8A;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x02&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Control Change Number&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #D8A;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x03&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;CC/Aftertouch Min&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #D8A;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x04&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;CC/Aftertouch Max&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #D8A;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x05&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;NRPN Left&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #D8A;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x06&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;NRPN Right&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #FE8;&quot;&gt;
        &lt;td&gt;0x02D4&lt;/td&gt;
        &lt;td&gt;Fader Mode&lt;/td&gt;
        &lt;td&gt;0:Control Change, 1:Aftertouch&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #FE8;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x01&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Channel&lt;/td&gt;
        &lt;td&gt;0:Common. 1-3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #FE8;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x02&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Control Change Number&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #FE8;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x03&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;CC/Aftertouch Min&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #FE8;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x04&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;CC/Aftertouch Max&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #AEE;&quot;&gt;
        &lt;td&gt;0x034C&lt;/td&gt;
        &lt;td&gt;Switch Mode&lt;/td&gt;
        &lt;td&gt;0:Control Change, 1:Program Change&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #AEE;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x01&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Channel&lt;/td&gt;
        &lt;td&gt;0:Common. 1-3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #AEE;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x02&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Control Change Number&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #AEE;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x03&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Trigger mode&lt;/td&gt;
        &lt;td&gt;0:Momentary 1, Toggle&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #AEE;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x04&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Program Number&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #AEE;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x05&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Bank MSB&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr style=&quot;background: #AEE;&quot;&gt;
        &lt;td&gt;&lt;i&gt;+0x06&lt;/i&gt;&lt;/td&gt;
        &lt;td&gt;Bank LSB&lt;/td&gt;
        &lt;td&gt;0-127&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;0x0408&lt;/td&gt;
        &lt;td&gt;SysEx End&lt;/td&gt;
        &lt;td&gt;(0xF7)&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;

&lt;p style=&quot;text-align:center;font-style:italic;margin-bottom:-2px;&quot;&gt;Color key:&lt;/p&gt;
&lt;table style=&quot;font-size:10px;&quot;&gt;
&lt;tr&gt;
&lt;td style=&quot;background:#9E9;&quot;&gt;Pad data block&lt;/td&gt;
&lt;td style=&quot;background:#D8A;&quot;&gt;Knob data block&lt;/td&gt;
&lt;td style=&quot;background:#FE8;&quot;&gt;Fader data block&lt;/td&gt;
&lt;td style=&quot;background:#AEE;&quot;&gt;Switch data block&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;working-with-the-data&quot;&gt;Working with the Data&lt;/h2&gt;
&lt;p&gt;So there it was, my very own preset map! Now I had the key to whatever
configuration I could dream up!  I did a quick test to verify that uploading a
.syx works, by changing the mpd_edit.syx’s name bytes from “Generic” to
“Doop” (with spaces filling in the remaining 4 bytes) in the bless hex editor&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/bless.png&quot; alt=&quot;Changing the name to Doop in bless&quot; style=&quot;display:block;margin:auto;width:100%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;and then uploaded it with our good pal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;amidi&lt;/code&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;$ amidi -p hw:1,0,0 -s mpd_edit.syx&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and what do you know…it worked!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/doop.png&quot; alt=&quot;MPD32 showing the name Doop&quot; style=&quot;display:block;margin:auto;width:60%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With basic editing and uploading confirmed to be working, I could finally code
any patch I wanted: polyphonic pressure, diatonic scales, momentary
buttons…anything!  I have plans to develop this into a GTK app, ideally one
that can edit many different MIDI controllers, but below is a snippet
demonstrating low-level manipulation of the .syx file. Then I can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aplay&lt;/code&gt;,
just like above, to upload this patch to the MPD32.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;PAD_BASE&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x2C&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PAD_WIDTH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x8&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PAD_MAX&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x40&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;SWITCH_BASE&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x34C&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SWITCH_WIDTH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x7&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;SWITCH_MAX&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x18&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;WHOLE&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;HALF&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MAJOR&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WHOLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WHOLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HALF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WHOLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WHOLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WHOLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HALF&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Load base file into list of bytes
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mpd_dump.syx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'rb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;rawSysex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sysex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unpack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;B&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawSysex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawSysex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Set all pads to polyphonic aftertouch!
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PAD_MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sysex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PAD_BASE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PAD_WIDTH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Arrange the pads to the E major scale!  (MIDI note 28 = E1)
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PAD_MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sysex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PAD_BASE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PAD_WIDTH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;base&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAJOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Set all of the buttons to momentary!
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SWITCH_MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sysex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SWITCH_BASE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SWITCH_WIDTH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;# Write modified values to new file
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mpd_edit.syx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'wb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sysex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;moving-forward&quot;&gt;Moving Forward&lt;/h2&gt;

&lt;p&gt;Like I said, my next step is turn this into an extensible GUI app, so I would
have to take care to design it in such a way that facilitates easy community
creation of other controllers. Furthermore, the very offline workflow of receive
.syx, modify .syx, send .syx could be replaced by dynamic, realtime editing,
including synchronized editing in the device and the app.  I know that the MPD
transmits and receives these events, using much smaller 10-byte realtime
messages reflecting just the edited parameter. This workflow is a bit nicer in
that changes are instantly synced to the device, and can be experimented with on
the fly, while the device is in use even.&lt;/p&gt;

&lt;p&gt;This exercise taught me a lot, and I look forward to seeing where this
goes…not to mention making some quite expressive instruments and grooving with
them!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/mpd_play.gif&quot; alt=&quot;MPD32 playing itself&quot; style=&quot;display:block;margin:auto;width:50%;border: 2px solid #000;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;feel free to contact me with any comments or corrections via the options listed
at the bottom
of the page.&lt;/em&gt;&lt;/p&gt;

</description>
        <pubDate>Mon, 27 Jun 2016 00:00:00 +0000</pubDate>
        <link>kneit.in/2016/06/27/sys-exploration.html</link>
        <guid isPermaLink="true">kneit.in/2016/06/27/sys-exploration.html</guid>
        
        
      </item>
    
      <item>
        <title>Picnote</title>
        <description>&lt;h1 id=&quot;a-tool-is-born-from-horrible-ui&quot;&gt;A Tool is Born from Horrible UI&lt;/h1&gt;
&lt;p&gt;Over the summer I was enrolled in a introductory level chemistry class to
fulfill a miscellaneous lab science credit.  Overall, I had a great time in
this class with the exception of the web app created by one of the major
textbook corporations that was required to complete the homework assignments.
Though there were certain questions that greatly benefited from an interactive
style, by and large, the app was a hindrance more than an aid.  The app would
reset and lose all of your progress if you were to use the browser navigation
buttons, and only one tab of the app could be open at a time.  These factors
become extremely frustrating when certain questions depended on the diagrams
featured in earlier ones’.  Because one habitually, and thus frequently uses the
browser buttons to go back, I lost my progress well into an assignment.&lt;/p&gt;

&lt;p&gt;To prevent this from happening again (as well as take a break from reaction
balancing) I whipped up a little solution to take a snapshot of an area of the
screen and display it in a small window on top of all others.  I call this
little utility &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;picnote&lt;/code&gt; and it has already proven to be very helpful for
carrying little bits of reference around my window manager.&lt;/p&gt;

&lt;p&gt;Here is a little gif of it in action:&lt;/p&gt;

&lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/img/picnote.gif&quot; alt=&quot;picnote demo&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;components-of-picnote&quot;&gt;Components of picnote&lt;/h1&gt;
&lt;p&gt;There are three programs that come together to make picnote:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Scrot&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scrot&lt;/code&gt;&lt;/a&gt;: a screenshot utility
with a command line interface for easy scripting.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://feh.finalrewind.org/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feh&lt;/code&gt;&lt;/a&gt;: a lightweight, minimal image viewer.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://i3wm.org/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i3&lt;/code&gt;&lt;/a&gt;: my window manager of choice.  Easy to configure and
very powerful.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;how-it-works&quot;&gt;How it works&lt;/h1&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;picnote&lt;/code&gt; is actually quite simple.  First, the following shell script handles
capturing the image and opening in feh.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;PIC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/tmp/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$RANDOM&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.png&quot;&lt;/span&gt;

scrot &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PIC&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; feh &lt;span class=&quot;nt&quot;&gt;--title&lt;/span&gt; feh:&lt;span class=&quot;nv&quot;&gt;$PIC&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PIC&lt;/span&gt; &amp;amp;
&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PIC&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Line number 3 sets up a variable to store the name and path of image to be a
randomly generated .png residing in /tmp/.  Line 5 begins by executing the image
capture with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scrot -s&lt;/code&gt;.  The “-s” flag indicates that upon execution, the next
mouse drag will select the box to be captured.  The image is saved as the file
from line 3.  Upon successful execution, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feh&lt;/code&gt; sets its title as “feh:&amp;lt;image
name from line 3&amp;gt;” in order to be easily identifiable as a picnote window for
the i3 rule I’ll show soon, and then opens the image.  Line 6 simply deletes
the image which is fine to do once feh has opened it.&lt;/p&gt;

&lt;p&gt;Because I primarily use i3 as a tiling window manager, and such have set that as
its default behavior, I needed to create a configuration rule to set all windows
titled feh:/tmp/… to be floating (which have the additional benefit of
displaying on top of all tiled windows).&lt;/p&gt;

&lt;p&gt;The .i3/config line to accomplish this is&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;for_window &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;feh:/tmp/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\*&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; floating &lt;span class=&quot;nb&quot;&gt;enable&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;which simply expresses that all windows whose title matches that regular
expression should be put into floating mode upon creation.&lt;/p&gt;

&lt;h1 id=&quot;usage&quot;&gt;Usage&lt;/h1&gt;
&lt;p&gt;It would be a pain to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;picnote&lt;/code&gt; from the command line every time it is
needed, so I simply mapped it to an unused media key on my ThinkPad for easy
use.  Once it is launched, you can close the image with Esc, Del, q, or your
window managers keybinding for closing windows.&lt;/p&gt;

&lt;p&gt;Thank you for reading this and I hope you enjoy picnote if it is a solution for
a problem you are having!&lt;/p&gt;
</description>
        <pubDate>Fri, 18 Sep 2015 00:00:00 +0000</pubDate>
        <link>kneit.in/2015/09/18/picnote.html</link>
        <guid isPermaLink="true">kneit.in/2015/09/18/picnote.html</guid>
        
        
      </item>
    
      <item>
        <title>Btrfs on dm-crypt</title>
        <description>&lt;p&gt;Two of the most important and exciting tools for data storage that Linux has
to offer are disk encryption via dm-crypt, and built-in RAID,
&lt;a href=&quot;https://en.wikipedia.org/wiki/Copy-on-write#Copy-on-write_in_storage_media&quot;&gt;copy-on-write&lt;/a&gt;,
and other modern storage features via the Btrfs file system.  Both dm-crypt and
Btrfs have volumes of information written on them, however, when I bought a
shiny (on second thought, matte)  new ThinkPad 450s with 3 SSDs and set out to implement a system utilizing
both features, I had trouble connecting the information into a cohesive process.
 I learned a lot from this installation and hope this tutorial will be helpful in providing
a clear guide to a system with Btrfs RAID on top of dm-crypt.&lt;/p&gt;

&lt;h3 id=&quot;who-this-tutorial-is-for&quot;&gt;Who This Tutorial is For&lt;/h3&gt;
&lt;p&gt;This article is written with a few assumptions.  First, I assume that you
have a general comfortability with the command line, as these steps have
to be executed in a manual or “guided” CLI installation.  Second, this tutorial
is written for Arch Linux, however, the majority of the article will remain
distro-agnostic as the main steps only concern disk setup and the boot process.&lt;/p&gt;

&lt;h3 id=&quot;brief-description-of-the-setup&quot;&gt;Brief Description of the Setup&lt;/h3&gt;
&lt;p&gt;To effectively describe how data is organized, processed, and stored, we will
follow a file as it traverses down the data hierarchy to the disk.
First, when we write a file, it will be stored inside of a directory, such as
/etc/ or /home/sarah/ (indicated in the figure below as orange).  That directory resides in a 
 &lt;em&gt;subvolume&lt;/em&gt; (kind of like a virtual partition, or LVM volume), such
as “home”, “var”, or “etc” (magenta).  Each subvolume is a member of the Btrfs pool (lavender), which
consists of one or more device mappings (blue).  These devices are dm-crypt containers that act
as an intermediary step, encrypting the data before finally writing it to a particular hard drive (green).&lt;/p&gt;

&lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;/img/btrfsdmcrypt.png&quot; alt=&quot;Data hierarchy diagram&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;attribution&quot;&gt;Attribution&lt;/h3&gt;
&lt;p&gt;Much of the information from this article was stitched together from the &lt;a href=&quot;https://wiki.archlinux.org/&quot;&gt;Arch
Wiki&lt;/a&gt;, primarily the page on
&lt;a href=&quot;https://wiki.archlinux.org/index.php/Btrfs&quot;&gt;Btrfs&lt;/a&gt;, and the series on
&lt;a href=&quot;https://wiki.archlinux.org/index.php/Dm-crypt&quot;&gt;dm-crypt&lt;/a&gt;. The use of the
encrypt2 hook comes from a
&lt;a href=&quot;https://aur.archlinux.org/packages.php?ID=56476&quot;&gt;comment by AUR user benke&lt;/a&gt;,
Btrfs subvolume organization and mounting the Btrfs top level subvolume to
/var/lib/btrfs_root comes from the Arch Forums user
&lt;a href=&quot;https://bbs.archlinux.org/viewtopic.php?pid=1378861#p1378861&quot;&gt;WorMzy’s excellent post&lt;/a&gt;.
Additional Btrfs insight and clarification was gained from Marc Merlin’s
Linuxcon 2014 talk &lt;a href=&quot;http://marc.merlins.org/perso/btrfs/post_2014-05-21_My-Btrfs-Talk-at-Linuxcon-JP-2014.html&quot;&gt;&lt;em&gt;“Why you should consider using Btrfs, real COW snapshots
and file level incremental server OS upgrades.”&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;contents&quot;&gt;Contents&lt;/h2&gt;
&lt;hr /&gt;
&lt;hr /&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;#preparation&quot;&gt;Preparation&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#securely-wiping-the-disks&quot;&gt;Securely Wiping the Disks&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#planning-your-disk-layout&quot;&gt;Planning Your Disk Layout&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#partitioning-the-disks&quot;&gt;Partitioning the Disks&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#setting-up-dm-crypt&quot;&gt;Setting Up dm-crypt&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#creating-the-containers&quot;&gt;Creating the Containers&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#opening-the-containers&quot;&gt;Opening the Containers&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#creating-the-filesystems&quot;&gt;Creating the Filesystems&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#mounting-the-btrfs-filesystem-and-creating-subvolumes&quot;&gt;Mounting the Btrfs Filesystem and Creating
Subvolumes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#assembling-the--filesystem&quot;&gt;Assembling the / Filesystem&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#a-bit-about-btrfs-mount-options&quot;&gt;A Bit about Btrfs Mount Options&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#mounting-the-subvolumes-and-boot-partition&quot;&gt;Mounting the Subvolumes and /boot Partition&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#proceeding-with-installation&quot;&gt;Proceeding with Installation&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#generating-the-fstab-file&quot;&gt;Generating the fstab file&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#setting-up-initramfs&quot;&gt;Setting Up initramfs&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#creating-additional-encrypt-hooks&quot;&gt;Creating Additional encrypt Hooks&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#editing-etcmkinitcpioconf&quot;&gt;Editing /etc/mkinicpio.conf&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#setting-the-boot-options&quot;&gt;Setting the Boot Options&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#post-installation-considerations-and-niceties&quot;&gt;Post Installation Considerations and Niceties&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#a-warning-about-kernel-updates&quot;&gt;A Warning About Kernel Updates&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#snapshots&quot;&gt;Snapshots&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#keyfiles-and-additional-luks-keys&quot;&gt;Keyfiles &amp;amp; Additional LUKS Keys&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#encrypted-swap&quot;&gt;Encrypted Swap&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;preparation&quot;&gt;Preparation&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;Once you’re booted into a live environment, you’ll want to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsblk&lt;/code&gt; to find
and verify the disks you will be working with based on their size and any
partitions on them. Since the naming of devices can
change, you should double check the devices if you reboot. If there are &lt;strong&gt;any&lt;/strong&gt;
partitions or disks that have important data on them, back them up now.&lt;/p&gt;

&lt;h3 id=&quot;securely-wiping-the-disks&quot;&gt;Securely Wiping the Disks&lt;/h3&gt;
&lt;p&gt;To ensure that no sensitive data remains on hard drive, the disk will be
overwritten with random data. To accomplish this, we will open temporary
encrypted containers on each disk and write data to the entire disks.&lt;/p&gt;

&lt;p&gt;First, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cryptsetup&lt;/code&gt; to set up the containers on each device to be
used. The following command creates a dm-crypt plain container for each partition (specified as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev/sdXY&lt;/code&gt;) or
disk (specified as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sdX&lt;/code&gt;), named
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;container_0&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;container_1&lt;/code&gt;, …, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;container_n&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# cryptsetup open --type plain /dev/sdXY container_n&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now that an encrypted container has been opened on each disk, &lt;em&gt;any&lt;/em&gt; data
written to it will appear to be suitably random.  Therefore, you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/zero&lt;/code&gt; to
generate data as it is much faster than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/urandom&lt;/code&gt;, which on my machine is ~100 times
slower to read from. To overwrite to disk, simply execute the following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dd&lt;/code&gt;
command for each dm-crypt container you created.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# dd if=/dev/zero of=/dev/mapper/container_XY&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Since writing a whole disk can take a long time, you can speed the process up by
opening a new virtual terminal by pressing &lt;strong&gt;Ctrl+Alt+F2&lt;/strong&gt; and then executing
the command on another disk. Note, it would not be effective to do this on two
partitions of the same disk, as the bus of one device would be handling both loads.&lt;/p&gt;

&lt;p&gt;If you are curious as to how the operation is progressing, simply
open a new virtual terminal and execute&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# watch -n 20 kill -USR1 $(pidof dd)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then switch back to the virtual console of the dd command for updates every 20
seconds. When the operation is done, switch back to the terminal you executed
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;watch&lt;/code&gt; in and press Ctrl+c.&lt;/p&gt;

&lt;p&gt;Once every dd operation has completed, you can proceed on to the actual
installation. I find that the easiest way to clean up the installation
environment of the containers is to simply reboot.&lt;/p&gt;

&lt;h3 id=&quot;planning-your-disk-layout&quot;&gt;Planning Your Disk Layout&lt;/h3&gt;
&lt;p&gt;Now you have to decide how exactly to layout the partitions and drives. 
In every case you will need a boot partition. Depending on your needs and
the number of disks in your system, there a few considerations to be had.
With one disk, the only partitioning considerations are whether or not you would
like a swap partition (note: hibernate will not be possible with btrfs and
dm-crypt) and/or additional free space for other operating systems. With two or
more disks, you can utilize the built in RAID features of Btrfs.  There are
three options for btrfs raid:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;RAID 0 - Data is striped, or distributed across all disks.&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Pros:&lt;/strong&gt;
        &lt;ul&gt;
          &lt;li&gt;Write/Read speed increased because disks are accessed in parallel&lt;/li&gt;
          &lt;li&gt;Total usable space is equal to that of the disk combined&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Cons:&lt;/strong&gt;
        &lt;ul&gt;
          &lt;li&gt;No redundancy.  If one disk fails, entire filesystem is unusable.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;RAID 1 - Data is mirrored, or copied to both devices.
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Pros:&lt;/strong&gt;
        &lt;ul&gt;
          &lt;li&gt;Resiliency. All but one drive can fail without harming the filesystem&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Cons:&lt;/strong&gt;
        &lt;ul&gt;
          &lt;li&gt;Total usable space is equal to that of the smallest disk&lt;/li&gt;
          &lt;li&gt;Read/Write speed is equal to that of the slowest disk.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;RAID 1+0 - Data is striped &lt;em&gt;and&lt;/em&gt; mirrored, as if a two or more RAID 1 pools were RAID
0’ed together.
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Pros:&lt;/strong&gt;
        &lt;ul&gt;
          &lt;li&gt;Resiliency. All but one drive from each RAID 1 pool can fail without harming the filesystem&lt;/li&gt;
          &lt;li&gt;Write/Read speed increased because disks are accessed in parallel&lt;/li&gt;
          &lt;li&gt;Total usable space is equal to that of each RAID 1 pool combined&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Cons:&lt;/strong&gt;
        &lt;ul&gt;
          &lt;li&gt;Requires 4 or more disks&lt;/li&gt;
          &lt;li&gt;Could have way more redundancy with 4 disks in RAID 1&lt;/li&gt;
          &lt;li&gt;Could have way more data throughput with 4 disks in RAID 0&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Clearly there is no correct choice for every situation.  In a file server where
data integrity is key, go with RAID1 or RAID10. On a desktop or laptop
workstation, RAID0 can greatly benefit one’s user experience. If you chose RAID
0, be sure to keep ample off-machine backups!&lt;/p&gt;

&lt;h2 id=&quot;partitioning-the-disks&quot;&gt;Partitioning the Disks&lt;/h2&gt;
&lt;hr /&gt;

&lt;p&gt;For remainder of the tutorial, I will write as though you are using two disks of
different sizes in RAID0, a pretty common setup for one’s first foray into RAID :)&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;/dev/sda: 128G&lt;/li&gt;
  &lt;li&gt;/dev/sdb: 120G&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For /dev/sda, create a 120G partition for one half of the btrfs pool, a
7.5G swap partition and a 500M boot partition.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# fdisk /dev/sda
Command (m for help): n
Partition number: &amp;lt;enter&amp;gt;
First sector: &amp;lt;enter&amp;gt;
Last Sector: +120G

Command (m for help): n
Partition number: &amp;lt;enter&amp;gt;
First sector: &amp;lt;enter&amp;gt;
Last Sector: +7.5G

Command (m for help): t
Partition number: &amp;lt;enter&amp;gt;
Hex code: 14

Command (m for help): n
Partition number: &amp;lt;enter&amp;gt;
First sector: &amp;lt;enter&amp;gt;
Last Sector: &amp;lt;enter&amp;gt;

Command (m for help): t
Partition number: &amp;lt;enter&amp;gt;
Hex code: 1

Command (m for help): w&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Since btrfs can reside on either a partition or an entire disk, you do not have
to partition /dev/sdb since you will be using the whole disk.&lt;/p&gt;

&lt;h2 id=&quot;setting-up-dm-crypt&quot;&gt;Setting up dm-crypt&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;With the disks partitioned, you can now setup the dm-crypt containers before
installing the filesystems. You will have to set up two containers, one for each of
the disks in the btrfs pool: /dev/sda1 and /dev/sdb.&lt;/p&gt;

&lt;p&gt;dm-crypt has two types of containers with different benefits, “plain” and
“luks”.  The plain encryption sets up a “dumb” container. It has no knowledge
that it is even encrypted until you open it, specifying every relevant parameter
each time (keysize, cipher, hash). LUKS on the other hand, places a header on
the disk that is aware of the parameters it was encrypted with and allows from
multiple decryption keys.  While LUKS seems like the obvious choice, one may
decide to use plain if &lt;a href=&quot;https://en.wikipedia.org/wiki/Plausible_deniability&quot;&gt;plausible
deniability&lt;/a&gt; is a concern,
as the disk will appear as simply random data to an adversary.  For this
tutorial, I will proceed using LUKS.&lt;/p&gt;

&lt;h3 id=&quot;creating-the-containers&quot;&gt;Creating the Containers&lt;/h3&gt;

&lt;p&gt;Different machines have different hardware support for the various crypto
hashes and ciphers, so to find what settings will work best for you, check the
results of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cryptsetup benchmark&lt;/code&gt; command.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# cryptsetup benchmark
# Tests are approximate using memory only (no storage IO).
PBKDF2-sha1      1178175 iterations per second
PBKDF2-sha256     755458 iterations per second
PBKDF2-sha512     613920 iterations per second
PBKDF2-ripemd160  678250 iterations per second
PBKDF2-whirlpool  252061 iterations per second
#  Algorithm | Key |  Encryption |  Decryption
     aes-cbc   128b   611.1 MiB/s  2632.0 MiB/s
 serpent-cbc   128b    81.7 MiB/s   521.5 MiB/s
 twofish-cbc   128b   181.6 MiB/s   333.4 MiB/s
     aes-cbc   256b   453.3 MiB/s  2011.5 MiB/s
 serpent-cbc   256b    82.9 MiB/s   523.0 MiB/s
 twofish-cbc   256b   183.9 MiB/s   334.6 MiB/s
     aes-xts   256b  2240.3 MiB/s  2236.9 MiB/s
 serpent-xts   256b   520.6 MiB/s   506.5 MiB/s
 twofish-xts   256b   325.5 MiB/s   330.1 MiB/s
     aes-xts   512b  1732.0 MiB/s  1714.2 MiB/s
 serpent-xts   512b   520.0 MiB/s   506.3 MiB/s
 twofish-xts   512b   325.7 MiB/s   329.8 MiB/s&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;What one wants is the best balance of strength to performance.  In terms of the
hashes, less iterations per second is stronger, as it increases the amount of
time needed to decrypt, which decreases the ability to brute force. Though by
that metric whirlpool seems best, sha256 is considered suitable secure and quite
standard. For the cipher,  aes-xts clearly has the best performance on this
computer. As a compromise of strength and speed, I will go with 256 bit aes-xts.&lt;/p&gt;

&lt;p&gt;Based on those choices, the resulting cryptsetup command is&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# cryptsetup --key-size 512 --hash sha256 --iter-time 5000 --use-random luksFormat /dev/sda1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The reason that the key size is 512 although we decided on 256 is explained on
the &lt;a href=&quot;https://wiki.archlinux.org/index.php/Dm-crypt/Device_encryption#Encryption_options_for_LUKS_mode&quot;&gt;Arch Wiki article on dm-crypt encryption&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;XTS splits the supplied key in half. So to use AES-256 instead of AES-128 you
would have to set the XTS key-size to 512.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--iter-time&lt;/code&gt; option sets how long we spend hashing the passphrase.  At 755458 iterations per second, the number of iterations is 755458*5=3777290.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;By default cryptsetup 1.6.2 uses aes-xts, so the –cipher option is unnecessary.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--use-random&lt;/code&gt; specifies to use /dev/random as an entropy (randomness)
source as opposed to the default of /dev/urandom.  The reason for this is that
on a fresh, live environment, /dev/urandom may not have suitable entropy.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Upon executing the command, you will be prompted to enter a passphrase, pick one
that is sufficiently strong.&lt;/p&gt;

&lt;p&gt;Repeat the command for any remaining partitions of the btrfs pool, in this case
just /dev/sdb. When prompted for a passphrase, it is best to enter one different
from the other disk’s, as both passphrases are necessary to decrypt the btrfs
pool. If using RAID1, the same passphrase could perhaps be used, as knowing
either passphrase can access the pool.&lt;/p&gt;

&lt;h3 id=&quot;opening-the-containers&quot;&gt;Opening the Containers&lt;/h3&gt;
&lt;p&gt;Now, the LUKS containers have to be opened in order to use them as block
devices.  Once opened, the containers reside in the filesystem as /dev/mapper/&lt;em&gt;container_name&lt;/em&gt;.
The syntax for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cryptsetup open&lt;/code&gt; command is&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# cryptsetup open --typeluks /dev/sdXY &amp;lt;container_name&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Where &lt;em&gt;container_name&lt;/em&gt; is an arbitrary name for the container.  We will use
“btrfs_pool0” and “btrfs_pool1” for organizational purposes, so the commands are&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# cryptsetup open --typeluks /dev/sda1 btrfs_pool0
# cryptsetup open --typeluks /dev/sdb btrfs_pool1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls /dev/mapper&lt;/code&gt; and verify that both containers are there.&lt;/p&gt;

&lt;h2 id=&quot;creating-the-filesystems&quot;&gt;Creating the Filesystems&lt;/h2&gt;
&lt;hr /&gt;

&lt;p&gt;There are three filesystems to create in this setup: boot, swap and the btrfs
pool. First create the boot partition.&lt;/p&gt;

&lt;p&gt;If using a legacy BIOS motherboard, you can simply run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkfs.ext3 /dev/sda3&lt;/code&gt;.
If you are on a computer with UEFI, you will need a EFI System Partition with a FAT32
filesystem, which can be created by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkfs.fat -F32 /dev/sda3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To create the swap partition, simply run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkswap /dev/sda2&lt;/code&gt; and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;swapon
/dev/sda2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To create the btrfs filesystem use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkfs.btrfs&lt;/code&gt; command.  You will need to
specify the RAID level for both the data (-d) and the metadata (-m).  We are using RAID0
for the data, but for the metadata we will use RAID1 so that it resides on both
disks.  Since metadata is both small and important, this is a good measure that
results in virtually no performance loss.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# mkfs.btrfs -m raid1 -d raid0 /dev/mapper/btrfs_pool0 /dev/mapper/btrfs_pool1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;mounting-the-btrfs-filesystem-and-creating-subvolumes&quot;&gt;Mounting the Btrfs Filesystem and Creating Subvolumes&lt;/h2&gt;
&lt;hr /&gt;

&lt;p&gt;Normally in an Arch Linux install, you would mount the root device to /mnt
and begin constructing the filesystem hierarchy, but first we are going to
utilize one of the coolest features of btrfs, subvolumes!&lt;/p&gt;

&lt;p&gt;Subvolumes are kind of like partitions, except much more flexible.  You do not
have to specify a fixed size when creating a subvolume, although you can set
disk quotas for each subvolume (for example, limit the size of each home
folder).  Arguably, the best feature of subvolumes is &lt;em&gt;snapshots&lt;/em&gt;.  A snapshot is
like a backup of the subvolume at some point in time that is
extremely fast and space efficient as it only stores changes from one point to another,
also known as delta backups.  This is made possible by the copy-on-write
mechanism of btrfs.&lt;/p&gt;

&lt;p&gt;To begin creating the various subvolumes, first make a directory to mount the
btrfs pool: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkdir /mnt/btrfs&lt;/code&gt;.  Now, mount either of the btrfs_pool containers
to this directory (it does not matter which one you choose).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# mount /dev/mapper/btrfs_pool0 /mnt/btrfs&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There are two common layouts of subvolumes: the parent method, and the sibling
method.  In the parent method, one would create a subvolume called “root” and
inside of it place others, such as “home” (which could contain the subvolumes
“alice” and “bob”), “etc” and “var”.  This may seem more traditionally intuitive, but other than
some ease in initial mounting during installation, it actually less ideal in
terms of snapshotting, as you’ll see later.
The sibling method places all snapshots at the same level of the core btrfs
filesystem.  The “root” subvolume, which will later be mounted as /, is at the
same level of the subvolume hierarchy as “bob”, “alice”, “etc”, “var”, and so
on. This makes snapshotting nicer in terms of scripting as all subvolumes live
in the same directory.&lt;/p&gt;

&lt;p&gt;Change your working directory to the btrfs mountpoint with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd /mnt/btrfs&lt;/code&gt; so
you can easily create the subvolumes.&lt;/p&gt;

&lt;p&gt;You create subvolumes using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;btrfs subvolume create&lt;/code&gt; command with the syntax&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# btrfs subvolume create &amp;lt;subvolume name&amp;gt; &amp;lt;location&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You will need to make a “root” subvolume, but other than that, it is up to you
to decide what subvolumes you would like to create. For a desktop use-case, 
you may want a “home” subvolume if you are the only user, or, if not, then a subvolume for
each user.  A “var” subvolume can be handy for package management purposes,
and perhaps you will want a “data” subvolume for shared files, such as music
and movies.  In a server scenario, you may want an “opt” subvolume for various
services.  I will demonstrate with the subvolumes I used (again, in the /mnt/btrfs/ directory):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# btrfs subvolume create root .
# btrfs subvolume create home .
# btrfs subvolume create var .
# btrfs subvolume create data .&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;btrfs subvolume list .&lt;/code&gt; and verify that they are all there.  You should
see output similar to:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# btrfs subvolume list .
    ID 259 gen 6244 top level 5 path home
    ID 260 gen 4778 top level 5 path data
    ID 271 gen 5976 top level 5 path root
    ID 272 gen 6219 top level 5 path var&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now that the subvolumes are created, we can move on to assembling the filesystem
hierarchy, just like in a traditional multi-partition install.&lt;/p&gt;

&lt;h2 id=&quot;assembling-the--filesystem&quot;&gt;Assembling the / Filesystem&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;Begin by changing your working directory to /mnt/&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# cd /mnt&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now create a directory that will be the mount point for the root filesystem
(just as you would mount you &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt; partition to /mnt or /mnt/arch in a normal
installation) with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkdir /mnt/arch&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;a-bit-about-btrfs-mount-options&quot;&gt;A Bit about Btrfs Mount Options&lt;/h3&gt;
&lt;p&gt;You may have used some options with the mount command before such as noatime or
defaults.  Btrfs has some additional options to specify some of its features,
most notably:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;ssd&lt;/strong&gt;: enables various optimizations for solid state disks&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;compress=&lt;/strong&gt; : enables on the fly compression of the data before writing it,
which uses less space and often, on modern hardware, is actually &lt;em&gt;faster&lt;/em&gt; than
non-compressed as hard drives (even SSDs) remain the bottleneck in data storage.
The available choices are zlib (better compression) or lzo (faster compression)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;subvol=&lt;/strong&gt; : specify which subvolume is to be mounted&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;mounting-the-subvolumes-and-boot-partition&quot;&gt;Mounting the Subvolumes and /boot Partition&lt;/h3&gt;
&lt;p&gt;The first step is to mount the “root” subvolume to the /mnt/arch directory, with
any options you may want.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# mount -t btrfs -o defaults,noatime,ssd,compress=lzo,subvol=root /dev/mapper/btrfs_pool0 /mnt/arch&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now make the necessary directories that will be mount points for the other
subvolumes and partitions:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# mkdir /mnt/arch/{boot,data,var,home}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And finish mounting the devices&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# mount -t btrfs -o defaults,noatime,ssd,compress=lzo,subvol=home /dev/mapper/btrfs_pool0 /mnt/arch/home
# mount -t btrfs -o defaults,noatime,ssd,compress=lzo,subvol=data /dev/mapper/btrfs_pool0 /mnt/arch/data
# mount -t btrfs -o defaults,noatime,ssd,compress=lzo,subvol=var /dev/mapper/btrfs_pool0 /mnt/arch/var
# mount -o defaults,noatime /dev/sdc3 /mnt/arch/boot&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There are two modifications we will now make to improve filesystem cleanliness
and ease of btrfs administration. First, since log files are written to
extremely frequently, you will want to disable copy-on-write for them to avoid
fragmentation of thousands of unnecessary log file revisions.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# mkdir /mnt/arch/var/log/
# chattr +C /mnt/arch/var/log/&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The second modification you’ll make will help with later snapshotting and other
btrfs operations.  Remember how easy creating subvolumes was when you mounted the
top level btrfs pool to /mnt/btrfs?  You can retain this functionality by
mounting the btrfs pool with no “subvol” option somewhere in your filesystem.  I
will do this at /var/lib/btrfs_root, but you are welcome to place this where
ever you like, such as /data/btrfs_pool.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# mkdir /mnt/arch/var/lib/btrfs_root
# mount -t btrfs -o defaults,noatime,ssd,compress=lzo /dev/mapper/btrfs_pool0 /mnt/arch/var/lib/btrfs_root&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;proceeding-with-installation&quot;&gt;Proceeding with Installation&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;Now that the filesystem is assembled, you can essentially follow the rest of the
Arch Installation guide with only a few slight differences.&lt;/p&gt;

&lt;h3 id=&quot;executing-pacstrap&quot;&gt;Executing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pacstrap&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;When running the pacstrap command, you need to include an additional package in
order to have the btrfs tools you have been using.  Also, since you mounted your filesystem
at /mnt/arch instead of /mnt/ as used in the guide, you need to account for that:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# pacstrap /mnt/arch base btrfs-progs&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;generating-the-fstab-file&quot;&gt;Generating the fstab File&lt;/h3&gt;
&lt;p&gt;In order for the system to properly locate and mount all of the partitions and
subvolumes at boot, a &lt;strong&gt;f&lt;/strong&gt;ile&lt;strong&gt;s&lt;/strong&gt;ystem &lt;strong&gt;tab&lt;/strong&gt;le needs to exist.  Arch Linux
has a great tool for simplifying the process, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;genfstab&lt;/code&gt;.
Although the Arch installation  guide does not specify this option, the ‘-U’ flag is quite
important, since the order the devices are recognized could change (switching
what is /dev/sda and /dev/sdb for example).  This is &lt;strong&gt;crucial&lt;/strong&gt; if you decide
to have an encrypted swap partition, which we’ll cover later.&lt;/p&gt;

&lt;p&gt;Before writing the fstab to new installation, first run it without the output
redirection (“»”) and verify it against &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls -l /dev/disk/by-uuid&lt;/code&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# genfstab -p -U /mnt/arch
# ls -l /dev/disk/by-uuid&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If everything looks good, write it to the new installation&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# genfstab -p -U /mnt/arch &amp;gt;&amp;gt; /mnt/arch/etc/fstab&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;further-configuration&quot;&gt;Further Configuration&lt;/h3&gt;
&lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arch-chroot /mnt/arch&lt;/code&gt; to chroot into the new system and follow the steps
in &lt;a href=&quot;https://wiki.archlinux.org/index.php/Installation_guide#Configure_the_system&quot;&gt;Configure the
System&lt;/a&gt;
until &lt;em&gt;Configure /etc/mkinitcpio.conf&lt;/em&gt;, where you will need to make some slight
modifications.&lt;/p&gt;

&lt;h2 id=&quot;setting-up-initramfs&quot;&gt;Setting Up initramfs&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;In order to decrypt devices when starting the system, you need to include the
“encrypt” hook in your initramfs.  There is a slight problem though, in that the
encrypt hook only allows you to specify 1 device to decrypt.  Since your btrfs
pool consists of 2 or more devices this will need some modifications.  The
“encrypt” hook is really just a shell script that lives at
/lib/initcpio/hooks/encrypt, with a corresponding install script that lives at
/lib/initcpio/install/encrypt.&lt;/p&gt;

&lt;p&gt;All you need to do is create copies of the two scripts make
some slight modifications.&lt;/p&gt;

&lt;h3 id=&quot;creating-additional-encrypt-hooks&quot;&gt;Creating Additional encrypt hooks&lt;/h3&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# cp /lib/initcpio/hooks/encrypt /lib/initcpio/hooks/encrypt2
# cp /lib/initcpio/install/encrypt /lib/initcpio/install/encrypt2&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The install/encrypt2 script is fine as-is, but you need to make some slight
adjustments to /lib/initcpio/hooks/encrypt2.  Using your favorite text editor,
change every occurrence of “$cryptkey” to “$cryptkey2” (there should be 2
occurrences), and “$cryptdevice” to “$cryptdevice2” (2 occurrences).
Additionally, find the line “mkdir /ckey” and remove it to avoid an annoying but
harmless error message upon startup.&lt;/p&gt;

&lt;p&gt;If you are using more than 2 encrypted disks for your pool, simply repeat the
process for encrypt3, encrypt4, etc.&lt;/p&gt;

&lt;h3 id=&quot;editing-etcmkinitcpioconf&quot;&gt;Editing /etc/mkinitcpio.conf&lt;/h3&gt;
&lt;p&gt;Now it is time to tell the initramfs to include the necessary components upon
being built. Open up /etc/mkinitcpio.conf in your favorite text editor.  Find
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MODULES=&quot;&quot;&lt;/code&gt; line and change it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MODULES=&quot;nls_cp437 crc32c&quot;&lt;/code&gt; &lt;em&gt;note: if
you would like to use a keyfile to automatically unlock your devices on boot
when in a safe place, include the kernel module for the unencrypted filesystem
it will reside on, such as vfat or ext3. Setting this up will be covered later
in the tutorial.&lt;/em&gt;
Now find the uncommented line beginning with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HOOKS=&lt;/code&gt; and change it to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HOOKS=&quot;base udev autodetect encrypt encrypt2 modconf block filesystems keyboard
fsck btrfs&quot;&lt;/code&gt; &lt;em&gt;in that order&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now simply run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkinicpio -p linux&lt;/code&gt; to generate the initramfs.&lt;/p&gt;

&lt;h2 id=&quot;setting-the-boot-options&quot;&gt;Setting the Boot Options&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;In order to decrypt the disks and mount the “root” subvolume to /, you need to
set a few kernel options in your boot loader configuration.  The specifics may be
slightly different depending on your choice of bootloader, but generally there
is an “options” line in the configuration for each boot entry.&lt;/p&gt;

&lt;p&gt;To unlock the encrypted LUKS containers, use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cryptdevice=&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cryptdevice2=&lt;/code&gt; (and any additional) options from your encrypt hooks.  The format
for this option is&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# cryptdevice=UUID=&amp;lt;uuid-of-disk-or-partition&amp;gt;:&amp;lt;name-of-encryption-container&amp;gt;:allow-discards&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Again, because device names of the form /dev/sdXY can change, UUIDs should be
used.  I find it helpful to append all of the disk UUIDs to the end of the boot
entry for easy copy and pasting: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls -l /dev/disk/by-uuid/ &amp;gt;&amp;gt;
/path/to/boot/entry&lt;/code&gt;, just be sure to delete all of the lines appended by ls
before saving the file!&lt;/p&gt;

&lt;p&gt;To indicate that you want to mount your Btrfs pool’s “root” subvolume as /, use
the standard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root=&lt;/code&gt; parameter but also include a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rootflags=&lt;/code&gt; parameter like
so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# root=/dev/mapper/btrfs_pool0 rootflags=subvol=root&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Putting everything together you get something like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;title       Arch Linux
linux       /vmlinuz-linux
initrd      /initramfs-linux.img
options      cryptdevice=UUID=72347efa-f59b-de3a-42fe-02849feacc72:btrfs_pool0:allow-discards cryptdevice2=UUID=987aefcc-f119-56aa-43c1-48cffeaa891e:btrfs_pool1:allow-discards root=/dev/mapper/btrfs_pool0 rootflags=subvol=root quiet rw&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now the base system is completely installed an ready to boot! Feel free to add
some users, install some other simple packages you prefer, or any other basic
administrative tasks before you reboot into the installation.  When you’re all
set, type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt; to leave the chroot environment, unmount the subvolumes and
disks with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;umount -R /mnt/arch&lt;/code&gt; and then reboot.  If all goes well, you
will boot into your new installation, asking you for the passphrase for each
disk.  If there are any problems, boot back into the live disk, mount your
filesystems and compare the configuration files to the ones listed in this
article.  The most likely culprits are your boot configuration, /etc/fstab,
 and /etc/mkinitcpio.conf.&lt;/p&gt;

&lt;h2 id=&quot;post-installation-considerations-and-niceties&quot;&gt;Post Installation Considerations and Niceties&lt;/h2&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;a-warning-about-kernel-updates&quot;&gt;A Warning About Kernel Updates&lt;/h3&gt;
&lt;p&gt;In the past, some kernel updates have been known to cause corruption to the
filesystem, most recently in the 3.19.1 kernel where a deadlock prevented
access.  The issue was easily repairable, and is now fixed, however instability
in your data storage can be frightening.  To prevent this type of surprise,
there are a few things that can be done.  The simplest, if your hardware is
supported, is to switch to a Long Term Support (LTS) kernel.  These are released
far less frequently and geared to provide the utmost stability.  They are
generally heavily tested before deployment to ensure that no bugs of this sort
can creep up.  If you must run a newer kernel, I recommend a reasonable external back up system of your most important files and waiting a few days to
upgrade after a new kernel version is released, in hopes that any such bugs can
be discovered and promptly patched.&lt;/p&gt;

&lt;h3 id=&quot;snapshots&quot;&gt;Snapshots&lt;/h3&gt;
&lt;p&gt;Snapshotting, again, is a wonderful feature of Btrfs that allows us to take fast,
lightweight backups of a subvolume.  Each snapshot essentially freezes a file in
in the state is it at that moment.  If a file hasn’t changed since a year ago,
that file in each snapshot is the same exact location on the disks, a file in a snapshot
only takes up space if it were to change. Because of Btrfs’ copy-on-write
mechanisms, when you change a file, it is written to a new location and the
previous snapshots are unaffected, and if you were to delete a file that was
previously snapshotted, the file tells Btrfs “Hey, I’m still wanted in this one snapshot…so don’t
forget about me!” and those blocks are not freed. If something were to happen to a file or directory, we can
simply roll back to an appropriate snapshot and retrieve that copy.&lt;/p&gt;

&lt;p&gt;To make a snapshot, first change directories into the /var/lib/btrfs_root
directory you made earlier.  Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; and you should see all of the subvolumes
you created. Feel free to poke around inside of them and see exactly how the
system is assembled.  When you’re ready to make a snapshot, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkdir
snapshots&lt;/code&gt; to create a directory to store them in.  This is just a standard
POSIX
directory at the top most level of your Btrfs system.&lt;/p&gt;

&lt;p&gt;I like to organize my
snapshots by date, so I usually create a new subdirectory in
/var/lib/btrfs_root/snapshots named after the date.  For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkdir snapshots/$(date +&quot;%F_%H-%M&quot;)&lt;/code&gt; would create the directory snapshots/2015-09-02_14-56/ because I ran it at 2:56pm on September 2nd, 2015.  This way of organizing snapshots works for me, but by all means, use something that is intuitive to you!  To actually create the snapshot use your good pal, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;btrfs&lt;/code&gt; command like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# btrfs subvolume snapshot &amp;lt;subvolume name&amp;gt; snapshots/2015-09-02_14-56/&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So for the system installed in this article:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# btrfs subvolume snapshot root snapshots/2015-09-02_14-56/
# btrfs subvolume snapshot home snapshots/2015-09-02_14-56/
# btrfs subvolume snapshot var snapshots/2015-09-02_14-56/
# btrfs subvolume snapshot data snapshots/2015-09-02_14-56/&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;To verify, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; in the snapshot/2015-09-02_14-56/ directory. You don’t have
to snapshot every subvolume at the same time, for instance, your personal data
in home is likely to change more and you may want to snapshot more often to be
able to revert to a very recent state.&lt;/p&gt;

&lt;p&gt;Btrfs snapshots are very powerful, and can be scripted by hand or with various
tools such as &lt;a href=&quot;https://en.opensuse.org/openSUSE:Snapper_Tutorial&quot;&gt;openSUSE’s &lt;em&gt;Snapper&lt;/em&gt;&lt;/a&gt;.
These operations are outside of the scope of this tutorial, but are by no
means difficult to do, so have fun checking them out!&lt;/p&gt;

&lt;h3 id=&quot;keyfiles-and-additional-luks-keys&quot;&gt;Keyfiles and Additional LUKS Keys&lt;/h3&gt;
&lt;p&gt;One of the most appealing aspects of the LUKS containers is the ability to have
up to 8 keys that are able to unlock the container.  The keys can be text
entered interactively at a prompt, like a standard password, or keyfiles, any
standard file, such as a picture, a song, a pdf…anything you could store on a
hard drive!  Additional text keys are helpful to have an unlocking mechanism in
case you forget or damage another key.  Keyfiles are nice as a quick, automatic
way to decrypt devices when in a safe place, or to have a key much longer than
a memorable text one.&lt;/p&gt;

&lt;p&gt;For example, my ThinkPad has an SD card slot.  When I am at home, a keyfile on
the card is used to automatically decrypt the drives in my Btrfs pool at boot.  If I
leave the house, I simply eject the SD card, and when I startup the computer, I
am asked for the passphrases for both of the involved disks.&lt;/p&gt;

&lt;p&gt;To add new key, use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cryptsetup luksAddKey&lt;/code&gt; command. To add a key to the
LUKS container at /dev/sda1 for example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# cryptsetup luksAddKey /dev/sda1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The command will prompt you to type in an existing key and then have you type
the new passphrase twice.&lt;/p&gt;

&lt;p&gt;If you would like to add a keyfile instead, simply add the path of the file to
your command:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# cryptsetup luksAddKey /dev/sda1 /path/to/file&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Once you enter in an existing key, the keyfile will be added. Revoking keys is
not difficult, but not in the scope of this article.  For more info on LUKS
management, check out the Arch Wiki page &lt;a href=&quot;https://wiki.archlinux.org/index.php/Dm-crypt/Device_encryption&quot;&gt;dm-crypt/Device
encryption&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Assuming that you added a keyfile to each partition’s LUKS header, you may now
setup your computer to use those upon boot thanks to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;encrypt&lt;/code&gt; hook.
Change your boot loader configuration entry’s options line to include cryptkey
and cryptkey2 options in the format
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cryptkey=/dev/disk/by-uuid/xxxx-xxxx:&amp;lt;filesystem
type&amp;gt;:/path/from/toplevel/of/device&lt;/code&gt; For example, to use a keyfile on my SD card
I would have the following options line:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;options     cryptdevice=UUID=6e3e4026-5b53-4bc0-8980-ffe59765f85d:btrfs_pool0:allow-discards cryptdevice2=UUID=16d8e0bf-5384-4f5d-a785-9c7eaf775fa4:btrfs_pool1:allow-discards root=/dev/mapper/btrfs_pool0 rootflags=subvol=root cryptkey=/dev/disk/by-uuid/F224-CEA1:vfat:/cats.gif cryptkey2=/dev/disk/by-uuid/F224-CEA1:vfat:/cats.gif quiet rw&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Though absurdly long, it is important (at least for my bootloader) that it all
to be on one line.&lt;/p&gt;

&lt;p&gt;Now reboot, and if your keyfile is present on the specified removable media, your
LUKS containers should automatically unlock themselves.  If your removable media
is not present, you will be brought to the standard prompt to type a key in by
hand.&lt;/p&gt;

&lt;h3 id=&quot;encrypted-swap&quot;&gt;Encrypted Swap&lt;/h3&gt;
&lt;p&gt;If you chose to have a swap partition on your system, it is a good idea to
encrypt it.  The process is quite simple and unobtrusive, requiring just a slight
change to both /etc/fstab and /etc/crypttab, with no additional passwords or
anything at boot since they are encrypted with a one-time random “throwaway”
passphrase.&lt;/p&gt;

&lt;p&gt;First, in /etc/crypttab, find the line with the swap entry:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;# swap         /dev/sdaX        /dev/urandom    swap,cipher=aes-cbc-essiv:sha256,size=256&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and uncomment it.  Then replace /dev/sdaX with the &lt;strong&gt;UUID&lt;/strong&gt; of your swap
partition in the same way you did for /etc/fstab and our boot options.  &lt;strong&gt;It is
extremely important to use the UUID here!&lt;/strong&gt;: in the boot options or /etc/fstab,
if the wrong partition was referenced, the system would simply not boot until
it is fixed.  Here, however, the partition would be  rendered completely
corrupted after mkswap gets called and a dm-crypt container is created.&lt;/p&gt;

&lt;p&gt;Now edit /etc/fstab. Find the line for the swap partition and change the
partition to /dev/mapper/swap like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;/dev/mapper/swap    none        swap        defaults    0 0&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;this indicates that swap partition will be the “swap” container that you just
told /etc/crypttab to create at boot time.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;hr /&gt;

&lt;p&gt;I hope that you have found this guide to be informative and helpful.  While it
is quite thorough in explaining the set up process, there is much more to learn
and use now that your system is up and running.  From automated snapshot
management, to LUKS header backup and removal, from Btrfs filesystem hygiene
practices, to further securing your system; there is a plethora of further fun,
security and data integrity topics to explore with these tools.  Have fun and
enjoy.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;feel free to contact me with any comments or corrections via the options listed at the bottom of the page.&lt;/em&gt;&lt;/p&gt;

</description>
        <pubDate>Thu, 17 Sep 2015 00:00:00 +0000</pubDate>
        <link>kneit.in/2015/09/17/brtfs-raid-on-dmcrypt.html</link>
        <guid isPermaLink="true">kneit.in/2015/09/17/brtfs-raid-on-dmcrypt.html</guid>
        
        
      </item>
    
  </channel>
</rss>
