Dpkg replacing files on a FAT filesystem

dpkgfathard link

When you upgrade or reinstall a package with dpkg (and ultimately anything that uses it, like apt-get etc) it backs up the existing files by creating a hard link to the file before replacing it. That way if the unpack fails it can easily put back the existing files. That's great, since it protects the operating system from Bad Thingsā„¢ happening.

Except… it only works if your filesystem supports hard links. Not all filesystems do – such as FAT filesystems.

I am working on a distribution of Debian for a specific embedded ARM platform, and the boot environment requires that certain files (the kernel included) are on a FAT filesystem so the boot code is able to locate and load them.

When you go to upgrade the kernel package (or any other package that has files in that FAT partition) the install fails with:

dpkg: error processing archive linux-image3.18.11+_3.18.11.2.armadillian_armhf.deb (--install):
 unable to make backup link of `./boot/vmlinuz-3.18.11+' before installing new version: Operation not permitted

And the whole upgrade fails.

I have scoured the web, and the only references I can find are specific people with specific problems when doing specific upgrades, the answer to which is usually "Delete /boot/vmlinuz-3.18.11+ and try again", and yes, that fixes that specific problem.

But that's not the answer for me. I am an OS distributor, not an OS user, so I need a way to fix this that doesn't involve the end user manually deleting their kernel files before doing an upgrade. I need a way to tell dpkg to "copy, not hard link" for files on /boot (or all files for all I care, though that would slow down the upgrade operation somewhat), or better yet "If a hard link fails, don't complain, just copy it instead".

I have tried such things as the --force-unsafe-io and even --force-all flags to dpkg, but nothing has any effect.

Best Answer

The behaviour you're seeing is implemented in archives.c in the dpkg source, line 1030 (for version 1.18.1):

debug(dbg_eachfiledetail, "tarobject nondirectory, 'link' backup");
if (link(fnamevb.buf,fnametmpvb.buf))
  ohshite(_("unable to make backup link of '%.255s' before installing new version"),
          ti->name);

It seems to me that you could handle the link failure by falling back to the rename behaviour used lines 1003 and following; something like (this is untested):

debug(dbg_eachfiledetail, "tarobject nondirectory, 'link' backup");
if (link(fnamevb.buf,fnametmpvb.buf)) {
  debug(dbg_eachfiledetail,"link failed, nonatomic");
  nifd->namenode->flags |= fnnf_no_atomic_overwrite;
  if (rename(fnamevb.buf,fnametmpvb.buf))
    ohshite(_("unable to move aside '%.255s' to install new version"),
            ti->name);
}

I'm not a dpkg expert though... (And there's no option already available in dpkg to provide this behaviour.)

Related Question