xref: /freebsd/sys/kern/kern_jaildesc.c (revision 66d8ffe3046ded1eb3f78599c6af8eb965482ef5)
1851dc7f8SJamie Gritton /*-
2851dc7f8SJamie Gritton  * SPDX-License-Identifier: BSD-2-Clause
3851dc7f8SJamie Gritton  *
4851dc7f8SJamie Gritton  * Copyright (c) 2025 James Gritton.
5851dc7f8SJamie Gritton  * All rights reserved.
6851dc7f8SJamie Gritton  *
7851dc7f8SJamie Gritton  * Redistribution and use in source and binary forms, with or without
8851dc7f8SJamie Gritton  * modification, are permitted provided that the following conditions
9851dc7f8SJamie Gritton  * are met:
10851dc7f8SJamie Gritton  * 1. Redistributions of source code must retain the above copyright
11851dc7f8SJamie Gritton  *    notice, this list of conditions and the following disclaimer.
12851dc7f8SJamie Gritton  * 2. Redistributions in binary form must reproduce the above copyright
13851dc7f8SJamie Gritton  *    notice, this list of conditions and the following disclaimer in the
14851dc7f8SJamie Gritton  *    documentation and/or other materials provided with the distribution.
15851dc7f8SJamie Gritton  *
16851dc7f8SJamie Gritton  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17851dc7f8SJamie Gritton  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18851dc7f8SJamie Gritton  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19851dc7f8SJamie Gritton  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20851dc7f8SJamie Gritton  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21851dc7f8SJamie Gritton  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22851dc7f8SJamie Gritton  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23851dc7f8SJamie Gritton  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24851dc7f8SJamie Gritton  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25851dc7f8SJamie Gritton  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26851dc7f8SJamie Gritton  * SUCH DAMAGE.
27851dc7f8SJamie Gritton  */
28851dc7f8SJamie Gritton 
29851dc7f8SJamie Gritton #include <sys/param.h>
30851dc7f8SJamie Gritton #include <sys/fcntl.h>
31851dc7f8SJamie Gritton #include <sys/file.h>
32851dc7f8SJamie Gritton #include <sys/filedesc.h>
33851dc7f8SJamie Gritton #include <sys/kernel.h>
34851dc7f8SJamie Gritton #include <sys/jail.h>
35851dc7f8SJamie Gritton #include <sys/jaildesc.h>
36851dc7f8SJamie Gritton #include <sys/lock.h>
37851dc7f8SJamie Gritton #include <sys/malloc.h>
38851dc7f8SJamie Gritton #include <sys/mutex.h>
39*66d8ffe3SJamie Gritton #include <sys/poll.h>
40851dc7f8SJamie Gritton #include <sys/priv.h>
41851dc7f8SJamie Gritton #include <sys/stat.h>
42851dc7f8SJamie Gritton #include <sys/sysproto.h>
43851dc7f8SJamie Gritton #include <sys/systm.h>
44851dc7f8SJamie Gritton #include <sys/ucred.h>
45d81b337dSJamie Gritton #include <sys/user.h>
46851dc7f8SJamie Gritton #include <sys/vnode.h>
47851dc7f8SJamie Gritton 
48851dc7f8SJamie Gritton MALLOC_DEFINE(M_JAILDESC, "jaildesc", "jail descriptors");
49851dc7f8SJamie Gritton 
50*66d8ffe3SJamie Gritton static fo_poll_t	jaildesc_poll;
51*66d8ffe3SJamie Gritton static fo_kqfilter_t	jaildesc_kqfilter;
52851dc7f8SJamie Gritton static fo_stat_t	jaildesc_stat;
53851dc7f8SJamie Gritton static fo_close_t	jaildesc_close;
54851dc7f8SJamie Gritton static fo_fill_kinfo_t	jaildesc_fill_kinfo;
55851dc7f8SJamie Gritton static fo_cmp_t		jaildesc_cmp;
56851dc7f8SJamie Gritton 
57851dc7f8SJamie Gritton static struct fileops jaildesc_ops = {
58851dc7f8SJamie Gritton 	.fo_read = invfo_rdwr,
59851dc7f8SJamie Gritton 	.fo_write = invfo_rdwr,
60851dc7f8SJamie Gritton 	.fo_truncate = invfo_truncate,
61851dc7f8SJamie Gritton 	.fo_ioctl = invfo_ioctl,
62*66d8ffe3SJamie Gritton 	.fo_poll = jaildesc_poll,
63*66d8ffe3SJamie Gritton 	.fo_kqfilter = jaildesc_kqfilter,
64851dc7f8SJamie Gritton 	.fo_stat = jaildesc_stat,
65851dc7f8SJamie Gritton 	.fo_close = jaildesc_close,
66d81b337dSJamie Gritton 	.fo_chmod = invfo_chmod,
67d81b337dSJamie Gritton 	.fo_chown = invfo_chown,
68851dc7f8SJamie Gritton 	.fo_sendfile = invfo_sendfile,
69851dc7f8SJamie Gritton 	.fo_fill_kinfo = jaildesc_fill_kinfo,
70851dc7f8SJamie Gritton 	.fo_cmp = jaildesc_cmp,
71851dc7f8SJamie Gritton 	.fo_flags = DFLAG_PASSABLE,
72851dc7f8SJamie Gritton };
73851dc7f8SJamie Gritton 
74851dc7f8SJamie Gritton /*
75d81b337dSJamie Gritton  * Given a jail descriptor number, return its prison and/or its
76d81b337dSJamie Gritton  * credential.  They are returned held, and will need to be released
77d81b337dSJamie Gritton  * by the caller.
78851dc7f8SJamie Gritton  */
79851dc7f8SJamie Gritton int
jaildesc_find(struct thread * td,int fd,struct prison ** prp,struct ucred ** ucredp)80d81b337dSJamie Gritton jaildesc_find(struct thread *td, int fd, struct prison **prp,
81d81b337dSJamie Gritton     struct ucred **ucredp)
82851dc7f8SJamie Gritton {
83851dc7f8SJamie Gritton 	struct file *fp;
84851dc7f8SJamie Gritton 	struct jaildesc *jd;
85851dc7f8SJamie Gritton 	struct prison *pr;
86851dc7f8SJamie Gritton 	int error;
87851dc7f8SJamie Gritton 
88851dc7f8SJamie Gritton 	error = fget(td, fd, &cap_no_rights, &fp);
89851dc7f8SJamie Gritton 	if (error != 0)
90851dc7f8SJamie Gritton 		return (error);
91851dc7f8SJamie Gritton 	if (fp->f_type != DTYPE_JAILDESC) {
9216f600dcSJamie Gritton 		error = EINVAL;
93851dc7f8SJamie Gritton 		goto out;
94851dc7f8SJamie Gritton 	}
95851dc7f8SJamie Gritton 	jd = fp->f_data;
96851dc7f8SJamie Gritton 	JAILDESC_LOCK(jd);
97851dc7f8SJamie Gritton 	pr = jd->jd_prison;
98851dc7f8SJamie Gritton 	if (pr == NULL || !prison_isvalid(pr)) {
99851dc7f8SJamie Gritton 		error = ENOENT;
100851dc7f8SJamie Gritton 		JAILDESC_UNLOCK(jd);
101851dc7f8SJamie Gritton 		goto out;
102851dc7f8SJamie Gritton 	}
103d81b337dSJamie Gritton 	if (prp != NULL) {
104851dc7f8SJamie Gritton 		prison_hold(pr);
105851dc7f8SJamie Gritton 		*prp = pr;
106d81b337dSJamie Gritton 	}
107851dc7f8SJamie Gritton 	JAILDESC_UNLOCK(jd);
108851dc7f8SJamie Gritton 	if (ucredp != NULL)
109851dc7f8SJamie Gritton 		*ucredp = crhold(fp->f_cred);
110851dc7f8SJamie Gritton  out:
111851dc7f8SJamie Gritton 	fdrop(fp, td);
112851dc7f8SJamie Gritton 	return (error);
113851dc7f8SJamie Gritton }
114851dc7f8SJamie Gritton 
115851dc7f8SJamie Gritton /*
116851dc7f8SJamie Gritton  * Allocate a new jail decriptor, not yet associated with a prison.
117851dc7f8SJamie Gritton  * Return the file pointer (with a reference held) and the descriptor
118851dc7f8SJamie Gritton  * number.
119851dc7f8SJamie Gritton  */
120851dc7f8SJamie Gritton int
jaildesc_alloc(struct thread * td,struct file ** fpp,int * fdp,int owning)121851dc7f8SJamie Gritton jaildesc_alloc(struct thread *td, struct file **fpp, int *fdp, int owning)
122851dc7f8SJamie Gritton {
123851dc7f8SJamie Gritton 	struct file *fp;
124851dc7f8SJamie Gritton 	struct jaildesc *jd;
125851dc7f8SJamie Gritton 	int error;
126851dc7f8SJamie Gritton 
127851dc7f8SJamie Gritton 	if (owning) {
128851dc7f8SJamie Gritton 		error = priv_check(td, PRIV_JAIL_REMOVE);
129851dc7f8SJamie Gritton 		if (error != 0)
130851dc7f8SJamie Gritton 			return (error);
131d81b337dSJamie Gritton 	}
132851dc7f8SJamie Gritton 	jd = malloc(sizeof(*jd), M_JAILDESC, M_WAITOK | M_ZERO);
133851dc7f8SJamie Gritton 	error = falloc_caps(td, &fp, fdp, 0, NULL);
134851dc7f8SJamie Gritton 	if (error != 0) {
135851dc7f8SJamie Gritton 		free(jd, M_JAILDESC);
136851dc7f8SJamie Gritton 		return (error);
137851dc7f8SJamie Gritton 	}
138d8d5324eSJamie Gritton 	finit(fp, priv_check_cred(fp->f_cred, PRIV_JAIL_SET) == 0 ?
139d8d5324eSJamie Gritton 	    FREAD | FWRITE : FREAD, DTYPE_JAILDESC, jd, &jaildesc_ops);
140851dc7f8SJamie Gritton 	JAILDESC_LOCK_INIT(jd);
141*66d8ffe3SJamie Gritton 	knlist_init_mtx(&jd->jd_selinfo.si_note, &jd->jd_lock);
142d81b337dSJamie Gritton 	if (owning)
143d81b337dSJamie Gritton 		jd->jd_flags |= JDF_OWNING;
144851dc7f8SJamie Gritton 	*fpp = fp;
145851dc7f8SJamie Gritton 	return (0);
146851dc7f8SJamie Gritton }
147851dc7f8SJamie Gritton 
148851dc7f8SJamie Gritton /*
149851dc7f8SJamie Gritton  * Assocate a jail descriptor with its prison.
150851dc7f8SJamie Gritton  */
151851dc7f8SJamie Gritton void
jaildesc_set_prison(struct file * fp,struct prison * pr)152851dc7f8SJamie Gritton jaildesc_set_prison(struct file *fp, struct prison *pr)
153851dc7f8SJamie Gritton {
154851dc7f8SJamie Gritton 	struct jaildesc *jd;
155851dc7f8SJamie Gritton 
156851dc7f8SJamie Gritton 	mtx_assert(&pr->pr_mtx, MA_OWNED);
157851dc7f8SJamie Gritton 	jd = fp->f_data;
158851dc7f8SJamie Gritton 	JAILDESC_LOCK(jd);
159851dc7f8SJamie Gritton 	jd->jd_prison = pr;
160851dc7f8SJamie Gritton 	LIST_INSERT_HEAD(&pr->pr_descs, jd, jd_list);
161851dc7f8SJamie Gritton 	prison_hold(pr);
162851dc7f8SJamie Gritton 	JAILDESC_UNLOCK(jd);
163851dc7f8SJamie Gritton }
164851dc7f8SJamie Gritton 
165851dc7f8SJamie Gritton /*
166d8d5324eSJamie Gritton  * Detach all the jail descriptors from a prison.
167851dc7f8SJamie Gritton  */
168851dc7f8SJamie Gritton void
jaildesc_prison_cleanup(struct prison * pr)169851dc7f8SJamie Gritton jaildesc_prison_cleanup(struct prison *pr)
170851dc7f8SJamie Gritton {
171851dc7f8SJamie Gritton 	struct jaildesc *jd;
172851dc7f8SJamie Gritton 
173851dc7f8SJamie Gritton 	mtx_assert(&pr->pr_mtx, MA_OWNED);
174851dc7f8SJamie Gritton 	while ((jd = LIST_FIRST(&pr->pr_descs))) {
175851dc7f8SJamie Gritton 		JAILDESC_LOCK(jd);
176851dc7f8SJamie Gritton 		LIST_REMOVE(jd, jd_list);
177851dc7f8SJamie Gritton 		jd->jd_prison = NULL;
178851dc7f8SJamie Gritton 		JAILDESC_UNLOCK(jd);
179851dc7f8SJamie Gritton 		prison_free(pr);
180851dc7f8SJamie Gritton 	}
181851dc7f8SJamie Gritton }
182851dc7f8SJamie Gritton 
183*66d8ffe3SJamie Gritton /*
184*66d8ffe3SJamie Gritton  * Pass a note to all listening kqueues.
185*66d8ffe3SJamie Gritton  */
186*66d8ffe3SJamie Gritton void
jaildesc_knote(struct prison * pr,long hint)187*66d8ffe3SJamie Gritton jaildesc_knote(struct prison *pr, long hint)
188*66d8ffe3SJamie Gritton {
189*66d8ffe3SJamie Gritton 	struct jaildesc *jd;
190*66d8ffe3SJamie Gritton 	int prison_locked;
191*66d8ffe3SJamie Gritton 
192*66d8ffe3SJamie Gritton 	if (!LIST_EMPTY(&pr->pr_descs)) {
193*66d8ffe3SJamie Gritton 		prison_locked = mtx_owned(&pr->pr_mtx);
194*66d8ffe3SJamie Gritton 		if (!prison_locked)
195*66d8ffe3SJamie Gritton 			prison_lock(pr);
196*66d8ffe3SJamie Gritton 		LIST_FOREACH(jd, &pr->pr_descs, jd_list) {
197*66d8ffe3SJamie Gritton 			JAILDESC_LOCK(jd);
198*66d8ffe3SJamie Gritton 			if (hint == NOTE_JAIL_REMOVE) {
199*66d8ffe3SJamie Gritton 				jd->jd_flags |= JDF_REMOVED;
200*66d8ffe3SJamie Gritton 				if (jd->jd_flags & JDF_SELECTED) {
201*66d8ffe3SJamie Gritton 					jd->jd_flags &= ~JDF_SELECTED;
202*66d8ffe3SJamie Gritton 					selwakeup(&jd->jd_selinfo);
203*66d8ffe3SJamie Gritton 				}
204*66d8ffe3SJamie Gritton 			}
205*66d8ffe3SJamie Gritton 			KNOTE_LOCKED(&jd->jd_selinfo.si_note, hint);
206*66d8ffe3SJamie Gritton 			JAILDESC_UNLOCK(jd);
207*66d8ffe3SJamie Gritton 		}
208*66d8ffe3SJamie Gritton 		if (!prison_locked)
209*66d8ffe3SJamie Gritton 			prison_unlock(pr);
210*66d8ffe3SJamie Gritton 	}
211*66d8ffe3SJamie Gritton }
212*66d8ffe3SJamie Gritton 
213851dc7f8SJamie Gritton static int
jaildesc_close(struct file * fp,struct thread * td)214851dc7f8SJamie Gritton jaildesc_close(struct file *fp, struct thread *td)
215851dc7f8SJamie Gritton {
216851dc7f8SJamie Gritton 	struct jaildesc *jd;
217851dc7f8SJamie Gritton 	struct prison *pr;
218851dc7f8SJamie Gritton 
219851dc7f8SJamie Gritton 	jd = fp->f_data;
220851dc7f8SJamie Gritton 	fp->f_data = NULL;
221851dc7f8SJamie Gritton 	if (jd != NULL) {
222851dc7f8SJamie Gritton 		JAILDESC_LOCK(jd);
223851dc7f8SJamie Gritton 		pr = jd->jd_prison;
224851dc7f8SJamie Gritton 		if (pr != NULL) {
225851dc7f8SJamie Gritton 			/*
226851dc7f8SJamie Gritton 			 * Free or remove the associated prison.
227851dc7f8SJamie Gritton 			 * This requires a second check after re-
228851dc7f8SJamie Gritton 			 * ordering locks.  This jaildesc can remain
229851dc7f8SJamie Gritton 			 * unlocked once we have a prison reference,
230851dc7f8SJamie Gritton 			 * because that prison is the only place that
231851dc7f8SJamie Gritton 			 * still points back to it.
232851dc7f8SJamie Gritton 			 */
233851dc7f8SJamie Gritton 			prison_hold(pr);
234851dc7f8SJamie Gritton 			JAILDESC_UNLOCK(jd);
235d81b337dSJamie Gritton 			if (jd->jd_flags & JDF_OWNING) {
236851dc7f8SJamie Gritton 				sx_xlock(&allprison_lock);
237851dc7f8SJamie Gritton 				prison_lock(pr);
238851dc7f8SJamie Gritton 				if (jd->jd_prison != NULL) {
239851dc7f8SJamie Gritton 					/*
240851dc7f8SJamie Gritton 					 * Unlink the prison, but don't free
241851dc7f8SJamie Gritton 					 * it; that will be done as part of
242851dc7f8SJamie Gritton 					 * of prison_remove.
243851dc7f8SJamie Gritton 					 */
244851dc7f8SJamie Gritton 					LIST_REMOVE(jd, jd_list);
245851dc7f8SJamie Gritton 					prison_remove(pr);
246851dc7f8SJamie Gritton 				} else {
247851dc7f8SJamie Gritton 					prison_unlock(pr);
248851dc7f8SJamie Gritton 					sx_xunlock(&allprison_lock);
249851dc7f8SJamie Gritton 				}
250851dc7f8SJamie Gritton 			} else {
251851dc7f8SJamie Gritton 				prison_lock(pr);
252851dc7f8SJamie Gritton 				if (jd->jd_prison != NULL) {
253851dc7f8SJamie Gritton 					LIST_REMOVE(jd, jd_list);
254851dc7f8SJamie Gritton 					prison_free(pr);
255851dc7f8SJamie Gritton 				}
256851dc7f8SJamie Gritton 				prison_unlock(pr);
257851dc7f8SJamie Gritton 			}
258851dc7f8SJamie Gritton 			prison_free(pr);
259851dc7f8SJamie Gritton 		}
260*66d8ffe3SJamie Gritton 		knlist_destroy(&jd->jd_selinfo.si_note);
261851dc7f8SJamie Gritton 		JAILDESC_LOCK_DESTROY(jd);
262851dc7f8SJamie Gritton 		free(jd, M_JAILDESC);
263851dc7f8SJamie Gritton 	}
264851dc7f8SJamie Gritton 	return (0);
265851dc7f8SJamie Gritton }
266851dc7f8SJamie Gritton 
267851dc7f8SJamie Gritton static int
jaildesc_poll(struct file * fp,int events,struct ucred * active_cred,struct thread * td)268*66d8ffe3SJamie Gritton jaildesc_poll(struct file *fp, int events, struct ucred *active_cred,
269*66d8ffe3SJamie Gritton     struct thread *td)
270*66d8ffe3SJamie Gritton {
271*66d8ffe3SJamie Gritton 	struct jaildesc *jd;
272*66d8ffe3SJamie Gritton 	int revents;
273*66d8ffe3SJamie Gritton 
274*66d8ffe3SJamie Gritton 	revents = 0;
275*66d8ffe3SJamie Gritton 	jd = fp->f_data;
276*66d8ffe3SJamie Gritton 	JAILDESC_LOCK(jd);
277*66d8ffe3SJamie Gritton 	if (jd->jd_flags & JDF_REMOVED)
278*66d8ffe3SJamie Gritton 		revents |= POLLHUP;
279*66d8ffe3SJamie Gritton 	if (revents == 0) {
280*66d8ffe3SJamie Gritton 		selrecord(td, &jd->jd_selinfo);
281*66d8ffe3SJamie Gritton 		jd->jd_flags |= JDF_SELECTED;
282*66d8ffe3SJamie Gritton 	}
283*66d8ffe3SJamie Gritton 	JAILDESC_UNLOCK(jd);
284*66d8ffe3SJamie Gritton 	return (revents);
285*66d8ffe3SJamie Gritton }
286*66d8ffe3SJamie Gritton 
287*66d8ffe3SJamie Gritton static void
jaildesc_kqops_detach(struct knote * kn)288*66d8ffe3SJamie Gritton jaildesc_kqops_detach(struct knote *kn)
289*66d8ffe3SJamie Gritton {
290*66d8ffe3SJamie Gritton 	struct jaildesc *jd;
291*66d8ffe3SJamie Gritton 
292*66d8ffe3SJamie Gritton 	jd = kn->kn_fp->f_data;
293*66d8ffe3SJamie Gritton 	knlist_remove(&jd->jd_selinfo.si_note, kn, 0);
294*66d8ffe3SJamie Gritton }
295*66d8ffe3SJamie Gritton 
296*66d8ffe3SJamie Gritton static int
jaildesc_kqops_event(struct knote * kn,long hint)297*66d8ffe3SJamie Gritton jaildesc_kqops_event(struct knote *kn, long hint)
298*66d8ffe3SJamie Gritton {
299*66d8ffe3SJamie Gritton 	struct jaildesc *jd;
300*66d8ffe3SJamie Gritton 	u_int event;
301*66d8ffe3SJamie Gritton 
302*66d8ffe3SJamie Gritton 	jd = kn->kn_fp->f_data;
303*66d8ffe3SJamie Gritton 	if (hint == 0) {
304*66d8ffe3SJamie Gritton 		/*
305*66d8ffe3SJamie Gritton 		 * Initial test after registration. Generate a
306*66d8ffe3SJamie Gritton 		 * NOTE_JAIL_REMOVE in case the prison already died
307*66d8ffe3SJamie Gritton 		 * before registration.
308*66d8ffe3SJamie Gritton 		 */
309*66d8ffe3SJamie Gritton 		event = jd->jd_flags & JDF_REMOVED ? NOTE_JAIL_REMOVE : 0;
310*66d8ffe3SJamie Gritton 	} else {
311*66d8ffe3SJamie Gritton 		/*
312*66d8ffe3SJamie Gritton 		 * Mask off extra data.  In the NOTE_JAIL_CHILD case,
313*66d8ffe3SJamie Gritton 		 * that's everything except the NOTE_JAIL_CHILD bit
314*66d8ffe3SJamie Gritton 		 * itself, since a JID is any positive integer.
315*66d8ffe3SJamie Gritton 		 */
316*66d8ffe3SJamie Gritton 		event = ((u_int)hint & NOTE_JAIL_CHILD) ? NOTE_JAIL_CHILD :
317*66d8ffe3SJamie Gritton 		    (u_int)hint & NOTE_JAIL_CTRLMASK;
318*66d8ffe3SJamie Gritton 	}
319*66d8ffe3SJamie Gritton 
320*66d8ffe3SJamie Gritton 	/* If the user is interested in this event, record it. */
321*66d8ffe3SJamie Gritton 	if (kn->kn_sfflags & event) {
322*66d8ffe3SJamie Gritton 		kn->kn_fflags |= event;
323*66d8ffe3SJamie Gritton 		/* Report the created jail id or attached process id. */
324*66d8ffe3SJamie Gritton 		if (event == NOTE_JAIL_CHILD || event == NOTE_JAIL_ATTACH) {
325*66d8ffe3SJamie Gritton 			if (kn->kn_data != 0)
326*66d8ffe3SJamie Gritton 				kn->kn_fflags |= NOTE_JAIL_MULTI;
327*66d8ffe3SJamie Gritton 			kn->kn_data = (kn->kn_fflags & NOTE_JAIL_MULTI) ? 0U :
328*66d8ffe3SJamie Gritton 			    (u_int)hint & ~event;
329*66d8ffe3SJamie Gritton 		}
330*66d8ffe3SJamie Gritton 	}
331*66d8ffe3SJamie Gritton 
332*66d8ffe3SJamie Gritton 	/* Prison is gone, so flag the event as finished. */
333*66d8ffe3SJamie Gritton 	if (event == NOTE_JAIL_REMOVE) {
334*66d8ffe3SJamie Gritton 		kn->kn_flags |= EV_EOF | EV_ONESHOT;
335*66d8ffe3SJamie Gritton 		if (kn->kn_fflags == 0)
336*66d8ffe3SJamie Gritton 			kn->kn_flags |= EV_DROP;
337*66d8ffe3SJamie Gritton 		return (1);
338*66d8ffe3SJamie Gritton 	}
339*66d8ffe3SJamie Gritton 
340*66d8ffe3SJamie Gritton 	return (kn->kn_fflags != 0);
341*66d8ffe3SJamie Gritton }
342*66d8ffe3SJamie Gritton 
343*66d8ffe3SJamie Gritton static const struct filterops jaildesc_kqops = {
344*66d8ffe3SJamie Gritton 	.f_isfd = 1,
345*66d8ffe3SJamie Gritton 	.f_detach = jaildesc_kqops_detach,
346*66d8ffe3SJamie Gritton 	.f_event = jaildesc_kqops_event,
347*66d8ffe3SJamie Gritton };
348*66d8ffe3SJamie Gritton 
349*66d8ffe3SJamie Gritton static int
jaildesc_kqfilter(struct file * fp,struct knote * kn)350*66d8ffe3SJamie Gritton jaildesc_kqfilter(struct file *fp, struct knote *kn)
351*66d8ffe3SJamie Gritton {
352*66d8ffe3SJamie Gritton 	struct jaildesc *jd;
353*66d8ffe3SJamie Gritton 
354*66d8ffe3SJamie Gritton 	jd = fp->f_data;
355*66d8ffe3SJamie Gritton 	switch (kn->kn_filter) {
356*66d8ffe3SJamie Gritton 	case EVFILT_JAILDESC:
357*66d8ffe3SJamie Gritton 		kn->kn_fop = &jaildesc_kqops;
358*66d8ffe3SJamie Gritton 		kn->kn_flags |= EV_CLEAR;
359*66d8ffe3SJamie Gritton 		knlist_add(&jd->jd_selinfo.si_note, kn, 0);
360*66d8ffe3SJamie Gritton 		return (0);
361*66d8ffe3SJamie Gritton 	default:
362*66d8ffe3SJamie Gritton 		return (EINVAL);
363*66d8ffe3SJamie Gritton 	}
364*66d8ffe3SJamie Gritton }
365*66d8ffe3SJamie Gritton 
366*66d8ffe3SJamie Gritton static int
jaildesc_stat(struct file * fp,struct stat * sb,struct ucred * active_cred)367851dc7f8SJamie Gritton jaildesc_stat(struct file *fp, struct stat *sb, struct ucred *active_cred)
368851dc7f8SJamie Gritton {
369851dc7f8SJamie Gritton 	struct jaildesc *jd;
370851dc7f8SJamie Gritton 
371851dc7f8SJamie Gritton 	bzero(sb, sizeof(struct stat));
372851dc7f8SJamie Gritton 	jd = fp->f_data;
373851dc7f8SJamie Gritton 	JAILDESC_LOCK(jd);
374851dc7f8SJamie Gritton 	if (jd->jd_prison != NULL) {
375d81b337dSJamie Gritton 		sb->st_ino = jd->jd_prison->pr_id;
376d81b337dSJamie Gritton 		sb->st_mode = S_IFREG | S_IRWXU;
377851dc7f8SJamie Gritton 	} else
378851dc7f8SJamie Gritton 		sb->st_mode = S_IFREG;
379851dc7f8SJamie Gritton 	JAILDESC_UNLOCK(jd);
380851dc7f8SJamie Gritton 	return (0);
381851dc7f8SJamie Gritton }
382851dc7f8SJamie Gritton 
383851dc7f8SJamie Gritton static int
jaildesc_fill_kinfo(struct file * fp,struct kinfo_file * kif,struct filedesc * fdp)384851dc7f8SJamie Gritton jaildesc_fill_kinfo(struct file *fp, struct kinfo_file *kif,
385851dc7f8SJamie Gritton     struct filedesc *fdp)
386851dc7f8SJamie Gritton {
387d81b337dSJamie Gritton 	struct jaildesc *jd;
388d81b337dSJamie Gritton 
389d81b337dSJamie Gritton 	jd = fp->f_data;
390d81b337dSJamie Gritton 	kif->kf_type = KF_TYPE_JAILDESC;
391d81b337dSJamie Gritton 	kif->kf_un.kf_jail.kf_jid = jd->jd_prison ? jd->jd_prison->pr_id : 0;
392d81b337dSJamie Gritton 	return (0);
393851dc7f8SJamie Gritton }
394851dc7f8SJamie Gritton 
395851dc7f8SJamie Gritton static int
jaildesc_cmp(struct file * fp1,struct file * fp2,struct thread * td)396851dc7f8SJamie Gritton jaildesc_cmp(struct file *fp1, struct file *fp2, struct thread *td)
397851dc7f8SJamie Gritton {
398851dc7f8SJamie Gritton 	struct jaildesc *jd1, *jd2;
399851dc7f8SJamie Gritton 	int jid1, jid2;
400851dc7f8SJamie Gritton 
401851dc7f8SJamie Gritton 	if (fp2->f_type != DTYPE_JAILDESC)
402851dc7f8SJamie Gritton 		return (3);
403851dc7f8SJamie Gritton 	jd1 = fp1->f_data;
404851dc7f8SJamie Gritton 	JAILDESC_LOCK(jd1);
405851dc7f8SJamie Gritton 	jid1 = jd1->jd_prison ? (uintptr_t)jd1->jd_prison->pr_id : 0;
406851dc7f8SJamie Gritton 	JAILDESC_UNLOCK(jd1);
407851dc7f8SJamie Gritton 	jd2 = fp2->f_data;
408851dc7f8SJamie Gritton 	JAILDESC_LOCK(jd2);
409851dc7f8SJamie Gritton 	jid2 = jd2->jd_prison ? (uintptr_t)jd2->jd_prison->pr_id : 0;
410851dc7f8SJamie Gritton 	JAILDESC_UNLOCK(jd2);
411851dc7f8SJamie Gritton 	return (kcmp_cmp(jid1, jid2));
412851dc7f8SJamie Gritton }
413