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/param.h>
31 #include <sys/kernel.h>
32 #include <sys/systm.h>
35 #include <sys/mutex.h>
37 #include <sys/malloc.h>
38 #include <sys/queue.h>
43 static MALLOC_DEFINE(M_ZONES, "zones_data", "Zones data");
46 * Structure to record list of ZFS datasets exported to a zone.
48 typedef struct zone_dataset {
49 LIST_ENTRY(zone_dataset) zd_next;
53 LIST_HEAD(zone_dataset_head, zone_dataset);
55 static struct prison_service *zone_prison_service = NULL;
58 zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid)
60 struct zone_dataset_head *head;
61 zone_dataset_t *zd, *zd2;
65 if ((error = priv_check_cred(cred, PRIV_ZFS_JAIL, 0)) != 0)
68 /* Allocate memory before we grab prison's mutex. */
69 zd = malloc(sizeof(*zd) + strlen(dataset) + 1, M_ZONES, M_WAITOK);
71 sx_slock(&allprison_lock);
72 pr = prison_find(jailid); /* Locks &pr->pr_mtx. */
73 sx_sunlock(&allprison_lock);
79 head = prison_service_data_get(zone_prison_service, pr);
80 LIST_FOREACH(zd2, head, zd_next) {
81 if (strcmp(dataset, zd2->zd_dataset) == 0) {
87 strcpy(zd->zd_dataset, dataset);
88 LIST_INSERT_HEAD(head, zd, zd_next);
90 mtx_unlock(&pr->pr_mtx);
95 zone_dataset_detach(struct ucred *cred, const char *dataset, int jailid)
97 struct zone_dataset_head *head;
102 if ((error = priv_check_cred(cred, PRIV_ZFS_JAIL, 0)) != 0)
105 sx_slock(&allprison_lock);
106 pr = prison_find(jailid);
107 sx_sunlock(&allprison_lock);
110 head = prison_service_data_get(zone_prison_service, pr);
111 LIST_FOREACH(zd, head, zd_next) {
112 if (strcmp(dataset, zd->zd_dataset) == 0) {
113 LIST_REMOVE(zd, zd_next);
120 mtx_unlock(&pr->pr_mtx);
125 * Returns true if the named dataset is visible in the current zone.
126 * The 'write' parameter is set to 1 if the dataset is also writable.
129 zone_dataset_visible(const char *dataset, int *write)
131 struct zone_dataset_head *head;
137 if (dataset[0] == '\0')
139 if (INGLOBALZONE(curproc)) {
144 pr = curthread->td_ucred->cr_prison;
145 mtx_lock(&pr->pr_mtx);
146 head = prison_service_data_get(zone_prison_service, pr);
149 * Walk the list once, looking for datasets which match exactly, or
150 * specify a dataset underneath an exported dataset. If found, return
151 * true and note that it is writable.
153 LIST_FOREACH(zd, head, zd_next) {
154 len = strlen(zd->zd_dataset);
155 if (strlen(dataset) >= len &&
156 bcmp(dataset, zd->zd_dataset, len) == 0 &&
157 (dataset[len] == '\0' || dataset[len] == '/' ||
158 dataset[len] == '@')) {
167 * Walk the list a second time, searching for datasets which are parents
168 * of exported datasets. These should be visible, but read-only.
170 * Note that we also have to support forms such as 'pool/dataset/', with
173 LIST_FOREACH(zd, head, zd_next) {
174 len = strlen(dataset);
175 if (dataset[len - 1] == '/')
176 len--; /* Ignore trailing slash */
177 if (len < strlen(zd->zd_dataset) &&
178 bcmp(dataset, zd->zd_dataset, len) == 0 &&
179 zd->zd_dataset[len] == '/') {
187 mtx_unlock(&pr->pr_mtx);
192 zone_create(struct prison_service *psrv, struct prison *pr)
194 struct zone_dataset_head *head;
196 head = malloc(sizeof(*head), M_ZONES, M_WAITOK);
198 mtx_lock(&pr->pr_mtx);
199 prison_service_data_set(psrv, pr, head);
200 mtx_unlock(&pr->pr_mtx);
205 zone_destroy(struct prison_service *psrv, struct prison *pr)
207 struct zone_dataset_head *head;
210 mtx_lock(&pr->pr_mtx);
211 head = prison_service_data_del(psrv, pr);
212 mtx_unlock(&pr->pr_mtx);
213 while ((zd = LIST_FIRST(head)) != NULL) {
214 LIST_REMOVE(zd, zd_next);
222 zone_sysinit(void *arg __unused)
225 zone_prison_service = prison_service_register("zfs", zone_create,
230 zone_sysuninit(void *arg __unused)
233 prison_service_deregister(zone_prison_service);
236 SYSINIT(zone_sysinit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysinit, NULL);
237 SYSUNINIT(zone_sysuninit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysuninit, NULL);