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.
3fa3a4fd15
...
5119063196
@ -94,16 +94,6 @@
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||
</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>
|
||||
</refsect1>
|
||||
|
||||
|
||||
@ -128,8 +128,7 @@ typedef struct {
|
||||
size_t n_entries;
|
||||
size_t idx_default;
|
||||
size_t idx_default_efivar;
|
||||
uint64_t timeout_sec; /* Actual timeout used (efi_main() override > smbios > efivar > config). */
|
||||
uint64_t timeout_sec_smbios;
|
||||
uint64_t timeout_sec; /* Actual timeout used (efi_main() override > efivar > config). */
|
||||
uint64_t timeout_sec_config;
|
||||
uint64_t timeout_sec_efivar;
|
||||
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 (EFI var)", config->timeout_sec_efivar);
|
||||
print_timeout_status(" timeout (smbios)", config->timeout_sec_smbios);
|
||||
|
||||
if (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);
|
||||
|
||||
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) {
|
||||
char *line;
|
||||
size_t pos = 0;
|
||||
@ -1055,12 +1018,21 @@ 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. */
|
||||
while ((line = line_get_key_value(content, " \t", &pos, &key, &value)))
|
||||
if (streq8(key, "timeout")) {
|
||||
EFI_STATUS err = config_timeout_sec_from_string(value, &config->timeout_sec_config);
|
||||
if (err != EFI_SUCCESS) {
|
||||
log_warning_status(err, "Error parsing 'timeout' config option, ignoring: %s",
|
||||
if (streq8(value, "menu-disabled"))
|
||||
config->timeout_sec_config = TIMEOUT_MENU_DISABLED;
|
||||
else if (streq8(value, "menu-force"))
|
||||
config->timeout_sec_config = TIMEOUT_MENU_FORCE;
|
||||
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;
|
||||
|
||||
} else if (streq8(key, "default")) {
|
||||
@ -1524,7 +1496,6 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
|
||||
.console_mode_efivar = CONSOLE_MODE_KEEP,
|
||||
.timeout_sec_config = 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);
|
||||
@ -1549,7 +1520,6 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
|
||||
config->timeout_sec = config->timeout_sec_efivar;
|
||||
else if (err != EFI_NOT_FOUND)
|
||||
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);
|
||||
if (err == EFI_SUCCESS) {
|
||||
|
||||
@ -3669,8 +3669,7 @@ static int pick_versions(
|
||||
r = path_pick(/* toplevel_path= */ NULL,
|
||||
/* toplevel_fd= */ AT_FDCWD,
|
||||
context->root_image,
|
||||
pick_filter_image_raw,
|
||||
ELEMENTSOF(pick_filter_image_raw),
|
||||
&pick_filter_image_raw,
|
||||
PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
|
||||
&result);
|
||||
if (r < 0) {
|
||||
@ -3694,8 +3693,7 @@ static int pick_versions(
|
||||
r = path_pick(/* toplevel_path= */ NULL,
|
||||
/* toplevel_fd= */ AT_FDCWD,
|
||||
context->root_directory,
|
||||
pick_filter_image_dir,
|
||||
ELEMENTSOF(pick_filter_image_dir),
|
||||
&pick_filter_image_dir,
|
||||
PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
|
||||
&result);
|
||||
if (r < 0) {
|
||||
|
||||
@ -566,8 +566,7 @@ static int append_extensions(
|
||||
r = path_pick(/* toplevel_path= */ NULL,
|
||||
/* toplevel_fd= */ AT_FDCWD,
|
||||
m->source,
|
||||
pick_filter_image_raw,
|
||||
ELEMENTSOF(pick_filter_image_raw),
|
||||
&pick_filter_image_raw,
|
||||
PICK_ARCHITECTURE|PICK_TRIES,
|
||||
&result);
|
||||
if (r == -ENOENT && m->ignore_enoent)
|
||||
@ -638,8 +637,7 @@ static int append_extensions(
|
||||
r = path_pick(/* toplevel_path= */ NULL,
|
||||
/* toplevel_fd= */ AT_FDCWD,
|
||||
e,
|
||||
pick_filter_image_dir,
|
||||
ELEMENTSOF(pick_filter_image_dir),
|
||||
&pick_filter_image_dir,
|
||||
PICK_ARCHITECTURE|PICK_TRIES,
|
||||
&result);
|
||||
if (r == -ENOENT && ignore_enoent)
|
||||
|
||||
@ -2041,8 +2041,7 @@ static int run(int argc, char *argv[]) {
|
||||
if (arg_image) {
|
||||
r = path_pick_update_warn(
|
||||
&arg_image,
|
||||
pick_filter_image_raw,
|
||||
ELEMENTSOF(pick_filter_image_raw),
|
||||
&pick_filter_image_raw,
|
||||
PICK_ARCHITECTURE|PICK_TRIES,
|
||||
/* ret_result= */ NULL);
|
||||
if (r < 0)
|
||||
@ -2052,8 +2051,7 @@ static int run(int argc, char *argv[]) {
|
||||
if (arg_root) {
|
||||
r = path_pick_update_warn(
|
||||
&arg_root,
|
||||
pick_filter_image_dir,
|
||||
ELEMENTSOF(pick_filter_image_dir),
|
||||
&pick_filter_image_dir,
|
||||
PICK_ARCHITECTURE|PICK_TRIES,
|
||||
/* ret_result= */ NULL);
|
||||
if (r < 0)
|
||||
|
||||
@ -3013,14 +3013,13 @@ static int pick_paths(void) {
|
||||
|
||||
if (arg_directory) {
|
||||
_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;
|
||||
|
||||
r = path_pick_update_warn(
|
||||
&arg_directory,
|
||||
&filter,
|
||||
/* n_filters= */ 1,
|
||||
PICK_ARCHITECTURE|PICK_TRIES,
|
||||
&result);
|
||||
if (r < 0) {
|
||||
@ -3033,14 +3032,13 @@ static int pick_paths(void) {
|
||||
|
||||
if (arg_image) {
|
||||
_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;
|
||||
|
||||
r = path_pick_update_warn(
|
||||
&arg_image,
|
||||
&filter,
|
||||
/* n_filters= */ 1,
|
||||
PICK_ARCHITECTURE|PICK_TRIES,
|
||||
&result);
|
||||
if (r < 0)
|
||||
@ -3051,14 +3049,13 @@ static int pick_paths(void) {
|
||||
|
||||
if (arg_template) {
|
||||
_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;
|
||||
|
||||
r = path_pick_update_warn(
|
||||
&arg_template,
|
||||
&filter,
|
||||
/* n_filters= */ 1,
|
||||
PICK_ARCHITECTURE,
|
||||
&result);
|
||||
if (r < 0)
|
||||
|
||||
@ -595,8 +595,7 @@ static int extract_image_and_extensions(
|
||||
r = path_pick(/* toplevel_path= */ NULL,
|
||||
/* toplevel_fd= */ AT_FDCWD,
|
||||
name_or_path,
|
||||
pick_filter_image_any,
|
||||
ELEMENTSOF(pick_filter_image_any),
|
||||
&pick_filter_image_any,
|
||||
PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
|
||||
&result);
|
||||
if (r < 0)
|
||||
@ -634,8 +633,7 @@ static int extract_image_and_extensions(
|
||||
r = path_pick(/* toplevel_path= */ NULL,
|
||||
/* toplevel_fd= */ AT_FDCWD,
|
||||
*p,
|
||||
pick_filter_image_any,
|
||||
ELEMENTSOF(pick_filter_image_any),
|
||||
&pick_filter_image_any,
|
||||
PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
|
||||
&ext_result);
|
||||
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,
|
||||
/* toplevel_fd= */ AT_FDCWD,
|
||||
*image_name_or_path,
|
||||
pick_filter_image_any,
|
||||
ELEMENTSOF(pick_filter_image_any),
|
||||
&pick_filter_image_any,
|
||||
PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
|
||||
&result);
|
||||
if (r < 0)
|
||||
|
||||
@ -821,7 +821,6 @@ int image_find(RuntimeScope scope,
|
||||
/* toplevel_fd= */ AT_FDCWD,
|
||||
vp,
|
||||
&filter,
|
||||
/* n_filters= */ 1,
|
||||
PICK_ARCHITECTURE|PICK_TRIES,
|
||||
&result);
|
||||
if (r < 0) {
|
||||
@ -1034,7 +1033,6 @@ int image_discover(
|
||||
/* toplevel_fd= */ AT_FDCWD,
|
||||
vp,
|
||||
&filter,
|
||||
/* n_filters= */ 1,
|
||||
PICK_ARCHITECTURE|PICK_TRIES,
|
||||
&result);
|
||||
if (r < 0) {
|
||||
|
||||
@ -26,53 +26,6 @@ void pick_result_done(PickResult *p) {
|
||||
*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(
|
||||
const PickFilter *filter,
|
||||
PickFlags flags,
|
||||
@ -216,13 +169,13 @@ static int pin_choice(
|
||||
return log_debug_errno(errno, "Failed to stat discovered inode '%s%s': %m",
|
||||
empty_to_root(toplevel_path), skip_leading_slash(inode_path));
|
||||
|
||||
if (filter->type_mask != 0 && !BIT_SET(filter->type_mask, IFTODT(st.st_mode))) {
|
||||
log_debug("Inode '%s/%s' has wrong type, found '%s'.",
|
||||
if (filter->type_mask != 0 &&
|
||||
!BIT_SET(filter->type_mask, IFTODT(st.st_mode)))
|
||||
return log_debug_errno(
|
||||
SYNTHETIC_ERRNO(errno_from_mode(filter->type_mask, st.st_mode)),
|
||||
"Inode '%s/%s' has wrong type, found '%s'.",
|
||||
empty_to_root(toplevel_path), skip_leading_slash(inode_path),
|
||||
inode_type_to_string(st.st_mode));
|
||||
*ret = PICK_RESULT_NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_cleanup_(pick_result_done) PickResult result = {
|
||||
.fd = TAKE_FD(inode_fd),
|
||||
@ -295,23 +248,6 @@ nomatch:
|
||||
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(
|
||||
const char *toplevel_path,
|
||||
int toplevel_fd,
|
||||
@ -321,7 +257,21 @@ static int make_choice(
|
||||
PickFlags flags,
|
||||
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;
|
||||
|
||||
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 */
|
||||
_cleanup_free_ char *j = NULL;
|
||||
r = format_fname(filter, flags, &j);
|
||||
if (r >= 0) {
|
||||
_cleanup_free_ char *object_path = NULL;
|
||||
|
||||
/* Yay! This worked! */
|
||||
_cleanup_free_ char *p = path_join(inode_path, j);
|
||||
p = path_join(inode_path, j);
|
||||
if (!p)
|
||||
return log_oom_debug();
|
||||
|
||||
_cleanup_close_ int object_fd = -EBADF;
|
||||
r = chaseat(toplevel_fd, p, CHASE_AT_RESOLVE_IN_ROOT, &object_path, &object_fd);
|
||||
if (r == -ENOENT) {
|
||||
*ret = PICK_RESULT_NULL;
|
||||
@ -373,22 +321,28 @@ static int make_choice(
|
||||
/* Underspecified, so we do our enumeration dance */
|
||||
|
||||
/* 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)
|
||||
return log_debug_errno(dir_fd, "Failed to reopen '%s/%s' as directory: %m",
|
||||
empty_to_root(toplevel_path), skip_leading_slash(inode_path));
|
||||
|
||||
_cleanup_free_ DirectoryEntries *de = NULL;
|
||||
r = readdir_all(dir_fd, 0, &de);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read directory '%s/%s': %m",
|
||||
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) {
|
||||
unsigned found_tries_done = UINT_MAX, found_tries_left = UINT_MAX;
|
||||
_cleanup_free_ char *dname = NULL;
|
||||
size_t found_architecture_index = SIZE_MAX;
|
||||
char *e;
|
||||
|
||||
dname = strdup((*entry)->d_name);
|
||||
@ -426,16 +380,20 @@ static int make_choice(
|
||||
}
|
||||
}
|
||||
|
||||
Architecture a = _ARCHITECTURE_INVALID;
|
||||
if (FLAGS_SET(flags, PICK_ARCHITECTURE)) {
|
||||
char *underscore = strrchr(e, '_');
|
||||
Architecture a;
|
||||
|
||||
if (underscore)
|
||||
a = architecture_from_string(underscore + 1);
|
||||
a = underscore ? architecture_from_string(underscore + 1) : _ARCHITECTURE_INVALID;
|
||||
|
||||
if (!architecture_matches(filter, a)) {
|
||||
log_debug("Found entry with architecture '%s' which is not what we are looking for, ignoring entry.",
|
||||
a < 0 ? "any" : architecture_to_string(a));
|
||||
for (size_t i = 0; i < n_architectures; i++)
|
||||
if (architectures[i] == 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;
|
||||
}
|
||||
|
||||
@ -454,55 +412,89 @@ static int make_choice(
|
||||
continue;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *p = path_join(inode_path, (*entry)->d_name);
|
||||
if (!p)
|
||||
return log_oom_debug();
|
||||
if (best_filename) { /* Already found one matching entry? Then figure out the better one */
|
||||
int d = 0;
|
||||
|
||||
_cleanup_(pick_result_done) PickResult found = PICK_RESULT_NULL;
|
||||
r = pin_choice(toplevel_path,
|
||||
toplevel_fd,
|
||||
p,
|
||||
/* _inode_fd= */ -EBADF,
|
||||
found_tries_left,
|
||||
found_tries_done,
|
||||
&(const PickFilter) {
|
||||
.type_mask = filter->type_mask,
|
||||
.basename = filter->basename,
|
||||
.version = empty_to_null(e),
|
||||
.architecture = a,
|
||||
.suffix = filter->suffix,
|
||||
},
|
||||
flags,
|
||||
&found);
|
||||
if (r == 0)
|
||||
/* First, prefer entries with tries left over those without */
|
||||
if (FLAGS_SET(flags, PICK_TRIES))
|
||||
d = CMP(found_tries_left != 0, best_tries_left != 0);
|
||||
|
||||
/* Second, prefer newer versions */
|
||||
if (d == 0)
|
||||
d = strverscmp_improved(e, best_version);
|
||||
|
||||
/* Third, prefer native architectures over secondary architectures */
|
||||
if (d == 0 &&
|
||||
FLAGS_SET(flags, PICK_ARCHITECTURE) &&
|
||||
found_architecture_index != SIZE_MAX && best_architecture_index != SIZE_MAX)
|
||||
d = -CMP(found_architecture_index, best_architecture_index);
|
||||
|
||||
/* Fourth, prefer entries with more tries left */
|
||||
if (FLAGS_SET(flags, PICK_TRIES)) {
|
||||
if (d == 0)
|
||||
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)
|
||||
return r;
|
||||
|
||||
if (!best.path) {
|
||||
best = TAKE_PICK_RESULT(found);
|
||||
continue;
|
||||
r = free_and_strdup_warn(&best_filename, (*entry)->d_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
best_architecture_index = found_architecture_index;
|
||||
best_tries_left = found_tries_left;
|
||||
best_tries_done = found_tries_done;
|
||||
}
|
||||
|
||||
if (pick_result_compare(&found, &best, flags) <= 0) {
|
||||
log_debug("Found entry '%s' but previously found entry '%s' matches better, hence skipping entry.", found.path, best.path);
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*ret = TAKE_PICK_RESULT(best);
|
||||
return 1;
|
||||
p = path_join(inode_path, best_filename);
|
||||
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,
|
||||
int toplevel_fd,
|
||||
const char *path,
|
||||
@ -652,60 +644,9 @@ bypass:
|
||||
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(
|
||||
char **path,
|
||||
const PickFilter filters[],
|
||||
size_t n_filters,
|
||||
const PickFilter *filter,
|
||||
PickFlags flags,
|
||||
PickResult *ret_result) {
|
||||
|
||||
@ -714,15 +655,14 @@ int path_pick_update_warn(
|
||||
|
||||
assert(path);
|
||||
assert(*path);
|
||||
assert(filters || n_filters == 0);
|
||||
assert(filter);
|
||||
|
||||
/* This updates the first argument if needed! */
|
||||
|
||||
r = path_pick(/* toplevel_path= */ NULL,
|
||||
/* toplevel_fd= */ AT_FDCWD,
|
||||
*path,
|
||||
filters,
|
||||
n_filters,
|
||||
filter,
|
||||
flags,
|
||||
&result);
|
||||
if (r == -ENOENT) {
|
||||
@ -786,17 +726,19 @@ int path_uses_vpick(const char *path) {
|
||||
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),
|
||||
.architecture = _ARCHITECTURE_INVALID,
|
||||
.suffix = STRV_MAKE(".raw"),
|
||||
},
|
||||
};
|
||||
|
||||
const PickFilter pick_filter_image_dir[1] = {
|
||||
{
|
||||
const PickFilter pick_filter_image_dir = {
|
||||
.type_mask = UINT32_C(1) << DT_DIR,
|
||||
.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", ""),
|
||||
};
|
||||
|
||||
@ -43,29 +43,22 @@ typedef struct PickResult {
|
||||
|
||||
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,
|
||||
const char *path,
|
||||
const PickFilter filters[],
|
||||
size_t n_filters,
|
||||
const PickFilter *filter,
|
||||
PickFlags flags,
|
||||
PickResult *ret);
|
||||
|
||||
int path_pick_update_warn(
|
||||
char **path,
|
||||
const PickFilter filters[],
|
||||
size_t n_filters,
|
||||
const PickFilter *filter,
|
||||
PickFlags flags,
|
||||
PickResult *ret_result);
|
||||
|
||||
int path_uses_vpick(const char *path);
|
||||
|
||||
extern const PickFilter pick_filter_image_raw[1];
|
||||
extern const PickFilter pick_filter_image_dir[1];
|
||||
|
||||
#define pick_filter_image_any (const PickFilter[]) { \
|
||||
pick_filter_image_raw[0], \
|
||||
pick_filter_image_dir[0], \
|
||||
}
|
||||
extern const PickFilter pick_filter_image_raw;
|
||||
extern const PickFilter pick_filter_image_dir;
|
||||
extern const PickFilter pick_filter_image_any;
|
||||
|
||||
@ -15,30 +15,34 @@ TEST(path_pick) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *p = NULL;
|
||||
_cleanup_close_ int dfd = -EBADF, sub_dfd = -EBADF;
|
||||
|
||||
dfd = ASSERT_OK(mkdtemp_open(NULL, O_DIRECTORY|O_CLOEXEC, &p));
|
||||
sub_dfd = ASSERT_OK(open_mkdir_at(dfd, "foo.v", O_CLOEXEC, 0777));
|
||||
dfd = mkdtemp_open(NULL, O_DIRECTORY|O_CLOEXEC, &p);
|
||||
assert(dfd >= 0);
|
||||
|
||||
ASSERT_OK(write_string_file_at(sub_dfd, "foo_5.5.raw", "5.5", WRITE_STRING_FILE_CREATE));
|
||||
ASSERT_OK(write_string_file_at(sub_dfd, "foo_55.raw", "55", WRITE_STRING_FILE_CREATE));
|
||||
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_OK(write_string_file_at(sub_dfd, "foo_7.raw", "7", WRITE_STRING_FILE_CREATE));
|
||||
ASSERT_OK(write_string_file_at(sub_dfd, "foo_7_x86-64.raw", "7 64bit", WRITE_STRING_FILE_CREATE));
|
||||
ASSERT_OK(write_string_file_at(sub_dfd, "foo_55_x86-64.raw", "55 64bit", WRITE_STRING_FILE_CREATE));
|
||||
ASSERT_OK(write_string_file_at(sub_dfd, "foo_55_x86.raw", "55 32bit", WRITE_STRING_FILE_CREATE));
|
||||
ASSERT_OK(write_string_file_at(sub_dfd, "foo_99_x86.raw", "99 32bit", WRITE_STRING_FILE_CREATE));
|
||||
sub_dfd = open_mkdir_at(dfd, "foo.v", O_CLOEXEC, 0777);
|
||||
assert(sub_dfd >= 0);
|
||||
|
||||
assert_se(write_string_file_at(sub_dfd, "foo_5.5.raw", "5.5", WRITE_STRING_FILE_CREATE) >= 0);
|
||||
assert_se(write_string_file_at(sub_dfd, "foo_55.raw", "55", WRITE_STRING_FILE_CREATE) >= 0);
|
||||
assert_se(write_string_file_at(sub_dfd, "foo_5.raw", "5", WRITE_STRING_FILE_CREATE) >= 0);
|
||||
assert_se(write_string_file_at(sub_dfd, "foo_5_ia64.raw", "5", WRITE_STRING_FILE_CREATE) >= 0);
|
||||
assert_se(write_string_file_at(sub_dfd, "foo_7.raw", "7", WRITE_STRING_FILE_CREATE) >= 0);
|
||||
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
|
||||
* on). This entry should hence always be ignored */
|
||||
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_OK(write_string_file_at(sub_dfd, "quux_2_s390+4-6.raw", "waldo2", WRITE_STRING_FILE_CREATE));
|
||||
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_1_s390.raw", "waldo1", WRITE_STRING_FILE_CREATE) >= 0);
|
||||
assert_se(write_string_file_at(sub_dfd, "quux_2_s390+4-6.raw", "waldo2", WRITE_STRING_FILE_CREATE) >= 0);
|
||||
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;
|
||||
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;
|
||||
|
||||
@ -48,118 +52,121 @@ TEST(path_pick) {
|
||||
};
|
||||
|
||||
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_TRUE(S_ISREG(result.st.st_mode));
|
||||
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
|
||||
assert_se(S_ISREG(result.st.st_mode));
|
||||
ASSERT_STREQ(result.version, "99");
|
||||
ASSERT_EQ(result.architecture, ARCHITECTURE_X86);
|
||||
ASSERT_TRUE(endswith(result.path, "/foo_99_x86.raw"));
|
||||
assert_se(result.architecture == ARCHITECTURE_X86);
|
||||
assert_se(endswith(result.path, "/foo_99_x86.raw"));
|
||||
|
||||
pick_result_done(&result);
|
||||
}
|
||||
|
||||
filter.architecture = ARCHITECTURE_X86_64;
|
||||
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result));
|
||||
ASSERT_TRUE(S_ISREG(result.st.st_mode));
|
||||
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
|
||||
assert_se(S_ISREG(result.st.st_mode));
|
||||
ASSERT_STREQ(result.version, "55");
|
||||
ASSERT_EQ(result.architecture, ARCHITECTURE_X86_64);
|
||||
ASSERT_TRUE(endswith(result.path, "/foo_55_x86-64.raw"));
|
||||
assert_se(result.architecture == ARCHITECTURE_X86_64);
|
||||
assert_se(endswith(result.path, "/foo_55_x86-64.raw"));
|
||||
pick_result_done(&result);
|
||||
|
||||
filter.architecture = ARCHITECTURE_IA64;
|
||||
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result));
|
||||
ASSERT_TRUE(S_ISREG(result.st.st_mode));
|
||||
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
|
||||
assert_se(S_ISREG(result.st.st_mode));
|
||||
ASSERT_STREQ(result.version, "5");
|
||||
ASSERT_EQ(result.architecture, ARCHITECTURE_IA64);
|
||||
ASSERT_TRUE(endswith(result.path, "/foo_5_ia64.raw"));
|
||||
assert_se(result.architecture == ARCHITECTURE_IA64);
|
||||
assert_se(endswith(result.path, "/foo_5_ia64.raw"));
|
||||
pick_result_done(&result);
|
||||
|
||||
filter.architecture = _ARCHITECTURE_INVALID;
|
||||
filter.version = "5";
|
||||
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result));
|
||||
ASSERT_TRUE(S_ISREG(result.st.st_mode));
|
||||
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
|
||||
assert_se(S_ISREG(result.st.st_mode));
|
||||
ASSERT_STREQ(result.version, "5");
|
||||
if (native_architecture() != ARCHITECTURE_IA64) {
|
||||
ASSERT_EQ(result.architecture, _ARCHITECTURE_INVALID);
|
||||
ASSERT_TRUE(endswith(result.path, "/foo_5.raw"));
|
||||
assert_se(result.architecture == _ARCHITECTURE_INVALID);
|
||||
assert_se(endswith(result.path, "/foo_5.raw"));
|
||||
}
|
||||
pick_result_done(&result);
|
||||
|
||||
filter.architecture = ARCHITECTURE_IA64;
|
||||
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result));
|
||||
ASSERT_TRUE(S_ISREG(result.st.st_mode));
|
||||
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
|
||||
assert_se(S_ISREG(result.st.st_mode));
|
||||
ASSERT_STREQ(result.version, "5");
|
||||
ASSERT_EQ(result.architecture, ARCHITECTURE_IA64);
|
||||
ASSERT_TRUE(endswith(result.path, "/foo_5_ia64.raw"));
|
||||
assert_se(result.architecture == ARCHITECTURE_IA64);
|
||||
assert_se(endswith(result.path, "/foo_5_ia64.raw"));
|
||||
pick_result_done(&result);
|
||||
|
||||
filter.architecture = ARCHITECTURE_CRIS;
|
||||
ASSERT_OK_ZERO(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result));
|
||||
ASSERT_EQ(result.st.st_mode, MODE_INVALID);
|
||||
ASSERT_NULL(result.version);
|
||||
ASSERT_LT(result.architecture, 0);
|
||||
ASSERT_NULL(result.path);
|
||||
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) == 0);
|
||||
assert_se(result.st.st_mode == MODE_INVALID);
|
||||
assert_se(!result.version);
|
||||
assert_se(result.architecture < 0);
|
||||
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.version = NULL;
|
||||
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_TRUE(S_ISREG(result.st.st_mode));
|
||||
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
|
||||
assert_se(S_ISREG(result.st.st_mode));
|
||||
ASSERT_STREQ(result.version, "55");
|
||||
|
||||
if (native_architecture() == ARCHITECTURE_X86_64) {
|
||||
ASSERT_EQ(result.architecture, ARCHITECTURE_X86_64);
|
||||
ASSERT_TRUE(endswith(result.path, "/foo_55_x86-64.raw"));
|
||||
assert_se(result.architecture == ARCHITECTURE_X86_64);
|
||||
assert_se(endswith(result.path, "/foo_55_x86-64.raw"));
|
||||
} else {
|
||||
ASSERT_EQ(result.architecture, ARCHITECTURE_X86);
|
||||
ASSERT_TRUE(endswith(result.path, "/foo_55_x86.raw"));
|
||||
assert_se(result.architecture == ARCHITECTURE_X86);
|
||||
assert_se(endswith(result.path, "/foo_55_x86.raw"));
|
||||
}
|
||||
pick_result_done(&result);
|
||||
}
|
||||
|
||||
/* Test explicit patterns in last component of path not being .v */
|
||||
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)) {
|
||||
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result));
|
||||
ASSERT_TRUE(S_ISREG(result.st.st_mode));
|
||||
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
|
||||
assert_se(S_ISREG(result.st.st_mode));
|
||||
ASSERT_STREQ(result.version, "55");
|
||||
ASSERT_EQ(result.architecture, native_architecture());
|
||||
ASSERT_TRUE(endswith(result.path, ".raw"));
|
||||
ASSERT_TRUE(!!strrstr(result.path, "/foo_55_x86"));
|
||||
assert_se(result.architecture == native_architecture());
|
||||
assert_se(endswith(result.path, ".raw"));
|
||||
assert_se(strrstr(result.path, "/foo_55_x86"));
|
||||
pick_result_done(&result);
|
||||
}
|
||||
|
||||
/* Specify an explicit path */
|
||||
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;
|
||||
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;
|
||||
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result));
|
||||
ASSERT_TRUE(S_ISREG(result.st.st_mode));
|
||||
ASSERT_NULL(result.version);
|
||||
ASSERT_EQ(result.architecture, _ARCHITECTURE_INVALID);
|
||||
ASSERT_TRUE(path_equal(result.path, pp));
|
||||
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
|
||||
assert_se(S_ISREG(result.st.st_mode));
|
||||
assert_se(!result.version);
|
||||
assert_se(result.architecture == _ARCHITECTURE_INVALID);
|
||||
assert_se(path_equal(result.path, pp));
|
||||
pick_result_done(&result);
|
||||
|
||||
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.basename = "quux";
|
||||
|
||||
ASSERT_OK_POSITIVE(path_pick(NULL, AT_FDCWD, pp, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE|PICK_TRIES, &result));
|
||||
ASSERT_TRUE(S_ISREG(result.st.st_mode));
|
||||
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
|
||||
assert_se(S_ISREG(result.st.st_mode));
|
||||
ASSERT_STREQ(result.version, "2");
|
||||
ASSERT_EQ(result.tries_left, 4U);
|
||||
ASSERT_EQ(result.tries_done, 6U);
|
||||
ASSERT_TRUE(endswith(result.path, "quux_2_s390+4-6.raw"));
|
||||
ASSERT_EQ(result.architecture, ARCHITECTURE_S390);
|
||||
assert_se(result.tries_left == 4);
|
||||
assert_se(result.tries_done == 6);
|
||||
assert_se(endswith(result.path, "quux_2_s390+4-6.raw"));
|
||||
assert_se(result.architecture == ARCHITECTURE_S390);
|
||||
}
|
||||
|
||||
TEST(path_uses_vpick) {
|
||||
@ -185,223 +192,4 @@ TEST(path_uses_vpick) {
|
||||
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);
|
||||
|
||||
@ -248,7 +248,6 @@ static int run(int argc, char *argv[]) {
|
||||
.suffix = STRV_MAKE(arg_filter_suffix),
|
||||
.type_mask = arg_filter_type_mask,
|
||||
},
|
||||
/* n_filters= */ 1,
|
||||
arg_flags,
|
||||
&result);
|
||||
if (r < 0)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user