Handle possible null pointers from malloc/strdup/strndup()
[zfs.git] / module / os / freebsd / spl / spl_zone.c
blob658ef0bf056d91d3d69f6d023f7fc5a30b94521e
1 /*
2 * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3 * All rights reserved.
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.
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.
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>
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>
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;
54 char zd_dataset[0];
55 } zone_dataset_t;
57 LIST_HEAD(zone_dataset_head, zone_dataset);
59 static int zone_slot;
61 int
62 zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid)
64 struct zone_dataset_head *head;
65 zone_dataset_t *zd, *zd2;
66 struct prison *pr;
67 int dofree, error;
69 if ((error = spl_priv_check_cred(cred, PRIV_ZFS_JAIL)) != 0)
70 return (error);
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);
78 if (pr == NULL) {
79 free(zd, M_ZONES);
80 return (ENOENT);
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;
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));
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);
115 zone_dataset_detach(struct ucred *cred, const char *dataset, int jailid)
117 struct zone_dataset_head *head;
118 zone_dataset_t *zd;
119 struct prison *pr;
120 int error;
122 if ((error = spl_priv_check_cred(cred, PRIV_ZFS_JAIL)) != 0)
123 return (error);
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;
135 LIST_FOREACH(zd, head, zd_next) {
136 if (strcmp(dataset, zd->zd_dataset) == 0)
137 break;
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;
148 end:
149 mtx_unlock(&pr->pr_mtx);
150 return (error);
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;
161 zone_dataset_t *zd;
162 struct prison *pr;
163 size_t len;
164 int ret = 0;
166 if (dataset[0] == '\0')
167 return (0);
168 if (INGLOBALZONE(curproc)) {
169 if (write != NULL)
170 *write = 1;
171 return (1);
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;
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] == '@')) {
190 if (write)
191 *write = 1;
192 ret = 1;
193 goto end;
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
202 * a trailing slash.
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] == '/') {
211 if (write)
212 *write = 0;
213 ret = 1;
214 goto end;
217 end:
218 mtx_unlock(&pr->pr_mtx);
219 return (ret);
222 static void
223 zone_destroy(void *arg)
225 struct zone_dataset_head *head;
226 zone_dataset_t *zd;
228 head = arg;
229 while ((zd = LIST_FIRST(head)) != NULL) {
230 LIST_REMOVE(zd, zd_next);
231 free(zd, M_ZONES);
233 free(head, M_ZONES);
236 uint32_t
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);
245 static void
246 zone_sysinit(void *arg __unused)
249 zone_slot = osd_jail_register(zone_destroy, NULL);
252 static void
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);