return -ENOMEM;
if (flags & PORTABLE_PREFER_COPY) {
+ CopyFlags copy_flags = COPY_REFLINK|COPY_FSYNC;
- r = copy_file_atomic(from, dropin, 0644, COPY_REFLINK|COPY_FSYNC);
+ if (flags & PORTABLE_FORCE_ATTACH)
+ copy_flags |= COPY_REPLACE;
+
+ r = copy_file_atomic(from, dropin, 0644, copy_flags);
if (r < 0)
return log_debug_errno(r, "Failed to copy %s %s %s: %m", from, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), dropin);
} else {
- if (symlink(from, dropin) < 0)
- return log_debug_errno(errno, "Failed to link %s %s %s: %m", from, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), dropin);
+ if (flags & PORTABLE_FORCE_ATTACH)
+ r = symlink_atomic(from, dropin);
+ else
+ r = RET_NERRNO(symlink(from, dropin));
+ if (r < 0)
+ return log_debug_errno(r, "Failed to link %s %s %s: %m", from, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), dropin);
(void) portable_changes_add(changes, n_changes, PORTABLE_SYMLINK, dropin, from);
}
if ((flags & PORTABLE_PREFER_SYMLINK) && m->source) {
- if (symlink(m->source, path) < 0)
- return log_debug_errno(errno, "Failed to symlink unit file '%s': %m", path);
+ if (flags & PORTABLE_FORCE_ATTACH)
+ r = symlink_atomic(m->source, path);
+ else
+ r = RET_NERRNO(symlink(m->source, path));
+ if (r < 0)
+ return log_debug_errno(r, "Failed to symlink unit file '%s': %m", path);
(void) portable_changes_add(changes, n_changes, PORTABLE_SYMLINK, path, m->source);
} else {
+ LinkTmpfileFlags link_flags = LINK_TMPFILE_SYNC;
_cleanup_(unlink_and_freep) char *tmp = NULL;
_cleanup_close_ int fd = -EBADF;
+ if (flags & PORTABLE_FORCE_ATTACH)
+ link_flags |= LINK_TMPFILE_REPLACE;
+
(void) mac_selinux_create_file_prepare_label(path, m->selinux_label);
fd = open_tmpfile_linkable(path, O_WRONLY|O_CLOEXEC, &tmp);
if (fchmod(fd, 0644) < 0)
return log_debug_errno(errno, "Failed to change unit file access mode for '%s': %m", path);
- r = link_tmpfile(fd, tmp, path, LINK_TMPFILE_SYNC);
+ r = link_tmpfile(fd, tmp, path, link_flags);
if (r < 0)
return log_debug_errno(r, "Failed to install unit file '%s': %m", path);
# Ensure we don't regress (again) when using --force
+mkdir -p /run/systemd/system.attached/minimal-app0.service.d/
+cat <<EOF >/run/systemd/system.attached/minimal-app0.service
+[Unit]
+Description=Minimal App 0
+EOF
+cat <<EOF >/run/systemd/system.attached/minimal-app0.service.d/10-profile.conf
+[Unit]
+Description=Minimal App 0
+EOF
+cat <<EOF >/run/systemd/system.attached/minimal-app0.service.d/20-portable.conf
+[Unit]
+Description=Minimal App 0
+EOF
+systemctl daemon-reload
+
portablectl "${ARGS[@]}" attach --force --now --runtime /usr/share/minimal_0.raw minimal-app0
portablectl is-attached --force minimal-app0
portablectl detach --now --runtime overlay app1
+# Ensure --force works also when symlinking
+mkdir -p /run/systemd/system.attached/app1.service.d
+cat <<EOF >/run/systemd/system.attached/app1.service
+[Unit]
+Description=App 1
+EOF
+cat <<EOF >/run/systemd/system.attached/app1.service.d/10-profile.conf
+[Unit]
+Description=App 1
+EOF
+cat <<EOF >/run/systemd/system.attached/app1.service.d/20-portable.conf
+[Unit]
+Description=App 1
+EOF
+systemctl daemon-reload
+
+portablectl "${ARGS[@]}" attach --force --copy=symlink --now --runtime /tmp/overlay app1
+
+systemctl is-active app1.service
+
+portablectl detach --now --runtime overlay app1
+
umount /tmp/overlay
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1