]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/cddl/compat/opensolaris/kern/opensolaris_zone.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / cddl / compat / opensolaris / kern / opensolaris_zone.c
1 /*-
2  * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/systm.h>
33 #include <sys/proc.h>
34 #include <sys/lock.h>
35 #include <sys/mutex.h>
36 #include <sys/sx.h>
37 #include <sys/malloc.h>
38 #include <sys/queue.h>
39 #include <sys/jail.h>
40 #include <sys/osd.h>
41 #include <sys/priv.h>
42 #include <sys/zone.h>
43
44 static MALLOC_DEFINE(M_ZONES, "zones_data", "Zones data");
45
46 /*
47  * Structure to record list of ZFS datasets exported to a zone.
48  */
49 typedef struct zone_dataset {
50         LIST_ENTRY(zone_dataset) zd_next;
51         char    zd_dataset[0];
52 } zone_dataset_t;
53
54 LIST_HEAD(zone_dataset_head, zone_dataset);
55
56 static int zone_slot;
57
58 int
59 zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid)
60 {
61         struct zone_dataset_head *head;
62         zone_dataset_t *zd, *zd2;
63         struct prison *pr;
64         int dofree, error;
65
66         if ((error = priv_check_cred(cred, PRIV_ZFS_JAIL, 0)) != 0)
67                 return (error);
68
69         /* Allocate memory before we grab prison's mutex. */
70         zd = malloc(sizeof(*zd) + strlen(dataset) + 1, M_ZONES, M_WAITOK);
71
72         sx_slock(&allprison_lock);
73         pr = prison_find(jailid);       /* Locks &pr->pr_mtx. */
74         sx_sunlock(&allprison_lock);
75         if (pr == NULL) {
76                 free(zd, M_ZONES);
77                 return (ENOENT);
78         }
79
80         head = osd_jail_get(pr, zone_slot);
81         if (head != NULL) {
82                 dofree = 0;
83                 LIST_FOREACH(zd2, head, zd_next) {
84                         if (strcmp(dataset, zd2->zd_dataset) == 0) {
85                                 free(zd, M_ZONES);
86                                 error = EEXIST;
87                                 goto end;
88                         }
89                 }
90         } else {
91                 dofree = 1;
92                 prison_hold_locked(pr);
93                 mtx_unlock(&pr->pr_mtx);
94                 head = malloc(sizeof(*head), M_ZONES, M_WAITOK);
95                 LIST_INIT(head);
96                 mtx_lock(&pr->pr_mtx);
97                 error = osd_jail_set(pr, zone_slot, head);
98                 KASSERT(error == 0, ("osd_jail_set() failed (error=%d)", error));
99         }
100         strcpy(zd->zd_dataset, dataset);
101         LIST_INSERT_HEAD(head, zd, zd_next);
102 end:
103         if (dofree)
104                 prison_free_locked(pr);
105         else
106                 mtx_unlock(&pr->pr_mtx);
107         return (error);
108 }
109
110 int
111 zone_dataset_detach(struct ucred *cred, const char *dataset, int jailid)
112 {
113         struct zone_dataset_head *head;
114         zone_dataset_t *zd;
115         struct prison *pr;
116         int error;
117
118         if ((error = priv_check_cred(cred, PRIV_ZFS_JAIL, 0)) != 0)
119                 return (error);
120
121         sx_slock(&allprison_lock);
122         pr = prison_find(jailid);
123         sx_sunlock(&allprison_lock);
124         if (pr == NULL)
125                 return (ENOENT);
126         head = osd_jail_get(pr, zone_slot);
127         if (head == NULL) {
128                 error = ENOENT;
129                 goto end;
130         }
131         LIST_FOREACH(zd, head, zd_next) {
132                 if (strcmp(dataset, zd->zd_dataset) == 0)
133                         break;
134         }
135         if (zd == NULL)
136                 error = ENOENT;
137         else {
138                 LIST_REMOVE(zd, zd_next);
139                 free(zd, M_ZONES);
140                 if (LIST_EMPTY(head))
141                         osd_jail_del(pr, zone_slot);
142                 error = 0;
143         }
144 end:
145         mtx_unlock(&pr->pr_mtx);
146         return (error);
147 }
148
149 /*
150  * Returns true if the named dataset is visible in the current zone.
151  * The 'write' parameter is set to 1 if the dataset is also writable.
152  */
153 int
154 zone_dataset_visible(const char *dataset, int *write)
155 {
156         struct zone_dataset_head *head;
157         zone_dataset_t *zd;
158         struct prison *pr;
159         size_t len;
160         int ret = 0;
161
162         if (dataset[0] == '\0')
163                 return (0);
164         if (INGLOBALZONE(curthread)) {
165                 if (write != NULL)
166                         *write = 1;
167                 return (1);
168         }
169         pr = curthread->td_ucred->cr_prison;
170         mtx_lock(&pr->pr_mtx);
171         head = osd_jail_get(pr, zone_slot);
172         if (head == NULL)
173                 goto end;
174
175         /*
176          * Walk the list once, looking for datasets which match exactly, or
177          * specify a dataset underneath an exported dataset.  If found, return
178          * true and note that it is writable.
179          */
180         LIST_FOREACH(zd, head, zd_next) {
181                 len = strlen(zd->zd_dataset);
182                 if (strlen(dataset) >= len &&
183                     bcmp(dataset, zd->zd_dataset, len) == 0 &&
184                     (dataset[len] == '\0' || dataset[len] == '/' ||
185                     dataset[len] == '@')) {
186                         if (write)
187                                 *write = 1;
188                         ret = 1;
189                         goto end;
190                 }
191         }
192
193         /*
194          * Walk the list a second time, searching for datasets which are parents
195          * of exported datasets.  These should be visible, but read-only.
196          *
197          * Note that we also have to support forms such as 'pool/dataset/', with
198          * a trailing slash.
199          */
200         LIST_FOREACH(zd, head, zd_next) {
201                 len = strlen(dataset);
202                 if (dataset[len - 1] == '/')
203                         len--;  /* Ignore trailing slash */
204                 if (len < strlen(zd->zd_dataset) &&
205                     bcmp(dataset, zd->zd_dataset, len) == 0 &&
206                     zd->zd_dataset[len] == '/') {
207                         if (write)
208                                 *write = 0;
209                         ret = 1;
210                         goto end;
211                 }
212         }
213 end:
214         mtx_unlock(&pr->pr_mtx);
215         return (ret);
216 }
217
218 static void
219 zone_destroy(void *arg)
220 {
221         struct zone_dataset_head *head;
222         zone_dataset_t *zd;
223
224         head = arg;
225         while ((zd = LIST_FIRST(head)) != NULL) {
226                 LIST_REMOVE(zd, zd_next);
227                 free(zd, M_ZONES);
228         }
229         free(head, M_ZONES);
230 }
231
232 uint32_t
233 zone_get_hostid(void *ptr)
234 {
235
236         KASSERT(ptr == NULL, ("only NULL pointer supported in %s", __func__));
237
238         return ((uint32_t)curthread->td_ucred->cr_prison->pr_hostid);
239 }
240
241 static void
242 zone_sysinit(void *arg __unused)
243 {
244
245         zone_slot = osd_jail_register(zone_destroy, NULL);
246 }
247
248 static void
249 zone_sysuninit(void *arg __unused)
250 {
251
252         osd_jail_deregister(zone_slot);
253 }
254
255 SYSINIT(zone_sysinit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysinit, NULL);
256 SYSUNINIT(zone_sysuninit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysuninit, NULL);