From: Serge Hallyn Date: Tue, 31 Jul 2012 14:03:30 +0000 (+0200) Subject: pin container's rootfs to prevent read-only remount X-Git-Tag: lxc-0.8.0~40 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0c54752318ae11763de5551bee0d6aea92fa5d82;p=thirdparty%2Flxc.git pin container's rootfs to prevent read-only remount Signed-off-by: Serge Hallyn Signed-off-by: Daniel Lezcano --- diff --git a/src/lxc/conf.c b/src/lxc/conf.c index e8088bb09..601f4bd80 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -452,6 +452,51 @@ static int mount_rootfs_block(const char *rootfs, const char *target) return mount_unknow_fs(rootfs, target, 0); } +/* + * pin_rootfs + * if rootfs is a directory, then open ${rootfs}.hold for writing for the + * duration of the container run, to prevent the container from marking the + * underlying fs readonly on shutdown. + * return -1 on error. + * return -2 if nothing needed to be pinned. + * return an open fd (>=0) if we pinned it. + */ +int pin_rootfs(const char *rootfs) +{ + char absrootfs[MAXPATHLEN]; + char absrootfspin[MAXPATHLEN]; + struct stat s; + int ret, fd; + + if (!realpath(rootfs, absrootfs)) { + SYSERROR("failed to get real path for '%s'", rootfs); + return -1; + } + + if (access(absrootfs, F_OK)) { + SYSERROR("'%s' is not accessible", absrootfs); + return -1; + } + + if (stat(absrootfs, &s)) { + SYSERROR("failed to stat '%s'", absrootfs); + return -1; + } + + if (!__S_ISTYPE(s.st_mode, S_IFDIR)) + return -2; + + ret = snprintf(absrootfspin, MAXPATHLEN, "%s%s", absrootfs, ".hold"); + if (ret >= MAXPATHLEN) { + SYSERROR("pathname too long for rootfs hold file"); + return -1; + } + + fd = open(absrootfspin, O_CREAT | O_RDWR, S_IWUSR|S_IRUSR); + INFO("opened %s as fd %d\n", absrootfspin, fd); + return fd; +} + static int mount_rootfs(const char *rootfs, const char *target) { char absrootfs[MAXPATHLEN]; diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 09f55cb3f..0d8f28ed8 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -223,6 +223,8 @@ struct lxc_conf { */ extern struct lxc_conf *lxc_conf_init(void); +extern int pin_rootfs(const char *rootfs); + extern int lxc_create_network(struct lxc_handler *handler); extern void lxc_delete_network(struct lxc_list *networks); extern int lxc_assign_network(struct lxc_list *networks, pid_t pid); diff --git a/src/lxc/start.c b/src/lxc/start.c index 920ff77c3..293853757 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -534,6 +534,7 @@ int lxc_spawn(struct lxc_handler *handler) int clone_flags; int failed_before_rename = 0; const char *name = handler->name; + int pinfd; if (lxc_sync_init(handler)) return -1; @@ -563,6 +564,17 @@ int lxc_spawn(struct lxc_handler *handler) } } + /* + * if the rootfs is not a blockdev, prevent the container from + * marking it readonly. + */ + + pinfd = pin_rootfs(handler->conf->rootfs.path); + if (pinfd == -1) { + ERROR("failed to pin the container's rootfs"); + goto out_abort; + } + /* Create a process in a new set of namespaces */ handler->pid = lxc_clone(do_start, handler, clone_flags); if (handler->pid < 0) { @@ -605,6 +617,10 @@ int lxc_spawn(struct lxc_handler *handler) } lxc_sync_fini(handler); + + if (pinfd >= 0) + close(pinfd); + return 0; out_delete_net: