Page MenuHomeFreeBSD

D28150.id84345.diff
No OneTemporary

D28150.id84345.diff

diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -136,8 +136,10 @@
struct prisonlist allprison = TAILQ_HEAD_INITIALIZER(allprison);
LIST_HEAD(, prison_racct) allprison_racct;
int lastprid = 0;
+int lastdeadid = 0;
static int get_next_prid(struct prison **insprp);
+static int get_next_deadid(struct prison **dinsprp);
static int do_jail_attach(struct thread *td, struct prison *pr, int drflags);
static void prison_complete(void *context, int pending);
static void prison_deref(struct prison *pr, int flags);
@@ -518,7 +520,7 @@
#endif
struct vfsopt *opt;
struct vfsoptlist *opts;
- struct prison *pr, *deadpr, *inspr, *mypr, *ppr, *tpr;
+ struct prison *pr, *deadpr, *dinspr, *inspr, *mypr, *ppr, *tpr;
struct vnode *root;
char *domain, *errmsg, *host, *name, *namelc, *p, *path, *uuid;
char *g_path, *osrelstr;
@@ -530,10 +532,10 @@
#endif
unsigned long hid;
size_t namelen, onamelen, pnamelen;
- int born, created, cuflags, descend, drflags, enforce;
+ int created, cuflags, descend, drflags, enforce;
int error, errmsg_len, errmsg_pos;
int gotchildmax, gotenforce, gothid, gotrsnum, gotslevel;
- int jid, jsys, len, level;
+ int deadid, jid, jsys, len, level;
int childmax, osreldt, rsnum, slevel;
#if defined(INET) || defined(INET6)
int ii, ij;
@@ -991,6 +993,7 @@
pr = NULL;
ppr = mypr;
inspr = NULL;
+ deadpr = NULL;
if (cuflags == JAIL_CREATE && jid == 0 && name != NULL) {
namelc = strrchr(name, '.');
jid = strtoul(namelc != NULL ? namelc + 1 : name, &p, 10);
@@ -1019,66 +1022,41 @@
continue;
if (inspr->pr_id > jid)
break;
- pr = inspr;
- KASSERT(prison_isvalid(pr),
+ KASSERT(prison_isvalid(inspr),
("Found invalid prison %p", pr));
- mtx_lock(&pr->pr_mtx);
- drflags |= PD_LOCKED;
+ if (prison_isalive(inspr)) {
+ pr = inspr;
+ mtx_lock(&pr->pr_mtx);
+ drflags |= PD_LOCKED;
+ } else {
+ /* Note a dying jail to handle later. */
+ deadpr = inspr;
+ }
inspr = NULL;
break;
}
- if (pr != NULL) {
- ppr = pr->pr_parent;
- /* Create: jid must not exist. */
- if (cuflags == JAIL_CREATE) {
- /*
- * Even creators that cannot see the jail will
- * get EEXIST.
- */
- error = EEXIST;
- vfs_opterror(opts, "jail %d already exists",
- jid);
- goto done_deref;
- }
- if (!prison_ischild(mypr, pr)) {
- /*
- * Updaters get ENOENT if they cannot see the
- * jail. This is true even for CREATE | UPDATE,
- * which normally cannot give this error.
- */
- error = ENOENT;
- vfs_opterror(opts, "jail %d not found", jid);
- goto done_deref;
- }
- if (!prison_isalive(pr)) {
- if (!(flags & JAIL_DYING)) {
- error = ENOENT;
- vfs_opterror(opts, "jail %d is dying",
- jid);
- goto done_deref;
- }
- if ((flags & JAIL_ATTACH) ||
- (pr_flags & PR_PERSIST)) {
- /*
- * A dying jail might be resurrected
- * (via attach or persist), but first
- * it must determine if another jail
- * has claimed its name. Accomplish
- * this by implicitly re-setting the
- * name.
- */
- if (name == NULL)
- name = prison_name(mypr, pr);
- }
- }
- } else {
- /* Update: jid must exist. */
- if (cuflags == JAIL_UPDATE) {
- error = ENOENT;
- vfs_opterror(opts, "jail %d not found", jid);
- goto done_deref;
- }
+ if (cuflags == JAIL_CREATE && pr != NULL) {
+ /*
+ * Even creators that cannot see the jail will get
+ * EEXIST.
+ */
+ error = EEXIST;
+ vfs_opterror(opts, "jail %d already exists", jid);
+ goto done_deref;
}
+ if ((pr == NULL)
+ ? cuflags == JAIL_UPDATE
+ : !prison_ischild(mypr, pr)) {
+ /*
+ * Updaters get ENOENT for nonexistent jails, or for
+ * jails they cannot see. The latter case is true
+ * even for CREATE | UPDATE, which normally cannot
+ * give this error.
+ */
+ error = ENOENT;
+ vfs_opterror(opts, "jail %d not found", jid);
+ goto done_deref;
+ }
}
/*
* If the caller provided a name, look for a jail by that name.
@@ -1115,14 +1093,13 @@
"jail \"%s\" not found", name);
goto done_deref;
}
+ mtx_unlock(&ppr->pr_mtx);
if (!prison_isalive(ppr)) {
- mtx_unlock(&ppr->pr_mtx);
error = ENOENT;
vfs_opterror(opts,
"jail \"%s\" is dying", name);
goto done_deref;
}
- mtx_unlock(&ppr->pr_mtx);
*namelc = '.';
}
namelc++;
@@ -1130,57 +1107,35 @@
if (namelc[0] != '\0') {
pnamelen =
(ppr == &prison0) ? 0 : strlen(ppr->pr_name) + 1;
- deadpr = NULL;
FOREACH_PRISON_CHILD(ppr, tpr) {
- if (tpr != pr &&
- !strcmp(tpr->pr_name + pnamelen, namelc)) {
- if (prison_isalive(tpr)) {
- if (pr == NULL &&
- cuflags != JAIL_CREATE) {
- /*
- * Use this jail
- * for updates.
- */
- pr = tpr;
- mtx_lock(&tpr->pr_mtx);
- drflags |= PD_LOCKED;
- break;
- }
- /*
- * Create, or update(jid):
- * name must not exist in an
- * active sibling jail.
- */
- error = EEXIST;
- vfs_opterror(opts,
- "jail \"%s\" already exists",
- name);
- goto done_deref;
- }
- if (pr == NULL &&
- cuflags != JAIL_CREATE) {
- deadpr = tpr;
- KASSERT(prison_isvalid(deadpr),
- ("Found invalid prison %p",
- deadpr));
- }
- }
- }
- /* If no active jail is found, use a dying one. */
- if (deadpr != NULL && pr == NULL) {
- if (flags & JAIL_DYING) {
- pr = deadpr;
- mtx_lock(&pr->pr_mtx);
- drflags |= PD_LOCKED;
- } else if (cuflags == JAIL_UPDATE) {
- error = ENOENT;
+ if (tpr == pr || !prison_isalive(tpr) ||
+ strcmp(tpr->pr_name + pnamelen, namelc))
+ continue;
+ KASSERT(prison_isvalid(tpr),
+ ("Found invalid prison %p", tpr));
+ if (cuflags == JAIL_CREATE || pr != NULL) {
+ /*
+ * Create, or update(jid): name must
+ * not exist in an active sibling jail.
+ */
+ error = EEXIST;
vfs_opterror(opts,
- "jail \"%s\" is dying", name);
+ "jail \"%s\" already exists", name);
goto done_deref;
}
+ /* Use this jail for updates. */
+ pr = tpr;
+ mtx_lock(&pr->pr_mtx);
+ drflags |= PD_LOCKED;
+ break;
}
- /* Update: name must exist if no jid. */
- else if (cuflags == JAIL_UPDATE && pr == NULL) {
+ /*
+ * Update: name must exist if no jid. As with the jid
+ * case, the jail must be currently visible, or else
+ * even CREATE | UPDATE will get an error.
+ */
+ if ((pr == NULL)
+ ? cuflags == JAIL_UPDATE : !prison_isalive(pr)) {
error = ENOENT;
vfs_opterror(opts, "jail \"%s\" not found",
name);
@@ -1204,14 +1159,40 @@
vfs_opterror(opts, "prison limit exceeded");
goto done_deref;
}
- prison_hold(ppr);
- prison_proc_hold(ppr);
+ if (deadpr != NULL) {
+ /*
+ * The prison being created has the same ID as a dying
+ * one. Handle this by giving the dying jail a new ID.
+ * This may cause some confusion to user space, but
+ * only to those listing dying jails.
+ */
+ deadid = get_next_deadid(&dinspr);
+ if (deadid == 0) {
+ error = EAGAIN;
+ vfs_opterror(opts, "no available jail IDs");
+ goto done_deref;
+ }
+ mtx_lock(&deadpr->pr_mtx);
+ deadpr->pr_id = deadid;
+ mtx_unlock(&deadpr->pr_mtx);
+ if (dinspr == deadpr)
+ inspr = deadpr;
+ else {
+ inspr = TAILQ_NEXT(deadpr, pr_list);
+ TAILQ_REMOVE(&allprison, deadpr, pr_list);
+ if (dinspr != NULL)
+ TAILQ_INSERT_AFTER(&allprison, dinspr,
+ deadpr, pr_list);
+ else
+ TAILQ_INSERT_HEAD(&allprison, deadpr,
+ pr_list);
+ }
+ }
+
if (jid == 0 && (jid = get_next_prid(&inspr)) == 0) {
error = EAGAIN;
vfs_opterror(opts, "no available jail IDs");
- pr = ppr;
- drflags |= PD_DEREF | PD_DEUREF;
goto done_deref;
}
@@ -1231,6 +1212,8 @@
TAILQ_INSERT_TAIL(&allprison, pr, pr_list);
pr->pr_parent = ppr;
+ prison_hold(ppr);
+ prison_proc_hold(ppr);
LIST_INSERT_HEAD(&ppr->pr_children, pr, pr_sibling);
for (tpr = ppr; tpr != NULL; tpr = tpr->pr_parent)
tpr->pr_childcount++;
@@ -1736,13 +1719,10 @@
* Persistent prisons get an extra reference, and prisons losing their
* persist flag lose that reference.
*/
- born = !prison_isalive(pr);
if (ch_flags & PR_PERSIST & (pr_flags ^ pr->pr_flags)) {
if (pr_flags & PR_PERSIST) {
prison_hold(pr);
- if (refcount_acquire(&pr->pr_uref) == 0 &&
- pr->pr_state == PRISON_STATE_DYING)
- pr->pr_state = PRISON_STATE_ALIVE;
+ refcount_acquire(&pr->pr_uref);
} else {
drflags |= PD_DEUREF;
prison_free_not_last(pr);
@@ -1814,7 +1794,7 @@
#endif
/* Let the modules do their work. */
- if (born) {
+ if (created) {
error = osd_jail_call(pr, PR_METHOD_CREATE, opts);
if (error)
goto done_deref;
@@ -1951,6 +1931,55 @@
*insprp = inspr;
lastprid = jid;
return (jid);
+}
+
+/*
+ * Find the next available ID for a renumbered dead prison. This is the same
+ * as get_next_prid, but counting backward from the end of the range.
+ */
+static int
+get_next_deadid(struct prison **dinsprp)
+{
+ struct prison *dinspr;
+ int deadid, minid;
+
+ deadid = lastdeadid ? lastdeadid - 1 : JAIL_MAX;
+ /*
+ * Take two reverse passes through the allprison list: first starting
+ * with the proposed deadid, then ending with it.
+ */
+ for (minid = 1; minid != 0; ) {
+ TAILQ_FOREACH_REVERSE(dinspr, &allprison, prisonlist, pr_list) {
+ if (dinspr->pr_id > deadid)
+ continue;
+ if (dinspr->pr_id < deadid) {
+ /* Found an opening. */
+ minid = 0;
+ break;
+ }
+ if (--deadid < minid) {
+ if (lastdeadid == minid || lastdeadid == 0)
+ {
+ /*
+ * The entire legal range
+ * has been traversed
+ */
+ return 0;
+ }
+ /* Try again from the start. */
+ deadid = JAIL_MAX;
+ minid = lastdeadid;
+ break;
+ }
+ }
+ if (dinspr == NULL) {
+ /* Found room at the end of the list. */
+ break;
+ }
+ }
+ *dinsprp = dinspr;
+ lastdeadid = deadid;
+ return (deadid);
}
/*
diff --git a/sys/sys/jail.h b/sys/sys/jail.h
--- a/sys/sys/jail.h
+++ b/sys/sys/jail.h
@@ -101,7 +101,7 @@
#define JAIL_UPDATE 0x02 /* Update parameters of existing jail */
#define JAIL_ATTACH 0x04 /* Attach to jail upon creation */
#define JAIL_DYING 0x08 /* Allow getting a dying jail */
-#define JAIL_SET_MASK 0x0f
+#define JAIL_SET_MASK 0x0f /* JAIL_DYING is deprecated/ignored here */
#define JAIL_GET_MASK 0x08
#define JAIL_SYS_DISABLE 0

File Metadata

Mime Type
text/plain
Expires
Fri, Jan 23, 8:27 PM (10 h, 17 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27889363
Default Alt Text
D28150.id84345.diff (10 KB)

Event Timeline