Replacing a symlink with a directory or a directory with any type of file
Due to a known limitation with RPM, it is not possible to replace a directory with any kind of file or symlink, nor is it possible to replace a symlink to a directory with a directory without RPM producing file conflict errors while trying to install the package. For more information on the issues involved, refer to bug 447156 and bug 646523.
Try to avoid the problem in the first place
While its obviously not possible to foresee all the cases
where the need might arise,
when the need is foreseeable,
such as with bundled libraries,
it is better to use a symlink from the beginning,
as the symlink target can be changed more easily.
For instance,
if you have a bundled libfoo
library
inside the package’s directory structure,
place it in, for example, a libfoo.bundled
directory
and make libfoo a symlink to that.
When the bundling is eventually removed,
you just need to drop the directory
and change the symlink to point
to the corresponding system library directory,
without resorting to the scriptlets described below.
Working around it with scriptlets
To work around this problem, you must include a %pretrans scriptlet that manually performs the conversion prior to RPM attempting to install the package.
Note that '%pretrans' scriptlets MUST be written in Lua
and thus use -p <lua>
in order to function during initial system installation
when no shell has yet been installed.
Please use whichever of the two following snippets is necessary
in packages that need this transition,
replacing /path/to/dir
with the path to the directory that is being converted.
Scriptlet to replace a directory
RPM cannot simply remove a directory
when it is replaced by a file or symlink,
since users may have added or modified files to the directory.
To protect against accidental data loss,
you MUST use the following scriptlet
which renames the directory with a .rpmmoved
suffix
so that users can find the backed up directory
if they need to after the package is upgraded.
(It also will append an integer
to the suffix in the rare event that directory also exists.)
%pretrans -p <lua> -- Define the path to directory being replaced below. -- DO NOT add a trailing slash at the end. path = "/path/to/dir" st = posix.stat(path) if st and st.type == "directory" then status = os.rename(path, path .. ".rpmmoved") if not status then suffix = 0 while not status do suffix = suffix + 1 status = os.rename(path .. ".rpmmoved", path .. ".rpmmoved." .. suffix) end os.rename(path, path .. ".rpmmoved") end end
Additionally, you should define the /path/to/dir.rpmmoved
directory as a %ghost
entry in the %files
list
in the package’s spec file,
so that the directory is not entirely orphaned
and can be deleted if the package is ever uninstalled
and the directory is empty.
Scriptlet to replace a symlink to a directory with a directory
Replacing a symlink to a directory with a regular directory is much simpler, since there’s no potential for accidentally removing files added externally. The following scriptlet checks for and removes the symlink. There is no need to create the directory here, as RPM will do so later in the transaction when the package is installed.
%pretrans -p <lua> -- Define the path to the symlink being replaced below. path = "/path/to/dir" st = posix.stat(path) if st and st.type == "link" then os.remove(path) end