1
0
mirror of https://github.com/systemd/systemd synced 2026-03-17 18:44:46 +01:00

Compare commits

..

No commits in common. "3fa3a4fd153c4ff836b3365d48adf02eac8c8221" and "51190631968f2a69acf5da3e3412b003805538f2" have entirely different histories.

12 changed files with 241 additions and 573 deletions

View File

@ -94,16 +94,6 @@
<xi:include href="version-info.xml" xpointer="v259"/></listitem> <xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>io.systemd.boot.timeout=</varname><replaceable>TIMEOUT</replaceable></term>
<listitem><para>This allows configuration of the menu timeout in seconds, and is read by <command>systemd-boot</command>.
For details see
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>

View File

@ -128,8 +128,7 @@ typedef struct {
size_t n_entries; size_t n_entries;
size_t idx_default; size_t idx_default;
size_t idx_default_efivar; size_t idx_default_efivar;
uint64_t timeout_sec; /* Actual timeout used (efi_main() override > smbios > efivar > config). */ uint64_t timeout_sec; /* Actual timeout used (efi_main() override > efivar > config). */
uint64_t timeout_sec_smbios;
uint64_t timeout_sec_config; uint64_t timeout_sec_config;
uint64_t timeout_sec_efivar; uint64_t timeout_sec_efivar;
char16_t *entry_default_config; char16_t *entry_default_config;
@ -324,7 +323,6 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
print_timeout_status(" timeout (config)", config->timeout_sec_config); print_timeout_status(" timeout (config)", config->timeout_sec_config);
print_timeout_status(" timeout (EFI var)", config->timeout_sec_efivar); print_timeout_status(" timeout (EFI var)", config->timeout_sec_efivar);
print_timeout_status(" timeout (smbios)", config->timeout_sec_smbios);
if (config->entry_default_config) if (config->entry_default_config)
printf(" default (config): %ls\n", config->entry_default_config); printf(" default (config): %ls\n", config->entry_default_config);
@ -1008,41 +1006,6 @@ static BootEntry* boot_entry_free(BootEntry *entry) {
DEFINE_TRIVIAL_CLEANUP_FUNC(BootEntry *, boot_entry_free); DEFINE_TRIVIAL_CLEANUP_FUNC(BootEntry *, boot_entry_free);
static EFI_STATUS config_timeout_sec_from_string(const char *value, uint64_t *dst) {
if (streq8(value, "menu-disabled"))
*dst = TIMEOUT_MENU_DISABLED;
else if (streq8(value, "menu-force"))
*dst = TIMEOUT_MENU_DISABLED;
else if (streq8(value, "menu-hidden"))
*dst = TIMEOUT_MENU_DISABLED;
else {
uint64_t u;
if (!parse_number8(value, &u, NULL) || u > TIMEOUT_TYPE_MAX)
return EFI_INVALID_PARAMETER;
*dst = u;
}
return EFI_SUCCESS;
}
static void config_timeout_load_from_smbios(Config *config) {
EFI_STATUS err;
if (is_confidential_vm())
return; /* Don't consume SMBIOS in Confidential Computing contexts */
const char *value = smbios_find_oem_string("io.systemd.boot.timeout=", /* after= */ NULL);
if (!value)
return;
err = config_timeout_sec_from_string(value, &config->timeout_sec_smbios);
if (err != EFI_SUCCESS) {
log_warning_status(err, "Error parsing 'timeout' smbios option, ignoring: %s",
value);
return;
}
config->timeout_sec = config->timeout_sec_smbios;
}
static void config_defaults_load_from_file(Config *config, char *content) { static void config_defaults_load_from_file(Config *config, char *content) {
char *line; char *line;
size_t pos = 0; size_t pos = 0;
@ -1055,11 +1018,20 @@ static void config_defaults_load_from_file(Config *config, char *content) {
* shared/bootspec.c@boot_loader_read_conf() to make parsing by bootctl/logind/etc. work. */ * shared/bootspec.c@boot_loader_read_conf() to make parsing by bootctl/logind/etc. work. */
while ((line = line_get_key_value(content, " \t", &pos, &key, &value))) while ((line = line_get_key_value(content, " \t", &pos, &key, &value)))
if (streq8(key, "timeout")) { if (streq8(key, "timeout")) {
EFI_STATUS err = config_timeout_sec_from_string(value, &config->timeout_sec_config); if (streq8(value, "menu-disabled"))
if (err != EFI_SUCCESS) { config->timeout_sec_config = TIMEOUT_MENU_DISABLED;
log_warning_status(err, "Error parsing 'timeout' config option, ignoring: %s", else if (streq8(value, "menu-force"))
value); config->timeout_sec_config = TIMEOUT_MENU_FORCE;
continue; else if (streq8(value, "menu-hidden"))
config->timeout_sec_config = TIMEOUT_MENU_HIDDEN;
else {
uint64_t u;
if (!parse_number8(value, &u, NULL) || u > TIMEOUT_TYPE_MAX) {
log_error("Error parsing 'timeout' config option, ignoring: %s",
value);
continue;
}
config->timeout_sec_config = u;
} }
config->timeout_sec = config->timeout_sec_config; config->timeout_sec = config->timeout_sec_config;
@ -1524,7 +1496,6 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
.console_mode_efivar = CONSOLE_MODE_KEEP, .console_mode_efivar = CONSOLE_MODE_KEEP,
.timeout_sec_config = TIMEOUT_UNSET, .timeout_sec_config = TIMEOUT_UNSET,
.timeout_sec_efivar = TIMEOUT_UNSET, .timeout_sec_efivar = TIMEOUT_UNSET,
.timeout_sec_smbios = TIMEOUT_UNSET,
}; };
err = file_read(root_dir, u"\\loader\\loader.conf", 0, 0, &content, &content_size); err = file_read(root_dir, u"\\loader\\loader.conf", 0, 0, &content, &content_size);
@ -1549,7 +1520,6 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
config->timeout_sec = config->timeout_sec_efivar; config->timeout_sec = config->timeout_sec_efivar;
else if (err != EFI_NOT_FOUND) else if (err != EFI_NOT_FOUND)
log_warning_status(err, "Error reading LoaderConfigTimeout EFI variable, ignoring: %m"); log_warning_status(err, "Error reading LoaderConfigTimeout EFI variable, ignoring: %m");
config_timeout_load_from_smbios(config);
err = efivar_get_timeout(u"LoaderConfigTimeoutOneShot", &config->timeout_sec); err = efivar_get_timeout(u"LoaderConfigTimeoutOneShot", &config->timeout_sec);
if (err == EFI_SUCCESS) { if (err == EFI_SUCCESS) {

View File

@ -3669,8 +3669,7 @@ static int pick_versions(
r = path_pick(/* toplevel_path= */ NULL, r = path_pick(/* toplevel_path= */ NULL,
/* toplevel_fd= */ AT_FDCWD, /* toplevel_fd= */ AT_FDCWD,
context->root_image, context->root_image,
pick_filter_image_raw, &pick_filter_image_raw,
ELEMENTSOF(pick_filter_image_raw),
PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE, PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
&result); &result);
if (r < 0) { if (r < 0) {
@ -3694,8 +3693,7 @@ static int pick_versions(
r = path_pick(/* toplevel_path= */ NULL, r = path_pick(/* toplevel_path= */ NULL,
/* toplevel_fd= */ AT_FDCWD, /* toplevel_fd= */ AT_FDCWD,
context->root_directory, context->root_directory,
pick_filter_image_dir, &pick_filter_image_dir,
ELEMENTSOF(pick_filter_image_dir),
PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE, PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
&result); &result);
if (r < 0) { if (r < 0) {

View File

@ -566,8 +566,7 @@ static int append_extensions(
r = path_pick(/* toplevel_path= */ NULL, r = path_pick(/* toplevel_path= */ NULL,
/* toplevel_fd= */ AT_FDCWD, /* toplevel_fd= */ AT_FDCWD,
m->source, m->source,
pick_filter_image_raw, &pick_filter_image_raw,
ELEMENTSOF(pick_filter_image_raw),
PICK_ARCHITECTURE|PICK_TRIES, PICK_ARCHITECTURE|PICK_TRIES,
&result); &result);
if (r == -ENOENT && m->ignore_enoent) if (r == -ENOENT && m->ignore_enoent)
@ -638,8 +637,7 @@ static int append_extensions(
r = path_pick(/* toplevel_path= */ NULL, r = path_pick(/* toplevel_path= */ NULL,
/* toplevel_fd= */ AT_FDCWD, /* toplevel_fd= */ AT_FDCWD,
e, e,
pick_filter_image_dir, &pick_filter_image_dir,
ELEMENTSOF(pick_filter_image_dir),
PICK_ARCHITECTURE|PICK_TRIES, PICK_ARCHITECTURE|PICK_TRIES,
&result); &result);
if (r == -ENOENT && ignore_enoent) if (r == -ENOENT && ignore_enoent)

View File

@ -2041,8 +2041,7 @@ static int run(int argc, char *argv[]) {
if (arg_image) { if (arg_image) {
r = path_pick_update_warn( r = path_pick_update_warn(
&arg_image, &arg_image,
pick_filter_image_raw, &pick_filter_image_raw,
ELEMENTSOF(pick_filter_image_raw),
PICK_ARCHITECTURE|PICK_TRIES, PICK_ARCHITECTURE|PICK_TRIES,
/* ret_result= */ NULL); /* ret_result= */ NULL);
if (r < 0) if (r < 0)
@ -2052,8 +2051,7 @@ static int run(int argc, char *argv[]) {
if (arg_root) { if (arg_root) {
r = path_pick_update_warn( r = path_pick_update_warn(
&arg_root, &arg_root,
pick_filter_image_dir, &pick_filter_image_dir,
ELEMENTSOF(pick_filter_image_dir),
PICK_ARCHITECTURE|PICK_TRIES, PICK_ARCHITECTURE|PICK_TRIES,
/* ret_result= */ NULL); /* ret_result= */ NULL);
if (r < 0) if (r < 0)

View File

@ -3013,14 +3013,13 @@ static int pick_paths(void) {
if (arg_directory) { if (arg_directory) {
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL; _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
PickFilter filter = *pick_filter_image_dir; PickFilter filter = pick_filter_image_dir;
filter.architecture = arg_architecture; filter.architecture = arg_architecture;
r = path_pick_update_warn( r = path_pick_update_warn(
&arg_directory, &arg_directory,
&filter, &filter,
/* n_filters= */ 1,
PICK_ARCHITECTURE|PICK_TRIES, PICK_ARCHITECTURE|PICK_TRIES,
&result); &result);
if (r < 0) { if (r < 0) {
@ -3033,14 +3032,13 @@ static int pick_paths(void) {
if (arg_image) { if (arg_image) {
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL; _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
PickFilter filter = *pick_filter_image_raw; PickFilter filter = pick_filter_image_raw;
filter.architecture = arg_architecture; filter.architecture = arg_architecture;
r = path_pick_update_warn( r = path_pick_update_warn(
&arg_image, &arg_image,
&filter, &filter,
/* n_filters= */ 1,
PICK_ARCHITECTURE|PICK_TRIES, PICK_ARCHITECTURE|PICK_TRIES,
&result); &result);
if (r < 0) if (r < 0)
@ -3051,14 +3049,13 @@ static int pick_paths(void) {
if (arg_template) { if (arg_template) {
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL; _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
PickFilter filter = *pick_filter_image_dir; PickFilter filter = pick_filter_image_dir;
filter.architecture = arg_architecture; filter.architecture = arg_architecture;
r = path_pick_update_warn( r = path_pick_update_warn(
&arg_template, &arg_template,
&filter, &filter,
/* n_filters= */ 1,
PICK_ARCHITECTURE, PICK_ARCHITECTURE,
&result); &result);
if (r < 0) if (r < 0)

View File

@ -595,8 +595,7 @@ static int extract_image_and_extensions(
r = path_pick(/* toplevel_path= */ NULL, r = path_pick(/* toplevel_path= */ NULL,
/* toplevel_fd= */ AT_FDCWD, /* toplevel_fd= */ AT_FDCWD,
name_or_path, name_or_path,
pick_filter_image_any, &pick_filter_image_any,
ELEMENTSOF(pick_filter_image_any),
PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE, PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
&result); &result);
if (r < 0) if (r < 0)
@ -634,8 +633,7 @@ static int extract_image_and_extensions(
r = path_pick(/* toplevel_path= */ NULL, r = path_pick(/* toplevel_path= */ NULL,
/* toplevel_fd= */ AT_FDCWD, /* toplevel_fd= */ AT_FDCWD,
*p, *p,
pick_filter_image_any, &pick_filter_image_any,
ELEMENTSOF(pick_filter_image_any),
PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE, PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
&ext_result); &ext_result);
if (r < 0) if (r < 0)
@ -1784,8 +1782,7 @@ static bool marker_matches_images(const char *marker, const char *name_or_path,
r = path_pick(/* toplevel_path= */ NULL, r = path_pick(/* toplevel_path= */ NULL,
/* toplevel_fd= */ AT_FDCWD, /* toplevel_fd= */ AT_FDCWD,
*image_name_or_path, *image_name_or_path,
pick_filter_image_any, &pick_filter_image_any,
ELEMENTSOF(pick_filter_image_any),
PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE, PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
&result); &result);
if (r < 0) if (r < 0)

View File

@ -821,7 +821,6 @@ int image_find(RuntimeScope scope,
/* toplevel_fd= */ AT_FDCWD, /* toplevel_fd= */ AT_FDCWD,
vp, vp,
&filter, &filter,
/* n_filters= */ 1,
PICK_ARCHITECTURE|PICK_TRIES, PICK_ARCHITECTURE|PICK_TRIES,
&result); &result);
if (r < 0) { if (r < 0) {
@ -1034,7 +1033,6 @@ int image_discover(
/* toplevel_fd= */ AT_FDCWD, /* toplevel_fd= */ AT_FDCWD,
vp, vp,
&filter, &filter,
/* n_filters= */ 1,
PICK_ARCHITECTURE|PICK_TRIES, PICK_ARCHITECTURE|PICK_TRIES,
&result); &result);
if (r < 0) { if (r < 0) {

View File

@ -26,53 +26,6 @@ void pick_result_done(PickResult *p) {
*p = PICK_RESULT_NULL; *p = PICK_RESULT_NULL;
} }
int pick_result_compare(const PickResult *a, const PickResult *b, PickFlags flags) {
int d;
assert(a);
assert(b);
/* Returns > 0 if 'a' is the better pick, < 0 if 'b' is the better pick, 0 if they are equal. */
/* Prefer entries with tries left over those without */
if (FLAGS_SET(flags, PICK_TRIES))
d = CMP(a->tries_left != 0, b->tries_left != 0);
else
d = 0;
/* Prefer newer versions */
if (d == 0)
d = strverscmp_improved(a->version, b->version);
if (FLAGS_SET(flags, PICK_ARCHITECTURE)) {
/* Prefer native architectures over non-native architectures */
if (d == 0)
d = CMP(a->architecture == native_architecture(), b->architecture == native_architecture());
/* Prefer secondary architectures over other architectures */
#ifdef ARCHITECTURE_SECONDARY
if (d == 0)
d = CMP(a->architecture == ARCHITECTURE_SECONDARY, b->architecture == ARCHITECTURE_SECONDARY);
#endif
}
/* Prefer entries with more tries left */
if (FLAGS_SET(flags, PICK_TRIES)) {
if (d == 0)
d = CMP(a->tries_left, b->tries_left);
/* Prefer entries with fewer attempts done so far */
if (d == 0)
d = -CMP(a->tries_done, b->tries_done);
}
/* Finally, just compare the filenames as strings */
if (d == 0)
d = path_compare_filename(a->path, b->path);
return d;
}
static int format_fname( static int format_fname(
const PickFilter *filter, const PickFilter *filter,
PickFlags flags, PickFlags flags,
@ -216,13 +169,13 @@ static int pin_choice(
return log_debug_errno(errno, "Failed to stat discovered inode '%s%s': %m", return log_debug_errno(errno, "Failed to stat discovered inode '%s%s': %m",
empty_to_root(toplevel_path), skip_leading_slash(inode_path)); empty_to_root(toplevel_path), skip_leading_slash(inode_path));
if (filter->type_mask != 0 && !BIT_SET(filter->type_mask, IFTODT(st.st_mode))) { if (filter->type_mask != 0 &&
log_debug("Inode '%s/%s' has wrong type, found '%s'.", !BIT_SET(filter->type_mask, IFTODT(st.st_mode)))
empty_to_root(toplevel_path), skip_leading_slash(inode_path), return log_debug_errno(
inode_type_to_string(st.st_mode)); SYNTHETIC_ERRNO(errno_from_mode(filter->type_mask, st.st_mode)),
*ret = PICK_RESULT_NULL; "Inode '%s/%s' has wrong type, found '%s'.",
return 0; empty_to_root(toplevel_path), skip_leading_slash(inode_path),
} inode_type_to_string(st.st_mode));
_cleanup_(pick_result_done) PickResult result = { _cleanup_(pick_result_done) PickResult result = {
.fd = TAKE_FD(inode_fd), .fd = TAKE_FD(inode_fd),
@ -295,23 +248,6 @@ nomatch:
return 0; return 0;
} }
static bool architecture_matches(const PickFilter *filter, Architecture a) {
assert(filter);
if (filter->architecture >= 0)
return a == filter->architecture;
if (a == native_architecture())
return true;
#ifdef ARCHITECTURE_SECONDARY
if (a == ARCHITECTURE_SECONDARY)
return true;
#endif
return a == _ARCHITECTURE_INVALID;
}
static int make_choice( static int make_choice(
const char *toplevel_path, const char *toplevel_path,
int toplevel_fd, int toplevel_fd,
@ -321,7 +257,21 @@ static int make_choice(
PickFlags flags, PickFlags flags,
PickResult *ret) { PickResult *ret) {
_cleanup_close_ int inode_fd = TAKE_FD(_inode_fd); static const Architecture local_architectures[] = {
/* In order of preference */
native_architecture(),
#ifdef ARCHITECTURE_SECONDARY
ARCHITECTURE_SECONDARY,
#endif
_ARCHITECTURE_INVALID, /* accept any arch, as last resort */
};
_cleanup_free_ DirectoryEntries *de = NULL;
_cleanup_free_ char *best_version = NULL, *best_filename = NULL, *p = NULL, *j = NULL;
_cleanup_close_ int dir_fd = -EBADF, object_fd = -EBADF, inode_fd = TAKE_FD(_inode_fd);
const Architecture *architectures;
unsigned best_tries_left = UINT_MAX, best_tries_done = UINT_MAX;
size_t n_architectures, best_architecture_index = SIZE_MAX;
int r; int r;
assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD); assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
@ -336,17 +286,15 @@ static int make_choice(
} }
/* Maybe the filter is fully specified? Then we can generate the file name directly */ /* Maybe the filter is fully specified? Then we can generate the file name directly */
_cleanup_free_ char *j = NULL;
r = format_fname(filter, flags, &j); r = format_fname(filter, flags, &j);
if (r >= 0) { if (r >= 0) {
_cleanup_free_ char *object_path = NULL; _cleanup_free_ char *object_path = NULL;
/* Yay! This worked! */ /* Yay! This worked! */
_cleanup_free_ char *p = path_join(inode_path, j); p = path_join(inode_path, j);
if (!p) if (!p)
return log_oom_debug(); return log_oom_debug();
_cleanup_close_ int object_fd = -EBADF;
r = chaseat(toplevel_fd, p, CHASE_AT_RESOLVE_IN_ROOT, &object_path, &object_fd); r = chaseat(toplevel_fd, p, CHASE_AT_RESOLVE_IN_ROOT, &object_path, &object_fd);
if (r == -ENOENT) { if (r == -ENOENT) {
*ret = PICK_RESULT_NULL; *ret = PICK_RESULT_NULL;
@ -373,22 +321,28 @@ static int make_choice(
/* Underspecified, so we do our enumeration dance */ /* Underspecified, so we do our enumeration dance */
/* Convert O_PATH to a regular directory fd */ /* Convert O_PATH to a regular directory fd */
_cleanup_close_ int dir_fd = fd_reopen(inode_fd, O_DIRECTORY|O_RDONLY|O_CLOEXEC); dir_fd = fd_reopen(inode_fd, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
if (dir_fd < 0) if (dir_fd < 0)
return log_debug_errno(dir_fd, "Failed to reopen '%s/%s' as directory: %m", return log_debug_errno(dir_fd, "Failed to reopen '%s/%s' as directory: %m",
empty_to_root(toplevel_path), skip_leading_slash(inode_path)); empty_to_root(toplevel_path), skip_leading_slash(inode_path));
_cleanup_free_ DirectoryEntries *de = NULL;
r = readdir_all(dir_fd, 0, &de); r = readdir_all(dir_fd, 0, &de);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to read directory '%s/%s': %m", return log_debug_errno(r, "Failed to read directory '%s/%s': %m",
empty_to_root(toplevel_path), skip_leading_slash(inode_path)); empty_to_root(toplevel_path), skip_leading_slash(inode_path));
_cleanup_(pick_result_done) PickResult best = PICK_RESULT_NULL; if (filter->architecture < 0) {
architectures = local_architectures;
n_architectures = ELEMENTSOF(local_architectures);
} else {
architectures = &filter->architecture;
n_architectures = 1;
}
FOREACH_ARRAY(entry, de->entries, de->n_entries) { FOREACH_ARRAY(entry, de->entries, de->n_entries) {
unsigned found_tries_done = UINT_MAX, found_tries_left = UINT_MAX; unsigned found_tries_done = UINT_MAX, found_tries_left = UINT_MAX;
_cleanup_free_ char *dname = NULL; _cleanup_free_ char *dname = NULL;
size_t found_architecture_index = SIZE_MAX;
char *e; char *e;
dname = strdup((*entry)->d_name); dname = strdup((*entry)->d_name);
@ -426,16 +380,20 @@ static int make_choice(
} }
} }
Architecture a = _ARCHITECTURE_INVALID;
if (FLAGS_SET(flags, PICK_ARCHITECTURE)) { if (FLAGS_SET(flags, PICK_ARCHITECTURE)) {
char *underscore = strrchr(e, '_'); char *underscore = strrchr(e, '_');
Architecture a;
if (underscore) a = underscore ? architecture_from_string(underscore + 1) : _ARCHITECTURE_INVALID;
a = architecture_from_string(underscore + 1);
if (!architecture_matches(filter, a)) { for (size_t i = 0; i < n_architectures; i++)
log_debug("Found entry with architecture '%s' which is not what we are looking for, ignoring entry.", if (architectures[i] == a) {
a < 0 ? "any" : architecture_to_string(a)); found_architecture_index = i;
break;
}
if (found_architecture_index == SIZE_MAX) { /* No matching arch found */
log_debug("Found entry with architecture '%s' which is not what we are looking for, ignoring entry.", a < 0 ? "any" : architecture_to_string(a));
continue; continue;
} }
@ -454,55 +412,89 @@ static int make_choice(
continue; continue;
} }
_cleanup_free_ char *p = path_join(inode_path, (*entry)->d_name); if (best_filename) { /* Already found one matching entry? Then figure out the better one */
if (!p) int d = 0;
return log_oom_debug();
_cleanup_(pick_result_done) PickResult found = PICK_RESULT_NULL; /* First, prefer entries with tries left over those without */
r = pin_choice(toplevel_path, if (FLAGS_SET(flags, PICK_TRIES))
toplevel_fd, d = CMP(found_tries_left != 0, best_tries_left != 0);
p,
/* _inode_fd= */ -EBADF, /* Second, prefer newer versions */
found_tries_left, if (d == 0)
found_tries_done, d = strverscmp_improved(e, best_version);
&(const PickFilter) {
.type_mask = filter->type_mask, /* Third, prefer native architectures over secondary architectures */
.basename = filter->basename, if (d == 0 &&
.version = empty_to_null(e), FLAGS_SET(flags, PICK_ARCHITECTURE) &&
.architecture = a, found_architecture_index != SIZE_MAX && best_architecture_index != SIZE_MAX)
.suffix = filter->suffix, d = -CMP(found_architecture_index, best_architecture_index);
},
flags, /* Fourth, prefer entries with more tries left */
&found); if (FLAGS_SET(flags, PICK_TRIES)) {
if (r == 0) if (d == 0)
continue; d = CMP(found_tries_left, best_tries_left);
/* Fifth, prefer entries with fewer attempts done so far */
if (d == 0)
d = -CMP(found_tries_done, best_tries_done);
}
/* Last, just compare the filenames as strings */
if (d == 0)
d = strcmp((*entry)->d_name, best_filename);
if (d < 0) {
log_debug("Found entry '%s' but previously found entry '%s' matches better, hence skipping entry.", (*entry)->d_name, best_filename);
continue;
}
}
r = free_and_strdup_warn(&best_version, e);
if (r < 0) if (r < 0)
return r; return r;
if (!best.path) { r = free_and_strdup_warn(&best_filename, (*entry)->d_name);
best = TAKE_PICK_RESULT(found); if (r < 0)
continue; return r;
}
if (pick_result_compare(&found, &best, flags) <= 0) { best_architecture_index = found_architecture_index;
log_debug("Found entry '%s' but previously found entry '%s' matches better, hence skipping entry.", found.path, best.path); best_tries_left = found_tries_left;
continue; best_tries_done = found_tries_done;
}
pick_result_done(&best);
best = TAKE_PICK_RESULT(found);
} }
if (!best.path) { /* Everything was good, but we didn't find any suitable entry */ if (!best_filename) { /* Everything was good, but we didn't find any suitable entry */
*ret = PICK_RESULT_NULL; *ret = PICK_RESULT_NULL;
return 0; return 0;
} }
*ret = TAKE_PICK_RESULT(best); p = path_join(inode_path, best_filename);
return 1; if (!p)
return log_oom_debug();
object_fd = openat(dir_fd, best_filename, O_CLOEXEC|O_PATH);
if (object_fd < 0)
return log_debug_errno(errno, "Failed to open '%s/%s': %m",
empty_to_root(toplevel_path), skip_leading_slash(inode_path));
return pin_choice(
toplevel_path,
toplevel_fd,
p,
TAKE_FD(object_fd),
best_tries_left,
best_tries_done,
&(const PickFilter) {
.type_mask = filter->type_mask,
.basename = filter->basename,
.version = empty_to_null(best_version),
.architecture = best_architecture_index != SIZE_MAX ? architectures[best_architecture_index] : _ARCHITECTURE_INVALID,
.suffix = filter->suffix,
},
flags,
ret);
} }
static int path_pick_one( int path_pick(
const char *toplevel_path, const char *toplevel_path,
int toplevel_fd, int toplevel_fd,
const char *path, const char *path,
@ -652,60 +644,9 @@ bypass:
ret); ret);
} }
int path_pick(const char *toplevel_path,
int toplevel_fd,
const char *path,
const PickFilter filters[],
size_t n_filters,
PickFlags flags,
PickResult *ret) {
_cleanup_(pick_result_done) PickResult best = PICK_RESULT_NULL;
int r;
assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
assert(path);
assert(filters || n_filters == 0);
assert(ret);
/* Iterate through all filters and pick the best result */
for (size_t i = 0; i < n_filters; i++) {
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
r = path_pick_one(toplevel_path, toplevel_fd, path, &filters[i], flags, &result);
if (r < 0)
return r;
if (r == 0)
continue;
if (!best.path) {
best = TAKE_PICK_RESULT(result);
continue;
}
if (pick_result_compare(&result, &best, flags) <= 0) {
log_debug("Found entry '%s' but previously found entry '%s' matches better, hence skipping entry.",
result.path, best.path);
continue;
}
pick_result_done(&best);
best = TAKE_PICK_RESULT(result);
}
if (!best.path) {
*ret = PICK_RESULT_NULL;
return 0;
}
*ret = TAKE_PICK_RESULT(best);
return 1;
}
int path_pick_update_warn( int path_pick_update_warn(
char **path, char **path,
const PickFilter filters[], const PickFilter *filter,
size_t n_filters,
PickFlags flags, PickFlags flags,
PickResult *ret_result) { PickResult *ret_result) {
@ -714,15 +655,14 @@ int path_pick_update_warn(
assert(path); assert(path);
assert(*path); assert(*path);
assert(filters || n_filters == 0); assert(filter);
/* This updates the first argument if needed! */ /* This updates the first argument if needed! */
r = path_pick(/* toplevel_path= */ NULL, r = path_pick(/* toplevel_path= */ NULL,
/* toplevel_fd= */ AT_FDCWD, /* toplevel_fd= */ AT_FDCWD,
*path, *path,
filters, filter,
n_filters,
flags, flags,
&result); &result);
if (r == -ENOENT) { if (r == -ENOENT) {
@ -786,17 +726,19 @@ int path_uses_vpick(const char *path) {
return !!endswith(parent, ".v"); return !!endswith(parent, ".v");
} }
const PickFilter pick_filter_image_raw[1] = { const PickFilter pick_filter_image_raw = {
{ .type_mask = (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK),
.type_mask = (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK), .architecture = _ARCHITECTURE_INVALID,
.architecture = _ARCHITECTURE_INVALID, .suffix = STRV_MAKE(".raw"),
.suffix = STRV_MAKE(".raw"),
},
}; };
const PickFilter pick_filter_image_dir[1] = { const PickFilter pick_filter_image_dir = {
{ .type_mask = UINT32_C(1) << DT_DIR,
.type_mask = UINT32_C(1) << DT_DIR, .architecture = _ARCHITECTURE_INVALID,
.architecture = _ARCHITECTURE_INVALID, };
},
const PickFilter pick_filter_image_any = {
.type_mask = (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) | (UINT32_C(1) << DT_DIR),
.architecture = _ARCHITECTURE_INVALID,
.suffix = STRV_MAKE(".raw", ""),
}; };

View File

@ -43,29 +43,22 @@ typedef struct PickResult {
void pick_result_done(PickResult *p); void pick_result_done(PickResult *p);
int pick_result_compare(const PickResult *a, const PickResult *b, PickFlags flags); int path_pick(
const char *toplevel_path,
int path_pick(const char *toplevel_path, int toplevel_fd,
int toplevel_fd, const char *path,
const char *path, const PickFilter *filter,
const PickFilter filters[], PickFlags flags,
size_t n_filters, PickResult *ret);
PickFlags flags,
PickResult *ret);
int path_pick_update_warn( int path_pick_update_warn(
char **path, char **path,
const PickFilter filters[], const PickFilter *filter,
size_t n_filters,
PickFlags flags, PickFlags flags,
PickResult *ret_result); PickResult *ret_result);
int path_uses_vpick(const char *path); int path_uses_vpick(const char *path);
extern const PickFilter pick_filter_image_raw[1]; extern const PickFilter pick_filter_image_raw;
extern const PickFilter pick_filter_image_dir[1]; extern const PickFilter pick_filter_image_dir;
extern const PickFilter pick_filter_image_any;
#define pick_filter_image_any (const PickFilter[]) { \
pick_filter_image_raw[0], \
pick_filter_image_dir[0], \
}

View File

@ -15,30 +15,34 @@ TEST(path_pick) {
_cleanup_(rm_rf_physical_and_freep) char *p = NULL; _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
_cleanup_close_ int dfd = -EBADF, sub_dfd = -EBADF; _cleanup_close_ int dfd = -EBADF, sub_dfd = -EBADF;
dfd = ASSERT_OK(mkdtemp_open(NULL, O_DIRECTORY|O_CLOEXEC, &p)); dfd = mkdtemp_open(NULL, O_DIRECTORY|O_CLOEXEC, &p);
sub_dfd = ASSERT_OK(open_mkdir_at(dfd, "foo.v", O_CLOEXEC, 0777)); assert(dfd >= 0);
ASSERT_OK(write_string_file_at(sub_dfd, "foo_5.5.raw", "5.5", WRITE_STRING_FILE_CREATE)); sub_dfd = open_mkdir_at(dfd, "foo.v", O_CLOEXEC, 0777);
ASSERT_OK(write_string_file_at(sub_dfd, "foo_55.raw", "55", WRITE_STRING_FILE_CREATE)); assert(sub_dfd >= 0);
ASSERT_OK(write_string_file_at(sub_dfd, "foo_5.raw", "5", WRITE_STRING_FILE_CREATE));
ASSERT_OK(write_string_file_at(sub_dfd, "foo_5_ia64.raw", "5", WRITE_STRING_FILE_CREATE)); assert_se(write_string_file_at(sub_dfd, "foo_5.5.raw", "5.5", WRITE_STRING_FILE_CREATE) >= 0);
ASSERT_OK(write_string_file_at(sub_dfd, "foo_7.raw", "7", WRITE_STRING_FILE_CREATE)); assert_se(write_string_file_at(sub_dfd, "foo_55.raw", "55", WRITE_STRING_FILE_CREATE) >= 0);
ASSERT_OK(write_string_file_at(sub_dfd, "foo_7_x86-64.raw", "7 64bit", WRITE_STRING_FILE_CREATE)); assert_se(write_string_file_at(sub_dfd, "foo_5.raw", "5", WRITE_STRING_FILE_CREATE) >= 0);
ASSERT_OK(write_string_file_at(sub_dfd, "foo_55_x86-64.raw", "55 64bit", WRITE_STRING_FILE_CREATE)); assert_se(write_string_file_at(sub_dfd, "foo_5_ia64.raw", "5", WRITE_STRING_FILE_CREATE) >= 0);
ASSERT_OK(write_string_file_at(sub_dfd, "foo_55_x86.raw", "55 32bit", WRITE_STRING_FILE_CREATE)); assert_se(write_string_file_at(sub_dfd, "foo_7.raw", "7", WRITE_STRING_FILE_CREATE) >= 0);
ASSERT_OK(write_string_file_at(sub_dfd, "foo_99_x86.raw", "99 32bit", WRITE_STRING_FILE_CREATE)); assert_se(write_string_file_at(sub_dfd, "foo_7_x86-64.raw", "7 64bit", WRITE_STRING_FILE_CREATE) >= 0);
assert_se(write_string_file_at(sub_dfd, "foo_55_x86-64.raw", "55 64bit", WRITE_STRING_FILE_CREATE) >= 0);
assert_se(write_string_file_at(sub_dfd, "foo_55_x86.raw", "55 32bit", WRITE_STRING_FILE_CREATE) >= 0);
assert_se(write_string_file_at(sub_dfd, "foo_99_x86.raw", "99 32bit", WRITE_STRING_FILE_CREATE) >= 0);
/* Let's add an entry for sparc (which is a valid arch, but almost certainly not what we test /* Let's add an entry for sparc (which is a valid arch, but almost certainly not what we test
* on). This entry should hence always be ignored */ * on). This entry should hence always be ignored */
if (native_architecture() != ARCHITECTURE_SPARC) if (native_architecture() != ARCHITECTURE_SPARC)
ASSERT_OK(write_string_file_at(sub_dfd, "foo_100_sparc.raw", "100 sparc", WRITE_STRING_FILE_CREATE)); assert_se(write_string_file_at(sub_dfd, "foo_100_sparc.raw", "100 sparc", WRITE_STRING_FILE_CREATE) >= 0);
ASSERT_OK(write_string_file_at(sub_dfd, "quux_1_s390.raw", "waldo1", WRITE_STRING_FILE_CREATE)); assert_se(write_string_file_at(sub_dfd, "quux_1_s390.raw", "waldo1", WRITE_STRING_FILE_CREATE) >= 0);
ASSERT_OK(write_string_file_at(sub_dfd, "quux_2_s390+4-6.raw", "waldo2", WRITE_STRING_FILE_CREATE)); assert_se(write_string_file_at(sub_dfd, "quux_2_s390+4-6.raw", "waldo2", WRITE_STRING_FILE_CREATE) >= 0);
ASSERT_OK(write_string_file_at(sub_dfd, "quux_3_s390+0-10.raw", "waldo3", WRITE_STRING_FILE_CREATE)); assert_se(write_string_file_at(sub_dfd, "quux_3_s390+0-10.raw", "waldo3", WRITE_STRING_FILE_CREATE) >= 0);
_cleanup_free_ char *pp = NULL; _cleanup_free_ char *pp = NULL;
pp = ASSERT_NOT_NULL(path_join(p, "foo.v")); pp = path_join(p, "foo.v");
assert_se(pp);
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL; _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
@ -48,118 +52,121 @@ TEST(path_pick) {
}; };
if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) { if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result)); assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
ASSERT_TRUE(S_ISREG(result.st.st_mode)); assert_se(S_ISREG(result.st.st_mode));
ASSERT_STREQ(result.version, "99"); ASSERT_STREQ(result.version, "99");
ASSERT_EQ(result.architecture, ARCHITECTURE_X86); assert_se(result.architecture == ARCHITECTURE_X86);
ASSERT_TRUE(endswith(result.path, "/foo_99_x86.raw")); assert_se(endswith(result.path, "/foo_99_x86.raw"));
pick_result_done(&result); pick_result_done(&result);
} }
filter.architecture = ARCHITECTURE_X86_64; filter.architecture = ARCHITECTURE_X86_64;
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result)); assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
ASSERT_TRUE(S_ISREG(result.st.st_mode)); assert_se(S_ISREG(result.st.st_mode));
ASSERT_STREQ(result.version, "55"); ASSERT_STREQ(result.version, "55");
ASSERT_EQ(result.architecture, ARCHITECTURE_X86_64); assert_se(result.architecture == ARCHITECTURE_X86_64);
ASSERT_TRUE(endswith(result.path, "/foo_55_x86-64.raw")); assert_se(endswith(result.path, "/foo_55_x86-64.raw"));
pick_result_done(&result); pick_result_done(&result);
filter.architecture = ARCHITECTURE_IA64; filter.architecture = ARCHITECTURE_IA64;
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result)); assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
ASSERT_TRUE(S_ISREG(result.st.st_mode)); assert_se(S_ISREG(result.st.st_mode));
ASSERT_STREQ(result.version, "5"); ASSERT_STREQ(result.version, "5");
ASSERT_EQ(result.architecture, ARCHITECTURE_IA64); assert_se(result.architecture == ARCHITECTURE_IA64);
ASSERT_TRUE(endswith(result.path, "/foo_5_ia64.raw")); assert_se(endswith(result.path, "/foo_5_ia64.raw"));
pick_result_done(&result); pick_result_done(&result);
filter.architecture = _ARCHITECTURE_INVALID; filter.architecture = _ARCHITECTURE_INVALID;
filter.version = "5"; filter.version = "5";
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result)); assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
ASSERT_TRUE(S_ISREG(result.st.st_mode)); assert_se(S_ISREG(result.st.st_mode));
ASSERT_STREQ(result.version, "5"); ASSERT_STREQ(result.version, "5");
if (native_architecture() != ARCHITECTURE_IA64) { if (native_architecture() != ARCHITECTURE_IA64) {
ASSERT_EQ(result.architecture, _ARCHITECTURE_INVALID); assert_se(result.architecture == _ARCHITECTURE_INVALID);
ASSERT_TRUE(endswith(result.path, "/foo_5.raw")); assert_se(endswith(result.path, "/foo_5.raw"));
} }
pick_result_done(&result); pick_result_done(&result);
filter.architecture = ARCHITECTURE_IA64; filter.architecture = ARCHITECTURE_IA64;
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result)); assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
ASSERT_TRUE(S_ISREG(result.st.st_mode)); assert_se(S_ISREG(result.st.st_mode));
ASSERT_STREQ(result.version, "5"); ASSERT_STREQ(result.version, "5");
ASSERT_EQ(result.architecture, ARCHITECTURE_IA64); assert_se(result.architecture == ARCHITECTURE_IA64);
ASSERT_TRUE(endswith(result.path, "/foo_5_ia64.raw")); assert_se(endswith(result.path, "/foo_5_ia64.raw"));
pick_result_done(&result); pick_result_done(&result);
filter.architecture = ARCHITECTURE_CRIS; filter.architecture = ARCHITECTURE_CRIS;
ASSERT_OK_ZERO(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result)); assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) == 0);
ASSERT_EQ(result.st.st_mode, MODE_INVALID); assert_se(result.st.st_mode == MODE_INVALID);
ASSERT_NULL(result.version); assert_se(!result.version);
ASSERT_LT(result.architecture, 0); assert_se(result.architecture < 0);
ASSERT_NULL(result.path); assert_se(!result.path);
ASSERT_OK_ERRNO(unlinkat(sub_dfd, "foo_99_x86.raw", 0)); assert_se(unlinkat(sub_dfd, "foo_99_x86.raw", 0) >= 0);
filter.architecture = _ARCHITECTURE_INVALID; filter.architecture = _ARCHITECTURE_INVALID;
filter.version = NULL; filter.version = NULL;
if (IN_SET(native_architecture(), ARCHITECTURE_X86_64, ARCHITECTURE_X86)) { if (IN_SET(native_architecture(), ARCHITECTURE_X86_64, ARCHITECTURE_X86)) {
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result)); assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
ASSERT_TRUE(S_ISREG(result.st.st_mode)); assert_se(S_ISREG(result.st.st_mode));
ASSERT_STREQ(result.version, "55"); ASSERT_STREQ(result.version, "55");
if (native_architecture() == ARCHITECTURE_X86_64) { if (native_architecture() == ARCHITECTURE_X86_64) {
ASSERT_EQ(result.architecture, ARCHITECTURE_X86_64); assert_se(result.architecture == ARCHITECTURE_X86_64);
ASSERT_TRUE(endswith(result.path, "/foo_55_x86-64.raw")); assert_se(endswith(result.path, "/foo_55_x86-64.raw"));
} else { } else {
ASSERT_EQ(result.architecture, ARCHITECTURE_X86); assert_se(result.architecture == ARCHITECTURE_X86);
ASSERT_TRUE(endswith(result.path, "/foo_55_x86.raw")); assert_se(endswith(result.path, "/foo_55_x86.raw"));
} }
pick_result_done(&result); pick_result_done(&result);
} }
/* Test explicit patterns in last component of path not being .v */ /* Test explicit patterns in last component of path not being .v */
free(pp); free(pp);
pp = ASSERT_NOT_NULL(path_join(p, "foo.v/foo___.raw")); pp = path_join(p, "foo.v/foo___.raw");
assert_se(pp);
if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) { if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result)); assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
ASSERT_TRUE(S_ISREG(result.st.st_mode)); assert_se(S_ISREG(result.st.st_mode));
ASSERT_STREQ(result.version, "55"); ASSERT_STREQ(result.version, "55");
ASSERT_EQ(result.architecture, native_architecture()); assert_se(result.architecture == native_architecture());
ASSERT_TRUE(endswith(result.path, ".raw")); assert_se(endswith(result.path, ".raw"));
ASSERT_TRUE(!!strrstr(result.path, "/foo_55_x86")); assert_se(strrstr(result.path, "/foo_55_x86"));
pick_result_done(&result); pick_result_done(&result);
} }
/* Specify an explicit path */ /* Specify an explicit path */
free(pp); free(pp);
pp = ASSERT_NOT_NULL(path_join(p, "foo.v/foo_5.raw")); pp = path_join(p, "foo.v/foo_5.raw");
assert_se(pp);
filter.type_mask = UINT32_C(1) << DT_DIR; filter.type_mask = UINT32_C(1) << DT_DIR;
ASSERT_OK_ZERO(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result)); assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) == -ENOTDIR);
filter.type_mask = UINT32_C(1) << DT_REG; filter.type_mask = UINT32_C(1) << DT_REG;
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result)); assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
ASSERT_TRUE(S_ISREG(result.st.st_mode)); assert_se(S_ISREG(result.st.st_mode));
ASSERT_NULL(result.version); assert_se(!result.version);
ASSERT_EQ(result.architecture, _ARCHITECTURE_INVALID); assert_se(result.architecture == _ARCHITECTURE_INVALID);
ASSERT_TRUE(path_equal(result.path, pp)); assert_se(path_equal(result.path, pp));
pick_result_done(&result); pick_result_done(&result);
free(pp); free(pp);
pp = ASSERT_NOT_NULL(path_join(p, "foo.v")); pp = path_join(p, "foo.v");
assert_se(pp);
filter.architecture = ARCHITECTURE_S390; filter.architecture = ARCHITECTURE_S390;
filter.basename = "quux"; filter.basename = "quux";
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result)); assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
ASSERT_TRUE(S_ISREG(result.st.st_mode)); assert_se(S_ISREG(result.st.st_mode));
ASSERT_STREQ(result.version, "2"); ASSERT_STREQ(result.version, "2");
ASSERT_EQ(result.tries_left, 4U); assert_se(result.tries_left == 4);
ASSERT_EQ(result.tries_done, 6U); assert_se(result.tries_done == 6);
ASSERT_TRUE(endswith(result.path, "quux_2_s390+4-6.raw")); assert_se(endswith(result.path, "quux_2_s390+4-6.raw"));
ASSERT_EQ(result.architecture, ARCHITECTURE_S390); assert_se(result.architecture == ARCHITECTURE_S390);
} }
TEST(path_uses_vpick) { TEST(path_uses_vpick) {
@ -185,223 +192,4 @@ TEST(path_uses_vpick) {
ASSERT_ERROR(path_uses_vpick(""), EINVAL); ASSERT_ERROR(path_uses_vpick(""), EINVAL);
} }
TEST(pick_filter_image_any) {
_cleanup_(rm_rf_physical_and_freep) char *p = NULL;
_cleanup_close_ int dfd = ASSERT_OK(mkdtemp_open(NULL, O_DIRECTORY|O_CLOEXEC, &p));
_cleanup_close_ int sub_dfd = ASSERT_OK(open_mkdir_at(dfd, "test.v", O_CLOEXEC, 0777));
/* Create .raw files (should match with pick_filter_image_raw and pick_filter_image_any) */
ASSERT_OK(write_string_file_at(sub_dfd, "test_1.raw", "version 1 raw", WRITE_STRING_FILE_CREATE));
ASSERT_OK(write_string_file_at(sub_dfd, "test_2.raw", "version 2 raw", WRITE_STRING_FILE_CREATE));
ASSERT_OK(write_string_file_at(sub_dfd, "test_3.raw", "version 3 raw", WRITE_STRING_FILE_CREATE));
/* Create directories (should match with pick_filter_image_dir and pick_filter_image_any) */
ASSERT_OK(mkdirat(sub_dfd, "test_4", 0755));
ASSERT_OK(mkdirat(sub_dfd, "test_5", 0755));
/* Create files without .raw suffix (should NOT match any of the pick_filter_image_* filters) */
ASSERT_OK(write_string_file_at(sub_dfd, "test_10.txt", "version 10 txt", WRITE_STRING_FILE_CREATE));
ASSERT_OK(write_string_file_at(sub_dfd, "test_11.img", "version 11 img", WRITE_STRING_FILE_CREATE));
ASSERT_OK(write_string_file_at(sub_dfd, "test_12", "version 12", WRITE_STRING_FILE_CREATE));
_cleanup_free_ char *pp = ASSERT_NOT_NULL(path_join(p, "test.v"));
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
/* Test pick_filter_image_any: should pick the highest version, which is the directory test_5 */
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_any, ELEMENTSOF(pick_filter_image_any), PICK_ARCHITECTURE, &result));
ASSERT_TRUE(S_ISDIR(result.st.st_mode));
ASSERT_STREQ(result.version, "5");
ASSERT_TRUE(endswith(result.path, "/test_5"));
pick_result_done(&result);
/* Remove directories, now it should pick the highest .raw file (test_3.raw) */
ASSERT_OK(unlinkat(sub_dfd, "test_4", AT_REMOVEDIR));
ASSERT_OK(unlinkat(sub_dfd, "test_5", AT_REMOVEDIR));
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_any, ELEMENTSOF(pick_filter_image_any), PICK_ARCHITECTURE, &result));
ASSERT_TRUE(S_ISREG(result.st.st_mode));
ASSERT_STREQ(result.version, "3");
ASSERT_TRUE(endswith(result.path, "/test_3.raw"));
pick_result_done(&result);
/* Verify that pick_filter_image_raw only matches .raw files */
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_raw, ELEMENTSOF(pick_filter_image_raw), PICK_ARCHITECTURE, &result));
ASSERT_TRUE(S_ISREG(result.st.st_mode));
ASSERT_STREQ(result.version, "3");
ASSERT_TRUE(endswith(result.path, "/test_3.raw"));
pick_result_done(&result);
/* Verify that files without .raw suffix are never picked by pick_filter_image_any */
/* Remove all .raw files */
ASSERT_OK(unlinkat(sub_dfd, "test_1.raw", 0));
ASSERT_OK(unlinkat(sub_dfd, "test_2.raw", 0));
ASSERT_OK(unlinkat(sub_dfd, "test_3.raw", 0));
/* Now only test_10.txt, test_11.img, and test_12 remain - none should match */
ASSERT_OK_ZERO(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_any, ELEMENTSOF(pick_filter_image_any), PICK_ARCHITECTURE, &result));
/* But if we add a directory, it should be picked */
ASSERT_OK(mkdirat(sub_dfd, "test_6", 0755));
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_any, ELEMENTSOF(pick_filter_image_any), PICK_ARCHITECTURE, &result));
ASSERT_TRUE(S_ISDIR(result.st.st_mode));
ASSERT_STREQ(result.version, "6");
ASSERT_TRUE(endswith(result.path, "/test_6"));
pick_result_done(&result);
/* Now test pick_filter_image_dir with a separate directory structure */
safe_close(sub_dfd);
sub_dfd = ASSERT_OK(open_mkdir_at(dfd, "myimage.v", O_CLOEXEC, 0777));
/* Create directories that pick_filter_image_dir should find */
ASSERT_OK(mkdirat(sub_dfd, "myimage_1", 0755));
ASSERT_OK(mkdirat(sub_dfd, "myimage_2", 0755));
free(pp);
pp = ASSERT_NOT_NULL(path_join(p, "myimage.v"));
pick_result_done(&result);
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_dir, ELEMENTSOF(pick_filter_image_dir), PICK_ARCHITECTURE, &result));
ASSERT_TRUE(S_ISDIR(result.st.st_mode));
ASSERT_STREQ(result.version, "2");
ASSERT_TRUE(endswith(result.path, "/myimage_2"));
pick_result_done(&result);
/* With no directories, pick_filter_image_dir should return nothing */
ASSERT_OK(unlinkat(sub_dfd, "myimage_1", AT_REMOVEDIR));
ASSERT_OK(unlinkat(sub_dfd, "myimage_2", AT_REMOVEDIR));
ASSERT_OK_ZERO(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_dir, ELEMENTSOF(pick_filter_image_dir), PICK_ARCHITECTURE, &result));
}
TEST(path_pick_resolve) {
_cleanup_(rm_rf_physical_and_freep) char *p = NULL;
_cleanup_close_ int dfd = ASSERT_OK(mkdtemp_open(NULL, O_DIRECTORY|O_CLOEXEC, &p));
_cleanup_close_ int sub_dfd = ASSERT_OK(open_mkdir_at(dfd, "resolve.v", O_CLOEXEC, 0777));
/* Create a target directory and file for symlinks */
ASSERT_OK(mkdirat(dfd, "target_dir", 0755));
ASSERT_OK(write_string_file_at(dfd, "target_file.raw", "target content", WRITE_STRING_FILE_CREATE));
/* Create symlinks inside the .v directory pointing to targets outside */
ASSERT_OK(symlinkat("../target_dir", sub_dfd, "resolve_1"));
ASSERT_OK(symlinkat("../target_file.raw", sub_dfd, "resolve_2.raw"));
_cleanup_free_ char *pp = ASSERT_NOT_NULL(path_join(p, "resolve.v"));
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
/* Test without PICK_RESOLVE - should return the symlink path */
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_any, ELEMENTSOF(pick_filter_image_any), PICK_ARCHITECTURE, &result));
ASSERT_STREQ(result.version, "2");
ASSERT_TRUE(endswith(result.path, "/resolve_2.raw"));
pick_result_done(&result);
/* Test with PICK_RESOLVE - should return the resolved (target) path */
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_any, ELEMENTSOF(pick_filter_image_any), PICK_ARCHITECTURE|PICK_RESOLVE, &result));
ASSERT_STREQ(result.version, "2");
ASSERT_TRUE(endswith(result.path, "/target_file.raw"));
pick_result_done(&result);
/* Test pick_filter_image_dir without PICK_RESOLVE */
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_dir, ELEMENTSOF(pick_filter_image_dir), PICK_ARCHITECTURE, &result));
ASSERT_TRUE(S_ISDIR(result.st.st_mode));
ASSERT_STREQ(result.version, "1");
ASSERT_TRUE(endswith(result.path, "/resolve_1"));
pick_result_done(&result);
/* Test pick_filter_image_dir with PICK_RESOLVE */
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_dir, ELEMENTSOF(pick_filter_image_dir), PICK_ARCHITECTURE|PICK_RESOLVE, &result));
ASSERT_TRUE(S_ISDIR(result.st.st_mode));
ASSERT_STREQ(result.version, "1");
ASSERT_TRUE(endswith(result.path, "/target_dir"));
pick_result_done(&result);
/* Test with a chain of symlinks */
ASSERT_OK(symlinkat("target_file.raw", dfd, "intermediate_link.raw"));
ASSERT_OK(symlinkat("../intermediate_link.raw", sub_dfd, "resolve_3.raw"));
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_raw, ELEMENTSOF(pick_filter_image_raw), PICK_ARCHITECTURE, &result));
ASSERT_STREQ(result.version, "3");
ASSERT_TRUE(endswith(result.path, "/resolve_3.raw"));
pick_result_done(&result);
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, pick_filter_image_raw, ELEMENTSOF(pick_filter_image_raw), PICK_ARCHITECTURE|PICK_RESOLVE, &result));
ASSERT_STREQ(result.version, "3");
/* The chain should be fully resolved to target_file.raw */
ASSERT_TRUE(endswith(result.path, "/target_file.raw"));
pick_result_done(&result);
}
TEST(pick_result_compare) {
PickResult a = PICK_RESULT_NULL, b = PICK_RESULT_NULL;
/* When everything is equal, compare paths */
a.path = (char*) "/a";
b.path = (char*) "/b";
ASSERT_LT(pick_result_compare(&a, &b, 0), 0);
ASSERT_GT(pick_result_compare(&b, &a, 0), 0);
ASSERT_EQ(pick_result_compare(&a, &a, 0), 0);
/* Prefer newer versions */
a.version = (char*) "1";
b.version = (char*) "2";
ASSERT_LT(pick_result_compare(&a, &b, 0), 0);
ASSERT_GT(pick_result_compare(&b, &a, 0), 0);
a.version = b.version = NULL;
/* Prefer entries with tries left over those without (only with PICK_TRIES) */
a.tries_left = 0;
b.tries_left = 1;
ASSERT_LT(pick_result_compare(&a, &b, 0), 0); /* Without PICK_TRIES, paths are compared */
ASSERT_LT(pick_result_compare(&a, &b, PICK_TRIES), 0);
ASSERT_GT(pick_result_compare(&b, &a, PICK_TRIES), 0);
/* Prefer entries with more tries left */
a.tries_left = 1;
b.tries_left = 5;
ASSERT_LT(pick_result_compare(&a, &b, PICK_TRIES), 0);
ASSERT_GT(pick_result_compare(&b, &a, PICK_TRIES), 0);
/* Prefer entries with fewer attempts done */
a.tries_left = b.tries_left = 3;
a.tries_done = 5;
b.tries_done = 1;
ASSERT_LT(pick_result_compare(&a, &b, PICK_TRIES), 0);
ASSERT_GT(pick_result_compare(&b, &a, PICK_TRIES), 0);
a.tries_left = b.tries_left = UINT_MAX;
a.tries_done = b.tries_done = UINT_MAX;
/* Prefer native architecture (only with PICK_ARCHITECTURE) */
a.architecture = native_architecture();
b.architecture = ARCHITECTURE_ALPHA; /* Unlikely to be native */
if (native_architecture() != ARCHITECTURE_ALPHA) {
ASSERT_LT(pick_result_compare(&a, &b, 0), 0); /* Without PICK_ARCHITECTURE, paths are compared */
ASSERT_GT(pick_result_compare(&a, &b, PICK_ARCHITECTURE), 0);
ASSERT_LT(pick_result_compare(&b, &a, PICK_ARCHITECTURE), 0);
}
a.architecture = b.architecture = _ARCHITECTURE_INVALID;
/* Version takes precedence over architecture */
a.version = (char*) "1";
b.version = (char*) "2";
a.architecture = native_architecture();
b.architecture = ARCHITECTURE_ALPHA;
if (native_architecture() != ARCHITECTURE_ALPHA)
ASSERT_LT(pick_result_compare(&a, &b, PICK_ARCHITECTURE), 0); /* b wins due to higher version */
a.version = b.version = NULL;
a.architecture = b.architecture = _ARCHITECTURE_INVALID;
/* Tries left takes precedence over version */
a.tries_left = 0;
b.tries_left = 1;
a.version = (char*) "2";
b.version = (char*) "1";
ASSERT_LT(pick_result_compare(&a, &b, PICK_TRIES), 0); /* b wins due to tries left */
a.tries_left = b.tries_left = UINT_MAX;
a.version = b.version = NULL;
}
DEFINE_TEST_MAIN(LOG_DEBUG); DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -248,7 +248,6 @@ static int run(int argc, char *argv[]) {
.suffix = STRV_MAKE(arg_filter_suffix), .suffix = STRV_MAKE(arg_filter_suffix),
.type_mask = arg_filter_type_mask, .type_mask = arg_filter_type_mask,
}, },
/* n_filters= */ 1,
arg_flags, arg_flags,
&result); &result);
if (r < 0) if (r < 0)