specified root directory or disk image root.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>MakeDirectories=</varname></term>
+
+ <listitem><para>akes one or more absolute paths, separated by whitespace, each declaring a directory
+ to create within the new file system. Behaviour is similar to <varname>CopyFiles=</varname>, but
+ instead of copying in a set of files this just creates the specified directories with the default
+ mode of 0755 owned by the root user and group, plus all their parent directories (with the same
+ ownership and access mode). To configure directories with different ownership or access mode, use
+ <varname>CopyFiles=</varname> and specify a source tree to copy containing appropriately
+ owned/configured directories. This option may be used more than once to create multiple
+ directories. When <varname>CopyFiles=</varname> and <varname>MakeDirectories=</varname> are used
+ together the former is applied first. If a directory listed already exists no operation is executed
+ (in particular, the ownership/access mode of the directories is left as is).</para>
+
+ <para>The primary usecase for this option is to create a minimal set of directories that may be
+ mounted over by other partitions contained in the same disk image. For example, a disk image where
+ the root file system is formatted at first boot might want to automatically pre-create
+ <filename>/usr/</filename> in it this way, so that the <literal>usr</literal> partition may
+ over-mount it.</para>
+
+ <para>Consider using
+ <citerefentry><refentrytitle>systemd-tmpfiles</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ with its <option>--image=</option> option to pre-create other, more complex directory hierarchies (as
+ well as other inodes) with fine-grained control of ownership, access modes and other file
+ attributes.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>Encrypt=</varname></term>
char *format;
char **copy_files;
+ char **make_directories;
EncryptMode encrypt;
LIST_FIELDS(Partition, partitions);
free(p->format);
strv_free(p->copy_files);
+ strv_free(p->make_directories);
return mfree(p);
}
return 0;
}
+static int config_parse_make_dirs(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Partition *partition = data;
+ const char *p = rvalue;
+ int r;
+
+ assert(rvalue);
+ assert(partition);
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *d = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r == 0)
+ return 0;
+
+ r = specifier_printf(word, specifier_table, NULL, &d);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to expand specifiers in MakeDirectories= parameter, ignoring: %s", word);
+ continue;
+ }
+
+ r = path_simplify_and_warn(d, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
+ continue;
+
+ r = strv_consume(&partition->make_directories, TAKE_PTR(d));
+ if (r < 0)
+ return log_oom();
+ }
+}
+
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_encrypt, encrypt_mode, EncryptMode, ENCRYPT_OFF, "Invalid encryption mode");
static int partition_read_definition(Partition *p, const char *path) {
{ "Partition", "CopyBlocks", config_parse_path, 0, &p->copy_blocks_path },
{ "Partition", "Format", config_parse_fstype, 0, &p->format },
{ "Partition", "CopyFiles", config_parse_copy_files, 0, p },
+ { "Partition", "MakeDirectories", config_parse_make_dirs, 0, p },
{ "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt },
{}
};
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Type= not defined, refusing.");
- if (p->copy_blocks_path && (p->format || !strv_isempty(p->copy_files)))
+ if (p->copy_blocks_path && (p->format || !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Format= and CopyBlocks= cannot be combined, refusing.");
- if (!strv_isempty(p->copy_files) && streq_ptr(p->format, "swap"))
+ if ((!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)) && streq_ptr(p->format, "swap"))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Format=swap and CopyFiles= cannot be combined, refusing.");
- if (!p->format && (!strv_isempty(p->copy_files) || (p->encrypt != ENCRYPT_OFF && !p->copy_blocks_path))) {
+ if (!p->format && (!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || (p->encrypt != ENCRYPT_OFF && !p->copy_blocks_path))) {
/* Pick "ext4" as file system if we are configured to copy files or encrypt the device */
p->format = strdup("ext4");
if (!p->format)
return 0;
}
-static int partition_copy_files(Partition *p, const char *node) {
+static int do_make_directories(Partition *p, const char *fs) {
+ char **d;
+ int r;
+
+ assert(p);
+ assert(fs);
+
+ STRV_FOREACH(d, p->make_directories) {
+
+ r = mkdir_p_root(fs, *d, UID_INVALID, GID_INVALID, 0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
+ }
+
+ return 0;
+}
+
+static int partition_populate(Partition *p, const char *node) {
int r;
assert(p);
assert(node);
- if (strv_isempty(p->copy_files))
+ if (strv_isempty(p->copy_files) && strv_isempty(p->make_directories))
return 0;
log_info("Populating partition %" PRIu64 " with files.", p->partno);
if (do_copy_files(p, fs) < 0)
_exit(EXIT_FAILURE);
+ if (do_make_directories(p, fs) < 0)
+ _exit(EXIT_FAILURE);
+
r = syncfs_path(AT_FDCWD, fs);
if (r < 0) {
log_error_errno(r, "Failed to synchronize written files: %m");
if (flock(encrypted_dev_fd, LOCK_UN) < 0)
return log_error_errno(errno, "Failed to unlock LUKS device: %m");
- r = partition_copy_files(p, fsdev);
+ r = partition_populate(p, fsdev);
if (r < 0) {
encrypted_dev_fd = safe_close(encrypted_dev_fd);
(void) deactivate_luks(cd, encrypted);