apk symbolic link handling
I suspect there is a bug in `apk add`’s handling of symlinks when
using `—root` pointing to an area of the file system not on the same
mount as the running root directory, when the symlink is to end up in
the root directory.
I had an APKBUILD with with a `package()` routine that included:
…
ln -s /media/data “$pkgdir”/data
ln -s /etc/crontabs “$pkgdir”/var/spool/cron/crontabs
ln -s /proc/mounts “$pkgdir”/etc/mtab
…
When installing the package using
apk —root targetroot —initdb add …(many packages)…
where `targetroot` was a mount point for the file system under
construction, it produced an error in apk’s `src/database.c`
[here](https://github.com/alpinelinux/apk-tools/blob/8fa193ecda2a71de3ff5f826205351b185d15054/src/database.c\#L2731-L2736),
/* Overwrite the old file */
if (renameat(db->root_fd, tmpname,
db->root_fd, name) != 0) {
apk_error(PKG_VER_FMT“: failed to rename %s to %s.”,
PKG_VER_PRINTF(ipkg->pkg), tmpname, name);
ipkg->broken_files = 1;
}
Adding a `perror(“renameat”)` before the `apk_error` showed that
the
cause of the failure was `EXDEV`:
EXDEV oldpath and newpath are not on the same mounted filesystem.
(Linux permits a filesystem to be mounted at multiple points,
but rename() does not work across different mount points, even
if the same filesystem is mounted on both.)
Changing the APKBUILD to omit the first symlink (`/data`),
leaving
the other two alone (`/var/spool/cron/crontabs` and `/etc/mtab`),
let
the installation complete successfully!
I suspect that the problem is in `format_tmpname` in `src/database.c`.
Specifically, I think that the tmpname is built like
the/target/directory + /.apk. + somerandomstuff
So for examples like the `/etc/mtab` symlink, the tmpname will be
etc + /.apk. + somerandomstuff
i.e. a relative path, BUT in the case where the target directory is
the ROOT directory, the tmpname will be
/.apk. + somerandomstuff
i.e. it will be an ABSOLUTE path.
Now, renameat(2) ignores `db->root_fd` if `tmpname` is an
absolute
path, and so it will attempt to move a file from the actual root to
somewhere in the `—root` folder, leading to EXDEV.
I believe the fix COULD involve changing `format_tmpname` to treat
an
empty `fdiri>dir->name` specially,
resulting in a relative tmpname,
but I am not 100% certain.
For now, I am working around the issue by removing the
ln -s /media/data “$pkgdir”/data
line from APKBUILD’s `package()`, and instead adding `ln -s
/media/data /data` to the `pre-install` script.
(from redmine: issue id 9634, created on 2018-11-09)