From Fedora Project Wiki
(Rename all uses of .rpmsave to .rpmmoved)
(Replacing a directory or a symlink with a directory: clarify when scriptlets are needed)
Line 1: Line 1:
 
== Replacing a directory or a symlink with a directory ==
 
== Replacing a directory or a symlink with a directory ==
  
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 with a directory, without RPM producing file conflict errors while trying to install the package.  For more information on the issues involved, refer to [https://bugzilla.redhat.com/show_bug.cgi?id=447156 bug 447156] and [https://bugzilla.redhat.com/show_bug.cgi?id=646523 bug 646523].
+
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 [https://bugzilla.redhat.com/show_bug.cgi?id=447156 bug 447156] and [https://bugzilla.redhat.com/show_bug.cgi?id=646523 bug 646523].
  
 
=== Try to avoid the problem in the first place ===
 
=== Try to avoid the problem in the first place ===
Line 39: Line 39:
 
end
 
end
 
</pre>
 
</pre>
 +
 +
{{admon/note|Use this scriptlet only when replacing a directory with a regular file or symlink.|
 +
*'''Do not''' use it to replace a symlink of any kind. (See the section below for information regarding this.)
 +
*'''Do not''' use it to replace a file.  (This does not require special action.)}}
  
 
Additionally, you should define the <code>/path/to/dir.rpmmoved</code> directory as a <code>%ghost</code> entry in the <code>%files</code> 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.
 
Additionally, you should define the <code>/path/to/dir.rpmmoved</code> directory as a <code>%ghost</code> entry in the <code>%files</code> 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 with a directory ====
+
==== Scriptlet to replace a symlink to a directory with a directory ====
  
Replacing a symlink with a directory is much simpler, since there's no potential for accidentally removing files added externally.  Simply remove the symlink (there is no need to create the directory here, rpm will do so later when it is used):
+
Replacing a symlink to a directory with a regular directory is much simpler, since there's no potential for accidentally removing files added externally.  Simply remove the symlink (there is no need to create the directory here, rpm will do so later when it is used):
  
 
<pre>
 
<pre>
Line 55: Line 59:
 
end
 
end
 
</pre>
 
</pre>
 +
 +
{{admon/note|Use this scriptlet only when replacing a symlink ''to a directory'' with a regular directory.|
 +
*'''Do not''' use it to replace a symlink that previously pointed to a file.  (This does not require special action.)
 +
*'''Do not''' use it to replace a symlink to a directory with another symlink, even if the target is a different directory. (This does not require special action.)
 +
*'''Do not''' use it to replace a file.  (This does not require special action.)
 +
*'''Do not''' use it to replace a directory.  (See the above section for more information regarding this.)}}

Revision as of 06:31, 7 March 2014

Replacing a directory or a symlink with a directory

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'd be better to use a symlink from the start, as the symlink target can be changed more easily. For instance, if you have a bundled libfoo library inside the packages directory structure, place it in a eg. 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.

%pretrans scriptlets must use -p <lua> to survive initial system installation, but since there will never be transitions like these to perform on initial system installation, you can count on a shell being present when it actually performs the conversion. This means that you need to use -p <lua> in order to test if the replacement needs to be made, but you can safely shell out using os.execute() to actually perform the replacement.

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

When replacing a directory with a file or symlink, you should rename the directory with the suffix .rpmmoved so that users can find the backed up directory if they need to after the package is upgraded. Removing the directory wholesale is not recommended in the rare event the end user may have added additional files to this directory, which would be deleted without warning to the user.

Warning.png
WARNING:
Never use this for directories where you generally expect the end user to add or modify files, such as in subdirectories of /etc or /var, or for directories where other packages may drop files (e.g. plugin directories).
%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
Note.png
Use this scriptlet only when replacing a directory with a regular file or symlink.
  • Do not use it to replace a symlink of any kind. (See the section below for information regarding this.)
  • Do not use it to replace a file. (This does not require special action.)

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. Simply remove the symlink (there is no need to create the directory here, rpm will do so later when it is used):

%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
Note.png
Use this scriptlet only when replacing a symlink to a directory with a regular directory.
  • Do not use it to replace a symlink that previously pointed to a file. (This does not require special action.)
  • Do not use it to replace a symlink to a directory with another symlink, even if the target is a different directory. (This does not require special action.)
  • Do not use it to replace a file. (This does not require special action.)
  • Do not use it to replace a directory. (See the above section for more information regarding this.)