diff options
59 files changed, 863 insertions, 565 deletions
diff --git a/.github/workflows/make_release.yml b/.github/workflows/make_release.yml index 47dbbea374..c789d33f16 100644 --- a/.github/workflows/make_release.yml +++ b/.github/workflows/make_release.yml @@ -5,14 +5,20 @@ on: tags: - "v*" +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest + permissions: + contents: write + if: ${{ github.repository_owner == 'systemd' }} steps: - name: Checkout uses: actions/checkout@v3 - name: Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 with: prerelease: ${{ contains(github.ref_name, '-rc') }} draft: ${{ github.repository == 'systemd/systemd' }} diff --git a/hwdb.d/60-keyboard.hwdb b/hwdb.d/60-keyboard.hwdb index 7195217bcd..82f3aaf051 100644 --- a/hwdb.d/60-keyboard.hwdb +++ b/hwdb.d/60-keyboard.hwdb @@ -933,7 +933,7 @@ evdev:input:b0003v17EFp6009* KEYBOARD_KEY_090010=f20 # Microphone mute button; should be micmute # Lenovo 3000 -evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*3000*:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*3000*:pvr* KEYBOARD_KEY_8b=switchvideomode # Fn+F7 video KEYBOARD_KEY_96=wlan # Fn+F5 wireless KEYBOARD_KEY_97=sleep # Fn+F4 suspend @@ -945,7 +945,7 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO:pn0769AP2:pvr3000N200:* KEYBOARD_KEY_b4=prog1 # Lenovo IdeaPad -evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*IdeaPad*:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*IdeaPad*:pvr* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pnS10-*:* KEYBOARD_KEY_81=rfkill # does nothing in BIOS KEYBOARD_KEY_83=display_off # BIOS toggles screen state @@ -960,7 +960,7 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*:pvrIdeaPad5*:* KEYBOARD_KEY_81=insert # Thinkpad X200_Tablet -evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*:pvrThinkPad*X2*T*:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*:pvrThinkPad*X2*T*:rvn* KEYBOARD_KEY_5d=menu KEYBOARD_KEY_63=fn KEYBOARD_KEY_66=screenlock @@ -969,7 +969,7 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*:pvrThinkPad*X2*T*:* KEYBOARD_KEY_6c=direction # rotate screen # ThinkPad X6 Tablet -evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*:pvrThinkPad*X6*Tablet*:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*:pvrThinkPad*X6*Tablet*:rvn* KEYBOARD_KEY_6c=direction # rotate KEYBOARD_KEY_68=leftmeta # toolbox KEYBOARD_KEY_6b=esc # escape @@ -993,20 +993,20 @@ evdev:name:Ideapad extra buttons:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*:* KEYBOARD_KEY_42=f23 KEYBOARD_KEY_43=f22 -evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*IdeaPad*Y550*:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*IdeaPad*Y550*:pvr* KEYBOARD_KEY_95=media KEYBOARD_KEY_a3=play -evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*IdeaPad*U300s*:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*IdeaPad*U300s*:pvr* KEYBOARD_KEY_f1=f21 KEYBOARD_KEY_ce=f20 # micmute -evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*IdeaPad*Z370*:* +evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*IdeaPad*Z370*:pvr* KEYBOARD_KEY_a0=!mute KEYBOARD_KEY_ae=!volumedown KEYBOARD_KEY_b0=!volumeup -evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*IdeaPadFlex5*:* +evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*IdeaPadFlex5*:pvr* KEYBOARD_KEY_a0=!mute KEYBOARD_KEY_ae=!volumedown KEYBOARD_KEY_b0=!volumeup @@ -1025,11 +1025,11 @@ evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO:pn81Q7*:pvrLenovoYogaS940:* KEYBOARD_KEY_b0=!volumeup # Lenovo Y50-70 -evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*20378*:* +evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*20378*:pvr* KEYBOARD_KEY_f3=f21 # Fn+F6 (toggle touchpad) # V480 -evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*Lenovo*V480*:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*Lenovo*V480*:pvr* KEYBOARD_KEY_f1=f21 # Lenovo ThinkCentre M800z/M820z/M920z AIO machines diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 50680e6b37..490c83bb96 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -2501,7 +2501,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { condition is a trigger condition, whether the condition is reversed, the right hand side of the condition (e.g. the path in case of <varname>ConditionPathExists</varname>), and the status. The status can be 0, in which case the condition hasn't been checked yet, a positive value, in which case the - condition passed, or a negative value, in which case the condition failed. Currently only 0, +1, and -1 + condition passed, or a negative value, in which case the condition is not met. Currently only 0, +1, and -1 are used, but additional values may be used in the future, retaining the meaning of zero/positive/negative values.</para> diff --git a/rules.d/60-persistent-storage-tape.rules b/rules.d/60-persistent-storage-tape.rules index 607e51ae2a..8c00cc76d9 100644 --- a/rules.d/60-persistent-storage-tape.rules +++ b/rules.d/60-persistent-storage-tape.rules @@ -6,7 +6,7 @@ ACTION=="remove", GOTO="persistent_storage_tape_end" ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_tape_end" # type 8 devices are "Medium Changers" -SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \ +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --allowlisted -d $devnode", \ SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL} tape/by-id/scsi-$env{ID_SERIAL}-changer" # iSCSI devices from the same host have all the same ID_SERIAL, @@ -22,7 +22,7 @@ SUBSYSTEM!="scsi_tape", GOTO="persistent_storage_tape_end" KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394" KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", ENV{.BSG_DEV}="$root/bsg/$id" -KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --whitelisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi" +KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --allowlisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi" KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}", OPTIONS+="link_priority=10" KERNEL=="st*[0-9]", ENV{ID_SCSI_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SCSI_SERIAL}" KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst" diff --git a/rules.d/60-persistent-storage.rules.in b/rules.d/60-persistent-storage.rules.in index c093e21500..89c064fb66 100644 --- a/rules.d/60-persistent-storage.rules.in +++ b/rules.d/60-persistent-storage.rules.in @@ -79,8 +79,8 @@ KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS= KERNEL=="sd*[!0-9]|sr*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" # SCSI devices -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi" -KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss" +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --allowlisted -d $devnode", ENV{ID_BUS}="scsi" +KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --allowlisted -d $devnode", ENV{ID_BUS}="cciss" KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}" KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n" # Previously, ata_id in the above might not be able to retrieve attributes correctly, diff --git a/shell-completion/bash/busctl b/shell-completion/bash/busctl index ee160a7900..5464225b15 100644 --- a/shell-completion/bash/busctl +++ b/shell-completion/bash/busctl @@ -86,7 +86,7 @@ _busctl() { --show-machine --unique --acquired --activatable --list -q --quiet --verbose --expect-reply=no --auto-start=no --allow-interactive-authorization=no --augment-creds=no - --watch-bind=yes -j -l --full' + --watch-bind=yes -j -l --full --xml-interface' [ARG]='--address -H --host -M --machine --match --timeout --size --json --destination' ) diff --git a/shell-completion/zsh/_busctl b/shell-completion/zsh/_busctl index 0cb1c44a43..b0cd4d5db5 100644 --- a/shell-completion/zsh/_busctl +++ b/shell-completion/zsh/_busctl @@ -276,6 +276,7 @@ _arguments \ '--list[Do not show tree, but simple object path list]' \ {-q,--quiet}'[Do not show method call reply]'\ '--verbose[Show result values in long format]' \ + '--xml-interface[Dump the XML description in introspect command]' \ '--json=[Show result values in long format]:format:_busctl_get_json' \ '-j[Show pretty json in interactive sessions, short json otherwise]' \ '--expect-reply=[Expect a method call reply]:boolean:(1 0)' \ diff --git a/src/analyze/analyze-blame.c b/src/analyze/analyze-blame.c index c9112685f8..81e5c590b9 100644 --- a/src/analyze/analyze-blame.c +++ b/src/analyze/analyze-blame.c @@ -16,7 +16,7 @@ int verb_blame(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_connect_error(r, arg_transport); - n = acquire_time_data(bus, ×); + n = acquire_time_data(bus, /* require_finished = */ false, ×); if (n <= 0) return n; diff --git a/src/analyze/analyze-critical-chain.c b/src/analyze/analyze-critical-chain.c index f782f95d5f..f80f3ddb63 100644 --- a/src/analyze/analyze-critical-chain.c +++ b/src/analyze/analyze-critical-chain.c @@ -93,7 +93,7 @@ static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, typesafe_qsort(deps, strv_length(deps), list_dependencies_compare); - r = acquire_boot_times(bus, &boot); + r = acquire_boot_times(bus, /* require_finished = */ true, &boot); if (r < 0) return r; @@ -178,7 +178,7 @@ static int list_dependencies(sd_bus *bus, const char *name) { times = hashmap_get(unit_times_hashmap, id); - r = acquire_boot_times(bus, &boot); + r = acquire_boot_times(bus, /* require_finished = */ true, &boot); if (r < 0) return r; @@ -205,7 +205,7 @@ int verb_critical_chain(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_connect_error(r, arg_transport); - n = acquire_time_data(bus, ×); + n = acquire_time_data(bus, /* require_finished = */ true, ×); if (n <= 0) return n; diff --git a/src/analyze/analyze-plot.c b/src/analyze/analyze-plot.c index e44b9c11f6..ef40e64631 100644 --- a/src/analyze/analyze-plot.c +++ b/src/analyze/analyze-plot.c @@ -439,7 +439,7 @@ int verb_plot(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_connect_error(r, arg_transport); - n = acquire_boot_times(bus, &boot); + n = acquire_boot_times(bus, /* require_finished = */ true, &boot); if (n < 0) return n; @@ -453,7 +453,7 @@ int verb_plot(int argc, char *argv[], void *userdata) { return n; } - n = acquire_time_data(bus, ×); + n = acquire_time_data(bus, /* require_finished = */ true, ×); if (n <= 0) return n; diff --git a/src/analyze/analyze-time-data.c b/src/analyze/analyze-time-data.c index 07843f74bc..baee3cedbb 100644 --- a/src/analyze/analyze-time-data.c +++ b/src/analyze/analyze-time-data.c @@ -17,7 +17,16 @@ static void subtract_timestamp(usec_t *a, usec_t b) { } } -int acquire_boot_times(sd_bus *bus, BootTimes **ret) { +static int log_not_finished(usec_t finish_time) { + return log_error_errno(SYNTHETIC_ERRNO(EINPROGRESS), + "Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n" + "Please try again later.\n" + "Hint: Use 'systemctl%s list-jobs' to see active jobs", + finish_time, + arg_runtime_scope == RUNTIME_SCOPE_SYSTEM ? "" : " --user"); +} + +int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret) { static const struct bus_properties_map property_map[] = { { "FirmwareTimestampMonotonic", "t", NULL, offsetof(BootTimes, firmware_time) }, { "LoaderTimestampMonotonic", "t", NULL, offsetof(BootTimes, loader_time) }, @@ -44,8 +53,14 @@ int acquire_boot_times(sd_bus *bus, BootTimes **ret) { static bool cached = false; int r; - if (cached) - goto finish; + if (cached) { + if (require_finished && times.finish_time <= 0) + return log_not_finished(times.finish_time); + + if (ret) + *ret = × + return 0; + } assert_cc(sizeof(usec_t) == sizeof(uint64_t)); @@ -61,13 +76,8 @@ int acquire_boot_times(sd_bus *bus, BootTimes **ret) { if (r < 0) return log_error_errno(r, "Failed to get timestamp properties: %s", bus_error_message(&error, r)); - if (times.finish_time <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EINPROGRESS), - "Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n" - "Please try again later.\n" - "Hint: Use 'systemctl%s list-jobs' to see active jobs", - times.finish_time, - arg_runtime_scope == RUNTIME_SCOPE_SYSTEM ? "" : " --user"); + if (require_finished && times.finish_time <= 0) + return log_not_finished(times.finish_time); if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && times.security_start_time > 0) { /* security_start_time is set when systemd is not running under container environment. */ @@ -85,7 +95,8 @@ int acquire_boot_times(sd_bus *bus, BootTimes **ret) { times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = times.security_start_time = times.security_finish_time = 0; - subtract_timestamp(×.finish_time, times.reverse_offset); + if (times.finish_time > 0) + subtract_timestamp(×.finish_time, times.reverse_offset); subtract_timestamp(×.generators_start_time, times.reverse_offset); subtract_timestamp(×.generators_finish_time, times.reverse_offset); @@ -96,8 +107,8 @@ int acquire_boot_times(sd_bus *bus, BootTimes **ret) { cached = true; -finish: - *ret = × + if (ret) + *ret = × return 0; } @@ -132,7 +143,7 @@ int pretty_boot_time(sd_bus *bus, char **ret) { BootTimes *t; int r; - r = acquire_boot_times(bus, &t); + r = acquire_boot_times(bus, /* require_finished = */ true, &t); if (r < 0) return r; @@ -214,7 +225,7 @@ UnitTimes* unit_times_free_array(UnitTimes *t) { return mfree(t); } -int acquire_time_data(sd_bus *bus, UnitTimes **out) { +int acquire_time_data(sd_bus *bus, bool require_finished, UnitTimes **out) { static const struct bus_properties_map property_map[] = { { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(UnitTimes, activating) }, { "ActiveEnterTimestampMonotonic", "t", NULL, offsetof(UnitTimes, activated) }, @@ -225,12 +236,12 @@ int acquire_time_data(sd_bus *bus, UnitTimes **out) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(unit_times_free_arrayp) UnitTimes *unit_times = NULL; - BootTimes *boot_times = NULL; + BootTimes *boot_times; size_t c = 0; UnitInfo u; int r; - r = acquire_boot_times(bus, &boot_times); + r = acquire_boot_times(bus, require_finished, &boot_times); if (r < 0) return r; diff --git a/src/analyze/analyze-time-data.h b/src/analyze/analyze-time-data.h index 02ee16a714..79240745cb 100644 --- a/src/analyze/analyze-time-data.h +++ b/src/analyze/analyze-time-data.h @@ -47,10 +47,10 @@ typedef struct UnitTimes { usec_t time; } UnitTimes; -int acquire_boot_times(sd_bus *bus, BootTimes **ret); +int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret); int pretty_boot_time(sd_bus *bus, char **ret); UnitTimes* unit_times_free_array(UnitTimes *t); DEFINE_TRIVIAL_CLEANUP_FUNC(UnitTimes*, unit_times_free_array); -int acquire_time_data(sd_bus *bus, UnitTimes **out); +int acquire_time_data(sd_bus *bus, bool require_finished, UnitTimes **out); diff --git a/src/basic/chase.c b/src/basic/chase.c index 318454ca88..e8d38279fd 100644 --- a/src/basic/chase.c +++ b/src/basic/chase.c @@ -77,7 +77,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int _cleanup_close_ int fd = -EBADF, root_fd = -EBADF; unsigned max_follow = CHASE_MAX; /* how many symlinks to follow before giving up and returning ELOOP */ bool exists = true, append_trail_slash = false; - struct stat previous_stat; + struct stat st; /* stat obtained from fd */ const char *todo; int r; @@ -176,7 +176,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int /* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root * set and doesn't care about any of the other special features we provide either. */ - r = openat(dir_fd, buffer ?: path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)); + r = openat(dir_fd, path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)); if (r < 0) return -errno; @@ -184,11 +184,9 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int return 0; } - if (!buffer) { - buffer = strdup(path); - if (!buffer) - return -ENOMEM; - } + buffer = strdup(path); + if (!buffer) + return -ENOMEM; /* If we receive an absolute path together with AT_FDCWD, we need to return an absolute path, because * a relative path would be interpreted relative to the current working directory. */ @@ -220,7 +218,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (fd < 0) return -errno; - if (fstat(fd, &previous_stat) < 0) + if (fstat(fd, &st) < 0) return -errno; if (flags & CHASE_TRAIL_SLASH) @@ -229,7 +227,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int for (todo = buffer;;) { _cleanup_free_ char *first = NULL; _cleanup_close_ int child = -EBADF; - struct stat st; + struct stat st_child; const char *e; r = path_find_first_component(&todo, /* accept_dot_dot= */ true, &e); @@ -250,6 +248,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (path_equal(first, "..")) { _cleanup_free_ char *parent = NULL; _cleanup_close_ int fd_parent = -EBADF; + struct stat st_parent; /* If we already are at the top, then going up will not change anything. This is * in-line with how the kernel handles this. */ @@ -260,13 +259,19 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (fd_parent < 0) return -errno; - if (fstat(fd_parent, &st) < 0) + if (fstat(fd_parent, &st_parent) < 0) return -errno; - /* If we opened the same directory, that means we're at the host root directory, so + /* If we opened the same directory, that _may_ indicate that we're at the host root + * directory. Let's confirm that in more detail with dir_fd_is_root(). And if so, * going up won't change anything. */ - if (st.st_dev == previous_stat.st_dev && st.st_ino == previous_stat.st_ino) - continue; + if (stat_inode_same(&st_parent, &st)) { + r = dir_fd_is_root(fd); + if (r < 0) + return r; + if (r > 0) + continue; + } r = path_extract_directory(done, &parent); if (r >= 0 || r == -EDESTADDRREQ) @@ -281,18 +286,16 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (flags & CHASE_STEP) goto chased_one; - if (flags & CHASE_SAFE) { - if (unsafe_transition(&previous_stat, &st)) - return log_unsafe_transition(fd, fd_parent, path, flags); - - previous_stat = st; - } + if (flags & CHASE_SAFE && + unsafe_transition(&st, &st_parent)) + return log_unsafe_transition(fd, fd_parent, path, flags); if (FLAGS_SET(flags, CHASE_PARENT) && isempty(todo)) break; + /* update fd and stat */ + st = st_parent; close_and_replace(fd, fd_parent); - continue; } @@ -324,19 +327,18 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int return r; } - if (fstat(child, &st) < 0) + if (fstat(child, &st_child) < 0) return -errno; + if ((flags & CHASE_SAFE) && - unsafe_transition(&previous_stat, &st)) + unsafe_transition(&st, &st_child)) return log_unsafe_transition(fd, child, path, flags); - previous_stat = st; - if ((flags & CHASE_NO_AUTOFS) && fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) return log_autofs_mount_point(child, path, flags); - if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { + if (S_ISLNK(st_child.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { _cleanup_free_ char *destination = NULL; if (flags & CHASE_PROHIBIT_SYMLINKS) @@ -363,15 +365,12 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (fd < 0) return fd; - if (flags & CHASE_SAFE) { - if (fstat(fd, &st) < 0) - return -errno; + if (fstat(fd, &st) < 0) + return -errno; - if (unsafe_transition(&previous_stat, &st)) - return log_unsafe_transition(child, fd, path, flags); - - previous_stat = st; - } + if (flags & CHASE_SAFE && + unsafe_transition(&st_child, &st)) + return log_unsafe_transition(child, fd, path, flags); r = free_and_strdup(&done, need_absolute ? "/" : NULL); if (r < 0) @@ -400,11 +399,12 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int break; /* And iterate again, but go one directory further down. */ + st = st_child; close_and_replace(fd, child); } if (flags & CHASE_PARENT) { - r = fd_verify_directory(fd); + r = stat_verify_directory(&st); if (r < 0) return r; } @@ -493,18 +493,19 @@ int chase(const char *path, const char *original_root, ChaseFlags flags, char ** return r; /* Simplify the root directory, so that it has no duplicate slashes and nothing at the - * end. While we won't resolve the root path we still simplify it. Note that dropping the - * trailing slash should not change behaviour, since when opening it we specify O_DIRECTORY - * anyway. Moreover at the end of this function after processing everything we'll always turn - * the empty string back to "/". */ - delete_trailing_chars(root, "/"); + * end. While we won't resolve the root path we still simplify it. */ path_simplify(root); + assert(path_is_absolute(root)); + assert(!empty_or_root(root)); + if (flags & CHASE_PREFIX_ROOT) { absolute = path_join(root, path); if (!absolute) return -ENOMEM; } + + flags |= CHASE_AT_RESOLVE_IN_ROOT; } if (!absolute) { @@ -524,9 +525,6 @@ int chase(const char *path, const char *original_root, ChaseFlags flags, char ** if (fd < 0) return -errno; - if (!empty_or_root(root)) - flags |= CHASE_AT_RESOLVE_IN_ROOT; - r = chaseat(fd, path, flags & ~CHASE_PREFIX_ROOT, ret_path ? &p : NULL, ret_fd ? &pfd : NULL); if (r < 0) return r; diff --git a/src/basic/chase.h b/src/basic/chase.h index 1314777cb4..7e9ebe0685 100644 --- a/src/basic/chase.h +++ b/src/basic/chase.h @@ -22,7 +22,9 @@ typedef enum ChaseFlags { CHASE_PROHIBIT_SYMLINKS = 1 << 9, /* Refuse all symlinks */ CHASE_PARENT = 1 << 10, /* Chase the parent directory of the given path. Note that the * full path is still stored in ret_path and only the returned - * file descriptor will point to the parent directory. */ + * file descriptor will point to the parent directory. Note that + * the result path is the root or '.', then the file descriptor + * also points to the result path even if this flag is set. */ CHASE_MKDIR_0755 = 1 << 11, /* Create any missing parent directories in the given path. */ CHASE_EXTRACT_FILENAME = 1 << 12, /* Only return the last component of the resolved path */ } ChaseFlags; @@ -51,4 +53,3 @@ int chase_and_accessat(int dir_fd, const char *path, ChaseFlags chase_flags, int int chase_and_fopenat_unlocked(int dir_fd, const char *path, ChaseFlags chase_flags, const char *open_flags, char **ret_path, FILE **ret_file); int chase_and_unlinkat(int dir_fd, const char *path, ChaseFlags chase_flags, int unlink_flags, char **ret_path); int chase_and_open_parent_at(int dir_fd, const char *path, ChaseFlags chase_flags, char **ret_filename); - diff --git a/src/basic/compress.c b/src/basic/compress.c index 59621dc05b..10e7aaec59 100644 --- a/src/basic/compress.c +++ b/src/basic/compress.c @@ -107,7 +107,7 @@ int compress_blob_xz(const void *src, uint64_t src_size, return -ENOBUFS; *dst_size = out_pos; - return COMPRESSION_XZ; + return 0; #else return -EPROTONOSUPPORT; #endif @@ -137,7 +137,7 @@ int compress_blob_lz4(const void *src, uint64_t src_size, unaligned_write_le64(dst, src_size); *dst_size = r + 8; - return COMPRESSION_LZ4; + return 0; #else return -EPROTONOSUPPORT; #endif @@ -160,7 +160,7 @@ int compress_blob_zstd( return zstd_ret_to_errno(k); *dst_size = k; - return COMPRESSION_ZSTD; + return 0; #else return -EPROTONOSUPPORT; #endif @@ -626,7 +626,7 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncom s.total_in, s.total_out, (double) s.total_out / s.total_in * 100); - return COMPRESSION_XZ; + return 0; } } } @@ -717,7 +717,7 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco total_in, total_out, (double) total_out / total_in * 100); - return COMPRESSION_LZ4; + return 0; #else return -EPROTONOSUPPORT; #endif @@ -961,7 +961,7 @@ int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unc log_debug("ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes)", in_bytes, max_bytes - left); - return COMPRESSION_ZSTD; + return 0; #else return -EPROTONOSUPPORT; #endif diff --git a/src/basic/compress.h b/src/basic/compress.h index 2201bca74c..1b5c645e32 100644 --- a/src/basic/compress.h +++ b/src/basic/compress.h @@ -63,7 +63,7 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_size); int decompress_stream_lz4(int fdf, int fdt, uint64_t max_size); int decompress_stream_zstd(int fdf, int fdt, uint64_t max_size); -static inline int compress_blob_explicit( +static inline int compress_blob( Compression compression, const void *src, uint64_t src_size, void *dst, size_t dst_alloc_size, size_t *dst_size) { @@ -80,12 +80,6 @@ static inline int compress_blob_explicit( } } -#define compress_blob(src, src_size, dst, dst_alloc_size, dst_size) \ - compress_blob_explicit( \ - DEFAULT_COMPRESSION, \ - src, src_size, \ - dst, dst_alloc_size, dst_size) - static inline int compress_stream(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) { switch (DEFAULT_COMPRESSION) { case COMPRESSION_ZSTD: diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index 215e7951dc..c31fe79ebd 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -23,26 +23,19 @@ #include "terminal-util.h" static int files_add( - Hashmap **h, + DIR *dir, + const char *dirpath, + Hashmap **files, Set **masked, const char *suffix, - const char *root, - unsigned flags, - const char *path) { + unsigned flags) { - _cleanup_free_ char *dirpath = NULL; - _cleanup_closedir_ DIR *dir = NULL; int r; - assert(h); + assert(dir); + assert(dirpath); + assert(files); assert(masked); - assert(path); - - r = chase_and_opendir(path, root, CHASE_PREFIX_ROOT, &dirpath, &dir); - if (r == -ENOENT) - return 0; - if (r < 0) - return log_debug_errno(r, "Failed to open directory '%s/%s': %m", empty_or_root(root) ? "" : root, dirpath); FOREACH_DIRENT(de, dir, return -errno) { _cleanup_free_ char *n = NULL, *p = NULL; @@ -53,7 +46,7 @@ static int files_add( continue; /* Has this file already been found in an earlier directory? */ - if (hashmap_contains(*h, de->d_name)) { + if (hashmap_contains(*files, de->d_name)) { log_debug("Skipping overridden file '%s/%s'.", dirpath, de->d_name); continue; } @@ -107,13 +100,13 @@ static int files_add( return -ENOMEM; if ((flags & CONF_FILES_BASENAME)) - r = hashmap_ensure_put(h, &string_hash_ops_free, n, n); + r = hashmap_ensure_put(files, &string_hash_ops_free, n, n); else { p = path_join(dirpath, de->d_name); if (!p) return -ENOMEM; - r = hashmap_ensure_put(h, &string_hash_ops_free_free, n, p); + r = hashmap_ensure_put(files, &string_hash_ops_free_free, n, p); } if (r < 0) return r; @@ -127,49 +120,99 @@ static int files_add( } static int base_cmp(char * const *a, char * const *b) { - return strcmp(basename(*a), basename(*b)); + assert(a); + assert(b); + return path_compare_filename(*a, *b); } -static int conf_files_list_strv_internal( +static int copy_and_sort_files_from_hashmap(Hashmap *fh, char ***ret) { + _cleanup_free_ char **sv = NULL; + char **files; + + assert(ret); + + sv = hashmap_get_strv(fh); + if (!sv) + return -ENOMEM; + + /* The entries in the array given by hashmap_get_strv() are still owned by the hashmap. */ + files = strv_copy(sv); + if (!files) + return -ENOMEM; + + typesafe_qsort(files, strv_length(files), base_cmp); + + *ret = files; + return 0; +} + +int conf_files_list_strv( char ***ret, const char *suffix, const char *root, unsigned flags, - char **dirs) { + const char * const *dirs) { _cleanup_hashmap_free_ Hashmap *fh = NULL; _cleanup_set_free_ Set *masked = NULL; - _cleanup_strv_free_ char **files = NULL; - _cleanup_free_ char **sv = NULL; int r; assert(ret); - /* This alters the dirs string array */ - if (!path_strv_resolve_uniq(dirs, root)) - return -ENOMEM; - STRV_FOREACH(p, dirs) { - r = files_add(&fh, &masked, suffix, root, flags, *p); + _cleanup_closedir_ DIR *dir = NULL; + _cleanup_free_ char *path = NULL; + + r = chase_and_opendir(*p, root, CHASE_PREFIX_ROOT, &path, &dir); + if (r < 0) { + if (r != -ENOENT) + log_debug_errno(r, "Failed to chase and open directory '%s', ignoring: %m", *p); + continue; + } + + r = files_add(dir, path, &fh, &masked, suffix, flags); if (r == -ENOMEM) return r; if (r < 0) - log_debug_errno(r, "Failed to search for files in %s, ignoring: %m", *p); + log_debug_errno(r, "Failed to search for files in '%s', ignoring: %m", path); } - sv = hashmap_get_strv(fh); - if (!sv) - return -ENOMEM; + return copy_and_sort_files_from_hashmap(fh, ret); +} - /* The entries in the array given by hashmap_get_strv() are still owned by the hashmap. */ - files = strv_copy(sv); - if (!files) - return -ENOMEM; +int conf_files_list_strv_at( + char ***ret, + const char *suffix, + int rfd, + unsigned flags, + const char * const *dirs) { - typesafe_qsort(files, strv_length(files), base_cmp); - *ret = TAKE_PTR(files); + _cleanup_hashmap_free_ Hashmap *fh = NULL; + _cleanup_set_free_ Set *masked = NULL; + int r; - return 0; + assert(rfd >= 0 || rfd == AT_FDCWD); + assert(ret); + + STRV_FOREACH(p, dirs) { + _cleanup_closedir_ DIR *dir = NULL; + _cleanup_free_ char *path = NULL; + + r = chase_and_opendirat(rfd, *p, CHASE_AT_RESOLVE_IN_ROOT, &path, &dir); + if (r < 0) { + if (r != -ENOENT) + log_debug_errno(r, "Failed to chase and open directory '%s', ignoring: %m", *p); + continue; + } + + r = files_add(dir, path, &fh, &masked, suffix, flags); + if (r == -ENOMEM) + return r; + if (r < 0) + log_debug_errno(r, "Failed to search for files in '%s', ignoring: %m", path); + } + + return copy_and_sort_files_from_hashmap(fh, ret); } int conf_files_insert(char ***strv, const char *root, char **dirs, const char *path) { @@ -240,31 +283,27 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p return r; } -int conf_files_list_strv(char ***ret, const char *suffix, const char *root, unsigned flags, const char* const* dirs) { - _cleanup_strv_free_ char **copy = NULL; - - assert(ret); - - copy = strv_copy((char**) dirs); - if (!copy) - return -ENOMEM; +int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir) { + return conf_files_list_strv(ret, suffix, root, flags, STRV_MAKE_CONST(dir)); +} - return conf_files_list_strv_internal(ret, suffix, root, flags, copy); +int conf_files_list_at(char ***ret, const char *suffix, int rfd, unsigned flags, const char *dir) { + return conf_files_list_strv_at(ret, suffix, rfd, flags, STRV_MAKE_CONST(dir)); } -int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir) { - _cleanup_strv_free_ char **dirs = NULL; +int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dirs) { + _cleanup_strv_free_ char **d = NULL; assert(ret); - dirs = strv_new(dir); - if (!dirs) + d = strv_split_nulstr(dirs); + if (!d) return -ENOMEM; - return conf_files_list_strv_internal(ret, suffix, root, flags, dirs); + return conf_files_list_strv(ret, suffix, root, flags, (const char**) d); } -int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dirs) { +int conf_files_list_nulstr_at(char ***ret, const char *suffix, int rfd, unsigned flags, const char *dirs) { _cleanup_strv_free_ char **d = NULL; assert(ret); @@ -273,7 +312,7 @@ int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, un if (!d) return -ENOMEM; - return conf_files_list_strv_internal(ret, suffix, root, flags, d); + return conf_files_list_strv_at(ret, suffix, rfd, flags, (const char**) d); } int conf_files_list_with_replacement( diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h index 7774ed7054..547c2fc137 100644 --- a/src/basic/conf-files.h +++ b/src/basic/conf-files.h @@ -12,8 +12,11 @@ enum { }; int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir); +int conf_files_list_at(char ***ret, const char *suffix, int rfd, unsigned flags, const char *dir); int conf_files_list_strv(char ***ret, const char *suffix, const char *root, unsigned flags, const char* const* dirs); +int conf_files_list_strv_at(char ***ret, const char *suffix, int rfd, unsigned flags, const char * const *dirs); int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dirs); +int conf_files_list_nulstr_at(char ***ret, const char *suffix, int rfd, unsigned flags, const char *dirs); int conf_files_insert(char ***strv, const char *root, char **dirs, const char *path); int conf_files_list_with_replacement( const char *root, diff --git a/src/basic/env-file.c b/src/basic/env-file.c index 01ed443d5f..7b3e209ddc 100644 --- a/src/basic/env-file.c +++ b/src/basic/env-file.c @@ -359,6 +359,23 @@ int parse_env_filev( return r; } +int parse_env_file_fdv(int fd, const char *fname, va_list ap) { + _cleanup_fclose_ FILE *f = NULL; + va_list aq; + int r; + + assert(fd >= 0); + + r = fdopen_independent(fd, "re", &f); + if (r < 0) + return r; + + va_copy(aq, ap); + r = parse_env_file_internal(f, fname, parse_env_file_push, &aq); + va_end(aq); + return r; +} + int parse_env_file_sentinel( FILE *f, const char *fname, @@ -381,18 +398,13 @@ int parse_env_file_fd_sentinel( const char *fname, /* only used for logging */ ...) { - _cleanup_fclose_ FILE *f = NULL; va_list ap; int r; assert(fd >= 0); - r = fdopen_independent(fd, "re", &f); - if (r < 0) - return r; - va_start(ap, fname); - r = parse_env_filev(f, fname, ap); + r = parse_env_file_fdv(fd, fname, ap); va_end(ap); return r; diff --git a/src/basic/env-file.h b/src/basic/env-file.h index fa22d2209c..2465eeddf4 100644 --- a/src/basic/env-file.h +++ b/src/basic/env-file.h @@ -8,6 +8,7 @@ #include "macro.h" int parse_env_filev(FILE *f, const char *fname, va_list ap); +int parse_env_file_fdv(int fd, const char *fname, va_list ap); int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_; #define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL) int parse_env_file_fd_sentinel(int fd, const char *fname, ...) _sentinel_; diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 3cc4f44bcd..7125e28e1b 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -903,6 +903,14 @@ int dir_fd_is_root(int dir_fd) { if (r < 0) return r; + r = statx_fallback(dir_fd, "..", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &pst.sx); + if (r < 0) + return r; + + /* First, compare inode. If these are different, the fd does not point to the root directory "/". */ + if (!statx_inode_same(&st.sx, &pst.sx)) + return false; + if (!FLAGS_SET(st.nsx.stx_mask, STATX_MNT_ID)) { int mntid; @@ -915,10 +923,6 @@ int dir_fd_is_root(int dir_fd) { st.nsx.stx_mask |= STATX_MNT_ID; } - r = statx_fallback(dir_fd, "..", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &pst.sx); - if (r < 0) - return r; - if (!FLAGS_SET(pst.nsx.stx_mask, STATX_MNT_ID)) { int mntid; @@ -931,14 +935,14 @@ int dir_fd_is_root(int dir_fd) { pst.nsx.stx_mask |= STATX_MNT_ID; } - /* If the parent directory is the same inode, the fd points to the root directory "/". We also check - * that the mount ids are the same. Otherwise, a construct like the following could be used to trick - * us: + /* Even if the parent directory has the same inode, the fd may not point to the root directory "/", + * and we also need to check that the mount ids are the same. Otherwise, a construct like the + * following could be used to trick us: * * $ mkdir /tmp/x /tmp/x/y * $ mount --bind /tmp/x /tmp/x/y */ - return statx_inode_same(&st.sx, &pst.sx) && statx_mount_same(&st.nsx, &pst.nsx); + return statx_mount_same(&st.nsx, &pst.nsx); } const char *accmode_to_string(int flags) { diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 340f9b4860..48ffb4e5e6 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -44,20 +44,6 @@ * can detect EOFs. */ #define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 2U) -int fopen_unlocked_at(int dir_fd, const char *path, const char *options, int flags, FILE **ret) { - int r; - - assert(ret); - - r = xfopenat(dir_fd, path, options, flags, ret); - if (r < 0) - return r; - - (void) __fsetlocking(*ret, FSETLOCKING_BYCALLER); - - return 0; -} - int fdopen_unlocked(int fd, const char *options, FILE **ret) { assert(ret); @@ -267,7 +253,8 @@ int write_string_file_ts_at( const struct timespec *ts) { _cleanup_fclose_ FILE *f = NULL; - int q, r, fd; + _cleanup_close_ int fd = -EBADF; + int q, r; assert(fn); assert(line); @@ -304,11 +291,9 @@ int write_string_file_ts_at( goto fail; } - r = fdopen_unlocked(fd, "w", &f); - if (r < 0) { - safe_close(fd); + r = take_fdopen_unlocked(&fd, "w", &f); + if (r < 0) goto fail; - } if (flags & WRITE_STRING_FILE_DISABLE_BUFFER) setvbuf(f, NULL, _IONBF, 0); @@ -354,18 +339,19 @@ int write_string_filef( return write_string_file(fn, p, flags); } -int read_one_line_file(const char *fn, char **line) { +int read_one_line_file_at(int dir_fd, const char *filename, char **ret) { _cleanup_fclose_ FILE *f = NULL; int r; - assert(fn); - assert(line); + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(filename); + assert(ret); - r = fopen_unlocked(fn, "re", &f); + r = fopen_unlocked_at(dir_fd, filename, "re", 0, &f); if (r < 0) return r; - return read_line(f, LONG_LINE_MAX, line); + return read_line(f, LONG_LINE_MAX, ret); } int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_extra_nl) { @@ -760,62 +746,19 @@ int read_full_file_full( size_t *ret_size) { _cleanup_fclose_ FILE *f = NULL; + XfopenFlags xflags = XFOPEN_UNLOCKED; int r; assert(filename); assert(ret_contents); - r = xfopenat(dir_fd, filename, "re", 0, &f); - if (r < 0) { - _cleanup_close_ int sk = -EBADF; - - /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */ - if (r != -ENXIO) - return r; - - /* If this is enabled, let's try to connect to it */ - if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET)) - return -ENXIO; + if (FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET) && /* If this is enabled, let's try to connect to it */ + offset == UINT64_MAX) /* Seeking is not supported on AF_UNIX sockets */ + xflags |= XFOPEN_SOCKET; - /* Seeking is not supported on AF_UNIX sockets */ - if (offset != UINT64_MAX) - return -ENXIO; - - sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); - if (sk < 0) - return -errno; - - if (bind_name) { - /* If the caller specified a socket name to bind to, do so before connecting. This is - * useful to communicate some minor, short meta-information token from the client to - * the server. */ - union sockaddr_union bsa; - - r = sockaddr_un_set_path(&bsa.un, bind_name); - if (r < 0) - return r; - - if (bind(sk, &bsa.sa, r) < 0) - return -errno; - } - - r = connect_unix_path(sk, dir_fd, filename); - if (IN_SET(r, -ENOTSOCK, -EINVAL)) /* propagate original error if this is not a socket after all */ - return -ENXIO; - if (r < 0) - return r; - - if (shutdown(sk, SHUT_WR) < 0) - return -errno; - - f = fdopen(sk, "r"); - if (!f) - return -errno; - - TAKE_FD(sk); - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + r = xfopenat_full(dir_fd, filename, "re", 0, xflags, bind_name, &f); + if (r < 0) + return r; return read_full_stream_full(f, filename, offset, size, flags, ret_contents, ret_size); } @@ -922,8 +865,7 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin } DIR *xopendirat(int fd, const char *name, int flags) { - int nfd; - DIR *d; + _cleanup_close_ int nfd = -EBADF; assert(!(flags & O_CREAT)); @@ -934,13 +876,7 @@ DIR *xopendirat(int fd, const char *name, int flags) { if (nfd < 0) return NULL; - d = fdopendir(nfd); - if (!d) { - safe_close(nfd); - return NULL; - } - - return d; + return take_fdopendir(&nfd); } int fopen_mode_to_flags(const char *mode) { @@ -989,33 +925,111 @@ int fopen_mode_to_flags(const char *mode) { return flags; } -int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret) { +static int xfopenat_regular(int dir_fd, const char *path, const char *mode, int open_flags, FILE **ret) { FILE *f; /* A combination of fopen() with openat() */ - if (dir_fd == AT_FDCWD && flags == 0) { + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); + assert(mode); + assert(ret); + + if (dir_fd == AT_FDCWD && open_flags == 0) f = fopen(path, mode); - if (!f) - return -errno; - } else { - int fd, mode_flags; + else { + _cleanup_close_ int fd = -EBADF; + int mode_flags; mode_flags = fopen_mode_to_flags(mode); if (mode_flags < 0) return mode_flags; - fd = openat(dir_fd, path, mode_flags | flags); + fd = openat(dir_fd, path, mode_flags | open_flags); if (fd < 0) return -errno; - f = fdopen(fd, mode); - if (!f) { - safe_close(fd); + f = take_fdopen(&fd, mode); + } + if (!f) + return -errno; + + *ret = f; + return 0; +} + +static int xfopenat_unix_socket(int dir_fd, const char *path, const char *bind_name, FILE **ret) { + _cleanup_close_ int sk = -EBADF; + FILE *f; + int r; + + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); + assert(ret); + + sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + if (sk < 0) + return -errno; + + if (bind_name) { + /* If the caller specified a socket name to bind to, do so before connecting. This is + * useful to communicate some minor, short meta-information token from the client to + * the server. */ + union sockaddr_union bsa; + + r = sockaddr_un_set_path(&bsa.un, bind_name); + if (r < 0) + return r; + + if (bind(sk, &bsa.sa, r) < 0) return -errno; - } } + r = connect_unix_path(sk, dir_fd, path); + if (r < 0) + return r; + + if (shutdown(sk, SHUT_WR) < 0) + return -errno; + + f = take_fdopen(&sk, "r"); + if (!f) + return -errno; + + *ret = f; + return 0; +} + +int xfopenat_full( + int dir_fd, + const char *path, + const char *mode, + int open_flags, + XfopenFlags flags, + const char *bind_name, + FILE **ret) { + + FILE *f = NULL; /* avoid false maybe-uninitialized warning */ + int r; + + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); + assert(mode); + assert(ret); + + r = xfopenat_regular(dir_fd, path, mode, open_flags, &f); + if (r == -ENXIO && FLAGS_SET(flags, XFOPEN_SOCKET)) { + /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */ + r = xfopenat_unix_socket(dir_fd, path, bind_name, &f); + if (IN_SET(r, -ENOTSOCK, -EINVAL)) + return -ENXIO; /* propagate original error if this is not a socket after all */ + } + if (r < 0) + return r; + + if (FLAGS_SET(flags, XFOPEN_UNLOCKED)) + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + *ret = f; return 0; } @@ -1040,11 +1054,10 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) { if (copy_fd < 0) return copy_fd; - f = fdopen(copy_fd, mode); + f = take_fdopen(©_fd, mode); if (!f) return -errno; - TAKE_FD(copy_fd); *ret = TAKE_PTR(f); return 0; } diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 0a88a19146..769bf394fd 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -43,10 +43,6 @@ typedef enum { READ_FULL_FILE_FAIL_WHEN_LARGER = 1 << 5, /* fail loading if file is larger than specified size */ } ReadFullFileFlags; -int fopen_unlocked_at(int dir_fd, const char *path, const char *options, int flags, FILE **ret); -static inline int fopen_unlocked(const char *path, const char *options, FILE **ret) { - return fopen_unlocked_at(AT_FDCWD, path, options, 0, ret); -} int fdopen_unlocked(int fd, const char *options, FILE **ret); int take_fdopen_unlocked(int *fd, const char *options, FILE **ret); FILE* take_fdopen(int *fd, const char *options); @@ -71,7 +67,10 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); -int read_one_line_file(const char *filename, char **line); +int read_one_line_file_at(int dir_fd, const char *filename, char **ret); +static inline int read_one_line_file(const char *filename, char **ret) { + return read_one_line_file_at(AT_FDCWD, filename, ret); +} int read_full_file_full(int dir_fd, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, const char *bind_name, char **ret_contents, size_t *ret_size); static inline int read_full_file_at(int dir_fd, const char *filename, char **ret_contents, size_t *ret_size) { return read_full_file_full(dir_fd, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size); @@ -104,7 +103,29 @@ int executable_is_script(const char *path, char **interpreter); int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field); DIR *xopendirat(int dirfd, const char *name, int flags); -int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret); + +typedef enum XfopenFlags { + XFOPEN_UNLOCKED = 1 << 0, /* call __fsetlocking(FSETLOCKING_BYCALLER) after opened */ + XFOPEN_SOCKET = 1 << 1, /* also try to open unix socket */ +} XfopenFlags; + +int xfopenat_full( + int dir_fd, + const char *path, + const char *mode, + int open_flags, + XfopenFlags flags, + const char *bind_name, + FILE **ret); +static inline int xfopenat(int dir_fd, const char *path, const char *mode, int open_flags, FILE **ret) { + return xfopenat_full(dir_fd, path, mode, open_flags, 0, NULL, ret); +} +static inline int fopen_unlocked_at(int dir_fd, const char *path, const char *mode, int open_flags, FILE **ret) { + return xfopenat_full(dir_fd, path, mode, open_flags, XFOPEN_UNLOCKED, NULL, ret); +} +static inline int fopen_unlocked(const char *path, const char *mode, FILE **ret) { + return fopen_unlocked_at(AT_FDCWD, path, mode, 0, ret); +} int fdopen_independent(int fd, const char *mode, FILE **ret); diff --git a/src/basic/os-util.c b/src/basic/os-util.c index 8cb8d9302b..5f1c26b87c 100644 --- a/src/basic/os-util.c +++ b/src/basic/os-util.c @@ -14,11 +14,21 @@ #include "parse-util.h" #include "path-util.h" #include "stat-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "utf8.h" #include "xattr-util.h" +static const char* const image_class_table[_IMAGE_CLASS_MAX] = { + [IMAGE_MACHINE] = "machine", + [IMAGE_PORTABLE] = "portable", + [IMAGE_SYSEXT] = "extension", + [IMAGE_CONFEXT] = "confext", +}; + +DEFINE_STRING_TABLE_LOOKUP(image_class, ImageClass); + /* Helper struct for naming simplicity and reusability */ static const struct { const char *release_file_directory; @@ -55,8 +65,6 @@ int path_is_extension_tree(ImageClass image_class, const char *path, const char int r; assert(path); - assert(image_class >= 0); - assert(image_class < _IMAGE_CLASS_MAX); /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir * always results in -ENOENT, and we can properly distinguish the case where the whole root doesn't exist from @@ -295,24 +303,12 @@ int _parse_os_release(const char *root, ...) { int r; va_start(ap, root); - r = parse_release_internal(root, -1, /* relax_extension_release_check= */ false, NULL, ap); + r = parse_release_internal(root, _IMAGE_CLASS_INVALID, /* relax_extension_release_check= */ false, NULL, ap); va_end(ap); return r; } -int load_os_release_pairs(const char *root, char ***ret) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - int r; - - r = fopen_os_release(root, &p, &f); - if (r < 0) - return r; - - return load_env_file_pairs(f, p, ret); -} - int load_os_release_pairs_with_prefix(const char *root, const char *prefix, char ***ret) { _cleanup_strv_free_ char **os_release_pairs = NULL, **os_release_pairs_prefixed = NULL; int r; @@ -347,9 +343,6 @@ int load_extension_release_pairs(const char *root, ImageClass image_class, const _cleanup_free_ char *p = NULL; int r; - assert(image_class >= 0); - assert(image_class < _IMAGE_CLASS_MAX); - r = fopen_extension_release(root, image_class, extension, relax_extension_release_check, &p, &f); if (r < 0) return r; diff --git a/src/basic/os-util.h b/src/basic/os-util.h index 2f641ba9cd..01dcde7a80 100644 --- a/src/basic/os-util.h +++ b/src/basic/os-util.h @@ -5,6 +5,7 @@ #include <stdio.h> #include "time-util.h" + typedef enum ImageClass { IMAGE_MACHINE, IMAGE_PORTABLE, @@ -24,17 +25,17 @@ bool image_name_is_valid(const char *s) _pure_; int path_is_extension_tree(ImageClass image_class, const char *path, const char *extension, bool relax_extension_release_check); static inline int path_is_os_tree(const char *path) { - return path_is_extension_tree(IMAGE_SYSEXT, path, NULL, false); + return path_is_extension_tree(_IMAGE_CLASS_INVALID, path, NULL, false); } int open_extension_release(const char *root, ImageClass image_class, const char *extension, bool relax_extension_release_check, char **ret_path, int *ret_fd); static inline int open_os_release(const char *root, char **ret_path, int *ret_fd) { - return open_extension_release(root, IMAGE_SYSEXT, NULL, false, ret_path, ret_fd); + return open_extension_release(root, _IMAGE_CLASS_INVALID, NULL, false, ret_path, ret_fd); } int fopen_extension_release(const char *root, ImageClass image_class, const char *extension, bool relax_extension_release_check, char **ret_path, FILE **ret_file); static inline int fopen_os_release(const char *root, char **ret_path, FILE **ret_file) { - return fopen_extension_release(root, IMAGE_SYSEXT, NULL, false, ret_path, ret_file); + return fopen_extension_release(root, _IMAGE_CLASS_INVALID, NULL, false, ret_path, ret_file); } int _parse_extension_release(const char *root, ImageClass image_class, bool relax_extension_release_check, const char *extension, ...) _sentinel_; @@ -43,7 +44,9 @@ int _parse_os_release(const char *root, ...) _sentinel_; #define parse_os_release(root, ...) _parse_os_release(root, __VA_ARGS__, NULL) int load_extension_release_pairs(const char *root, ImageClass image_class, const char *extension, bool relax_extension_release_check, char ***ret); -int load_os_release_pairs(const char *root, char ***ret); +static inline int load_os_release_pairs(const char *root, char ***ret) { + return load_extension_release_pairs(root, _IMAGE_CLASS_INVALID, NULL, false, ret); +} int load_os_release_pairs_with_prefix(const char *root, const char *prefix, char ***ret); int os_release_support_ended(const char *support_end, bool quiet, usec_t *ret_eol); diff --git a/src/basic/path-util.c b/src/basic/path-util.c index ae0b25d155..fa2e26789f 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -100,6 +100,34 @@ int path_make_absolute_cwd(const char *p, char **ret) { return 0; } +int path_prefix_root_cwd(const char *p, const char *root, char **ret) { + _cleanup_free_ char *root_abs = NULL; + char *c; + int r; + + assert(p); + assert(ret); + + /* Unlike path_make_absolute(), this always prefixes root path if specified. + * The root path is always simplified, but the provided path will not. + * This is useful for prefixing the result of chaseat(). */ + + if (empty_or_root(root)) + return path_make_absolute_cwd(p, ret); + + r = path_make_absolute_cwd(root, &root_abs); + if (r < 0) + return r; + + path_simplify(root_abs); + c = path_join(root_abs, p); + if (!c) + return -ENOMEM; + + *ret = c; + return 0; +} + int path_make_relative(const char *from, const char *to, char **ret) { _cleanup_free_ char *result = NULL; unsigned n_parents; @@ -491,25 +519,33 @@ bool path_equal_or_files_same(const char *a, const char *b, int flags) { return path_equal(a, b) || files_same(a, b, flags) > 0; } -bool path_equal_filename(const char *a, const char *b) { - _cleanup_free_ char *a_basename = NULL, *b_basename = NULL; - int r; +int path_compare_filename(const char *a, const char *b) { + _cleanup_free_ char *fa = NULL, *fb = NULL; + int r, j, k; - assert(a); - assert(b); + /* Order NULL before non-NULL */ + r = CMP(!!a, !!b); + if (r != 0) + return r; - r = path_extract_filename(a, &a_basename); - if (r < 0) { - log_debug_errno(r, "Failed to parse basename of %s: %m", a); - return false; - } - r = path_extract_filename(b, &b_basename); - if (r < 0) { - log_debug_errno(r, "Failed to parse basename of %s: %m", b); - return false; - } + j = path_extract_filename(a, &fa); + k = path_extract_filename(b, &fb); - return path_equal(a_basename, b_basename); + /* When one of paths is "." or root, then order it earlier. */ + r = CMP(j != -EADDRNOTAVAIL, k != -EADDRNOTAVAIL); + if (r != 0) + return r; + + /* When one of paths is invalid (or we get OOM), order invalid path after valid one. */ + r = CMP(j < 0, k < 0); + if (r != 0) + return r; + + /* fallback to use strcmp() if both paths are invalid. */ + if (j < 0) + return strcmp(a, b); + + return strcmp(fa, fb); } char* path_extend_internal(char **x, ...) { @@ -893,6 +929,8 @@ static const char *skip_slash_or_dot_backward(const char *path, const char *q) { continue; if (q > path && strneq(q - 1, "/.", 2)) continue; + if (q == path && *q == '.') + continue; break; } return q; @@ -917,6 +955,12 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char * * ret: "bbbbb/cc//././" * return value: 5 (== strlen("bbbbb")) * + * Input: path: "//.//aaa///bbbbb/cc//././" + * next: "///bbbbb/cc//././" + * Output: next: "//.//aaa///bbbbb/cc//././" (next == path) + * ret: "aaa///bbbbb/cc//././" + * return value: 3 (== strlen("aaa")) + * * Input: path: "/", ".", "", or NULL * Output: next: equivalent to path * ret: NULL diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 56f01f41d8..a0af9de674 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -60,21 +60,25 @@ int path_split_and_make_absolute(const char *p, char ***ret); char* path_make_absolute(const char *p, const char *prefix); int safe_getcwd(char **ret); int path_make_absolute_cwd(const char *p, char **ret); +int path_prefix_root_cwd(const char *p, const char *root, char **ret); int path_make_relative(const char *from, const char *to, char **ret); int path_make_relative_parent(const char *from_child, const char *to, char **ret); char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_; static inline char* path_startswith(const char *path, const char *prefix) { return path_startswith_full(path, prefix, true); } -int path_compare(const char *a, const char *b) _pure_; +int path_compare(const char *a, const char *b) _pure_; static inline bool path_equal(const char *a, const char *b) { return path_compare(a, b) == 0; } +int path_compare_filename(const char *a, const char *b); +static inline bool path_equal_filename(const char *a, const char *b) { + return path_compare_filename(a, b) == 0; +} + bool path_equal_or_files_same(const char *a, const char *b, int flags); -/* Compares only the last portion of the input paths, ie: the filenames */ -bool path_equal_filename(const char *a, const char *b); char* path_extend_internal(char **x, ...); #define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX) diff --git a/src/boot/bootctl-uki.c b/src/boot/bootctl-uki.c index 718bac5ab2..8808c30569 100644 --- a/src/boot/bootctl-uki.c +++ b/src/boot/bootctl-uki.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include <fcntl.h> + #include "alloc-util.h" #include "bootctl-uki.h" #include "kernel-image.h" @@ -8,7 +10,7 @@ int verb_kernel_identify(int argc, char *argv[], void *userdata) { KernelImageType t; int r; - r = inspect_kernel(argv[1], &t, NULL, NULL, NULL); + r = inspect_kernel(AT_FDCWD, argv[1], &t, NULL, NULL, NULL); if (r < 0) return r; @@ -21,7 +23,7 @@ int verb_kernel_inspect(int argc, char *argv[], void *userdata) { KernelImageType t; int r; - r = inspect_kernel(argv[1], &t, &cmdline, &uname, &pname); + r = inspect_kernel(AT_FDCWD, argv[1], &t, &cmdline, &uname, &pname); if (r < 0) return r; diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 37ba05680f..90f20c05a1 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -2307,6 +2307,7 @@ static int help(void) { " --verbose Show result values in long format\n" " --json=MODE Output as JSON\n" " -j Same as --json=pretty on tty, --json=short otherwise\n" + " --xml-interface Dump the XML description in introspect command\n" " --expect-reply=BOOL Expect a method call reply\n" " --auto-start=BOOL Auto-start destination service\n" " --allow-interactive-authorization=BOOL\n" diff --git a/src/core/unit.c b/src/core/unit.c index 846d15b415..409801aed2 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -1890,12 +1890,12 @@ int unit_start(Unit *u, ActivationDetails *details) { if (UNIT_VTABLE(u)->once_only && dual_timestamp_is_set(&u->inactive_enter_timestamp)) return -ESTALE; - /* If the conditions failed, don't do anything at all. If we already are activating this call might + /* If the conditions were unmet, don't do anything at all. If we already are activating this call might * still be useful to speed up activation in case there is some hold-off time, but we don't want to * recheck the condition in that case. */ if (state != UNIT_ACTIVATING && !unit_test_condition(u)) - return log_unit_debug_errno(u, SYNTHETIC_ERRNO(ECOMM), "Starting requested but condition failed. Not starting unit."); + return log_unit_debug_errno(u, SYNTHETIC_ERRNO(ECOMM), "Starting requested but condition not met. Not starting unit."); /* If the asserts failed, fail the entire job */ if (state != UNIT_ACTIVATING && diff --git a/src/fuzz/fuzz-compress.c b/src/fuzz/fuzz-compress.c index 10956cc548..9cd571dfb8 100644 --- a/src/fuzz/fuzz-compress.c +++ b/src/fuzz/fuzz-compress.c @@ -45,7 +45,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { } size_t csize; - r = compress_blob_explicit(alg, h->data, data_len, buf, size, &csize); + r = compress_blob(alg, h->data, data_len, buf, size, &csize); if (r < 0) { log_error_errno(r, "Compression failed: %m"); return 0; diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index a616390ad8..3753e29d0d 100644 --- a/src/libsystemd/sd-journal/journal-file.c +++ b/src/libsystemd/sd-journal/journal-file.c @@ -1671,7 +1671,7 @@ static int journal_file_append_field( return 0; } -static Compression maybe_compress_payload(JournalFile *f, uint8_t *dst, const uint8_t *src, uint64_t size, size_t *rsize) { +static int maybe_compress_payload(JournalFile *f, uint8_t *dst, const uint8_t *src, uint64_t size, size_t *rsize) { assert(f); assert(f->header); @@ -1681,21 +1681,17 @@ static Compression maybe_compress_payload(JournalFile *f, uint8_t *dst, const ui c = JOURNAL_FILE_COMPRESSION(f); if (c == COMPRESSION_NONE || size < f->compress_threshold_bytes) - return COMPRESSION_NONE; + return 0; - r = compress_blob_explicit(c, src, size, dst, size - 1, rsize); - if (r < 0) { - log_debug_errno(r, "Failed to compress data object using %s, ignoring: %m", compression_to_string(c)); - /* Compression didn't work, we don't really care why, let's continue without compression */ - return COMPRESSION_NONE; - } + r = compress_blob(c, src, size, dst, size - 1, rsize); + if (r < 0) + return log_debug_errno(r, "Failed to compress data object using %s, ignoring: %m", compression_to_string(c)); - assert(r == c); log_debug("Compressed data object %"PRIu64" -> %zu using %s", size, *rsize, compression_to_string(c)); - return c; + return 1; /* compressed */ #else - return COMPRESSION_NONE; + return 0; #endif } @@ -1709,7 +1705,6 @@ static int journal_file_append_data( uint64_t hash, p, osize; Object *o, *fo; size_t rsize = 0; - Compression c; const void *eq; int r; @@ -1737,13 +1732,18 @@ static int journal_file_append_data( o->data.hash = htole64(hash); - c = maybe_compress_payload(f, journal_file_data_payload_field(f, o), data, size, &rsize); + r = maybe_compress_payload(f, journal_file_data_payload_field(f, o), data, size, &rsize); + if (r <= 0) + /* We don't really care failures, let's continue without compression */ + memcpy_safe(journal_file_data_payload_field(f, o), data, size); + else { + Compression c = JOURNAL_FILE_COMPRESSION(f); + + assert(c >= 0 && c < _COMPRESSION_MAX && c != COMPRESSION_NONE); - if (c != COMPRESSION_NONE) { o->object.size = htole64(journal_file_data_payload_offset(f) + rsize); o->object.flags |= COMPRESSION_TO_OBJECT_FLAG(c); - } else - memcpy_safe(journal_file_data_payload_field(f, o), data, size); + } r = journal_file_link_data(f, o, p, hash); if (r < 0) diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 447f1a70b0..71b9d0d4c1 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -1454,9 +1454,7 @@ static int get_settings_path(const char *name, char **ret_path) { } static int edit_settings(int argc, char *argv[], void *userdata) { - _cleanup_(edit_file_context_done) EditFileContext context = { - .remove_parent = false, - }; + _cleanup_(edit_file_context_done) EditFileContext context = {}; int r; if (!on_tty()) diff --git a/src/portable/portabled-image-bus.c b/src/portable/portabled-image-bus.c index 6c4cb6ec9d..be8e65df3f 100644 --- a/src/portable/portabled-image-bus.c +++ b/src/portable/portabled-image-bus.c @@ -83,9 +83,9 @@ static int append_fd(sd_bus_message *m, PortableMetadata *d) { if (d) { assert(d->fd >= 0); - f = take_fdopen(&d->fd, "r"); - if (!f) - return -errno; + r = fdopen_independent(d->fd, "r", &f); + if (r < 0) + return r; r = read_full_stream(f, &buf, &n); if (r < 0) diff --git a/src/shared/copy.c b/src/shared/copy.c index 23d72ad1ca..f283394545 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -157,6 +157,7 @@ int copy_bytes_full( copy_progress_bytes_t progress, void *userdata) { + _cleanup_close_ int fdf_opened = -EBADF, fdt_opened = -EBADF; bool try_cfr = true, try_sendfile = true, try_splice = true, copied_something = false; int r, nonblock_pipe = -1; size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */ @@ -177,6 +178,13 @@ int copy_bytes_full( if (ret_remains_size) *ret_remains_size = 0; + fdf = fd_reopen_condition(fdf, O_CLOEXEC | O_NOCTTY | O_RDONLY, O_PATH, &fdf_opened); + if (fdf < 0) + return fdf; + fdt = fd_reopen_condition(fdt, O_CLOEXEC | O_NOCTTY | O_RDWR, O_PATH, &fdt_opened); + if (fdt < 0) + return fdt; + /* Try btrfs reflinks first. This only works on regular, seekable files, hence let's check the file offsets of * source and destination first. */ if ((copy_flags & COPY_REFLINK)) { diff --git a/src/shared/device-nodes.c b/src/shared/device-nodes.c index 40e469379f..bdbbb98c2c 100644 --- a/src/shared/device-nodes.c +++ b/src/shared/device-nodes.c @@ -8,12 +8,12 @@ #include "string-util.h" #include "utf8.h" -int allow_listed_char_for_devnode(char c, const char *white) { +int allow_listed_char_for_devnode(char c, const char *additional) { return ascii_isdigit(c) || ascii_isalpha(c) || strchr("#+-.:=@_", c) || - (white && strchr(white, c)); + (additional && strchr(additional, c)); } int encode_devnode_name(const char *str, char *str_enc, size_t len) { diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 0343d2e20b..d0b3245a27 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -1326,12 +1326,3 @@ static const char* const image_type_table[_IMAGE_TYPE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType); - -static const char* const image_class_table[_IMAGE_CLASS_MAX] = { - [IMAGE_MACHINE] = "machine", - [IMAGE_PORTABLE] = "portable", - [IMAGE_SYSEXT] = "extension", - [IMAGE_CONFEXT] = "confext" -}; - -DEFINE_STRING_TABLE_LOOKUP(image_class, ImageClass); diff --git a/src/shared/edit-util.c b/src/shared/edit-util.c index f1eb7987f4..9fd74973aa 100644 --- a/src/shared/edit-util.c +++ b/src/shared/edit-util.c @@ -12,11 +12,9 @@ #include "mkdir-label.h" #include "path-util.h" #include "process-util.h" -#include "selinux-util.h" -#include "stat-util.h" #include "string-util.h" #include "strv.h" -#include "tmpfile-util.h" +#include "tmpfile-util-label.h" void edit_file_context_done(EditFileContext *context) { int r; @@ -103,6 +101,9 @@ int edit_files_add( static int create_edit_temp_file(EditFile *e) { _cleanup_(unlink_and_freep) char *temp = NULL; + _cleanup_fclose_ FILE *f = NULL; + const char *source; + bool has_original, has_target; unsigned line = 1; int r; @@ -114,62 +115,37 @@ static int create_edit_temp_file(EditFile *e) { if (e->temp) return 0; - r = tempfn_random(e->path, NULL, &temp); + r = mkdir_parents_label(e->path, 0755); if (r < 0) - return log_error_errno(r, "Failed to determine temporary filename for \"%s\": %m", e->path); + return log_error_errno(r, "Failed to create parent directories for '%s': %m", e->path); - r = mkdir_parents_label(e->path, 0755); + r = fopen_temporary_label(e->path, e->path, &f, &temp); if (r < 0) - return log_error_errno(r, "Failed to create parent directories for \"%s\": %m", e->path); + return log_error_errno(r, "Failed to create temporary file for '%s': %m", e->path); - if (!e->original_path && !e->comment_paths) { - r = mac_selinux_create_file_prepare(e->path, S_IFREG); - if (r < 0) - return r; + if (fchmod(fileno(f), 0644) < 0) + return log_error_errno(errno, "Failed to change mode of temporary file '%s': %m", temp); - r = touch(temp); - mac_selinux_create_file_clear(); - if (r < 0) - return log_error_errno(r, "Failed to create temporary file \"%s\": %m", temp); - } + has_original = e->original_path && access(e->original_path, F_OK) >= 0; + has_target = access(e->path, F_OK) >= 0; - if (e->original_path) { - r = mac_selinux_create_file_prepare(e->path, S_IFREG); - if (r < 0) - return r; - - r = copy_file(e->original_path, temp, 0, 0644, COPY_REFLINK); - if (r == -ENOENT) { - r = touch(temp); - mac_selinux_create_file_clear(); - if (r < 0) - return log_error_errno(r, "Failed to create temporary file \"%s\": %m", temp); - } else { - mac_selinux_create_file_clear(); - if (r < 0) - return log_error_errno(r, "Failed to create temporary file for \"%s\": %m", e->path); - } - } + if (has_original && (!has_target || e->context->overwrite_with_origin)) + /* We are asked to overwrite target with original_path or target doesn't exist. */ + source = e->original_path; + else if (has_target) + /* Target exists and shouldn't be overwritten. */ + source = e->path; + else + source = NULL; if (e->comment_paths) { - _cleanup_free_ char *target_contents = NULL; - _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *source_contents = NULL; - r = mac_selinux_create_file_prepare(e->path, S_IFREG); - if (r < 0) - return r; - - f = fopen(temp, "we"); - mac_selinux_create_file_clear(); - if (!f) - return log_error_errno(errno, "Failed to open temporary file \"%s\": %m", temp); - - if (fchmod(fileno(f), 0644) < 0) - return log_error_errno(errno, "Failed to change mode of temporary file \"%s\": %m", temp); - - r = read_full_file(e->path, &target_contents, NULL); - if (r < 0 && r != -ENOENT) - return log_error_errno(r, "Failed to read target file \"%s\": %m", e->path); + if (source) { + r = read_full_file(source, &source_contents, NULL); + if (r < 0) + return log_error_errno(r, "Failed to read source file '%s': %m", source); + } fprintf(f, "### Editing %s\n" @@ -180,41 +156,47 @@ static int create_edit_temp_file(EditFile *e) { "%s\n", e->path, e->context->marker_start, - strempty(target_contents), - target_contents && endswith(target_contents, "\n") ? "" : "\n", + strempty(source_contents), + source_contents && endswith(source_contents, "\n") ? "" : "\n", e->context->marker_end); line = 4; /* Start editing at the contents area */ - /* Add a comment with the contents of the original files */ STRV_FOREACH(path, e->comment_paths) { - _cleanup_free_ char *contents = NULL; + _cleanup_free_ char *comment = NULL; - /* Skip the file that's being edited, already processed in above */ - if (path_equal(*path, e->path)) + /* Skip the file which is being edited and the source file (can be the same) */ + if (PATH_IN_SET(*path, e->path, source)) continue; - r = read_full_file(*path, &contents, NULL); + r = read_full_file(*path, &comment, NULL); if (r < 0) - return log_error_errno(r, "Failed to read original file \"%s\": %m", *path); + return log_error_errno(r, "Failed to read comment file '%s': %m", *path); fprintf(f, "\n\n### %s", *path); - if (!isempty(contents)) { - _cleanup_free_ char *commented_contents = NULL; - commented_contents = strreplace(strstrip(contents), "\n", "\n# "); - if (!commented_contents) + if (!isempty(comment)) { + _cleanup_free_ char *c = NULL; + + c = strreplace(strstrip(comment), "\n", "\n# "); + if (!c) return log_oom(); - fprintf(f, "\n# %s", commented_contents); + fprintf(f, "\n# %s", c); } } - - r = fflush_and_check(f); - if (r < 0) - return log_error_errno(r, "Failed to create temporary file \"%s\": %m", temp); + } else if (source) { + r = copy_file_fd(source, fileno(f), COPY_REFLINK); + if (r < 0) { + assert(r != -ENOENT); + return log_error_errno(r, "Failed to copy file '%s' to temporary file '%s': %m", source, temp); + } } + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write to temporary file '%s': %m", temp); + e->temp = TAKE_PTR(temp); e->line = line; @@ -310,7 +292,7 @@ static int strip_edit_temp_file(EditFile *e) { r = read_full_file(e->temp, &old_contents, NULL); if (r < 0) - return log_error_errno(r, "Failed to read temporary file \"%s\": %m", e->temp); + return log_error_errno(r, "Failed to read temporary file '%s': %m", e->temp); if (e->context->marker_start) { /* Trim out the lines between the two markers */ @@ -344,7 +326,7 @@ static int strip_edit_temp_file(EditFile *e) { r = write_string_file(e->temp, new_contents, WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_TRUNCATE | WRITE_STRING_FILE_AVOID_NEWLINE); if (r < 0) - return log_error_errno(r, "Failed to modify temporary file \"%s\": %m", e->temp); + return log_error_errno(r, "Failed to strip temporary file '%s': %m", e->temp); return 1; /* Contents have real changes and are changed after stripping */ } @@ -377,7 +359,10 @@ int do_edit_files_and_install(EditFileContext *context) { r = RET_NERRNO(rename(i->temp, i->path)); if (r < 0) - return log_error_errno(r, "Failed to rename \"%s\" to \"%s\": %m", i->temp, i->path); + return log_error_errno(r, + "Failed to rename temporary file '%s' to target file '%s': %m", + i->temp, + i->path); i->temp = mfree(i->temp); log_info("Successfully installed edited file '%s'.", i->path); diff --git a/src/shared/edit-util.h b/src/shared/edit-util.h index 63c6190ef8..83b3df8683 100644 --- a/src/shared/edit-util.h +++ b/src/shared/edit-util.h @@ -3,6 +3,9 @@ #include <stdbool.h> +#define DROPIN_MARKER_START "### Anything between here and the comment below will become the contents of the drop-in file" +#define DROPIN_MARKER_END "### Edits below this comment will be discarded" + typedef struct EditFile EditFile; typedef struct EditFileContext EditFileContext; @@ -21,6 +24,7 @@ struct EditFileContext { const char *marker_start; const char *marker_end; bool remove_parent; + bool overwrite_with_origin; /* whether to always overwrite target with original file */ }; void edit_file_context_done(EditFileContext *context); diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c index 0d45249d63..6a0002a2bd 100644 --- a/src/shared/find-esp.c +++ b/src/shared/find-esp.c @@ -540,11 +540,9 @@ int find_esp_and_warn( return r; if (ret_path) { - char *q = path_join(empty_to_root(root), p); - if (!q) - return -ENOMEM; - - *ret_path = TAKE_PTR(q); + r = path_prefix_root_cwd(p, root, ret_path); + if (r < 0) + return r; } if (ret_part) *ret_part = part; @@ -861,11 +859,9 @@ int find_xbootldr_and_warn( return r; if (ret_path) { - char *q = path_join(empty_to_root(root), p); - if (!q) - return -ENOMEM; - - *ret_path = TAKE_PTR(q); + r = path_prefix_root_cwd(p, root, ret_path); + if (r < 0) + return r; } if (ret_uuid) *ret_uuid = uuid; diff --git a/src/shared/kernel-image.c b/src/shared/kernel-image.c index b5ea1bb2a0..3d2ec820d9 100644 --- a/src/shared/kernel-image.c +++ b/src/shared/kernel-image.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "fd-util.h" +#include "fileio.h" #include "env-file.h" #include "kernel-image.h" #include "os-util.h" @@ -255,6 +256,7 @@ static int inspect_uki( } int inspect_kernel( + int dir_fd, const char *filename, KernelImageType *ret_type, char **ret_cmdline, @@ -267,11 +269,12 @@ int inspect_kernel( KernelImageType t; int r; + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); assert(filename); - f = fopen(filename, "re"); - if (!f) - return log_error_errno(errno, "Failed to open kernel image file '%s': %m", filename); + r = xfopenat(dir_fd, filename, "re", 0, &f); + if (r < 0) + return log_error_errno(r, "Failed to open kernel image file '%s': %m", filename); r = pe_sections(f, §ions, &scount); if (r < 0) diff --git a/src/shared/kernel-image.h b/src/shared/kernel-image.h index d1875920cc..41b2c08f9a 100644 --- a/src/shared/kernel-image.h +++ b/src/shared/kernel-image.h @@ -16,6 +16,7 @@ typedef enum KernelImageType { const char* kernel_image_type_to_string(KernelImageType t) _const_; int inspect_kernel( + int dir_fd, const char *filename, KernelImageType *ret_type, char **ret_cmdline, diff --git a/src/systemctl/systemctl-edit.c b/src/systemctl/systemctl-edit.c index 5f42dc239f..561b01a67a 100644 --- a/src/systemctl/systemctl-edit.c +++ b/src/systemctl/systemctl-edit.c @@ -13,9 +13,6 @@ #include "systemctl.h" #include "terminal-util.h" -#define EDIT_MARKER_START "### Anything between here and the comment below will become the contents of the drop-in file" -#define EDIT_MARKER_END "### Edits below this comment will be discarded" - int verb_cat(int argc, char *argv[], void *userdata) { _cleanup_(hashmap_freep) Hashmap *cached_name_map = NULL, *cached_id_map = NULL; _cleanup_(lookup_paths_free) LookupPaths lp = {}; @@ -316,9 +313,10 @@ static int find_paths_to_edit( int verb_edit(int argc, char *argv[], void *userdata) { _cleanup_(edit_file_context_done) EditFileContext context = { - .marker_start = EDIT_MARKER_START, - .marker_end = EDIT_MARKER_END, + .marker_start = DROPIN_MARKER_START, + .marker_end = DROPIN_MARKER_END, .remove_parent = !arg_full, + .overwrite_with_origin = true, }; _cleanup_(lookup_paths_free) LookupPaths lp = {}; _cleanup_strv_free_ char **names = NULL; diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c index 4a14c105dd..6422550af4 100644 --- a/src/systemctl/systemctl-show.c +++ b/src/systemctl/systemctl-show.c @@ -516,7 +516,7 @@ static void print_status_info( if (!i->condition_result && i->condition_timestamp > 0) { int n = 0; - printf(" Condition: start %scondition failed%s at %s; %s\n", + printf(" Condition: start %scondition unmet%s at %s; %s\n", ansi_highlight_yellow(), ansi_normal(), FORMAT_TIMESTAMP_STYLE(i->condition_timestamp, arg_timestamp_style), FORMAT_TIMESTAMP_RELATIVE(i->condition_timestamp)); diff --git a/src/test/test-compress-benchmark.c b/src/test/test-compress-benchmark.c index da0f5e137a..6180e19839 100644 --- a/src/test/test-compress-benchmark.c +++ b/src/test/test-compress-benchmark.c @@ -102,7 +102,7 @@ static void test_compress_decompress(const char* label, const char* type, r = compress(text, size, buf, size, &j); /* assume compression must be successful except for small or random inputs */ - assert_se(r > 0 || (size < 2048 && r == -ENOBUFS) || streq(type, "random")); + assert_se(r >= 0 || (size < 2048 && r == -ENOBUFS) || streq(type, "random")); /* check for overwrites */ assert_se(buf[size] == 0); diff --git a/src/test/test-compress.c b/src/test/test-compress.c index f5ec47cb3c..18f8ce3b35 100644 --- a/src/test/test-compress.c +++ b/src/test/test-compress.c @@ -46,7 +46,6 @@ typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size); #if HAVE_COMPRESSION _unused_ static void test_compress_decompress( - int flag, const char *compression, compress_blob_t compress, decompress_blob_t decompress, @@ -67,7 +66,7 @@ _unused_ static void test_compress_decompress( log_info_errno(r, "compression failed: %m"); assert_se(may_fail); } else { - assert_se(r == flag); + assert_se(r >= 0); r = decompress(compressed, csize, (void **) &decompressed, &csize, 0); assert_se(r == 0); @@ -120,7 +119,7 @@ _unused_ static void test_decompress_startswith(const char *compression, assert_se(compressed2); r = compress(data, data_len, compressed, BUFSIZE_2, &csize); } - assert_se(r > 0); + assert_se(r >= 0); len = strlen(data); @@ -151,7 +150,7 @@ _unused_ static void test_decompress_startswith_short(const char *compression, log_info("/* %s with %s */", __func__, compression); r = compress(TEXT, sizeof TEXT, buf, sizeof buf, &csize); - assert_se(r > 0); + assert_se(r >= 0); for (size_t i = 1; i < strlen(TEXT); i++) { _cleanup_free_ void *buf2 = NULL; @@ -163,8 +162,7 @@ _unused_ static void test_decompress_startswith_short(const char *compression, } } -_unused_ static void test_compress_stream(int flag, - const char *compression, +_unused_ static void test_compress_stream(const char *compression, const char *cat, compress_stream_t compress, decompress_stream_t decompress, @@ -195,7 +193,7 @@ _unused_ static void test_compress_stream(int flag, assert_se((dst = mkostemp_safe(pattern)) >= 0); - assert_se(compress(src, dst, -1, &uncompressed_size) == flag); + assert_se(compress(src, dst, -1, &uncompressed_size) >= 0); if (cat) { assert_se(asprintf(&cmd, "%s %s | diff %s -", cat, pattern, srcfile) > 0); @@ -293,11 +291,9 @@ int main(int argc, char *argv[]) { random_bytes(data + 7, sizeof(data) - 7); #if HAVE_XZ - test_compress_decompress(COMPRESSION_XZ, "XZ", - compress_blob_xz, decompress_blob_xz, + test_compress_decompress("XZ", compress_blob_xz, decompress_blob_xz, text, sizeof(text), false); - test_compress_decompress(COMPRESSION_XZ, "XZ", - compress_blob_xz, decompress_blob_xz, + test_compress_decompress("XZ", compress_blob_xz, decompress_blob_xz, data, sizeof(data), true); test_decompress_startswith("XZ", @@ -310,7 +306,7 @@ int main(int argc, char *argv[]) { compress_blob_xz, decompress_startswith_xz, huge, HUGE_SIZE, true); - test_compress_stream(COMPRESSION_XZ, "XZ", "xzcat", + test_compress_stream("XZ", "xzcat", compress_stream_xz, decompress_stream_xz, srcfile); test_decompress_startswith_short("XZ", compress_blob_xz, decompress_startswith_xz); @@ -320,11 +316,9 @@ int main(int argc, char *argv[]) { #endif #if HAVE_LZ4 - test_compress_decompress(COMPRESSION_LZ4, "LZ4", - compress_blob_lz4, decompress_blob_lz4, + test_compress_decompress("LZ4", compress_blob_lz4, decompress_blob_lz4, text, sizeof(text), false); - test_compress_decompress(COMPRESSION_LZ4, "LZ4", - compress_blob_lz4, decompress_blob_lz4, + test_compress_decompress("LZ4", compress_blob_lz4, decompress_blob_lz4, data, sizeof(data), true); test_decompress_startswith("LZ4", @@ -337,7 +331,7 @@ int main(int argc, char *argv[]) { compress_blob_lz4, decompress_startswith_lz4, huge, HUGE_SIZE, true); - test_compress_stream(COMPRESSION_LZ4, "LZ4", "lz4cat", + test_compress_stream("LZ4", "lz4cat", compress_stream_lz4, decompress_stream_lz4, srcfile); test_lz4_decompress_partial(); @@ -349,11 +343,9 @@ int main(int argc, char *argv[]) { #endif #if HAVE_ZSTD - test_compress_decompress(COMPRESSION_ZSTD, "ZSTD", - compress_blob_zstd, decompress_blob_zstd, + test_compress_decompress("ZSTD", compress_blob_zstd, decompress_blob_zstd, text, sizeof(text), false); - test_compress_decompress(COMPRESSION_ZSTD, "ZSTD", - compress_blob_zstd, decompress_blob_zstd, + test_compress_decompress("ZSTD", compress_blob_zstd, decompress_blob_zstd, data, sizeof(data), true); test_decompress_startswith("ZSTD", @@ -366,7 +358,7 @@ int main(int argc, char *argv[]) { compress_blob_zstd, decompress_startswith_zstd, huge, HUGE_SIZE, true); - test_compress_stream(COMPRESSION_ZSTD, "ZSTD", "zstdcat", + test_compress_stream("ZSTD", "zstdcat", compress_stream_zstd, decompress_stream_zstd, srcfile); test_decompress_startswith_short("ZSTD", compress_blob_zstd, decompress_startswith_zstd); diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c index beda749fb8..4253490443 100644 --- a/src/test/test-conf-files.c +++ b/src/test/test-conf-files.c @@ -8,89 +8,151 @@ #include "alloc-util.h" #include "conf-files.h" +#include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "macro.h" #include "mkdir.h" -#include "parse-util.h" #include "path-util.h" #include "rm-rf.h" #include "string-util.h" #include "strv.h" #include "tests.h" -#include "user-util.h" +#include "tmpfile-util.h" -static void setup_test_dir(char *tmp_dir, const char *files, ...) { - va_list ap; +TEST(conf_files_list) { + _cleanup_(rm_rf_physical_and_freep) char *t = NULL; + _cleanup_close_ int tfd = -EBADF; + _cleanup_strv_free_ char **result = NULL; + const char *search1, *search2, *search1_a, *search1_b, *search1_c, *search2_aa; + + tfd = mkdtemp_open("/tmp/test-conf-files-XXXXXX", O_PATH, &t); + assert(tfd >= 0); - assert_se(mkdtemp(tmp_dir)); + assert_se(mkdirat(tfd, "dir1", 0755) >= 0); + assert_se(mkdirat(tfd, "dir2", 0755) >= 0); - va_start(ap, files); - while (files) { - _cleanup_free_ char *path; + search1 = strjoina(t, "/dir1/"); + search2 = strjoina(t, "/dir2/"); - assert_se(path = path_join(tmp_dir, files)); - assert_se(write_string_file(path, "foobar", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); + FOREACH_STRING(p, "a.conf", "b.conf", "c.foo") { + _cleanup_free_ char *path = NULL; - files = va_arg(ap, const char *); + assert_se(path = path_join(search1, p)); + assert_se(write_string_file(path, "foobar", WRITE_STRING_FILE_CREATE) >= 0); } - va_end(ap); -} -static void test_conf_files_list_one(bool use_root) { - char tmp_dir[] = "/tmp/test-conf-files-XXXXXX"; - _cleanup_strv_free_ char **found_files = NULL, **found_files2 = NULL; - const char *root_dir, *search, *expect_a, *expect_b, *expect_c, *mask; + assert_se(symlinkat("/dev/null", tfd, "dir1/m.conf") >= 0); + + FOREACH_STRING(p, "a.conf", "aa.conf", "m.conf") { + _cleanup_free_ char *path = NULL; - log_info("/* %s(%s) */", __func__, yes_no(use_root)); + assert_se(path = path_join(search2, p)); + assert_se(write_string_file(path, "hogehoge", WRITE_STRING_FILE_CREATE) >= 0); + } - setup_test_dir(tmp_dir, - "/dir/a.conf", - "/dir/b.conf", - "/dir/c.foo", - NULL); + search1_a = strjoina(search1, "a.conf"); + search1_b = strjoina(search1, "b.conf"); + search1_c = strjoina(search1, "c.foo"); + search2_aa = strjoina(search2, "aa.conf"); - mask = strjoina(tmp_dir, "/dir/d.conf"); - assert_se(symlink("/dev/null", mask) >= 0); + /* search dir1 without suffix */ + assert_se(conf_files_list(&result, NULL, NULL, CONF_FILES_FILTER_MASKED, search1) >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE(search1_a, search1_b, search1_c))); - if (use_root) { - root_dir = tmp_dir; - search = "/dir"; - } else { - root_dir = NULL; - search = strjoina(tmp_dir, "/dir"); - } + result = strv_free(result); - expect_a = strjoina(tmp_dir, "/dir/a.conf"); - expect_b = strjoina(tmp_dir, "/dir/b.conf"); - expect_c = strjoina(tmp_dir, "/dir/c.foo"); + assert_se(conf_files_list(&result, NULL, t, CONF_FILES_FILTER_MASKED, "/dir1/") >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE(search1_a, search1_b, search1_c))); - log_debug("/* Check when filtered by suffix */"); + result = strv_free(result); - assert_se(conf_files_list(&found_files, ".conf", root_dir, CONF_FILES_FILTER_MASKED, search) == 0); - strv_print(found_files); + assert_se(conf_files_list_at(&result, NULL, AT_FDCWD, CONF_FILES_FILTER_MASKED, search1) >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE(search1_a, search1_b, search1_c))); - assert_se(found_files); - assert_se(streq_ptr(found_files[0], expect_a)); - assert_se(streq_ptr(found_files[1], expect_b)); - assert_se(!found_files[2]); + result = strv_free(result); - log_debug("/* Check when unfiltered */"); - assert_se(conf_files_list(&found_files2, NULL, root_dir, CONF_FILES_FILTER_MASKED, search) == 0); - strv_print(found_files2); + assert_se(conf_files_list_at(&result, NULL, tfd, CONF_FILES_FILTER_MASKED, "/dir1/") >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE("dir1/a.conf", "dir1/b.conf", "dir1/c.foo"))); - assert_se(found_files2); - assert_se(streq_ptr(found_files2[0], expect_a)); - assert_se(streq_ptr(found_files2[1], expect_b)); - assert_se(streq_ptr(found_files2[2], expect_c)); - assert_se(!found_files2[3]); + result = strv_free(result); - assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); -} + /* search dir1 with suffix */ + assert_se(conf_files_list(&result, ".conf", NULL, CONF_FILES_FILTER_MASKED, search1) >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE(search1_a, search1_b))); -TEST(conf_files_list) { - test_conf_files_list_one(false); - test_conf_files_list_one(true); + result = strv_free(result); + + assert_se(conf_files_list(&result, ".conf", t, CONF_FILES_FILTER_MASKED, "/dir1/") >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE(search1_a, search1_b))); + + result = strv_free(result); + + assert_se(conf_files_list_at(&result, ".conf", AT_FDCWD, CONF_FILES_FILTER_MASKED, search1) >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE(search1_a, search1_b))); + + result = strv_free(result); + + assert_se(conf_files_list_at(&result, ".conf", tfd, CONF_FILES_FILTER_MASKED, "/dir1/") >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE("dir1/a.conf", "dir1/b.conf"))); + + result = strv_free(result); + + /* search two dirs */ + assert_se(conf_files_list_strv(&result, ".conf", NULL, CONF_FILES_FILTER_MASKED, STRV_MAKE_CONST(search1, search2)) >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE(search1_a, search2_aa, search1_b))); + + result = strv_free(result); + + assert_se(conf_files_list_strv(&result, ".conf", t, CONF_FILES_FILTER_MASKED, STRV_MAKE_CONST("/dir1/", "/dir2/")) >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE(search1_a, search2_aa, search1_b))); + + result = strv_free(result); + + assert_se(conf_files_list_strv_at(&result, ".conf", AT_FDCWD, CONF_FILES_FILTER_MASKED, STRV_MAKE_CONST(search1, search2)) >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE(search1_a, search2_aa, search1_b))); + + result = strv_free(result); + + assert_se(conf_files_list_strv_at(&result, ".conf", tfd, CONF_FILES_FILTER_MASKED, STRV_MAKE_CONST("/dir1/", "/dir2/")) >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE("dir1/a.conf", "dir2/aa.conf", "dir1/b.conf"))); + + result = strv_free(result); + + /* filename only */ + assert_se(conf_files_list_strv(&result, ".conf", NULL, CONF_FILES_FILTER_MASKED | CONF_FILES_BASENAME, STRV_MAKE_CONST(search1, search2)) >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE("a.conf", "aa.conf", "b.conf"))); + + result = strv_free(result); + + assert_se(conf_files_list_strv(&result, ".conf", t, CONF_FILES_FILTER_MASKED | CONF_FILES_BASENAME, STRV_MAKE_CONST("/dir1/", "/dir2/")) >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE("a.conf", "aa.conf", "b.conf"))); + + result = strv_free(result); + + assert_se(conf_files_list_strv_at(&result, ".conf", AT_FDCWD, CONF_FILES_FILTER_MASKED | CONF_FILES_BASENAME, STRV_MAKE_CONST(search1, search2)) >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE("a.conf", "aa.conf", "b.conf"))); + + result = strv_free(result); + + assert_se(conf_files_list_strv_at(&result, ".conf", tfd, CONF_FILES_FILTER_MASKED | CONF_FILES_BASENAME, STRV_MAKE_CONST("/dir1/", "/dir2/")) >= 0); + strv_print(result); + assert_se(strv_equal(result, STRV_MAKE("a.conf", "aa.conf", "b.conf"))); } static void test_conf_files_insert_one(const char *root) { diff --git a/src/test/test-copy.c b/src/test/test-copy.c index 4cd83dbeea..df08b7ed61 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -301,7 +301,7 @@ static void test_copy_bytes_regular_file_one(const char *src, bool try_reflink, log_info("%s try_reflink=%s max_bytes=%" PRIu64, __func__, yes_no(try_reflink), max_bytes); - fd = open(src, O_RDONLY | O_CLOEXEC | O_NOCTTY); + fd = open(src, O_CLOEXEC | O_PATH); assert_se(fd >= 0); fd2 = mkostemp_safe(fn2); diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index d8d622e44d..a570941cf3 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -1067,7 +1067,8 @@ static void test_read_virtual_file_one(size_t max_size) { IN_SET(r, -ENOENT, /* Some of the files might be absent */ -EINVAL, /* too small reads from /proc/self/pagemap trigger EINVAL */ - -EFBIG)); /* /proc/kcore and /proc/self/pagemap should be too large */ + -EFBIG, /* /proc/kcore and /proc/self/pagemap should be too large */ + -EBADF)); /* /proc/kcore is masked when we are running in docker. */ } else log_info("read_virtual_file(\"%s\", %zu): %s (%zu bytes)", filename, max_size, r ? "non-truncated" : "truncated", size); } diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 136005d51f..22e8f3481a 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -46,11 +46,6 @@ TEST(path) { assert_se(!path_equal_ptr("/a", "/b")); assert_se(!path_equal_ptr("/a", NULL)); assert_se(!path_equal_ptr(NULL, "/a")); - - assert_se(path_equal_filename("/a/c", "/b/c")); - assert_se(path_equal_filename("/a", "/a")); - assert_se(!path_equal_filename("/a/b", "/a/c")); - assert_se(!path_equal_filename("/b", "/c")); } static void test_path_simplify_one(const char *in, const char *out) { @@ -150,6 +145,55 @@ TEST(path_compare) { test_path_compare_one("/foo/a/b", "/foo/aaa", -1); } +static void test_path_compare_filename_one(const char *a, const char *b, int expected) { + int r; + + assert_se(path_compare_filename(a, a) == 0); + assert_se(path_compare_filename(b, b) == 0); + + r = path_compare_filename(a, b); + assert_se((r > 0) == (expected > 0) && (r < 0) == (expected < 0)); + r = path_compare_filename(b, a); + assert_se((r < 0) == (expected > 0) && (r > 0) == (expected < 0)); + + assert_se(path_equal_filename(a, a) == 1); + assert_se(path_equal_filename(b, b) == 1); + assert_se(path_equal_filename(a, b) == (expected == 0)); + assert_se(path_equal_filename(b, a) == (expected == 0)); +} + +TEST(path_compare_filename) { + test_path_compare_filename_one("/goo", "/goo", 0); + test_path_compare_filename_one("/goo", "/goo", 0); + test_path_compare_filename_one("//goo", "/goo", 0); + test_path_compare_filename_one("//goo/////", "/goo", 0); + test_path_compare_filename_one("goo/////", "goo", 0); + test_path_compare_filename_one("/goo/boo", "/goo//boo", 0); + test_path_compare_filename_one("//goo/boo", "/goo/boo//", 0); + test_path_compare_filename_one("//goo/././//./boo//././//", "/goo/boo//.", 0); + test_path_compare_filename_one("/.", "//.///", -1); + test_path_compare_filename_one("/x", "x/", 0); + test_path_compare_filename_one("x/", "/", 1); + test_path_compare_filename_one("/x/./y", "x/y", 0); + test_path_compare_filename_one("/x/./y", "/x/y", 0); + test_path_compare_filename_one("/x/./././y", "/x/y/././.", 0); + test_path_compare_filename_one("./x/./././y", "./x/y/././.", 0); + test_path_compare_filename_one(".", "./.", -1); + test_path_compare_filename_one(".", "././.", -1); + test_path_compare_filename_one("./..", ".", 1); + test_path_compare_filename_one("x/.y", "x/y", -1); + test_path_compare_filename_one("foo", "/foo", 0); + test_path_compare_filename_one("/foo", "/foo/bar", 1); + test_path_compare_filename_one("/foo/aaa", "/foo/b", -1); + test_path_compare_filename_one("/foo/aaa", "/foo/b/a", 1); + test_path_compare_filename_one("/foo/a", "/foo/aaa", -1); + test_path_compare_filename_one("/foo/a/b", "/foo/aaa", 1); + test_path_compare_filename_one("/a/c", "/b/c", 0); + test_path_compare_filename_one("/a", "/a", 0); + test_path_compare_filename_one("/a/b", "/a/c", -1); + test_path_compare_filename_one("/b", "/c", -1); +} + TEST(path_equal_root) { /* Nail down the details of how path_equal("/", ...) works. */ @@ -450,6 +494,45 @@ TEST(fsck_exists) { assert_se(fsck_exists_for_fstype("/../bin/") == 0); } +TEST(path_prefix_root_cwd) { + _cleanup_free_ char *cwd = NULL, *ret = NULL, *expected = NULL; + + assert_se(safe_getcwd(&cwd) >= 0); + + assert_se(path_prefix_root_cwd("hoge", NULL, &ret) >= 0); + assert_se(expected = path_join(cwd, "hoge")); + assert_se(streq(ret, expected)); + + ret = mfree(ret); + expected = mfree(expected); + + assert_se(path_prefix_root_cwd("/hoge", NULL, &ret) >= 0); + assert_se(streq(ret, "/hoge")); + + ret = mfree(ret); + + assert_se(path_prefix_root_cwd("hoge", "/a/b//./c///", &ret) >= 0); + assert_se(streq(ret, "/a/b/c/hoge")); + + ret = mfree(ret); + + assert_se(path_prefix_root_cwd("hoge", "a/b//./c///", &ret) >= 0); + assert_se(expected = path_join(cwd, "a/b/c/hoge")); + assert_se(streq(ret, expected)); + + ret = mfree(ret); + expected = mfree(expected); + + assert_se(path_prefix_root_cwd("/../hoge/aaa/../././b", "/a/b//./c///", &ret) >= 0); + assert_se(streq(ret, "/a/b/c/../hoge/aaa/../././b")); + + ret = mfree(ret); + + assert_se(path_prefix_root_cwd("/../hoge/aaa/../././b", "a/b//./c///", &ret) >= 0); + assert_se(expected = path_join(cwd, "a/b/c/../hoge/aaa/../././b")); + assert_se(streq(ret, expected)); +} + static void test_path_make_relative_one(const char *from, const char *to, const char *expected) { _cleanup_free_ char *z = NULL; int r; @@ -632,9 +715,10 @@ static void test_path_find_first_component_one( r = path_find_first_component(&p, accept_dot_dot, &e); if (r <= 0) { if (r == 0) { - if (path) + if (path) { assert_se(p == path + strlen_ptr(path)); - else + assert_se(isempty(p)); + } else assert_se(!p); assert_se(!e); } @@ -647,6 +731,15 @@ static void test_path_find_first_component_one( assert_se(strcspn(e, "/") == (size_t) r); assert_se(strlen_ptr(*expected) == (size_t) r); assert_se(strneq(e, *expected++, r)); + + assert_se(p); + log_debug("p=%s", p); + if (!isempty(*expected)) + assert_se(startswith(p, *expected)); + else if (ret >= 0) { + assert_se(p == path + strlen_ptr(path)); + assert_se(isempty(p)); + } } } @@ -668,7 +761,7 @@ TEST(path_find_first_component) { test_path_find_first_component_one("././//.///aa/bbb//./ccc", false, STRV_MAKE("aa", "bbb", "ccc"), 0); test_path_find_first_component_one("././//.///aa/.../../bbb//./ccc/.", false, STRV_MAKE("aa", "..."), -EINVAL); test_path_find_first_component_one("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/.", false, STRV_MAKE("aaa", ".bbb"), -EINVAL); - test_path_find_first_component_one("a/foo./b", false, STRV_MAKE("a", "foo.", "b"), 0); + test_path_find_first_component_one("a/foo./b//././/", false, STRV_MAKE("a", "foo.", "b"), 0); test_path_find_first_component_one(NULL, true, NULL, 0); test_path_find_first_component_one("", true, NULL, 0); @@ -684,7 +777,7 @@ TEST(path_find_first_component) { test_path_find_first_component_one("././//.///aa/bbb//./ccc", true, STRV_MAKE("aa", "bbb", "ccc"), 0); test_path_find_first_component_one("././//.///aa/.../../bbb//./ccc/.", true, STRV_MAKE("aa", "...", "..", "bbb", "ccc"), 0); test_path_find_first_component_one("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/.", true, STRV_MAKE("aaa", ".bbb", "..", "c.", "d.dd", "..eeee"), 0); - test_path_find_first_component_one("a/foo./b", true, STRV_MAKE("a", "foo.", "b"), 0); + test_path_find_first_component_one("a/foo./b//././/", true, STRV_MAKE("a", "foo.", "b"), 0); memset(foo, 'a', sizeof(foo) -1); char_array_0(foo); @@ -726,6 +819,15 @@ static void test_path_find_last_component_one( assert_se(strcspn(e, "/") == (size_t) r); assert_se(strlen_ptr(*expected) == (size_t) r); assert_se(strneq(e, *expected++, r)); + + assert_se(next); + log_debug("path=%s\nnext=%s", path, next); + if (!isempty(*expected)) { + assert_se(next < path + strlen(path)); + assert_se(next >= path + strlen(*expected)); + assert_se(startswith(next - strlen(*expected), *expected)); + } else if (ret >= 0) + assert_se(next == path); } } diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c index 364d567705..e7d8da7c50 100644 --- a/src/udev/scsi_id/scsi_id.c +++ b/src/udev/scsi_id/scsi_id.c @@ -32,8 +32,10 @@ static const struct option options[] = { { "device", required_argument, NULL, 'd' }, { "config", required_argument, NULL, 'f' }, { "page", required_argument, NULL, 'p' }, - { "blacklisted", no_argument, NULL, 'b' }, - { "whitelisted", no_argument, NULL, 'g' }, + { "denylisted", no_argument, NULL, 'b' }, + { "allowlisted", no_argument, NULL, 'g' }, + { "blacklisted", no_argument, NULL, 'b' }, /* backward compat */ + { "whitelisted", no_argument, NULL, 'g' }, /* backward compat */ { "replace-whitespace", no_argument, NULL, 'u' }, { "sg-version", required_argument, NULL, 's' }, { "verbose", no_argument, NULL, 'v' }, @@ -222,8 +224,8 @@ static void help(void) { " -f --config= Location of config file\n" " -p --page=0x80|0x83|pre-spc3-83 SCSI page (0x80, 0x83, pre-spc3-83)\n" " -s --sg-version=3|4 Use SGv3 or SGv4\n" - " -b --blacklisted Treat device as blacklisted\n" - " -g --whitelisted Treat device as whitelisted\n" + " -b --denylisted Treat device as denylisted\n" + " -g --allowlisted Treat device as allowlisted\n" " -u --replace-whitespace Replace all whitespace by underscores\n" " -v --verbose Verbose logging\n" " -x --export Print values as environment keys\n", diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c index c619506877..a271b1786c 100644 --- a/src/udev/scsi_id/scsi_serial.c +++ b/src/udev/scsi_id/scsi_serial.c @@ -161,7 +161,7 @@ static int scsi_dump_sense(struct scsi_id_device *dev_scsi, * Figure out and print the sense key, asc and ascq. * * If you want to suppress these for a particular drive model, add - * a black list entry in the scsi_id config file. + * a deny list entry in the scsi_id config file. * * XXX We probably need to: lookup the sense/asc/ascq in a retry * table, and if found return 1 (after dumping the sense, asc, and diff --git a/test/fuzz/fuzz-udev-rules/60-persistent-storage-tape.rules b/test/fuzz/fuzz-udev-rules/60-persistent-storage-tape.rules index 0136140a46..40d6261c6e 100644 --- a/test/fuzz/fuzz-udev-rules/60-persistent-storage-tape.rules +++ b/test/fuzz/fuzz-udev-rules/60-persistent-storage-tape.rules @@ -6,7 +6,7 @@ ACTION=="remove", GOTO="persistent_storage_tape_end" ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_tape_end" # type 8 devices are "Medium Changers" -SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \ +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --allowlisted -d $devnode", \ SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL}" # iSCSI devices from the same host have all the same ID_SERIAL, @@ -22,7 +22,7 @@ SUBSYSTEM!="scsi_tape", GOTO="persistent_storage_tape_end" KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394" KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", ENV{.BSG_DEV}="$root/bsg/$id" -KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --whitelisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi" +KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --allowlisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi" KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}" KERNEL=="st*[0-9]", ENV{ID_SCSI_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SCSI_SERIAL}" KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst" diff --git a/test/fuzz/fuzz-udev-rules/60-persistent-storage.rules b/test/fuzz/fuzz-udev-rules/60-persistent-storage.rules index 93d562b070..b63225875b 100644 --- a/test/fuzz/fuzz-udev-rules/60-persistent-storage.rules +++ b/test/fuzz/fuzz-udev-rules/60-persistent-storage.rules @@ -49,8 +49,8 @@ KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS= KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" # SCSI devices -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi" -KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss" +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --allowlisted -d $devnode", ENV{ID_BUS}="scsi" +KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --allowlisted -d $devnode", ENV{ID_BUS}="cciss" KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}" KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n" diff --git a/test/udev-test.pl b/test/udev-test.pl index 16c82bec0c..5873a5b646 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -2191,7 +2191,7 @@ TAGS=="aaa", SYMLINK+="bad" EOF }, { - desc => "continuations with white only line", + desc => "continuations with space only line", devices => [ { devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", diff --git a/test/units/testsuite-01.sh b/test/units/testsuite-01.sh index 469c5fe278..cd18a9fd74 100755 --- a/test/units/testsuite-01.sh +++ b/test/units/testsuite-01.sh @@ -3,33 +3,29 @@ set -eux set -o pipefail -STTY_ORIGINAL="$(stty --file=/dev/console --save)" - -at_exit() { - set +e - stty --file=/dev/console "${STTY_ORIGINAL:?}" -} - -trap at_exit EXIT - -# Do one reexec beforehand to get /dev/console into some predictable state -systemctl daemon-reexec - -# Check if we do skip the early setup when doing daemon-reexec -# See: https://github.com/systemd/systemd/issues/27106 +# Check if we properly differentiate between a full systemd setup and a "light" +# version of it that's done during daemon-reexec # -# Change a couple of console settings, do a reexec, and then check if our -# changes persisted, since we reset the terminal stuff only on "full" reexec -# -# Relevant function: reset_terminal_fd() from terminal-util.cs -stty --file=/dev/console brkint igncr inlcr istrip iuclc -icrnl -imaxbel -iutf8 \ - kill ^K quit ^I -STTY_NEW="$(stty --file=/dev/console --save)" -systemctl daemon-reexec -diff <(echo "$STTY_NEW") <(stty --file=/dev/console --save) +# See: https://github.com/systemd/systemd/issues/27106 +if systemd-detect-virt -q --container; then + # We initialize /run/systemd/container only during a full setup + test -e /run/systemd/container + cp -afv /run/systemd/container /tmp/container + rm -fv /run/systemd/container + systemctl daemon-reexec + test ! -e /run/systemd/container + cp -afv /tmp/container /run/systemd/container +else + # We bring the loopback netdev up only during a full setup, so it should + # not get brought back up during reexec if we disable it beforehand + [[ "$(ip -o link show lo)" =~ LOOPBACK,UP ]] + ip link set lo down + [[ "$(ip -o link show lo)" =~ state\ DOWN ]] + systemctl daemon-reexec + [[ "$(ip -o link show lo)" =~ state\ DOWN ]] + ip link set lo up -if ! systemd-detect-virt -qc; then - # We also disable coredumps when doing a "full" reexec, so check for that too + # We also disable coredumps only during a full setup sysctl -w kernel.core_pattern=dont-overwrite-me systemctl daemon-reexec diff <(echo dont-overwrite-me) <(sysctl --values kernel.core_pattern) @@ -39,4 +35,10 @@ fi systemctl --state=failed --no-legend --no-pager | tee /failed systemctl daemon-reload +# Check that the early setup is actually skipped on reexec. +# If the early setup is done more than once, then several timestamps, +# e.g. SecurityStartTimestamp, are re-initialized, and causes an ABRT +# of systemd-analyze blame. See issue #27187. +systemd-analyze blame + echo OK >/testok diff --git a/test/units/testsuite-29.sh b/test/units/testsuite-29.sh index c1cb2bc33e..1e4bd4ec42 100755 --- a/test/units/testsuite-29.sh +++ b/test/units/testsuite-29.sh @@ -29,7 +29,7 @@ if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then STATE_DIRECTORY=/var/lib/ fi # Bump the timeout if we're running with plain QEMU -[[ "$(systemd-detect-virt -v)" == "qemu" ]] && TIMEOUT=60 || TIMEOUT=30 +[[ "$(systemd-detect-virt -v)" == "qemu" ]] && TIMEOUT=90 || TIMEOUT=30 systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service' systemd-dissect --no-pager /usr/share/minimal_1.raw | grep -q '✓ portable service' diff --git a/test/units/testsuite-65.sh b/test/units/testsuite-65.sh index edaf667107..0ee56dfc00 100755 --- a/test/units/testsuite-65.sh +++ b/test/units/testsuite-65.sh @@ -11,13 +11,15 @@ export SYSTEMD_LOG_LEVEL=debug # Sanity checks # -# We can't really test time, blame, critical-chain and plot verbs here, as +# We can't really test time, critical-chain and plot verbs here, as # the testsuite service is a part of the boot transaction, so let's assume # they fail systemd-analyze || : systemd-analyze time || : -systemd-analyze blame || : systemd-analyze critical-chain || : +# blame +systemd-analyze blame +systemd-run --wait --user --pipe -M testuser@.host systemd-analyze blame # plot systemd-analyze plot >/dev/null || : systemd-analyze plot --json=pretty >/dev/null || : diff --git a/test/units/testsuite-75.sh b/test/units/testsuite-75.sh index ddd86d09bb..c5487555b6 100755 --- a/test/units/testsuite-75.sh +++ b/test/units/testsuite-75.sh @@ -46,7 +46,7 @@ monitor_check_rr() ( # Test for resolvectl, resolvconf systemctl unmask systemd-resolved.service -systemctl start systemd-resolved.service +systemctl enable --now systemd-resolved.service systemctl service-log-level systemd-resolved.service debug ip link add hoge type dummy ip link add hoge.foo type dummy |