[CRIU] [PATCH v5 1/3] files-reg: Create ghost files in first existing parent directory

Kirill Tkhai ktkhai at virtuozzo.com
Thu Jan 28 03:39:31 PST 2016


Since a deleted file may belong to a deleted directory
(which, in turn, may belong to another deleted, etc),
it's necessary to recreate all missing dentries in the path.

Doing this in mkreg_ghost() is not a good idea, because
another ghost file may need some of the dentries in the path,
and this requires to do refcouting of recreated directories.

To avoid this, we restore a ghost file in the first existing
parent directory of the patch. This guarantees, the ghost file
will be in the same mount with target file (rfi_remap() needs
that, because it uses link()).
---
 files-reg.c |   83 +++++++++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 66 insertions(+), 17 deletions(-)

diff --git a/files-reg.c b/files-reg.c
index a394e65..1567174 100644
--- a/files-reg.c
+++ b/files-reg.c
@@ -95,6 +95,33 @@ static int note_link_remap(char *path, struct ns_id *nsid)
 	return -1;
 }
 
+/* Trim "a/b/c/d" to "a/b/d" */
+static int trim_last_parent(char *path)
+{
+	char *fname, *p;
+
+	p = strrchr(path, '/');
+	fname = p + 1;
+	if (!p || *fname == '\0')
+		return -1;
+
+	while (p >= path && *p == '/')
+		p--;
+
+	if (p < path)
+		return -1;
+
+	while (p >= path && *p != '/')
+		p--;
+	p++;
+
+	while (*fname != '\0')
+		*p++ = *fname++;
+	*p = '\0';
+
+	return 0;
+}
+
 static int mkreg_ghost(char *path, u32 mode, struct ghost_file *gf, struct cr_img *img)
 {
 	int gfd, ret;
@@ -142,45 +169,67 @@ static int ghost_apply_metadata(const char *path, GhostFileEntry *gfe)
 
 static int create_ghost(struct ghost_file *gf, GhostFileEntry *gfe, struct cr_img *img)
 {
+	int ret, len, root_len, try = 0;
 	char path[PATH_MAX];
-	int ret;
+	char *msg;
 
-	ret = rst_get_mnt_root(gf->remap.rmnt_id, path, sizeof(path));
+	root_len = ret = rst_get_mnt_root(gf->remap.rmnt_id, path, sizeof(path));
 	if (ret < 0) {
 		pr_err("The %d mount is not found for ghost\n", gf->remap.rmnt_id);
 		goto err;
 	}
 
-	snprintf(path + ret, sizeof(path) - ret, "/%s", gf->remap.rpath);
+	len = snprintf(path + ret, sizeof(path) - ret, "/%s", gf->remap.rpath) + ret;
 	ret = -1;
-
+again:
 	if (S_ISFIFO(gfe->mode)) {
-		if (mknod(path, gfe->mode, 0)) {
-			pr_perror("Can't create node for ghost file");
-			goto err;
-		}
+		if ((ret = mknod(path, gfe->mode, 0)) < 0)
+			msg = "Can't create node for ghost file";
 	} else if (S_ISCHR(gfe->mode) || S_ISBLK(gfe->mode)) {
 		if (!gfe->has_rdev) {
 			pr_err("No rdev for ghost device\n");
 			goto err;
 		}
-
-		if (mknod(path, gfe->mode, gfe->rdev)) {
-			pr_perror("Can't create node for ghost dev");
-			goto err;
-		}
+		if ((ret = mknod(path, gfe->mode, gfe->rdev)) < 0)
+			msg = "Can't create node for ghost dev";
 	} else if (S_ISDIR(gfe->mode)) {
-		if (mkdir(path, gfe->mode)) {
+		if ((ret = mkdir(path, gfe->mode)) < 0) {
 			pr_perror("Can't make ghost dir");
 			goto err;
 		}
 	} else {
-		if (mkreg_ghost(path, gfe->mode, gf, img)) {
-			pr_perror("Can't create ghost regfile\n");
+		if ((ret = mkreg_ghost(path, gfe->mode, gf, img)) < 0)
+			msg = "Can't create ghost regfile\n";
+	}
+
+	if (ret < 0) {
+		/* Use grand parent, if parent directory does not exist */
+		if (errno == ENOENT) {
+			if (trim_last_parent(path) < 0) {
+				pr_err("trim failed: @%s@\n", path);
+				goto err;
+			}
+			len = strlen(path);
+			goto again;
+		}
+
+		/* Use numeric suffix, if a namesake already exists */
+		if (errno != EEXIST) {
+			pr_perror("%s", msg);
 			goto err;
-               }
+		}
+
+		if (++try == INT_MAX) {
+			pr_err("Can't find available file name\n");
+			goto err;
+		}
+		sprintf(path + len, ".%x", try);
+		goto again;
 	}
 
+	strcpy(gf->remap.rpath, path + root_len + 1);
+	pr_debug("Remap rpath is %s\n", gf->remap.rpath);
+
 	ret = -1;
 	if (ghost_apply_metadata(path, gfe))
 		goto err;



More information about the CRIU mailing list