From: Dwight Engen Date: Mon, 11 Mar 2013 20:36:25 +0000 (-0400) Subject: uidmap: fix writing multiple ranges X-Git-Tag: lxc-0.9.0.rc1~2^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=251d0d2a8b4ec7a12c22ba63b5d0e558289c2ef6;p=thirdparty%2Flxc.git uidmap: fix writing multiple ranges 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 Acked-by: Serge E. Hallyn --- diff --git a/src/lxc/conf.c b/src/lxc/conf.c index e2abc72df..9700c7aa2 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//[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 61456ae18..4c25970f3 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 0f68fe44d..cd02b2e2c 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -1114,7 +1114,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; @@ -1139,10 +1139,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')