[CRIU] [PATCH 3/5] files: Rework clean_one_remap to order ghost dirs removal

Pavel Tikhomirov ptikhomirov at virtuozzo.com
Mon Feb 25 17:20:11 MSK 2019


On 2/22/19 7:02 PM, Cyrill Gorcunov wrote:
> Ghost files may lay inside ghost directories so we should
> order their cleanup to eliminate situation where we remove
> toplevel directory which still has not yet cleaned up entries
> 
>   | (00.002446) Unlink remap static/unlink_dir.test/0.cr.1.ghost
>   | (00.002461) Unlink remap static/unlink_dir.test/1.cr.2.ghost
>   | (00.002467) Unlink remap static/unlink_dir.test/gd1/gd2
>   | (00.002477) Error (criu/files-reg.c:794): Couldn't unlink remap / static/unlink_dir.test/gd1/gd2: Directory not empty
>   | (00.002480) Unlink remap static/unlink_dir.test/gd1/gd2/2.cr.4.ghost
>   | (00.002490) Unlink remap static/unlink_dir.test/gd1/gd2/3.cr.5.ghost
> 
> Thus upon dentries collection lets order them so they would be
> cleaned up in reverse order. Same time if two directories are
> pointing to same entry we should not fail if ENOENT returned.
> 
> Signed-off-by: Cyrill Gorcunov <gorcunov at gmail.com>
> ---
>   criu/files-reg.c | 115 ++++++++++++++++++++++++++++++++++++++++++++---
>   1 file changed, 108 insertions(+), 7 deletions(-)
> 
> diff --git a/criu/files-reg.c b/criu/files-reg.c
> index 9d5e1d743117..0b3f6493f718 100644
> --- a/criu/files-reg.c
> +++ b/criu/files-reg.c
> @@ -43,6 +43,7 @@
>   
>   #include "files-reg.h"
>   #include "plugin.h"
> +#include "criu-log.h"
>   
>   int setfsuid(uid_t fsuid);
>   int setfsgid(gid_t fsuid);
> @@ -681,6 +682,89 @@ static int prepare_one_remap(struct remap_info *ri)
>   	return ret;
>   }
>   
> +static int remap_info_cmp(const void *_a, const void *_b)
> +{
> +	struct remap_info *a = ((struct remap_info **)_a)[0];
> +	struct remap_info *b = ((struct remap_info **)_b)[0];
> +	int32_t mnt_id_a = a->rfi->rfe->mnt_id;
> +	int32_t mnt_id_b = b->rfi->rfe->mnt_id;
> +
> +	/*
> +	 * If entries are laying on same mount, which is
> +	 * a common case, we can safely use paths comparision.
> +	 */
> +	if (mnt_id_a == mnt_id_b)
> +		return -strcmp(a->rfi->path, b->rfi->path);
> +
> +	/*
> +	 * Otherwise simply order by mnt_id order, we just
> +	 * can't do anything beter (it is early stage where
> +	 * mounts are not yet processed).

What exact problem is with different mounts, can you show an example 
paths? (AFAICS Mount trees are already built at these point)

> +	 */
> +	return mnt_id_a > mnt_id_b ? 1 : -1;
> +}
> +
> +/*
> + * Ghost directories may carry ghost files but file descriptors
> + * are unordered in compare with this ghost paths, thus on cleanup
> + * we might try to remove the directory itself without waiting
> + * all files (and subdirectories) are cleaned up first.
> + *
> + * What we do here is we're move all ghost dirs into own list,
> + * sort them (to address subdirectories order) and move back
> + * to the end of the remap list.
> + */
> +static int order_remap_dirs(void)
> +{
> +	struct remap_info *ri, *tmp;
> +	struct remap_info **p, **t;
> +	size_t nr_remaps = 0, i;
> +	LIST_HEAD(ghost_dirs);
> +
> +	list_for_each_entry_safe(ri, tmp, &remaps, list) {
> +		if (ri->rpe->remap_type != REMAP_TYPE__GHOST)
> +			continue;
> +		if (!ri->rfi->remap->is_dir)
> +			continue;
> +		list_move_tail(&ri->list, &ghost_dirs);
> +		nr_remaps++;
> +	}
> +
> +	if (list_empty(&ghost_dirs))
> +		return 0;
> +
> +	p = t = xmalloc(sizeof(*p) * nr_remaps);
> +	if (!p) {
> +		list_splice_tail_init(&ghost_dirs, &remaps);
> +		return -ENOMEM;
> +	}
> +
> +	list_for_each_entry_safe(ri, tmp, &ghost_dirs, list) {
> +		list_del_init(&ri->list);
> +		p[0] = ri, p++;
> +	}
> +
> +	qsort(t, nr_remaps, sizeof(t[0]), remap_info_cmp);
> +
> +	for (i = 0; i < nr_remaps; i++) {
> +		list_add_tail(&t[i]->list, &remaps);
> +		pr_debug("remap: ghost mov dir %s\n", t[i]->rfi->path);
> +	}
> +
> +	if (!pr_quelled(LOG_DEBUG)) {
> +		list_for_each_entry_safe(ri, tmp, &remaps, list) {
> +			if (ri->rpe->remap_type != REMAP_TYPE__GHOST)
> +				continue;
> +			pr_debug("remap: ghost ord %3s %s\n",
> +				 ri->rfi->remap->is_dir ? "dir" : "fil",
> +				 ri->rfi->path);
> +		}
> +	}
> +
> +	xfree(t);
> +	return 0;
> +}
> +
>   int prepare_remaps(void)
>   {
>   	struct remap_info *ri;
> @@ -696,7 +780,21 @@ int prepare_remaps(void)
>   			break;
>   	}
>   
> -	return ret;
> +	return ret ? : order_remap_dirs();
> +}
> +
> +static int clean_ghost_dir(int rmntns_root, struct remap_info *ri)
> +{
> +	struct file_remap *remap = ri->rfi->remap;
> +	int ret = unlinkat(rmntns_root, remap->rpath, AT_REMOVEDIR);
> +	/*
> +	 * When deleting ghost directories here is two issues:
> +	 * - names might duplicate, so we may receive ENOENT
> +	 *   and should not treat it as an error
> +	 */
> +	if (ret)
> +		return errno == ENOENT ? 0 : -errno;
> +	return 0;
>   }
>   
>   static int clean_one_remap(struct remap_info *ri)
> @@ -733,15 +831,17 @@ static int clean_one_remap(struct remap_info *ri)
>   
>   	pr_info("Unlink remap %s\n", remap->rpath);
>   
> -	ret = unlinkat(rmntns_root, remap->rpath, remap->is_dir ? AT_REMOVEDIR : 0);
> -	if (ret < 0) {
> -		close(rmntns_root);
> -		pr_perror("Couldn't unlink remap %s %s", path, remap->rpath);
> +	if (remap->is_dir)
> +		ret = clean_ghost_dir(rmntns_root, ri);
> +	else
> +		ret = unlinkat(rmntns_root, remap->rpath, 0);
> +
> +	if (ret) {
> +		pr_perror("Couldn't unlink remap %d %s", rmntns_root, remap->rpath);
>   		return -1;
>   	}
> -	close(rmntns_root);

Don't you leak rmntns_root fd here?

> -	remap->rpath[0] = 0;
>   
> +	remap->rpath[0] = 0;
>   	return 0;
>   }
>   
> @@ -767,6 +867,7 @@ static struct collect_image_info remap_cinfo = {
>   	.pb_type = PB_REMAP_FPATH,
>   	.priv_size = sizeof(struct remap_info),
>   	.collect = collect_one_remap,
> +	.flags = COLLECT_SHARED,
>   };
>   
>   /* Tiny files don't need to generate chunks in ghost image. */
> 

-- 
Best regards, Tikhomirov Pavel
Software Developer, Virtuozzo.



More information about the CRIU mailing list