One of the 'features' of overlayfs is that depending on whether a file is on the upper or lower dir you get back a different device from stat. That breaks our lxc_rmdir_onedev. So at lxc_rmdir_ondev check the device of the directory being deleted. If it is overlayfs, then skip the device check. Note this is unrelated to overlayfs snapshots - in those cases when you delete a container, /var/lib/lxc/$container/ does not actually have an overlayfs under it. Rather, to reproduce this you would sudo mkdir /opt/{lower,upper,workdir} sudo mount -t overlayfs -o lower=/opt/lower,upper=/opt/upper,workdir=/opt/workdir \ lxc /var/lib/lxc sudo lxc-create -t download -n c1 -- -d ubuntu -r trusty -a amd64 sudo lxc-destroy -n c1 Signed-off-by: Serge Hallyn --- src/lxc/utils.c | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -68,8 +69,8 @@ lxc_log_define(lxc_utils, lxc); -static int _recursive_rmdir_onedev(char *dirname, dev_t pdev, - const char *exclude, int level) +static int _recursive_rmdir(char *dirname, dev_t pdev, + const char *exclude, int level, bool onedev) { struct dirent dirent, *direntp; DIR *dir; @@ -106,7 +107,7 @@ static int _recursive_rmdir_onedev(char if (ret < 0) { switch(errno) { case ENOTEMPTY: - INFO("Not deleting snapshots"); + INFO("Not deleting snapshot %s", pathname); hadexclude = true; break; case ENOTDIR: @@ -129,14 +130,14 @@ static int _recursive_rmdir_onedev(char failed=1; continue; } - if (mystat.st_dev != pdev) + if (onedev && mystat.st_dev != pdev) continue; if (S_ISDIR(mystat.st_mode)) { - if (_recursive_rmdir_onedev(pathname, pdev, exclude, level+1) < 0) + if (_recursive_rmdir(pathname, pdev, exclude, level+1, onedev) < 0) failed=1; } else { if (unlink(pathname) < 0) { - ERROR("%s: failed to delete %s", __func__, pathname); + SYSERROR("%s: failed to delete %s", __func__, pathname); failed=1; } } @@ -158,17 +159,41 @@ static int _recursive_rmdir_onedev(char return failed ? -1 : 0; } +/* we have two different magic values for overlayfs, yay */ +#define OVERLAYFS_SUPER_MAGIC 0x794c764f +#define OVERLAY_SUPER_MAGIC 0x794c7630 +/* + * In overlayfs, st_dev is unreliable. so on overlayfs we don't do + * the lxc_rmdir_onedev() + */ +static bool is_native_overlayfs(const char *path) +{ + struct statfs sb; + + if (statfs(path, &sb) < 0) + return false; + if (sb.f_type == OVERLAYFS_SUPER_MAGIC || + sb.f_type == OVERLAY_SUPER_MAGIC) + return true; + return false; +} + /* returns 0 on success, -1 if there were any failures */ extern int lxc_rmdir_onedev(char *path, const char *exclude) { struct stat mystat; + bool onedev = true; + + if (is_native_overlayfs(path)) { + onedev = false; + } if (lstat(path, &mystat) < 0) { ERROR("%s: failed to stat %s", __func__, path); return -1; } - return _recursive_rmdir_onedev(path, mystat.st_dev, exclude, 0); + return _recursive_rmdir(path, mystat.st_dev, exclude, 0, onedev); } static int mount_fs(const char *source, const char *target, const char *type)