#include "fileutils.h"
#include "mount-api-utils.h"
-static int tmptgt_cleanup(int old_ns_fd);
-
struct hookset_data {
char *subdir;
char *org_target;
int old_ns_fd;
+ int new_ns_fd;
+ unsigned int tmp_umounted : 1;
};
+static int tmptgt_cleanup(struct hookset_data *);
+
static void free_hookset_data( struct libmnt_context *cxt,
const struct libmnt_hookset *hs)
{
if (!hsd)
return;
if (hsd->old_ns_fd >= 0)
- tmptgt_cleanup(hsd->old_ns_fd);
+ tmptgt_cleanup(hsd);
free(hsd->org_target);
free(hsd->subdir);
* Initialize MNT_PATH_TMPTGT; mkdir, create a new namespace and
* mark (bind mount) the directory as private.
*/
-static int tmptgt_unshare(int *old_ns_fd)
+static int tmptgt_unshare(struct hookset_data *hsd)
{
#ifdef USE_LIBMOUNT_SUPPORT_NAMESPACES
- int rc = 0, fd = -1;
+ int rc = 0;
- assert(old_ns_fd);
+ hsd->old_ns_fd = hsd->new_ns_fd = -1;
- *old_ns_fd = -1;
+ /* create directory */
+ rc = ul_mkdir_p(MNT_PATH_TMPTGT, S_IRWXU);
+ if (rc)
+ goto fail;
/* remember the current namespace */
- fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
- if (fd < 0)
+ hsd->old_ns_fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
+ if (hsd->old_ns_fd < 0)
goto fail;
/* create new namespace */
if (unshare(CLONE_NEWNS) != 0)
goto fail;
- /* create directory */
- rc = ul_mkdir_p(MNT_PATH_TMPTGT, S_IRWXU);
- if (rc)
- goto fail;
-
/* try to set top-level directory as private, this is possible if
* MNT_RUNTIME_TOPDIR (/run) is a separated filesystem. */
if (mount("none", MNT_RUNTIME_TOPDIR, NULL, MS_PRIVATE, NULL) != 0) {
goto fail;
}
+ /* remember the new namespace */
+ hsd->new_ns_fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
+ if (hsd->new_ns_fd < 0)
+ goto fail;
+
DBG(UTILS, ul_debug(MNT_PATH_TMPTGT " unshared"));
- *old_ns_fd = fd;
return 0;
fail:
if (rc == 0)
rc = errno ? -errno : -EINVAL;
- tmptgt_cleanup(fd);
+ tmptgt_cleanup(hsd);
DBG(UTILS, ul_debug(MNT_PATH_TMPTGT " unshare failed"));
return rc;
#else
/*
* Clean up MNT_PATH_TMPTGT; umount and switch back to old namespace
*/
-static int tmptgt_cleanup(int old_ns_fd)
+static int tmptgt_cleanup(struct hookset_data *hsd)
{
#ifdef USE_LIBMOUNT_SUPPORT_NAMESPACES
- umount(MNT_PATH_TMPTGT);
+ if (!hsd->tmp_umounted) {
+ umount(MNT_PATH_TMPTGT);
+ hsd->tmp_umounted = 1;
+ }
+
+ if (hsd->new_ns_fd >= 0)
+ close(hsd->new_ns_fd);
- if (old_ns_fd >= 0) {
- setns(old_ns_fd, CLONE_NEWNS);
- close(old_ns_fd);
+ if (hsd->old_ns_fd >= 0) {
+ setns(hsd->old_ns_fd, CLONE_NEWNS);
+ close(hsd->old_ns_fd);
}
+ hsd->new_ns_fd = hsd->old_ns_fd = -1;
DBG(UTILS, ul_debug(MNT_PATH_TMPTGT " cleanup done"));
return 0;
#else
#endif
}
+/*
+ * Attach (move) MNT_PATH_TMPTGT/subdir to the parental namespace.
+ */
static int do_mount_subdir(
struct libmnt_context *cxt,
+ struct hookset_data *hsd,
const char *root,
- const char *subdir,
const char *target)
{
int rc = 0;
+ const char *subdir = hsd->subdir;
#ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
struct libmnt_sysapi *api;
api = mnt_context_get_sysapi(cxt);
if (api) {
/* FD based way - unfortunately, it's impossible to open
- * sub-directory on not-yet attached mount. It means hook_mount.c
- * attaches to FS to temporary directory, and we clone
- * and move the subdir, and umount the old temporary tree.
+ * sub-directory on not-yet attached mount. It means
+ * hook_mount.c attaches FS to temporary directory, and we
+ * clone and move the subdir, and umount the old unshared
+ * temporary tree.
*
* The old mount(2) way does the same, but by BIND.
*/
rc = -errno;
if (!rc) {
+ /* Note that the original parental namespace could be
+ * private, in this case, it will not see our final mount,
+ * so we need to move the the orignal namespace.
+ */
+ setns(hsd->old_ns_fd, CLONE_NEWNS);
+
rc = move_mount(fd, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH);
set_syscall_status(cxt, "move_mount", rc == 0);
if (rc)
rc = -errno;
+
+ /* And move back to our private namespace to cleanup */
+ setns(hsd->new_ns_fd, CLONE_NEWNS);
}
if (!rc) {
close(api->fd_tree);
set_syscall_status(cxt, "umount", rc == 0);
if (rc)
rc = -errno;
+ hsd->tmp_umounted = 1;
+
}
return rc;
mnt_fs_set_target(cxt->fs, hsd->org_target);
/* bind subdir to the real target, umount temporary target */
- rc = do_mount_subdir(cxt, MNT_PATH_TMPTGT,
- hsd->subdir,
+ rc = do_mount_subdir(cxt, hsd,
+ MNT_PATH_TMPTGT,
mnt_fs_get_target(cxt->fs));
if (rc)
return rc;
- tmptgt_cleanup(hsd->old_ns_fd);
- hsd->old_ns_fd = -1;
+ tmptgt_cleanup(hsd);
return rc;
}
if (!hsd->org_target)
rc = -ENOMEM;
if (!rc)
- rc = tmptgt_unshare(&hsd->old_ns_fd);
+ rc = tmptgt_unshare(hsd);
if (!rc)
mnt_fs_set_target(cxt->fs, MNT_PATH_TMPTGT);
if (!rc)