projg2 / unsymlink-lib

Migration tool to SYMLINK_LIB=no system
8 stars 2 forks source link

SYMLINK_LIB=no migration tool (c) 2017 Michał Górny Licensed under 2-clause BSD license

TL;DR: Run 'unsymlink-lib' as root and follow the on-screen instructions.

== Goal ==

The tools are meant to help safely migrating live amd64 (or alike) system from the SYMLINK_LIB=yes to SYMLINK_LIB=no layout.

The old (SYMLINK_LIB=yes) layout looks like this:

lib -> lib64 -- mostly libexec-like, plus 32-bit ld.so lib64 -- 64-bit stuff lib32 -- 32-bit stuff

The new (SYMLINK_LIB=no) layout looks like this:

lib -- libexec-like + 32-bit stuff lib64 -- 64-bit stuff

Therefore, the migration involves splitting lib and lib64, and merging lib32 into the former. Since lib contains key system components including kernel modules, gcc, portage, a unsafe migration procedure could result in serious, hard-to-fix breakage.

== Procedure ==

The following steps are used to accomplish a safe migration:

  1. A new temporary 'lib.new' directory is created. All files and directories belonging to the 'lib' directory (based on recorded file lists) are copied into this directory. All files and directories from lib32 are copied into this directory. Therefore, it resembles the new 'lib' directory.

  2. The 'lib' symlink is replaced with a symlink to 'lib.new'. User can now reboot to test whether the migrated system will work. If it does not boot, reverting is as simple as restoring the old 'lib' symlink (and removing the failed 'lib.new' variant, as part of cleanup).

  3. If the system works, the migration can continue. The 'lib' symlink is removed, and 'lib.new' is renamed into 'lib'. 'lib32' is removed and replaced by a symlink to 'lib'. The stray files are removed from 'lib64'.

  4. Eventually, once user rebuilds gcc (and possibly other packages relying on 'lib32' directory), the 'lib32' symlink can be removed.

== Dependencies ==

The tool is written as a Python script. It should work with Python 2.7 up to 3.6. It should also work with newer versions of Python but this depends on how incompatible they will be.

The analysis phase requires Portage package manager installed. After this phase is complete, Portage is no longer required, i.e. the tool will work fine if it becomes broken in the process.

The tool assumes cp executable compatible with GNU coreutils, capable of preserving all file properties.

== How to use? ==

The tool is designed to use a three-step semi-automatic procedure. The steps are:

  1. installed file analysis (--analyze),

  2. basic migration (--migrate),

3a. post-migration cleanup (--finish), or

3b. migration rollback (--rollback).

During the analysis phase, the tool queries Portage for the list of all installed files. The action plan is constructed (detailing which files are relocated where) and the files are checked for potential collisions. If the analysis phase yields no errors, the tool prints resulting split (i.e. 'check for obvious mistakes' list) and writes a state file to the home directory.

The migration phase constructs a 'lib.new' directory as outlined above, and if no error occurred updates the 'lib' symlink appropriately. At this point, the user is asked to test the new setup. The migration can either be finished or reverted afterwards.

The post-migration cleanup phase replaces the 'lib' symlink with the real directory, and replaces the 'lib32' directory with a symlink, effectively completing the migration. Rollback becomes no longer possible. The user is asked to rebuild packages depending on 'lib32'.

The migration rollback phase restores the old target of the 'lib' symlink, and removes the unsuccessful 'lib.new' directory. It can be used to quickly fix a system that has been broken by the migration.

== Use on Prefix systems ==

To migrate a Prefix system, pass the path to the system as --root option. If you are using unprivileged Prefix, you may use --unprivileged to disable root privilege checks.

== Failure protection ==

The script tries to be resilient to failures, and avoid causing damage to the system at all cost. However, there are a few cases of potential damage. Notably:

  1. Any failures during the analysis phase do not affect the live system. If the analysis fails, state file is simply not written (note that if there is an older state file around, it will not be removed).

  2. Any failures during 'lib.new' creation (migration phase) result in aborted migration with no damage to the live system. The 'lib.new' directory is left for debugging purposes. It can be cleaned up via the '--force-rollback' action.

  3. A failure during the actual migration, post-migration cleanup or rollback is fatal, and requires manual action. Depending on the exact stage, '--force-rollback' or '--resume-finish' actions are provided for recovery.