diff --git a/ns.c b/ns.c index 9b68f36..2e3e747 100644 --- a/ns.c +++ b/ns.c @@ -88,31 +88,6 @@ void opts_to_nsactions(const char *shares[], enum nsaction *nsactions) } } -static int is_setns(const struct nsid *ns) -{ - switch (ns->action) { - case NSACTION_UNSHARE: - case NSACTION_SHARE_WITH_PARENT: - return 0; - default: - return 1; - } -} - -/* cmp_nsids compares two ns IDs in a stable manner, such that - namespaces that are entered via setns are sorted before those that - are entered via unshare (or not changed at all). */ -static int cmp_nsids(const void *lhs, const void *rhs) -{ - int diff = is_setns(rhs) - is_setns(lhs); - if (diff != 0) { - return diff; - } - /* Both namespaces are the same kind -- keep ordering intact by comparing - pointer values. */ - return (int) ((intptr_t) lhs - (intptr_t) rhs); -} - static void ns_enter_one(struct nsid *ns) { switch (ns->action) { @@ -140,6 +115,8 @@ static void ns_enter_one(struct nsid *ns) } } +// Note that for namespaces that want to enter into a specific namespace, +// we actually setns those before forking. static bool is_postfork_ns(struct nsid *ns) { /* For now, only the cgroup namespace needs to be unshared postfork */ @@ -151,40 +128,44 @@ void ns_enter_prefork(struct nsid *namespaces, size_t *len) /* Enter all relevant namespaces. It's hard to check in advance which namespaces are supported, so we unshare them one by one in order. */ - /* If we have CAP_SYS_ADMIN from the get-go, starting by entering - the userns may restrict us from joining additional namespaces, so - we rearrange the order so that we setns into target nsfs files first. */ - if (capable(BST_CAP_SYS_ADMIN)) { - qsort(namespaces, *len, sizeof (namespaces[0]), - cmp_nsids); - } - - struct nsid *first_postfork = NULL; + // First we setns the things that have a specific fd to share into. struct nsid *ns = &namespaces[0]; for (; ns < namespaces + *len; ++ns) { - if (ns->action != NSACTION_SHARE_WITH_PARENT && is_postfork_ns(ns)) { - first_postfork = ns; - break; + if (ns->action < 0) { + continue; } + // Note that we also setns the postfork namespaces here. If they + // have a specific namespace to share into then we must share into + // that namespace while we are still in the user namespace of that + // target namespace. ns_enter_one(ns); } - size_t i = 0; - for (; ns < namespaces + *len; ++ns, ++i) { - if (first_postfork != NULL && !is_postfork_ns(ns)) { - errx(1, "incompatible options: %s namespace must be entered before " - "forking, but must be done after %s namespace is entered post-fork.", - ns_name(ns->ns), - ns_name(first_postfork->ns)); + // Then setns the things that just need a blanket unshare (postfork + // namespaces with NSACTION_UNSHARE need to be shard post-fork). + ns = &namespaces[0]; + for (; ns < namespaces + *len; ++ns) { + if (is_postfork_ns(ns)) { + continue; } - namespaces[i] = *ns; + if (ns->action >= 0) { + continue; + } + ns_enter_one(ns); } - *len = i; } void ns_enter_postfork(struct nsid *namespaces, size_t len) { for (struct nsid *ns = &namespaces[0]; ns < namespaces + len; ++ns) { + if (!is_postfork_ns(ns)) { + // Already handled in ns_enter_prefork. + continue; + } + if (ns->action >= 0) { + // If there is an fd action then we already did this prefork. + continue; + } ns_enter_one(ns); } } diff --git a/ns.h b/ns.h index 35fcf9f..4550cc6 100644 --- a/ns.h +++ b/ns.h @@ -37,13 +37,13 @@ enum { enum nstype { NS_CGROUP = 0, - NS_IPC, - NS_MNT, - NS_NET, - NS_PID, - NS_TIME, - NS_USER, - NS_UTS, + NS_IPC, // 1 + NS_MNT, // 2 + NS_NET, // 3 + NS_PID, // 4 + NS_TIME,// 5 + NS_USER,// 6 + NS_UTS, // 7 MAX_NS };