[lxc-devel] [PATCH] mod_rdep(): Write path and name of clone to file

Christian Brauner christianvanbrauner at gmail.com
Thu Aug 27 19:16:23 UTC 2015


If we currently create clone-snapshots via lxc-clone only the plain total
number of the containers it serves as a base-container is written to the file
"lxc-snapshots". This commit modifies mod_rdep() so it will store the paths and
names to the containers that are clone-snapshots (similar to the "lxc_rdepends"
file for the clones). **Users which still have containers that have a non-empty
(with a number > 0 as an entry) "lxc-snapshots" file in the old format are not
affected by this change. It will be used until all old clones have been
deleted!** For all others, the "lxc_snapshots" file placed under the original
container now looks like this:

      /var/lib/lxc
      bb
      /var/lib/lxc
      cc
      /opt
      dd

This is an example of a container that provides the base for three
clone-snapshots bb, cc, and dd. Where bb and cc both are placed in the usual
path for privileged containers and dd is placed in a custom path.

- Add additional argument to function that takes in the clone-snapshotted
  lxc_container.
- Have mod_rdep() write the path and name of the clone-snapshotted container the
  file lxc_snapshots of the original container.
- If a clone-snapshot gets deleted the corresponding line in the file
  lxc_snapshot of the original container will be deleted and the file updated
  via mmap() + memmove() + munmap().
- Adapt has_fs_snapshots().
- **If an lxc-snapshot file in the old format is found we'll keep using it.**

Signed-off-by: Christian Brauner <christianvanbrauner at gmail.com>
---
 src/lxc/lxccontainer.c | 164 ++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 128 insertions(+), 36 deletions(-)

diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index 5291634..f022f0d 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -19,6 +19,7 @@
  */
 
 #define _GNU_SOURCE
+#include <sys/mman.h>
 #include <assert.h>
 #include <stdarg.h>
 #include <pthread.h>
@@ -1970,47 +1971,130 @@ out:
 
 WRAP_API_1(bool, lxcapi_save_config, const char *)
 
-static bool mod_rdep(struct lxc_container *c, bool inc)
+
+static bool mod_rdep(struct lxc_container *c0, struct lxc_container *c, bool inc)
 {
+	FILE *f1;
+	struct stat fbuf;
+	char *buf = NULL;
+	char *del;
 	char path[MAXPATHLEN];
-	int ret, v = 0;
-	FILE *f;
+	char newpath[MAXPATHLEN];
+	int fd, ret, n = 0, v = 0;
 	bool bret = false;
+	size_t len;
 
-	if (container_disk_lock(c))
+	if (container_disk_lock(c0))
 		return false;
-	ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path,
-			c->name);
+
+	ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c0->config_path, c0->name);
 	if (ret < 0 || ret > MAXPATHLEN)
 		goto out;
