diff --git a/compat-usrmerge.changes b/compat-usrmerge.changes index 6a2d438..94eb82c 100644 --- a/compat-usrmerge.changes +++ b/compat-usrmerge.changes @@ -1,3 +1,30 @@ +------------------------------------------------------------------- +Tue Jun 8 12:03:52 UTC 2021 - Ludwig Nussel + +- early exit in case of overlayfs (boo#1187027) + +------------------------------------------------------------------- +Tue Jun 8 07:37:33 UTC 2021 - Fabian Vogt + +- Avoid dependency on mountpoint from util-linux +- Also check for availability of find + +------------------------------------------------------------------- +Mon Jun 7 09:02:56 UTC 2021 - Ludwig Nussel + +- fix conversion with split /usr (boo#1186781) + +------------------------------------------------------------------- +Wed Jun 2 15:29:15 UTC 2021 - Ludwig Nussel + +- exit early if one of the affected directories has mountpoint + beneath it + +------------------------------------------------------------------- +Tue Jun 1 23:27:08 UTC 2021 - Niklas Haas + +- add fallback for filesystems without renameat2 (boo#1186637) + ------------------------------------------------------------------- Thu Mar 25 09:50:48 UTC 2021 - Ludwig Nussel diff --git a/convertfs b/convertfs index 8126a2c..e82a95d 100644 --- a/convertfs +++ b/convertfs @@ -3,28 +3,59 @@ ROOT= +is_usrmerged() { + local r=1 + for dir in bin sbin lib lib64; do + [ -d "$ROOT/$dir" ] || continue + [ -L "$ROOT/$dir" ] || return 1 + r=0 + done + return "$r" +} + +# check if there's anything to do +is_usrmerged && exit 0 + # the package is installed with AutoReq off, so no guarantee that # coreutils actually works -if ! (cp --help && mountpoint --help && mountpoint --help) > /dev/null; then +if ! { cp --help && find --help; } > /dev/null; then echo "tools not functional, exit" exit 1 fi # clean up after ourselves no matter how we die. cleanup() { - echo "Something failed, cleaning up" + echo "UsrMerge conversion failed, cleaning up" for dir in bin sbin lib lib64; do rm -rf -- "$ROOT/usr/${dir}.usrmerge" done + echo "!!! ATTENTION: Do NOT proceed if you see this message during" + echo "!!! distribution upgrade. Chances are high that your system might" + echo "!!! break beyond repair if you do." } trap 'ret=$?; [[ $ret -ne 0 ]] && cleanup;exit $ret;' EXIT trap 'exit 1;' SIGINT -mountpoint -q "$ROOT/usr" || CP_HARDLINK="-l" - set -e +if [ "$(stat -f -c %T "${ROOT:-/}")" = "overlayfs" ]; then + echo "UsrMerge conversion does not work on overlayfs" + exit 1 +fi + +CP_HARDLINK="-l" +while read dev mp other; do + [ "$mp" = "$ROOT/usr" ] && CP_HARDLINK="" + for dir in bin sbin lib lib64; do + [ -d "$ROOT/$dir" ] || continue + if [ "${mp#$ROOT/$dir}" != "$mp" ] || [ "${mp#$ROOT/usr/$dir}" != "$mp" ]; then + echo "Please unmount $mp before the conversion" + exit 1 + fi + done +done < /proc/mounts + # merge / and /usr in new dir in /usr for dir in bin sbin lib lib64; do rm -rf -- "$ROOT/usr/${dir}.usrmerge" @@ -32,7 +63,7 @@ for dir in bin sbin lib lib64; do [[ -d "$ROOT/$dir" ]] || continue echo "Make a copy of \`$ROOT/$dir'." [[ -d "$ROOT/$dir" ]] \ - && cp -ax -l "$ROOT/$dir" "$ROOT/usr/${dir}.usrmerge" + && cp -ax $CP_HARDLINK "$ROOT/$dir" "$ROOT/usr/${dir}.usrmerge" # cp can't handle copying a dir over non-directories. So move # those away in advance. Happened with /lib/udev existing as # link on older distros @@ -43,7 +74,7 @@ for dir in bin sbin lib lib64; do rm -rf "$f.usrmerge~" mv "$f" "$f.usrmerge~" fi - done < <(find "$ROOT/usr/$dir" -type d -printf "%P\n" ) + done < <(find "$ROOT/usr/$dir" -xdev -type d -printf "%P\n" ) echo "Merge the copy with \`$ROOT/usr/$dir'." [[ -d "$ROOT/usr/${dir}.usrmerge" ]] \ || mkdir -p "$ROOT/usr/${dir}.usrmerge" @@ -51,14 +82,14 @@ for dir in bin sbin lib lib64; do echo "Clean up duplicates in \`$ROOT/usr/$dir'." # delete all symlinks that have been backed up. /usr versions # override / ones - find "$ROOT/usr/${dir}.usrmerge" -type l -name '*.usrmerge~' -delete || : + find "$ROOT/usr/${dir}.usrmerge" -xdev -type l -name '*.usrmerge~' -delete || : # in rare cases the symlinks may point from /usr to /, so remove # the link in that case while read file; do o=${file%%.usrmerge~} [ -L "$o" ] && mv -f "$file" "$o" done < <(find "$ROOT/usr/${dir}.usrmerge" \ - -name '*.usrmerge~' -type f) + -xdev -name '*.usrmerge~' -type f) done # switch over merged dirs in /usr for dir in bin sbin lib lib64; do @@ -72,7 +103,7 @@ done for dir in bin sbin lib lib64; do if [ ! -L "$ROOT/$dir" -a -d "$ROOT/$dir" ]; then echo "Create \`$ROOT/$dir' symlink." - rm -rf "$ROOT/${dir}.usrmerge" || : + rm --one-file-system -rf "$ROOT/${dir}.usrmerge" || : ln -s usr/$dir "$ROOT/${dir}.usrmerge" /usr/libexec/xmv "$ROOT/$dir" "$ROOT/${dir}.usrmerge" fi @@ -88,7 +119,7 @@ for dir in bin sbin lib lib64; do if [ -d "$d" ]; then echo "$d ..." mv "$d" "$d~" - rm -rf "$d~" + rm --one-file-system -rf "$d~" fi done done @@ -105,10 +136,4 @@ done set +e echo "Run ldconfig." -ldconfig -r "$ROOT" - -#. $ROOT/etc/selinux/config -#if [ -n "$(command -v setfiles)" ] && [ "$SELINUX" != "disabled" ] && [ -f /etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts ]; then -# echo "Fixing SELinux labels" -# setfiles -r $ROOT -p /etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts $ROOT/sbin $ROOT/bin $ROOT/lib $ROOT/lib64 $ROOT/usr/lib $ROOT/usr/lib64 $ROOT/etc/ld.so.cache $ROOT/var/cache/ldconfig || : -#fi +ldconfig -r "$ROOT" || : diff --git a/xmv.c b/xmv.c index f7c31a4..d3bc20d 100644 --- a/xmv.c +++ b/xmv.c @@ -31,6 +31,7 @@ #include #include #include +#include #ifndef RENAME_EXCHANGE # define RENAME_EXCHANGE (1 << 1) @@ -66,10 +67,36 @@ int main(int argc, char** argv) if (argc-optind < 2) help(argv[0], 1); - r = syscall (SYS_renameat2, AT_FDCWD, argv[optind], AT_FDCWD, argv[optind+1], RENAME_EXCHANGE); + const char *source = argv[optind], *target = argv[optind+1]; + r = syscall (SYS_renameat2, AT_FDCWD, source, AT_FDCWD, target, RENAME_EXCHANGE); if (r < 0) { - perror("renameat2"); - return 1; + if (errno != EINVAL) { + perror("renameat2"); + return 1; + } + + // Fallback for systems without renameat2 support + puts("!!! WARNING: rename2 RENAME_EXCHANGE not supported !!!"); + puts("!!! fallback to unsafe rename !!!"); + char tmp[PATH_MAX]; + r = snprintf(tmp, sizeof(tmp), "%s.XXXXXX", target); + if (r < 0) { + perror("asprintf"); + return 1; + } + // not intended to be use in /tmp so good enough + mktemp(tmp); + + r = renameat(AT_FDCWD, target, AT_FDCWD, tmp); + if (!r) + r = renameat(AT_FDCWD, source, AT_FDCWD, target); + if (!r) + r = renameat(AT_FDCWD, tmp, AT_FDCWD, source); + + if (r < 0) { + perror("renameat"); + return 1; + } } return 0;