]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/openzfs/module/os/freebsd/spl/spl_zone.c
ZFS: MFV 2.0-rc1-gfd20a8
[FreeBSD/FreeBSD.git] / sys / contrib / openzfs / module / os / freebsd / spl / spl_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/types.h>
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/systm.h>
34 #include <sys/proc.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37 #include <sys/sx.h>
38 #include <sys/malloc.h>
39 #include <sys/queue.h>
40 #include <sys/jail.h>
41 #include <sys/osd.h>
42 #include <sys/priv.h>
43 #include <sys/zone.h>
44
45 #include <sys/policy.h>
46
47 static MALLOC_DEFINE(M_ZONES, "zones_data", "Zones data");
48
49 /*
50  * Structure to record list of ZFS datasets exported to a zone.
51  */
52 typedef struct zone_dataset {
53         LIST_ENTRY(zone_dataset) zd_next;
54         char    zd_dataset[0];
55 } zone_dataset_t;
56
57 LIST_HEAD(zone_dataset_head, zone_dataset);
58
59 static int zone_slot;
60
61 int
62 zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid)
63 {
64         struct zone_dataset_head *head;
65         zone_dataset_t *zd, *zd2;
66         struct prison *pr;
67         int dofree, error;
68
69         if ((error = spl_priv_check_cred(cred, PRIV_ZFS_JAIL)) != 0)
70                 return (error);
71
72         /* Allocate memory before we grab prison's mutex. */
73         zd = malloc(sizeof (*zd) + strlen(dataset) + 1, M_ZONES, M_WAITOK);
74
75         sx_slock(&allprison_lock);
76         pr = prison_find(jailid);       /* Locks &pr->pr_mtx. */
77         sx_sunlock(&allprison_lock);
78         if (pr == NULL) {
79                 free(zd, M_ZONES);
80                 return (ENOENT);
81         }
82
83         head = osd_jail_get(pr, zone_slot);
84         if (head != NULL) {
85                 dofree = 0;
86                 LIST_FOREACH(zd2, head, zd_next) {
87                         if (strcmp(dataset, zd2->zd_dataset) == 0) {
88                                 free(zd, M_ZONES);
89                                 error = EEXIST;
90                                 goto end;
91                         }
92                 }
93         } else {
94                 dofree = 1;
95                 prison_hold_locked(pr);
96                 mtx_unlock(&pr->pr_mtx);
97                 head = malloc(sizeof (*head), M_ZONES, M_WAITOK);
98                 LIST_INIT(head);
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)",
102                     error));
103         }
104         strcpy(zd->zd_dataset, dataset);
105         LIST_INSERT_HEAD(head, zd, zd_next);
106 end:
107         if (dofree)
108                 prison_free_locked(pr);
109         else
110                 mtx_unlock(&pr->pr_mtx);
111         return (error);
112 }
113
114 int
115 zone_dataset_detach(struct ucred *cred, const char *dataset, int jailid)
116 {
117         struct zone_dataset_head *head;
118         zone_dataset_t *zd;
119         struct prison *pr;
120         int error;
121
122         if ((error = spl_priv_check_cred(cred, PRIV_ZFS_JAIL)) != 0)
123                 return (error);
124
125         sx_slock(&allprison_lock);
126         pr = prison_find(jailid);
127         sx_sunlock(&allprison_lock);
128         if (pr == NULL)
129                 return (ENOENT);
130         head = osd_jail_get(pr, zone_slot);
131         if (head == NULL) {
132                 error = ENOENT;
133                 goto end;
134         }
135         LIST_FOREACH(zd, head, zd_next) {
136                 if (strcmp(dataset, zd->zd_dataset) == 0)
137                         break;
138         }
139         if (zd == NULL)
140                 error = ENOENT;
141         else {
142                 LIST_REMOVE(zd, zd_next);
143                 free(zd, M_ZONES);
144                 if (LIST_EMPTY(head))
145                         osd_jail_del(pr, zone_slot);
146                 error = 0;
147         }
148 end:
149         mtx_unlock(&pr->pr_mtx);
150         return (error);
151 }
152
153 /*
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.
156  */
157 int
158 zone_dataset_visible(const char *dataset, int *write)
159 {
160         struct zone_dataset_head *head;
161         zone_dataset_t *zd;
162         struct prison *pr;
163         size_t len;
164         int ret = 0;
165
166         if (dataset[0] == '\0')
167                 return (0);
168         if (INGLOBALZONE(curproc)) {
169                 if (write != NULL)
170                         *write = 1;
171                 return (1);
172         }
173         pr = curthread->td_ucred->cr_prison;
174         mtx_lock(&pr->pr_mtx);
175         head = osd_jail_get(pr, zone_slot);
176         if (head == NULL)
177                 goto end;
178
179         /*
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.
183          */
184         LIST_FOREACH(zd, head, zd_next) {
185                 len = strlen(zd->zd_dataset);
186                 if (strlen(dataset) >= len &&
187                     bcmp(dataset, zd->zd_dataset, len) == 0 &&
188                     (dataset[len] == '\0' || dataset[len] == '/' ||
189                     dataset[len] == '@')) {
190                         if (write)
191                                 *write = 1;
192                         ret = 1;
193                         goto end;
194                 }
195         }
196
197         /*
198          * Walk the list a second time, searching for datasets which are parents
199          * of exported datasets.  These should be visible, but read-only.
200          *
201          * Note that we also have to support forms such as 'pool/dataset/', with
202          * a trailing slash.
203          */
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                     bcmp(dataset, zd->zd_dataset, len) == 0 &&
210                     zd->zd_dataset[len] == '/') {
211                         if (write)
212                                 *write = 0;
213                         ret = 1;
214                         goto end;
215                 }
216         }
217 end:
218         mtx_unlock(&pr->pr_mtx);
219         return (ret);
220 }
221
222 static void
223 zone_destroy(void *arg)
224 {
225         struct zone_dataset_head *head;
226         zone_dataset_t *zd;
227
228         head = arg;
229         while ((zd = LIST_FIRST(head)) != NULL) {
230                 LIST_REMOVE(zd, zd_next);
231                 free(zd, M_ZONES);
232         }
233         free(head, M_ZONES);
234 }
235
236 uint32_t
237 zone_get_hostid(void *ptr)
238 {
239
240         KASSERT(ptr == NULL, ("only NULL pointer supported in %s", __func__));
241
242         return ((uint32_t)curthread->td_ucred->cr_prison->pr_hostid);
243 }
244
245 static void
246 zone_sysinit(void *arg __unused)
247 {
248
249         zone_slot = osd_jail_register(zone_destroy, NULL);
250 }
251
252 static void
253 zone_sysuninit(void *arg __unused)
254 {
255
256         osd_jail_deregister(zone_slot);
257 }
258
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);