2 * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/systm.h>
36 #include <sys/mutex.h>
38 #include <sys/malloc.h>
39 #include <sys/queue.h>
45 #include <sys/policy.h>
47 static MALLOC_DEFINE(M_ZONES
, "zones_data", "Zones data");
50 * Structure to record list of ZFS datasets exported to a zone.
52 typedef struct zone_dataset
{
53 LIST_ENTRY(zone_dataset
) zd_next
;
57 LIST_HEAD(zone_dataset_head
, zone_dataset
);
62 zone_dataset_attach(struct ucred
*cred
, const char *dataset
, int jailid
)
64 struct zone_dataset_head
*head
;
65 zone_dataset_t
*zd
, *zd2
;
69 if ((error
= spl_priv_check_cred(cred
, PRIV_ZFS_JAIL
)) != 0)
72 /* Allocate memory before we grab prison's mutex. */
73 zd
= malloc(sizeof (*zd
) + strlen(dataset
) + 1, M_ZONES
, M_WAITOK
);
75 sx_slock(&allprison_lock
);
76 pr
= prison_find(jailid
); /* Locks &pr->pr_mtx. */
77 sx_sunlock(&allprison_lock
);
83 head
= osd_jail_get(pr
, zone_slot
);
86 LIST_FOREACH(zd2
, head
, zd_next
) {
87 if (strcmp(dataset
, zd2
->zd_dataset
) == 0) {
95 prison_hold_locked(pr
);
96 mtx_unlock(&pr
->pr_mtx
);
97 head
= malloc(sizeof (*head
), M_ZONES
, M_WAITOK
);
99 mtx_lock(&pr
->pr_mtx
);
100 error
= osd_jail_set(pr
, zone_slot
, head
);
101 KASSERT(error
== 0, ("osd_jail_set() failed (error=%d)",
104 strcpy(zd
->zd_dataset
, dataset
);
105 LIST_INSERT_HEAD(head
, zd
, zd_next
);
108 prison_free_locked(pr
);
110 mtx_unlock(&pr
->pr_mtx
);
115 zone_dataset_detach(struct ucred
*cred
, const char *dataset
, int jailid
)
117 struct zone_dataset_head
*head
;
122 if ((error
= spl_priv_check_cred(cred
, PRIV_ZFS_JAIL
)) != 0)
125 sx_slock(&allprison_lock
);
126 pr
= prison_find(jailid
);
127 sx_sunlock(&allprison_lock
);
130 head
= osd_jail_get(pr
, zone_slot
);
135 LIST_FOREACH(zd
, head
, zd_next
) {
136 if (strcmp(dataset
, zd
->zd_dataset
) == 0)
142 LIST_REMOVE(zd
, zd_next
);
144 if (LIST_EMPTY(head
))
145 osd_jail_del(pr
, zone_slot
);
149 mtx_unlock(&pr
->pr_mtx
);
154 * Returns true if the named dataset is visible in the current zone.
155 * The 'write' parameter is set to 1 if the dataset is also writable.
158 zone_dataset_visible(const char *dataset
, int *write
)
160 struct zone_dataset_head
*head
;
166 if (dataset
[0] == '\0')
168 if (INGLOBALZONE(curproc
)) {
173 pr
= curthread
->td_ucred
->cr_prison
;
174 mtx_lock(&pr
->pr_mtx
);
175 head
= osd_jail_get(pr
, zone_slot
);
180 * Walk the list once, looking for datasets which match exactly, or
181 * specify a dataset underneath an exported dataset. If found, return
182 * true and note that it is writable.
184 LIST_FOREACH(zd
, head
, zd_next
) {
185 len
= strlen(zd
->zd_dataset
);
186 if (strlen(dataset
) >= len
&&
187 memcmp(dataset
, zd
->zd_dataset
, len
) == 0 &&
188 (dataset
[len
] == '\0' || dataset
[len
] == '/' ||
189 dataset
[len
] == '@')) {
198 * Walk the list a second time, searching for datasets which are parents
199 * of exported datasets. These should be visible, but read-only.
201 * Note that we also have to support forms such as 'pool/dataset/', with
204 LIST_FOREACH(zd
, head
, zd_next
) {
205 len
= strlen(dataset
);
206 if (dataset
[len
- 1] == '/')
207 len
--; /* Ignore trailing slash */
208 if (len
< strlen(zd
->zd_dataset
) &&
209 memcmp(dataset
, zd
->zd_dataset
, len
) == 0 &&
210 zd
->zd_dataset
[len
] == '/') {
218 mtx_unlock(&pr
->pr_mtx
);
223 zone_destroy(void *arg
)
225 struct zone_dataset_head
*head
;
229 while ((zd
= LIST_FIRST(head
)) != NULL
) {
230 LIST_REMOVE(zd
, zd_next
);
237 zone_get_hostid(void *ptr
)
240 KASSERT(ptr
== NULL
, ("only NULL pointer supported in %s", __func__
));
242 return ((uint32_t)curthread
->td_ucred
->cr_prison
->pr_hostid
);
246 zone_sysinit(void *arg __unused
)
249 zone_slot
= osd_jail_register(zone_destroy
, NULL
);
253 zone_sysuninit(void *arg __unused
)
256 osd_jail_deregister(zone_slot
);
259 SYSINIT(zone_sysinit
, SI_SUB_DRIVERS
, SI_ORDER_ANY
, zone_sysinit
, NULL
);
260 SYSUNINIT(zone_sysuninit
, SI_SUB_DRIVERS
, SI_ORDER_ANY
, zone_sysuninit
, NULL
);