#!/bin/bash # based on code from dracut convertfs.sh 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 && find --help; } > /dev/null; then echo "tools not functional, exit" exit 1 fi # clean up after ourselves no matter how we die. cleanup() { 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 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" [[ -L "$ROOT/$dir" ]] && continue [[ -d "$ROOT/$dir" ]] || continue echo "Make a copy of \`$ROOT/$dir'." [[ -d "$ROOT/$dir" ]] \ && 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 while read d; do f="$ROOT/usr/$dir.usrmerge/$d" if test -L "$f" -o \( -e "$f" -a ! -d "$f" \); then echo "Warning: /$dir/$d conflicts with directory /usr/$dir/$d and will be removed" rm -rf "$f.usrmerge~" mv "$f" "$f.usrmerge~" fi 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" cp -axT -l --backup --suffix=.usrmerge~ "$ROOT/usr/$dir" "$ROOT/usr/${dir}.usrmerge" 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" -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" \ -xdev -name '*.usrmerge~' -type f) done # switch over merged dirs in /usr for dir in bin sbin lib lib64; do if [ -d "$ROOT/usr/${dir}.usrmerge" ]; then echo "Switch to new \`$ROOT/usr/$dir'." /usr/libexec/xmv "$ROOT/usr/${dir}.usrmerge" "$ROOT/usr/$dir" fi done # replace dirs in / with links to /usr for dir in bin sbin lib lib64; do if [ ! -L "$ROOT/$dir" -a -d "$ROOT/$dir" ]; then echo "Create \`$ROOT/$dir' symlink." rm --one-file-system -rf "$ROOT/${dir}.usrmerge" || : ln -s usr/$dir "$ROOT/${dir}.usrmerge" /usr/libexec/xmv "$ROOT/$dir" "$ROOT/${dir}.usrmerge" fi done echo "Clean up backup files." # everything seems to work; cleanup for dir in bin sbin lib lib64; do for pfx in usr/ /; do # if we get killed in the middle of "rm -rf", ensure not to leave # an incomplete directory, which is moved back by cleanup() d="$ROOT/${pfx}${dir}.usrmerge" if [ -d "$d" ]; then echo "$d ..." mv "$d" "$d~" rm --one-file-system -rf "$d~" fi done done # XXX: confirm this is needed for dir in lib lib64; do [[ -d "$ROOT/$dir" ]] || continue for lib in "$ROOT"/usr/${dir}/lib*.so*.usrmerge~; do [[ -f $lib ]] || continue mv -v $lib ${lib/.so/_so} done done set +e echo "Run ldconfig." ldconfig -r "$ROOT" || :