]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/cddl/compat/opensolaris/kern/opensolaris_zone.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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/priv.h>
41 #include <sys/zone.h>
42
43 static MALLOC_DEFINE(M_ZONES, "zones_data", "Zones data");
44
45 /*
46  * Structure to record list of ZFS datasets exported to a zone.
47  */
48 typedef struct zone_dataset {
49         LIST_ENTRY(zone_dataset) zd_next;
50         char    zd_dataset[0];
51 } zone_dataset_t;
52
53 LIST_HEAD(zone_dataset_head, zone_dataset);
54
55 static struct prison_service *zone_prison_service = NULL;
56
57 int
58 zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid)
59 {
60         struct zone_dataset_head *head;
61         zone_dataset_t *zd, *zd2;
62         struct prison *pr;
63         int error;
64
65         if ((error = priv_check_cred(cred, PRIV_ZFS_JAIL, 0)) != 0)
66                 return (error);
67
68         /* Allocate memory before we grab prison's mutex. */
69         zd = malloc(sizeof(*zd) + strlen(dataset) + 1, M_ZONES, M_WAITOK);
70
71         sx_slock(&allprison_lock);
72         pr = prison_find(jailid);       /* Locks &pr->pr_mtx. */
73         sx_sunlock(&allprison_lock);
74         if (pr == NULL) {
75                 free(zd, M_ZONES);
76                 return (ENOENT);
77         }
78
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) {
82                         free(zd, M_ZONES);
83                         error = EEXIST;
84                         goto failure;
85                 }
86         }
87         strcpy(zd->zd_dataset, dataset);
88         LIST_INSERT_HEAD(head, zd, zd_next);
89 failure:
90         mtx_unlock(&pr->pr_mtx);
91         return (error);
92 }
93
94 int
95 zone_dataset_detach(struct ucred *cred, const char *dataset, int jailid)
96 {
97         struct zone_dataset_head *head;
98         zone_dataset_t *zd;
99         struct prison *pr;
100         int error;
101
102         if ((error = priv_check_cred(cred, PRIV_ZFS_JAIL, 0)) != 0)
103                 return (error);
104
105         sx_slock(&allprison_lock);
106         pr = prison_find(jailid);
107         sx_sunlock(&allprison_lock);
108         if (pr == NULL)
109                 return (ENOENT);
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);
114                         free(zd, M_ZONES);
115                         goto success;
116                 }
117         }
118         error = ENOENT;
119 success:
120         mtx_unlock(&pr->pr_mtx);
121         return (error);
122 }
123
124 /*
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.
127  */
128 int
129 zone_dataset_visible(const char *dataset, int *write)
130 {
131         struct zone_dataset_head *head;
132         zone_dataset_t *zd;
133         struct prison *pr;
134         size_t len;
135         int ret = 0;
136
137         if (dataset[0] == '\0')
138                 return (0);
139         if (INGLOBALZONE(curproc)) {
140                 if (write != NULL)
141                         *write = 1;
142                 return (1);
143         }
144         pr = curthread->td_ucred->cr_prison;
145         mtx_lock(&pr->pr_mtx);
146         head = prison_service_data_get(zone_prison_service, pr);
147
148         /*
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.
152          */
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] == '@')) {
159                         if (write)
160                                 *write = 1;
161                         ret = 1;
162                         goto end;
163                 }
164         }
165
166         /*
167          * Walk the list a second time, searching for datasets which are parents
168          * of exported datasets.  These should be visible, but read-only.
169          *
170          * Note that we also have to support forms such as 'pool/dataset/', with
171          * a trailing slash.
172          */
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] == '/') {
180                         if (write)
181                                 *write = 0;
182                         ret = 1;
183                         goto end;
184                 }
185         }
186 end:
187         mtx_unlock(&pr->pr_mtx);
188         return (ret);
189 }
190
191 static int
192 zone_create(struct prison_service *psrv, struct prison *pr)
193 {
194         struct zone_dataset_head *head;
195
196         head = malloc(sizeof(*head), M_ZONES, M_WAITOK);
197         LIST_INIT(head);
198         mtx_lock(&pr->pr_mtx);
199         prison_service_data_set(psrv, pr, head);
200         mtx_unlock(&pr->pr_mtx);
201         return (0);
202 }
203
204 static int
205 zone_destroy(struct prison_service *psrv, struct prison *pr)
206 {
207         struct zone_dataset_head *head;
208         zone_dataset_t *zd;
209
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);
215                 free(zd, M_ZONES);
216         }
217         free(head, M_ZONES);
218         return (0);
219 }
220
221 static void
222 zone_sysinit(void *arg __unused)
223 {
224
225         zone_prison_service = prison_service_register("zfs", zone_create,
226             zone_destroy);
227 }
228
229 static void
230 zone_sysuninit(void *arg __unused)
231 {
232
233         prison_service_deregister(zone_prison_service);
234 }
235
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);