{"id":702,"date":"2018-05-20T10:03:44","date_gmt":"2018-05-20T08:03:44","guid":{"rendered":"https:\/\/blogs.gentoo.org\/mgorny\/?p=702"},"modified":"2018-08-13T18:00:09","modified_gmt":"2018-08-13T16:00:09","slug":"empty-directories-into-dodir-keepdir-and-tmpfiles-d","status":"publish","type":"post","link":"https:\/\/blogs.gentoo.org\/mgorny\/2018\/05\/20\/empty-directories-into-dodir-keepdir-and-tmpfiles-d\/","title":{"rendered":"Empty directories, *into, dodir, keepdir and\u00a0tmpfiles.d"},"content":{"rendered":"<p>There seems to be some serious confusion around the\u00a0way directories are installed in\u00a0Gentoo.  In\u00a0this post, I would like to shortly explain the\u00a0differences between different methods of\u00a0creating directories in\u00a0ebuilds, and\u00a0instruct how to handle the\u00a0issues related to installing empty directories and\u00a0volatile locations.<\/p>\n<p><!--more--><\/p>\n<h2>Empty directories are not\u00a0guaranteed to be\u00a0installed<\/h2>\n<p>First things first.  The\u00a0standards are pretty clear here:<\/p>\n<blockquote>\n<p>Behaviour upon encountering an empty directory is undefined. Ebuilds must not attempt to install an empty directory.<\/p>\n<p>    <cite><a rel='external' href='https:\/\/projects.gentoo.org\/pms\/7\/pms.html#x1-14200013.2.2'>PMS 13.2.2 Empty directories (EAPI 7 version)<\/a><\/cite>\n<\/p><\/blockquote>\n<p>What does that mean in\u00a0practice?  It means that if an\u00a0empty directory is found in\u00a0the\u00a0installation image, it may or\u00a0may not be installed.  Or it may be\u00a0installed, and\u00a0incidentally removed later (that&#8217;s the\u00a0historical Portage behavior!).  In\u00a0any case, you can&#8217;t rely on\u00a0either behavior.  If you really need a\u00a0directory to exist once the\u00a0package is installed, you need to\u00a0make it non-empty (see: <a href='#keepdir'>keepdir<\/a> below).  If you really need a\u00a0directory not to\u00a0exist, you need to <kbd>rmdir<\/kbd> it from the\u00a0image.<\/p>\n<p>That said, this behavior <em>does<\/em> makes sense.  It guarantees that the\u00a0Gentoo installation is secured against empty directory pruning tools.<\/p>\n<h2>*into<\/h2>\n<p>The <kbd>*into<\/kbd> family of\u00a0functions is used to\u00a0control install destination for other ebuild helpers.  By\u00a0design, either they or\u00a0the\u00a0respective helpers create the\u00a0install directories as\u00a0necessary.  In\u00a0other words, you do not need to call <a href='#dodir'>dodir<\/a> when using <kbd>*into<\/kbd>.<\/p>\n<h2 id='dodir'>dodir<\/h2>\n<p><kbd>dodir<\/kbd> is not\u00a0really special in\u00a0any way.  It is just a\u00a0convenient wrapper for\u00a0<kbd>install -d<\/kbd> that prepends <kbd>${ED}<\/kbd> to the\u00a0path.  It creates an\u00a0empty directory the\u00a0same way the\u00a0upstream build system would have created it, and\u00a0if the\u00a0directory is\u00a0left empty, it is not guaranteed to be\u00a0preserved.<\/p>\n<p>So when do you use it?  You use it when you need to create a\u00a0directory that will not be\u00a0created otherwise and\u00a0that will become non-empty at the\u00a0end of\u00a0the\u00a0build process.  Example use cases are working around broken build systems (that fail due to non-existing directories but do not\u00a0create them), and\u00a0creating directories when you want to manually write to a\u00a0file there.<\/p>\n<pre>src_install() {\r\n    # build system is broken and fails\r\n    # if ${D}\/usr\/bin does not exist\r\n    dodir \/usr\/bin\r\n    default\r\n\r\n    dodir \/etc\/foo\r\n    sed -e \"s:@libdir@:$(get_libdir):\" \\\r\n        \"${FILESDIR}\"\/foo.conf.in \\\r\n        &gt; \"${ED}\"\/etc\/foo\/foo.conf || die\r\n}<\/pre>\n<h2 id='keepdir'>keepdir<\/h2>\n<p><kbd>keepdir<\/kbd> is the\u00a0function specifically meant for installing empty directories.  It creates the\u00a0directory, and\u00a0a\u00a0keep-file inside it.  The\u00a0directory becomes non-empty, and\u00a0therefore guaranteed to be\u00a0installed and\u00a0preserved.  When using <kbd>keepdir<\/kbd>, you do not call <kbd>dodir<\/kbd> as well.<\/p>\n<p>Note that actually preserving the\u00a0empty directories is not always necessary.  Sometimes packages are\u00a0perfectly capable of\u00a0recreating the\u00a0directories themselves.  However, make sure to\u00a0verify that the\u00a0permissions are correct afterwards.<\/p>\n<pre>src_install() {\r\n    default\r\n\r\n    # install empty directory\r\n    keepdir \/var\/lib\/foo\r\n}<\/pre>\n<h2>Volatile locations<\/h2>\n<p>The\u00a0<kbd>keepdir<\/kbd> method works fine for\u00a0persistent locations.  However, it will not work correctly in\u00a0directories such as\u00a0<kbd>\/run<\/kbd> that are\u00a0volatile or <kbd>\/var\/cache<\/kbd> that may be\u00a0subject to wiping by\u00a0user.  On\u00a0Gentoo, this also includes <kbd>\/var\/run<\/kbd> (which OpenRC maintainers unilaterally decided to turn into a\u00a0<kbd>\/run<\/kbd> symlink), and\u00a0<kbd>\/var\/lock<\/kbd>.<\/p>\n<p>Since the\u00a0package manager does not\u00a0handle recreating those directories e.g.\u00a0after a\u00a0reboot, something else needs to.  There are three common approaches to it, most preferred first:<\/p>\n<ol>\n<li>Application creates all necessary directories at\u00a0startup.<\/li>\n<li><kbd>tmpfiles.d<\/kbd> file is installed to create the\u00a0files at\u00a0boot.<\/li>\n<li>Init script creates the\u00a0directories before starting the\u00a0service (<kbd>checkpath<\/kbd>).<\/li>\n<\/ol>\n<p>The\u00a0preferred approach is for applications to\u00a0create those directories themselves.  However, not all applications do that, and\u00a0not all actually can.  For\u00a0example, applications that are running unprivileged generally can&#8217;t create those directories.<\/p>\n<p>The\u00a0second approach is to install a\u00a0<a rel='external' href='https:\/\/www.freedesktop.org\/software\/systemd\/man\/tmpfiles.d.html'>tmpfiles.d<\/a> file to create (and\u00a0maintain) the\u00a0directory.  Those files are work both for\u00a0systemd and\u00a0OpenRC users (via\u00a0opentmpfiles) out of\u00a0the\u00a0box.  The\u00a0directories are\u00a0(re-)created at\u00a0boot, and\u00a0optionally cleaned up periodically.  The\u00a0ebuild should also use <a rel='external' href='https:\/\/devmanual.gentoo.org\/eclass-reference\/tmpfiles.eclass\/index.html'>tmpfiles.eclass<\/a> to trigger directory creation after installing the\u00a0package.<\/p>\n<p>The\u00a0third approach is to make the\u00a0init script create the\u00a0directory.  This was the\u00a0traditional way but nowadays it is generally discouraged as\u00a0it causes duplication between different init systems, and\u00a0the\u00a0directories are not\u00a0created when\u00a0the\u00a0application is started directly by the\u00a0user.<\/p>\n<h2>Summary<\/h2>\n<p>To summarize:<\/p>\n<ol>\n<li>when you install files via\u00a0<kbd>*into<\/kbd>, installation directories are automatically created for you;<\/li>\n<li>when you need to create a\u00a0directory into\u00a0which files are installed in\u00a0other way than ebuild helpers, use <kbd>dodir<\/kbd>;<\/li>\n<li>when you need to install an\u00a0empty directory in\u00a0a\u00a0non-volatile location (and\u00a0application can&#8217;t just create it on\u00a0start), use <kbd>keepdir<\/kbd>;<\/li>\n<li>when you need to install a\u00a0directory into a\u00a0volatile location (and\u00a0application can&#8217;t just create it on\u00a0start), use <kbd>tmpfiles.d<\/kbd>.<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>There seems to be some serious confusion around the\u00a0way directories are installed in\u00a0Gentoo. In\u00a0this post, I would like to shortly explain the\u00a0differences between different methods of\u00a0creating directories in\u00a0ebuilds, and\u00a0instruct how to handle the\u00a0issues related to installing empty directories and\u00a0volatile locations.<\/p>\n","protected":false},"author":137,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[11],"tags":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts\/702"}],"collection":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/users\/137"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/comments?post=702"}],"version-history":[{"count":18,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts\/702\/revisions"}],"predecessor-version":[{"id":721,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts\/702\/revisions\/721"}],"wp:attachment":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/media?parent=702"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/categories?post=702"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/tags?post=702"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}