-	f = fopen(path, "r");
-	if (f) {
-		ret = fscanf(f, "%d", &v);
-		fclose(f);
-		if (ret != 1) {
-			ERROR("Corrupted file %s", path);
-			goto out;
-		}
-	}
-	v += inc ? 1 : -1;
-	f = fopen(path, "w");
-	if (!f)
-		goto out;
-	if (fprintf(f, "%d\n", v) < 0) {
-		ERROR("Error writing new snapshots value");
-		fclose(f);
+	ret = snprintf(newpath, MAXPATHLEN, "%s\n%s\n", c->config_path, c->name);
+	if (ret < 0 || ret > MAXPATHLEN)
 		goto out;
+
+	/* If we find an lxc-snapshot file using the old format only listing the
+	 * number of snapshots we will keep using it. */
+	f1 = fopen(path, "r");
+	if (f1) {
+		n = fscanf(f1, "%d", &v);
+		fclose(f1);
+		if (n == 1 && v == 0) {
+			remove(path);
+			n = 0;
+		}
 	}
-	ret = fclose(f);
-	if (ret != 0) {
-		SYSERROR("Error writing to or closing snapshots file");
-		goto out;
+	if (n == 1) {
+		v += inc ? 1 : -1;
+		f1 = fopen(path, "w");
+		if (!f1)
+			goto out;
+		if (fprintf(f1, "%d\n", v) < 0) {
+			ERROR("Error writing new snapshots value");
+			fclose(f1);
+			goto out;
+		}
+		ret = fclose(f1);
+		if (ret != 0) {
+			SYSERROR("Error writing to or closing snapshots file");
+			goto out;
+		}
+	} else {
+		/* Here we know that we have or can use an lxc-snapshot file
+		 * using the new format. */
+		if (inc) {
+			f1 = fopen(path, "a");
+			if (!f1)
+				goto out;
+
+			if (fprintf(f1, "%s", newpath) < 0) {
+				ERROR("Error writing new snapshots entry");
+				ret = fclose(f1);
+				if (ret != 0)
+					SYSERROR("Error writing to or closing snapshots file");
+				goto out;
+			}
+
+			ret = fclose(f1);
+			if (ret != 0) {
+				SYSERROR("Error writing to or closing snapshots file");
+				goto out;
+			}
+		} else if (!inc) {
+			fd = open(path, O_RDWR | O_CLOEXEC);
+			if (fd < 0)
+				goto out;
+
+			ret = fstat(fd, &fbuf);
+			if (ret < 0) {
+				close(fd);
+				goto out;
+			}
+
+			if (fbuf.st_size != 0) {
+				buf = mmap(NULL, fbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+				if (buf == MAP_FAILED) {
+					SYSERROR("Failed to create mapping %s", path);
+					close(fd);
+					goto out;
+				}
+			}
+
+			len = strlen(newpath);
+
+			/* mmap()ed memory is only \0-terminated when it is not
+			 * a multiple of a pagesize. Hence, we'll use memmem(). */
+			if ((del = memmem(buf, fbuf.st_size, newpath, len))) {
+				/* remove container entry */
+				memmove(del, del + len, strlen(del) - len + 1);
+
+				munmap(buf, fbuf.st_size);
+
+				if (ftruncate(fd, fbuf.st_size - len) < 0) {
+					SYSERROR("Failed to truncate file %s", path);
+					close(fd);
+					goto out;
+				}
+			} else {
+				munmap(buf, fbuf.st_size);
+			}
+
+			close(fd);
+		}
+
+		/* If the lxc-snapshot file is empty, remove it. */
+		if (stat(path, &fbuf) < 0)
+			goto out;
+		if (!fbuf.st_size) {
+			remove(path);
+		}
 	}
 
 	bret = true;
 
 out:
-	container_disk_unlock(c);
+	container_disk_unlock(c0);
 	return bret;
 }
 
@@ -2052,8 +2136,8 @@ static void mod_all_rdeps(struct lxc_container *c, bool inc)
 				lxcpath, lxcname);
 			continue;
 		}
-		if (!mod_rdep(p, inc))
-			ERROR("Failed to increase numsnapshots for %s:%s",
+		if (!mod_rdep(p, c, inc))
+			ERROR("Failed to update snapshots file for %s:%s",
 				lxcpath, lxcname);
 		lxc_container_put(p);
 	}
@@ -2065,22 +2149,30 @@ out:
 
 static bool has_fs_snapshots(struct lxc_container *c)
 {
+	FILE *f;
 	char path[MAXPATHLEN];
 	int ret, v;
-	FILE *f;
+	struct stat fbuf;
 	bool bret = false;
 
 	ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path,
 			c->name);
 	if (ret < 0 || ret > MAXPATHLEN)
 		goto out;
-	f = fopen(path, "r");
-	if (!f)
-		goto out;
-	ret = fscanf(f, "%d", &v);
-	fclose(f);
-	if (ret != 1)
+	/* If the file doesn't exist there are no snapshots. */
+	if (stat(path, &fbuf) < 0)
 		goto out;
+	v = fbuf.st_size;
+	if (v != 0) {
+		f = fopen(path, "r");
+		if (!f)
+			goto out;
+		ret = fscanf(f, "%d", &v);
+		fclose(f);
+		// TODO: Figure out what to do with the return value of fscanf.
+		if (ret != 1)
+			INFO("Container uses new lxc-snapshots format %s", path);
+	}
 	bret = v != 0;
 
 out:
-- 
2.5.0



More information about the lxc-devel mailing list