1168404Spjd/*-
2168404Spjd * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3168404Spjd * All rights reserved.
4168404Spjd *
5168404Spjd * Redistribution and use in source and binary forms, with or without
6168404Spjd * modification, are permitted provided that the following conditions
7168404Spjd * are met:
8168404Spjd * 1. Redistributions of source code must retain the above copyright
9168404Spjd *    notice, this list of conditions and the following disclaimer.
10168404Spjd * 2. Redistributions in binary form must reproduce the above copyright
11168404Spjd *    notice, this list of conditions and the following disclaimer in the
12168404Spjd *    documentation and/or other materials provided with the distribution.
13168404Spjd *
14168404Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15168404Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16168404Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17168404Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18168404Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19168404Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20168404Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21168404Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22168404Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23168404Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24168404Spjd * SUCH DAMAGE.
25168404Spjd */
26168404Spjd
27168404Spjd#include <sys/cdefs.h>
28168404Spjd__FBSDID("$FreeBSD$");
29168404Spjd
30168404Spjd#include <sys/param.h>
31168404Spjd#include <sys/kernel.h>
32168404Spjd#include <sys/systm.h>
33168404Spjd#include <sys/proc.h>
34168404Spjd#include <sys/lock.h>
35168404Spjd#include <sys/mutex.h>
36168404Spjd#include <sys/sx.h>
37168404Spjd#include <sys/malloc.h>
38168404Spjd#include <sys/queue.h>
39168404Spjd#include <sys/jail.h>
40185029Spjd#include <sys/osd.h>
41168404Spjd#include <sys/priv.h>
42168404Spjd#include <sys/zone.h>
43168404Spjd
44168404Spjdstatic MALLOC_DEFINE(M_ZONES, "zones_data", "Zones data");
45168404Spjd
46168404Spjd/*
47168404Spjd * Structure to record list of ZFS datasets exported to a zone.
48168404Spjd */
49168404Spjdtypedef struct zone_dataset {
50168404Spjd	LIST_ENTRY(zone_dataset) zd_next;
51168404Spjd	char	zd_dataset[0];
52168404Spjd} zone_dataset_t;
53168404Spjd
54168404SpjdLIST_HEAD(zone_dataset_head, zone_dataset);
55168404Spjd
56185029Spjdstatic int zone_slot;
57168404Spjd
58168404Spjdint
59168404Spjdzone_dataset_attach(struct ucred *cred, const char *dataset, int jailid)
60168404Spjd{
61168404Spjd	struct zone_dataset_head *head;
62168404Spjd	zone_dataset_t *zd, *zd2;
63168404Spjd	struct prison *pr;
64185029Spjd	int dofree, error;
65168404Spjd
66168404Spjd	if ((error = priv_check_cred(cred, PRIV_ZFS_JAIL, 0)) != 0)
67168404Spjd		return (error);
68168404Spjd
69168404Spjd	/* Allocate memory before we grab prison's mutex. */
70168404Spjd	zd = malloc(sizeof(*zd) + strlen(dataset) + 1, M_ZONES, M_WAITOK);
71168404Spjd
72168404Spjd	sx_slock(&allprison_lock);
73168404Spjd	pr = prison_find(jailid);	/* Locks &pr->pr_mtx. */
74168404Spjd	sx_sunlock(&allprison_lock);
75168404Spjd	if (pr == NULL) {
76168404Spjd		free(zd, M_ZONES);
77168404Spjd		return (ENOENT);
78168404Spjd	}
79168404Spjd
80185029Spjd	head = osd_jail_get(pr, zone_slot);
81185029Spjd	if (head != NULL) {
82185029Spjd		dofree = 0;
83185029Spjd		LIST_FOREACH(zd2, head, zd_next) {
84185029Spjd			if (strcmp(dataset, zd2->zd_dataset) == 0) {
85185029Spjd				free(zd, M_ZONES);
86185029Spjd				error = EEXIST;
87185029Spjd				goto end;
88185029Spjd			}
89168404Spjd		}
90185029Spjd	} else {
91185029Spjd		dofree = 1;
92185029Spjd		prison_hold_locked(pr);
93185029Spjd		mtx_unlock(&pr->pr_mtx);
94185029Spjd		head = malloc(sizeof(*head), M_ZONES, M_WAITOK);
95185029Spjd		LIST_INIT(head);
96185029Spjd		mtx_lock(&pr->pr_mtx);
97185029Spjd		error = osd_jail_set(pr, zone_slot, head);
98185029Spjd		KASSERT(error == 0, ("osd_jail_set() failed (error=%d)", error));
99168404Spjd	}
100168404Spjd	strcpy(zd->zd_dataset, dataset);
101168404Spjd	LIST_INSERT_HEAD(head, zd, zd_next);
102185029Spjdend:
103185029Spjd	if (dofree)
104185029Spjd		prison_free_locked(pr);
105185029Spjd	else
106185029Spjd		mtx_unlock(&pr->pr_mtx);
107168404Spjd	return (error);
108168404Spjd}
109168404Spjd
110168404Spjdint
111168404Spjdzone_dataset_detach(struct ucred *cred, const char *dataset, int jailid)
112168404Spjd{
113168404Spjd	struct zone_dataset_head *head;
114168404Spjd	zone_dataset_t *zd;
115168404Spjd	struct prison *pr;
116168404Spjd	int error;
117168404Spjd
118168404Spjd	if ((error = priv_check_cred(cred, PRIV_ZFS_JAIL, 0)) != 0)
119168404Spjd		return (error);
120168404Spjd
121168404Spjd	sx_slock(&allprison_lock);
122168404Spjd	pr = prison_find(jailid);
123168404Spjd	sx_sunlock(&allprison_lock);
124168404Spjd	if (pr == NULL)
125168404Spjd		return (ENOENT);
126185029Spjd	head = osd_jail_get(pr, zone_slot);
127185029Spjd	if (head == NULL) {
128185029Spjd		error = ENOENT;
129185029Spjd		goto end;
130185029Spjd	}
131168404Spjd	LIST_FOREACH(zd, head, zd_next) {
132185029Spjd		if (strcmp(dataset, zd->zd_dataset) == 0)
133185029Spjd			break;
134168404Spjd	}
135185029Spjd	if (zd == NULL)
136185029Spjd		error = ENOENT;
137185029Spjd	else {
138185029Spjd		LIST_REMOVE(zd, zd_next);
139185029Spjd		free(zd, M_ZONES);
140185029Spjd		if (LIST_EMPTY(head))
141185029Spjd			osd_jail_del(pr, zone_slot);
142185029Spjd		error = 0;
143185029Spjd	}
144185029Spjdend:
145168404Spjd	mtx_unlock(&pr->pr_mtx);
146168404Spjd	return (error);
147168404Spjd}
148168404Spjd
149168404Spjd/*
150168404Spjd * Returns true if the named dataset is visible in the current zone.
151168404Spjd * The 'write' parameter is set to 1 if the dataset is also writable.
152168404Spjd */
153168404Spjdint
154168404Spjdzone_dataset_visible(const char *dataset, int *write)
155168404Spjd{
156168404Spjd	struct zone_dataset_head *head;
157168404Spjd	zone_dataset_t *zd;
158168404Spjd	struct prison *pr;
159168404Spjd	size_t len;
160168404Spjd	int ret = 0;
161168404Spjd
162168404Spjd	if (dataset[0] == '\0')
163168404Spjd		return (0);
164185029Spjd	if (INGLOBALZONE(curthread)) {
165168404Spjd		if (write != NULL)
166168404Spjd			*write = 1;
167168404Spjd		return (1);
168168404Spjd	}
169168404Spjd	pr = curthread->td_ucred->cr_prison;
170168404Spjd	mtx_lock(&pr->pr_mtx);
171185029Spjd	head = osd_jail_get(pr, zone_slot);
172185029Spjd	if (head == NULL)
173185029Spjd		goto end;
174168404Spjd
175168404Spjd	/*
176168404Spjd	 * Walk the list once, looking for datasets which match exactly, or
177168404Spjd	 * specify a dataset underneath an exported dataset.  If found, return
178168404Spjd	 * true and note that it is writable.
179168404Spjd	 */
180168404Spjd	LIST_FOREACH(zd, head, zd_next) {
181168404Spjd		len = strlen(zd->zd_dataset);
182168404Spjd		if (strlen(dataset) >= len &&
183168404Spjd		    bcmp(dataset, zd->zd_dataset, len) == 0 &&
184168404Spjd		    (dataset[len] == '\0' || dataset[len] == '/' ||
185168404Spjd		    dataset[len] == '@')) {
186168404Spjd			if (write)
187168404Spjd				*write = 1;
188168404Spjd			ret = 1;
189168404Spjd			goto end;
190168404Spjd		}
191168404Spjd	}
192168404Spjd
193168404Spjd	/*
194168404Spjd	 * Walk the list a second time, searching for datasets which are parents
195168404Spjd	 * of exported datasets.  These should be visible, but read-only.
196168404Spjd	 *
197168404Spjd	 * Note that we also have to support forms such as 'pool/dataset/', with
198168404Spjd	 * a trailing slash.
199168404Spjd	 */
200168404Spjd	LIST_FOREACH(zd, head, zd_next) {
201168404Spjd		len = strlen(dataset);
202168404Spjd		if (dataset[len - 1] == '/')
203168404Spjd			len--;	/* Ignore trailing slash */
204168404Spjd		if (len < strlen(zd->zd_dataset) &&
205168404Spjd		    bcmp(dataset, zd->zd_dataset, len) == 0 &&
206168404Spjd		    zd->zd_dataset[len] == '/') {
207168404Spjd			if (write)
208168404Spjd				*write = 0;
209168404Spjd			ret = 1;
210168404Spjd			goto end;
211168404Spjd		}
212168404Spjd	}
213168404Spjdend:
214168404Spjd	mtx_unlock(&pr->pr_mtx);
215168404Spjd	return (ret);
216168404Spjd}
217168404Spjd
218185029Spjdstatic void
219185029Spjdzone_destroy(void *arg)
220168404Spjd{
221168404Spjd	struct zone_dataset_head *head;
222168404Spjd	zone_dataset_t *zd;
223168404Spjd
224185029Spjd	head = arg;
225185029Spjd        while ((zd = LIST_FIRST(head)) != NULL) {
226185029Spjd                LIST_REMOVE(zd, zd_next);
227185029Spjd                free(zd, M_ZONES);
228185029Spjd        }
229185029Spjd        free(head, M_ZONES);
230168404Spjd}
231168404Spjd
232219089Spjduint32_t
233219089Spjdzone_get_hostid(void *ptr)
234219089Spjd{
235219089Spjd
236219089Spjd	KASSERT(ptr == NULL, ("only NULL pointer supported in %s", __func__));
237219089Spjd
238219089Spjd	return ((uint32_t)curthread->td_ucred->cr_prison->pr_hostid);
239219089Spjd}
240219089Spjd
241168404Spjdstatic void
242168404Spjdzone_sysinit(void *arg __unused)
243168404Spjd{
244168404Spjd
245191673Sjamie	zone_slot = osd_jail_register(zone_destroy, NULL);
246168404Spjd}
247168404Spjd
248168404Spjdstatic void
249168404Spjdzone_sysuninit(void *arg __unused)
250168404Spjd{
251168404Spjd
252185029Spjd	osd_jail_deregister(zone_slot);
253168404Spjd}
254168404Spjd
255168404SpjdSYSINIT(zone_sysinit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysinit, NULL);
256168404SpjdSYSUNINIT(zone_sysuninit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysuninit, NULL);
257