[lxc-devel] [PATCH] uidmap: fix writing multiple ranges

Dwight Engen dwight.engen at oracle.com
Mon Mar 11 20:36:25 UTC 2013


The kernel requires a single atomic write for setting the /proc
idmap files. We were calling write(2) more than once when multiple
ranges were configured so instead build a buffer to pass in one write(2)
call.

Change id types to unsigned long to handle large id mappings gracefully.

Fix max id in example comment.

Signed-off-by: Dwight Engen <dwight.engen at oracle.com>
---
 src/lxc/conf.c    | 34 ++++++++++++++++++++++++++++------
 src/lxc/conf.h    | 16 ++++++++--------
 src/lxc/confile.c |  6 +++---
 3 files changed, 39 insertions(+), 17 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index e2abc72..9700c7a 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -2447,7 +2447,8 @@ int lxc_assign_network(struct lxc_list *network, pid_t pid)
 	return 0;
 }
 
-static int add_id_mapping(enum idtype idtype, pid_t pid, uid_t ns_start, uid_t host_start, int range)
+static int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf,
+			    size_t buf_size)
 {
 	char path[PATH_MAX];
 	int ret, closeret;
@@ -2463,7 +2464,7 @@ static int add_id_mapping(enum idtype idtype, pid_t pid, uid_t ns_start, uid_t h
 		perror("open");
 		return -EINVAL;
 	}
-	ret = fprintf(f, "%d %d %d", ns_start, host_start, range);
+	ret = fwrite(buf, buf_size, 1, f);
 	if (ret < 0)
 		SYSERROR("writing id mapping");
 	closeret = fclose(f);
@@ -2477,13 +2478,34 @@ int lxc_map_ids(struct lxc_list *idmap, pid_t pid)
 	struct lxc_list *iterator;
 	struct id_map *map;
 	int ret = 0;
-
-	lxc_list_for_each(iterator, idmap) {
-		map = iterator->elem;
-		ret = add_id_mapping(map->idtype, pid, map->nsid, map->hostid, map->range);
+	char *buf,*pos;
+	enum idtype type;
+
+	/* The kernel only takes <= 4k for writes to /proc/<nr>/[ug]id_map */
+	buf = pos = malloc(4096);
+	if (!buf)
+		return -ENOMEM;
+
+	for(type = ID_TYPE_UID; type <= ID_TYPE_GID; type++) {
+		int left,fill;
+		lxc_list_for_each(iterator, idmap) {
+			map = iterator->elem;
+			if (map->idtype == type) {
+				left = 4096 - (pos - buf);
+				fill = snprintf(pos, left, "%lu %lu %lu\n",
+					map->nsid, map->hostid, map->range);
+				if (fill <= 0 || fill >= left)
+					SYSERROR("snprintf failed, too many mappings");
+				pos += fill;
+			}
+		}
+		ret = write_id_mapping(type, pid, buf, pos-buf);
 		if (ret)
 			break;
+		pos = buf;
 	}
+
+	free(buf);
 	return ret;
 }
 
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index f20fb2f..cc7961a 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -149,17 +149,17 @@ enum idtype {
 
 /*
  * id_map is an id map entry.  Form in confile is:
- * lxc.id_map = U 9800 0 100
- * lxc.id_map = U 9900 1000 100
- * lxc.id_map = G 9800 0 100
- * lxc.id_map = G 9900 1000 100
- * meaning the container can use uids and gids 0-100 and 1000-1100,
- * with uid 0 mapping to uid 9800 on the host, and gid 1000 to
- * gid 9900 on the host.
+ * lxc.id_map = u 0    9800 100
+ * lxc.id_map = u 1000 9900 100
+ * lxc.id_map = g 0    9800 100
+ * lxc.id_map = g 1000 9900 100
+ * meaning the container can use uids and gids 0-99 and 1000-1099,
+ * with [ug]id 0 mapping to [ug]id 9800 on the host, and [ug]id 1000 to
+ * [ug]id 9900 on the host.
  */
 struct id_map {
 	enum idtype idtype;
-	int hostid, nsid, range;
+	unsigned long hostid, nsid, range;
 };
 
 /*
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 59cedef..0b1fd51 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -1024,7 +1024,7 @@ static int config_idmap(const char *key, const char *value, struct lxc_conf *lxc
 	char *subkey;
 	struct lxc_list *idmaplist = NULL;
 	struct id_map *idmap = NULL;
-	int hostid, nsid, range;
+	unsigned long hostid, nsid, range;
 	char type;
 	int ret;
 
@@ -1049,10 +1049,10 @@ static int config_idmap(const char *key, const char *value, struct lxc_conf *lxc
 
 	lxc_list_add_tail(&lxc_conf->id_map, idmaplist);
 
-	ret = sscanf(value, "%c %d %d %d", &type, &nsid, &hostid, &range);
+	ret = sscanf(value, "%c %lu %lu %lu", &type, &nsid, &hostid, &range);
 	if (ret != 4)
 		goto out;
-	INFO("read uid map: type %c nsid %d hostid %d range %d", type, nsid, hostid, range);
+	INFO("read uid map: type %c nsid %lu hostid %lu range %lu", type, nsid, hostid, range);
 	if (type == 'u')
 		idmap->idtype = ID_TYPE_UID;
 	else if (type == 'g')
-- 
1.7.12.3





More information about the lxc-devel mailing list