#779060 dpkg: removing file/package might lead to deleted symlinks of underlying directories

Package:
dpkg
Source:
dpkg
Description:
Debian package management system
Submitter:
Michael Prokop
Date:
2015-02-25 11:09:12 UTC
Severity:
wishlist
#779060#5
Date:
2015-02-23 23:44:02 UTC
From:
To:
Hi,

I've reproduced this behaviour on Debian/jessie with dpkg 1.17.23,
but the original issue appeared on Debian/wheezy with dpkg 1.16.15
(and old-stable as well as unstable might be very probably affected
as well).

What happened (with anonymized data): a Debian package shipped a
file called /srv/repository/release/myscript.sh. Its underlying
directory /srv/repository on the file system of the host is a
*symlink* pointing to a different directory (because the data is
served via NFS from a different system). When the package gets
removed/purged (or in a package update the file
/srv/repository/release/myscript.sh is no longer provided) the
symlink /srv/repository gets removed by dpkg, even though there are
other files inside the directory.

The original data behind the symlink isn't removed/touched/modified,
so no data loss from that perspective. *But* by removing the symlink
- even though there's still data inside the directory behind the
symlink - this can cause serious service disruptions (and also
conflicting data sets), as it happened at a customer of mine a few
hours ago. [BTW, for the investigation it would have been nice if
there would be some information about such removals in dpkg's
logs, it was far from trivial to identify this behaviour.]

Demonstration follows:

,---- [ simple example for reproducing ]
| root@demo ~ # ls -la /symlinktarget
| total 0
| drwxr-xr-x  3 root root  60 Feb 23 22:56 ./
| drwxr-xr-x 34 root root 300 Feb 23 22:56 ../
| drwxr-xr-x  2 root root  60 Feb 23 22:58 repository/
| root@demo ~ # ls -la /symlinktarget/repository
| total 4
| drwxr-xr-x 2 root root 60 Feb 23 22:58 ./
| drwxr-xr-x 3 root root 60 Feb 23 22:56 ../
| -rw-r--r-- 1 root root  4 Feb 23 22:57 foo
| root@demo ~ #
| root@demo ~ # ln -s /symlinktarget/repository /srv/repository
| root@demo ~ # ls -la /srv/repository
| lrwxrwxrwx 1 root root 25 Feb 23 22:59 /srv/repository -> /symlinktarget/repository/
| root@demo ~ #
| root@demo ~ # dpkg -i foobar-demo_0.0.2_all.deb
| Selecting previously unselected package foobar-demo.
| (Reading database ... 123190 files and directories currently installed.)
| Preparing to unpack foobar-demo_0.0.2_all.deb ...
| Unpacking foobar-demo (0.0.2) ...
| Setting up foobar-demo (0.0.2) ...
| root@demo ~ # ls -la /srv/repository/release/myscript.sh
| total 32
| drwxr-xr-x 2 root root   100 Feb 23 22:59 ./
| drwxr-xr-x 3 root root    80 Feb 23 22:59 ../
| -rwxr-xr-x 1 root root  1855 Feb 23 22:49 myscript.sh
| root@demo ~ # dpkg -S /srv/repository/release/myscript.sh
| foobar-demo: /srv/repository/release/myscript.sh
| root@demo ~ # echo foo > /srv/repository/foo
| root@demo ~ # ls -la /srv/repository/
| total 4
| drwxr-xr-x 3 root root  80 Feb 23 22:59 ./
| drwxr-xr-x 3 root root  60 Feb 23 22:56 ../
| -rw-r--r-- 1 root root   4 Feb 23 22:59 foo
| drwxr-xr-x 2 root root 100 Feb 23 22:59 release/
| root@demo ~ # ls -la /srv/repository/foo
| -rw-r--r-- 1 root root 4 Feb 23 22:59 /srv/repository/foo
| root@demo ~ # apt-get --purge remove foobar-demo
| Reading package lists... Done
| Building dependency tree
| Reading state information... Done
| The following packages will be REMOVED:
|   foobar-demo*
| 0 upgraded, 0 newly installed, 1 to remove and 3 not upgraded.
| After this operation, 152 kB disk space will be freed.
| Do you want to continue? [Y/n] y
| (Reading database ... 123223 files and directories currently installed.)
| Removing foobar-demo (0.0.2) ...
| root@demo ~ # ls -la /srv/repository/foo
| ls: cannot access /srv/repository/foo: No such file or directory
| root@demo ~ # ls -la /srv/repository
| ls: cannot access /srv/repository: No such file or directory
| root@demo ~ # ls -la /symlinktarget/repository
| total 4
| drwxr-xr-x 2 root root 60 Feb 23 23:00 ./
| drwxr-xr-x 3 root root 60 Feb 23 22:56 ../
| -rw-r--r-- 1 root root  4 Feb 23 22:59 foo
| root@demo ~ #
`----

regards,
-mika-

#779060#10
Date:
2015-02-25 11:05:29 UTC
From:
To:
Hi!

Yes, I'm afraid that's currently "expected" behavior. dpkg expects to
manage all paths it touches in the filesystem. In this case you are
placing files inside an already existing (and externally managed)
hierarchy, but dpkg does not know that, and when no other package owns
that path it tries to remove it. Fixing this requires to track file
meta-data, because otherwise dpkg does not know if there's been any
local changes, or whether a path is really a symlink or a directory.

A workaround could be to avoid removing a path if it contains other
paths inside the same package, but that has too many corner cases.
Consider the following, a package A ships /dir and /dir/file, and /dir
is a symlink on the filesystem. And suddenly package B Replaces A and
takes over /dir/file. If package B gets removed, then package A only
owns /dir, and when that gets removed dpkg cannot know if it needs to
remove it or not, because it does not contain any owned paths.

So this is for now either a packaging or admin problem, and a wishlist
from dpkg PoV.

Thanks,
Guillem