PATTERN_READ_ONLY,
PATTERN_GROWFS,
PATTERN_SHA256SUM,
+ PATTERN_SLASH,
_PATTERN_ELEMENT_TYPE_MAX,
_PATTERN_ELEMENT_TYPE_INVALID = -EINVAL,
} PatternElementType;
if ((unsigned) x < ' ' || x >= 127)
return false;
- return !IN_SET(x, '$', '*', '?', '[', ']', '!', '\\', '/', '|');
+ return !IN_SET(x, '$', '*', '?', '[', ']', '!', '\\', '|');
}
static int pattern_split(
PatternElement **ret) {
_cleanup_(pattern_element_free_allp) PatternElement *first = NULL;
- bool at = false, last_literal = true;
+ bool at = false, last_literal = true, last_slash = false;
PatternElement *last = NULL;
uint64_t mask_found = 0;
size_t l, k = 0;
/* We insist that two pattern field markers are separated by some literal string that
* we can use to separate the fields when parsing. */
- if (!last_literal)
+ if (!last_literal && !last_slash)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Found two pattern field markers without separating literal.");
if (ret) {
}
mask_found |= bit;
- last_literal = at = false;
+ last_slash = last_literal = at = false;
continue;
}
+ if (*e == '/') {
+ if (ret) {
+ PatternElement *z;
+
+ z = malloc(offsetof(PatternElement, literal));
+ if (!z)
+ return -ENOMEM;
+
+ z->type = PATTERN_SLASH;
+ LIST_INSERT_AFTER(elements, first, last, z);
+ last = z;
+ }
+
+ last_literal = false;
+ last_slash = true;
+ continue ;
+ }
+
if (!valid_char(*e))
return log_debug_errno(
SYNTHETIC_ERRNO(EBADRQC),
(unsigned) *e);
last_literal = true;
+ last_slash = false;
if (!ret)
continue;
_cleanup_free_ char *t = NULL;
const char *n;
+ if (e->type == PATTERN_SLASH) {
+ if (*p == '/') {
+ ++p;
+ continue;
+ } else if (*p == '\0')
+ goto retry;
+ else
+ goto nope;
+ }
+
if (e->type == PATTERN_LITERAL) {
const char *k;
found = (InstanceMetadata) INSTANCE_METADATA_NULL;
}
- return true;
+ return PATTERN_MATCH_YES;
nope:
if (ret)
*ret = (InstanceMetadata) INSTANCE_METADATA_NULL;
- return false;
+ return PATTERN_MATCH_NO;
+
+retry:
+ if (ret)
+ *ret = (InstanceMetadata) INSTANCE_METADATA_NULL;
+
+ return PATTERN_MATCH_RETRY;
}
int pattern_match_many(char **patterns, const char *s, InstanceMetadata *ret) {
found = (InstanceMetadata) INSTANCE_METADATA_NULL;
}
- return true;
+ return r;
}
}
if (ret)
*ret = (InstanceMetadata) INSTANCE_METADATA_NULL;
- return false;
+ return PATTERN_MATCH_NO;
}
int pattern_valid(const char *pattern) {
switch (e->type) {
+ case PATTERN_SLASH:
+ if (!strextend(&j, "/"))
+ return -ENOMEM;
+
+ break;
+
case PATTERN_LITERAL:
if (!strextend(&j, e->literal))
return -ENOMEM;
#include "sysupdate-instance.h"
#include "time-util.h"
+enum {
+ PATTERN_MATCH_NO,
+ PATTERN_MATCH_YES,
+ PATTERN_MATCH_RETRY,
+};
+
int pattern_match(const char *pattern, const char *s, InstanceMetadata *ret);
int pattern_match_many(char **patterns, const char *s, InstanceMetadata *ret);
int pattern_valid(const char *pattern);
return 0;
}
-static int resource_load_from_directory(
+static int resource_load_from_directory_recursive(
Resource *rr,
+ DIR* d,
+ const char* relpath,
mode_t m) {
-
- _cleanup_closedir_ DIR *d = NULL;
int r;
- assert(rr);
- assert(IN_SET(rr->type, RESOURCE_TAR, RESOURCE_REGULAR_FILE, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME));
- assert(IN_SET(m, S_IFREG, S_IFDIR));
-
- d = opendir(rr->path);
- if (!d) {
- if (errno == ENOENT) {
- log_debug("Directory %s does not exist, not loading any resources.", rr->path);
- return 0;
- }
-
- return log_error_errno(errno, "Failed to open directory '%s': %m", rr->path);
- }
-
for (;;) {
_cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL;
- _cleanup_free_ char *joined = NULL;
+ _cleanup_free_ char *joined = NULL, *rel_joined = NULL;
Instance *instance;
struct dirent *de;
struct stat st;
break;
case DT_DIR:
- if (m != S_IFDIR)
+ if (!IN_SET(m, S_IFDIR, S_IFREG))
continue;
break;
return log_error_errno(errno, "Failed to stat %s/%s: %m", rr->path, de->d_name);
}
- if ((st.st_mode & S_IFMT) != m)
+ if (!(S_ISDIR(st.st_mode) && S_ISREG(m)) && ((st.st_mode & S_IFMT) != m))
continue;
- r = pattern_match_many(rr->patterns, de->d_name, &extracted_fields);
- if (r < 0)
+ rel_joined = path_join(relpath, de->d_name);
+ if (!rel_joined)
+ return log_oom();
+
+ r = pattern_match_many(rr->patterns, rel_joined, &extracted_fields);
+ if (r == PATTERN_MATCH_RETRY) {
+ _cleanup_closedir_ DIR *subdir = NULL;
+
+ subdir = xopendirat(dirfd(d), rel_joined, 0);
+ if (!subdir)
+ continue;
+
+ r = resource_load_from_directory_recursive(rr, subdir, rel_joined, m);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+ }
+ else if (r < 0)
return log_error_errno(r, "Failed to match pattern: %m");
- if (r == 0)
+ else if (r == PATTERN_MATCH_NO)
+ continue;
+
+ if (de->d_type == DT_DIR && m != S_IFDIR)
continue;
- joined = path_join(rr->path, de->d_name);
+ joined = path_join(rr->path, rel_joined);
if (!joined)
return log_oom();
return 0;
}
+static int resource_load_from_directory(
+ Resource *rr,
+ mode_t m) {
+ _cleanup_closedir_ DIR *d = NULL;
+
+ assert(rr);
+ assert(IN_SET(rr->type, RESOURCE_TAR, RESOURCE_REGULAR_FILE, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME));
+ assert(IN_SET(m, S_IFREG, S_IFDIR));
+
+ d = opendir(rr->path);
+ if (!d) {
+ if (errno == ENOENT) {
+ log_debug_errno(errno, "Directory %s does not exist, not loading any resources: %m", rr->path);
+ return 0;
+ }
+
+ return log_error_errno(errno, "Failed to open directory '%s': %m", rr->path);
+ }
+
+ return resource_load_from_directory_recursive(rr, d, NULL, m);
+}
+
static int resource_load_from_blockdev(Resource *rr) {
_cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
_cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
r = pattern_match_many(rr->patterns, pinfo.label, &extracted_fields);
if (r < 0)
return log_error_errno(r, "Failed to match pattern: %m");
- if (r == 0)
+ if (IN_SET(r, PATTERN_MATCH_NO, PATTERN_MATCH_RETRY))
continue;
r = resource_add_instance(rr, pinfo.device, &extracted_fields, &instance);
r = pattern_match_many(rr->patterns, fn, &extracted_fields);
if (r < 0)
return log_error_errno(r, "Failed to match pattern: %m");
- if (r > 0) {
+ if (r == PATTERN_MATCH_YES) {
_cleanup_free_ char *path = NULL;
r = import_url_append_component(rr->path, fn, &path);
#include "gpt.h"
#include "hexdecoct.h"
#include "install-file.h"
+#include "mkdir.h"
#include "parse-helpers.h"
#include "parse-util.h"
#include "process-util.h"
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "Failed to make room, deleting '%s' failed: %m", oldest->path);
+ (void) rmdir_parents(oldest->path, t->target.path);
+
break;
case RESOURCE_PARTITION: {
if (RESOURCE_IS_FILESYSTEM(t->target.type)) {
- if (!filename_is_valid(formatted_pattern))
+ if (!path_is_valid_full(formatted_pattern, /* accept_dot_dot = */ false))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as file name, refusing: %s", formatted_pattern);
t->final_path = path_join(t->target.path, formatted_pattern);
if (!t->final_path)
return log_oom();
+ r = mkdir_parents(t->final_path, 0755);
+ if (r < 0)
+ return log_error_errno(r, "Cannot create target directory: %m");
+
r = tempfn_random(t->final_path, "sysupdate", &t->temporary_path);
if (r < 0)
return log_error_errno(r, "Failed to generate temporary target path: %m");
# Create a random "UKI" payload
echo $RANDOM >"/var/tmp/72-source/uki-$2.efi"
+ # Create a random extra payload
+ echo $RANDOM >"/var/tmp/72-source/uki-extra-$2.efi"
+
# Create tarball of a directory
mkdir -p "/var/tmp/72-source/dir-$2"
echo $RANDOM >"/var/tmp/72-source/dir-$2/foo.txt"
cmp "/var/tmp/72-source/uki-$3.efi" "/var/tmp/72-xbootldr/EFI/Linux/uki_$3+3-0.efi"
test -z "$(ls -A /var/tmp/72-esp/EFI/Linux)"
+ # Check the extra efi
+ cmp "/var/tmp/72-source/uki-extra-$3.efi" "/var/tmp/72-xbootldr/EFI/Linux/uki_$3.efi.extra.d/extra.addon.efi"
+
# Check the directories
cmp "/var/tmp/72-source/dir-$3/foo.txt" /var/tmp/72-dirs/current/foo.txt
cmp "/var/tmp/72-source/dir-$3/bar.txt" /var/tmp/72-dirs/current/bar.txt
TriesLeft=3
TriesDone=0
InstancesMax=2
+EOF
+
+ cat >/var/tmp/72-defs/05-fifth.conf <<EOF
+[Source]
+Type=regular-file
+Path=/var/tmp/72-source
+MatchPattern=uki-extra-@v.efi
+
+[Target]
+Type=regular-file
+Path=/EFI/Linux
+PathRelativeTo=boot
+MatchPattern=uki_@v.efi.extra.d/extra.addon.efi
+Mode=0444
+InstancesMax=2
EOF
rm -rf /var/tmp/72-esp /var/tmp/72-xbootldr
update_now
verify_version "$blockdev" "$sector_size" v3 1 3
test ! -f "/var/tmp/72-xbootldr/EFI/Linux/uki_v1+3-0.efi"
+ test ! -f "/var/tmp/72-xbootldr/EFI/Linux/uki_v1.efi.extra.d/extra.addon.efi"
+ test ! -d "/var/tmp/72-xbootldr/EFI/Linux/uki_v1.efi.extra.d"
# Create fourth version, and update through a file:// URL. This should be
# almost as good as testing HTTP, but is simpler for us to set up. file:// is