<refsect2>
<title>Type</title>
- <para>The type consists of a single letter and optionally an exclamation mark (<literal>!</literal>)
- minus sign (<literal>-</literal>), and/or equals sign (<literal>=</literal>).</para>
+ <para>The type consists of a single letter and optionally a plus sign (<literal>+</literal>),
+ exclamation mark (<literal>!</literal>), minus sign (<literal>-</literal>), equals sign
+ (<literal>=</literal>) and/or tilde character (<literal>~</literal>).</para>
<para>The following line types are understood:</para>
exists and is not empty. Instead, the entire copy operation is
skipped. If the argument is omitted, files from the source directory
<filename>/usr/share/factory/</filename> with the same name
- are copied. Does not follow symlinks. Contents of the directories
+ are copied. Does not follow symlinks. Contents of the directories
are subject to time based cleanup if the age argument is specified.
</para></listitem>
</varlistentry>
be either directories or directory symlinks). For example, if there is a FIFO in place of one of the parent path
components it will be replaced with a directory.</para>
+ <para>If the tilde character (<literal>~</literal>) is used, the argument (i.e. 6th) column is <ulink
+ url="https://www.rfc-editor.org/rfc/rfc4648.html">Base64 decoded</ulink> before use. This modifier is
+ only supported on line types that can write file contents, i.e. <varname>f</varname>,
+ <varname>f+</varname>, <varname>w</varname>. This is useful for writing arbitrary binary data
+ (including newlines and NUL bytes) to files. Note that if this switch is used, the argument is not
+ subject to specifier expansion, neither before nor after Base64 decoding.</para>
+
<para>Note that for all line types that result in creation of any kind of file node
(i.e. <varname>f</varname>/<varname>F</varname>,
<varname>d</varname>/<varname>D</varname>/<varname>v</varname>/<varname>q</varname>/<varname>Q</varname>,
#include "format-util.h"
#include "fs-util.h"
#include "glob-util.h"
+#include "hexdecoct.h"
#include "io-util.h"
#include "label.h"
#include "log.h"
char *path;
char *argument;
+ void *binary_argument; /* set if binary data, in which case it takes precedence over 'argument' */
+ size_t binary_argument_size;
char **xattrs;
#if HAVE_ACL
acl_t acl_access;
return set_contains(unix_sockets, fn);
}
+/* Accessors for the argument in binary format */
+static const void* item_binary_argument(const Item *i) {
+ assert(i);
+ return i->binary_argument ?: i->argument;
+}
+
+static size_t item_binary_argument_size(const Item *i) {
+ assert(i);
+ return i->binary_argument ? i->binary_argument_size : strlen_ptr(i->argument);
+}
+
static DIR* xopendirat_nomod(int dirfd, const char *path) {
DIR *dir;
return fd_set_attribute(item, fd, path, NULL);
}
+static int write_argument_data(Item *i, int fd, const char *path) {
+ int r;
+
+ assert(i);
+ assert(fd >= 0);
+ assert(path);
+
+ if (item_binary_argument_size(i) == 0)
+ return 0;
+
+ assert(item_binary_argument(i));
+
+ log_debug("Writing to \"%s\".", path);
+
+ r = loop_write(fd, item_binary_argument(i), item_binary_argument_size(i), /* do_poll= */ false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write file \"%s\": %m", path);
+
+ return 0;
+}
+
static int write_one_file(Item *i, const char *path) {
_cleanup_close_ int fd = -1, dir_fd = -1;
_cleanup_free_ char *bn = NULL;
assert(i);
assert(path);
- assert(i->argument);
assert(i->type == WRITE_FILE);
r = path_extract_filename(path, &bn);
}
/* 'w' is allowed to write into any kind of files. */
- log_debug("Writing to \"%s\".", path);
- r = loop_write(fd, i->argument, strlen(i->argument), false);
+ r = write_argument_data(i, fd, path);
if (r < 0)
- return log_error_errno(r, "Failed to write file \"%s\": %m", path);
+ return r;
return fd_set_perms(i, fd, path, NULL);
}
path);
st = &stbuf;
- } else {
-
- log_debug("\"%s\" has been created.", path);
-
- if (i->argument) {
- log_debug("Writing to \"%s\".", path);
-
- r = loop_write(fd, i->argument, strlen(i->argument), false);
- if (r < 0)
- return log_error_errno(r, "Failed to write file \"%s\": %m", path);
- }
+ } else if (item_binary_argument(i)) {
+ r = write_argument_data(i, fd, path);
+ if (r < 0)
+ return r;
}
return fd_set_perms(i, fd, path, st);
log_debug("\"%s\" has been created.", path);
- if (i->argument) {
- log_debug("Writing to \"%s\".", path);
-
- r = loop_write(fd, i->argument, strlen(i->argument), false);
+ if (item_binary_argument(i)) {
+ r = write_argument_data(i, fd, path);
if (r < 0)
- return log_error_errno(erofs ? -EROFS : r, "Failed to write file %s: %m", path);
+ return r;
}
return fd_set_perms(i, fd, path, st);
assert(i);
free(i->path);
free(i->argument);
+ free(i->binary_argument);
strv_free(i->xattrs);
#if HAVE_ACL
if (takes_ownership(a->type) && takes_ownership(b->type))
/* check if the items are the same */
- return streq_ptr(a->argument, b->argument) &&
+ return memcmp_nn(item_binary_argument(a), item_binary_argument_size(a),
+ item_binary_argument(b), item_binary_argument_size(b)) == 0 &&
a->uid_set == b->uid_set &&
a->uid == b->uid &&
ItemArray *existing;
OrderedHashmap *h;
int r, pos;
- bool append_or_force = false, boot = false, allow_failure = false, try_replace = false;
+ bool append_or_force = false, boot = false, allow_failure = false, try_replace = false, unbase64 = false;
assert(fname);
assert(line >= 1);
allow_failure = true;
else if (action[pos] == '=' && !try_replace)
try_replace = true;
+ else if (action[pos] == '~' && !unbase64)
+ unbase64 = true;
else {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Unknown modifiers in command '%s'", action);
break;
case CREATE_SYMLINK:
+ if (unbase64) {
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for symlink targets.");
+ }
+
if (!i.argument) {
i.argument = path_join("/usr/share/factory", i.path);
if (!i.argument)
break;
case COPY_FILES:
+ if (unbase64) {
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for copy sources.");
+ }
+
if (!i.argument) {
i.argument = path_join("/usr/share/factory", i.path);
if (!i.argument)
return log_oom();
-
} else if (!path_is_absolute(i.argument)) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Source path '%s' is not absolute.", i.argument);
case CREATE_CHAR_DEVICE:
case CREATE_BLOCK_DEVICE:
+ if (unbase64) {
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for device node creation.");
+ }
+
if (!i.argument) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Device file requires argument.");
case SET_XATTR:
case RECURSIVE_SET_XATTR:
+ if (unbase64) {
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for extended attributes.");
+ }
if (!i.argument) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
case SET_ACL:
case RECURSIVE_SET_ACL:
+ if (unbase64) {
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for ACLs.");
+ }
if (!i.argument) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
case SET_ATTRIBUTE:
case RECURSIVE_SET_ATTRIBUTE:
+ if (unbase64) {
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for file attributes.");
+ }
if (!i.argument) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
if (!should_include_path(i.path))
return 0;
- r = specifier_expansion_from_arg(specifier_table, &i);
- if (r == -ENXIO)
- return log_unresolvable_specifier(fname, line);
- if (r < 0) {
- if (IN_SET(r, -EINVAL, -EBADSLT))
- *invalid_config = true;
- return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to substitute specifiers in argument: %m");
+ if (unbase64) {
+ if (i.argument) {
+ r = unbase64mem(i.argument, SIZE_MAX, &i.binary_argument, &i.binary_argument_size);
+ if (r < 0)
+ return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to base64 decode specified argument '%s': %m", i.argument);
+ }
+ } else {
+ r = specifier_expansion_from_arg(specifier_table, &i);
+ if (r == -ENXIO)
+ return log_unresolvable_specifier(fname, line);
+ if (r < 0) {
+ if (IN_SET(r, -EINVAL, -EBADSLT))
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to substitute specifiers in argument: %m");
+ }
}
if (!empty_or_root(arg_root)) {