mirror of
				https://git.proxmox.com/git/mirror_zfs
				synced 2025-10-31 20:25:43 +00:00 
			
		
		
		
	 861166b027
			
		
	
	
		861166b027
		
	
	
	
	
		
			
			bcopy() has a confusing argument order and is actually a move, not a copy; they're all deprecated since POSIX.1-2001 and removed in -2008, and we shim them out to mem*() on Linux anyway Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz> Closes #12996
		
			
				
	
	
		
			261 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 | |
|  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | |
|  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 | |
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | |
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | |
|  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | |
|  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | |
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | |
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|  * SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include <sys/cdefs.h>
 | |
| __FBSDID("$FreeBSD$");
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <sys/param.h>
 | |
| #include <sys/kernel.h>
 | |
| #include <sys/systm.h>
 | |
| #include <sys/proc.h>
 | |
| #include <sys/lock.h>
 | |
| #include <sys/mutex.h>
 | |
| #include <sys/sx.h>
 | |
| #include <sys/malloc.h>
 | |
| #include <sys/queue.h>
 | |
| #include <sys/jail.h>
 | |
| #include <sys/osd.h>
 | |
| #include <sys/priv.h>
 | |
| #include <sys/zone.h>
 | |
| 
 | |
| #include <sys/policy.h>
 | |
| 
 | |
| static MALLOC_DEFINE(M_ZONES, "zones_data", "Zones data");
 | |
| 
 | |
| /*
 | |
|  * Structure to record list of ZFS datasets exported to a zone.
 | |
|  */
 | |
| typedef struct zone_dataset {
 | |
| 	LIST_ENTRY(zone_dataset) zd_next;
 | |
| 	char	zd_dataset[0];
 | |
| } zone_dataset_t;
 | |
| 
 | |
| LIST_HEAD(zone_dataset_head, zone_dataset);
 | |
| 
 | |
| static int zone_slot;
 | |
| 
 | |
| int
 | |
| zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid)
 | |
| {
 | |
| 	struct zone_dataset_head *head;
 | |
| 	zone_dataset_t *zd, *zd2;
 | |
| 	struct prison *pr;
 | |
| 	int dofree, error;
 | |
| 
 | |
| 	if ((error = spl_priv_check_cred(cred, PRIV_ZFS_JAIL)) != 0)
 | |
| 		return (error);
 | |
| 
 | |
| 	/* Allocate memory before we grab prison's mutex. */
 | |
| 	zd = malloc(sizeof (*zd) + strlen(dataset) + 1, M_ZONES, M_WAITOK);
 | |
| 
 | |
| 	sx_slock(&allprison_lock);
 | |
| 	pr = prison_find(jailid);	/* Locks &pr->pr_mtx. */
 | |
| 	sx_sunlock(&allprison_lock);
 | |
| 	if (pr == NULL) {
 | |
| 		free(zd, M_ZONES);
 | |
| 		return (ENOENT);
 | |
| 	}
 | |
| 
 | |
| 	head = osd_jail_get(pr, zone_slot);
 | |
| 	if (head != NULL) {
 | |
| 		dofree = 0;
 | |
| 		LIST_FOREACH(zd2, head, zd_next) {
 | |
| 			if (strcmp(dataset, zd2->zd_dataset) == 0) {
 | |
| 				free(zd, M_ZONES);
 | |
| 				error = EEXIST;
 | |
| 				goto end;
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		dofree = 1;
 | |
| 		prison_hold_locked(pr);
 | |
| 		mtx_unlock(&pr->pr_mtx);
 | |
| 		head = malloc(sizeof (*head), M_ZONES, M_WAITOK);
 | |
| 		LIST_INIT(head);
 | |
| 		mtx_lock(&pr->pr_mtx);
 | |
| 		error = osd_jail_set(pr, zone_slot, head);
 | |
| 		KASSERT(error == 0, ("osd_jail_set() failed (error=%d)",
 | |
| 		    error));
 | |
| 	}
 | |
| 	strcpy(zd->zd_dataset, dataset);
 | |
| 	LIST_INSERT_HEAD(head, zd, zd_next);
 | |
| end:
 | |
| 	if (dofree)
 | |
| 		prison_free_locked(pr);
 | |
| 	else
 | |
| 		mtx_unlock(&pr->pr_mtx);
 | |
| 	return (error);
 | |
| }
 | |
| 
 | |
| int
 | |
| zone_dataset_detach(struct ucred *cred, const char *dataset, int jailid)
 | |
| {
 | |
| 	struct zone_dataset_head *head;
 | |
| 	zone_dataset_t *zd;
 | |
| 	struct prison *pr;
 | |
| 	int error;
 | |
| 
 | |
| 	if ((error = spl_priv_check_cred(cred, PRIV_ZFS_JAIL)) != 0)
 | |
| 		return (error);
 | |
| 
 | |
| 	sx_slock(&allprison_lock);
 | |
| 	pr = prison_find(jailid);
 | |
| 	sx_sunlock(&allprison_lock);
 | |
| 	if (pr == NULL)
 | |
| 		return (ENOENT);
 | |
| 	head = osd_jail_get(pr, zone_slot);
 | |
| 	if (head == NULL) {
 | |
| 		error = ENOENT;
 | |
| 		goto end;
 | |
| 	}
 | |
| 	LIST_FOREACH(zd, head, zd_next) {
 | |
| 		if (strcmp(dataset, zd->zd_dataset) == 0)
 | |
| 			break;
 | |
| 	}
 | |
| 	if (zd == NULL)
 | |
| 		error = ENOENT;
 | |
| 	else {
 | |
| 		LIST_REMOVE(zd, zd_next);
 | |
| 		free(zd, M_ZONES);
 | |
| 		if (LIST_EMPTY(head))
 | |
| 			osd_jail_del(pr, zone_slot);
 | |
| 		error = 0;
 | |
| 	}
 | |
| end:
 | |
| 	mtx_unlock(&pr->pr_mtx);
 | |
| 	return (error);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Returns true if the named dataset is visible in the current zone.
 | |
|  * The 'write' parameter is set to 1 if the dataset is also writable.
 | |
|  */
 | |
| int
 | |
| zone_dataset_visible(const char *dataset, int *write)
 | |
| {
 | |
| 	struct zone_dataset_head *head;
 | |
| 	zone_dataset_t *zd;
 | |
| 	struct prison *pr;
 | |
| 	size_t len;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (dataset[0] == '\0')
 | |
| 		return (0);
 | |
| 	if (INGLOBALZONE(curproc)) {
 | |
| 		if (write != NULL)
 | |
| 			*write = 1;
 | |
| 		return (1);
 | |
| 	}
 | |
| 	pr = curthread->td_ucred->cr_prison;
 | |
| 	mtx_lock(&pr->pr_mtx);
 | |
| 	head = osd_jail_get(pr, zone_slot);
 | |
| 	if (head == NULL)
 | |
| 		goto end;
 | |
| 
 | |
| 	/*
 | |
| 	 * Walk the list once, looking for datasets which match exactly, or
 | |
| 	 * specify a dataset underneath an exported dataset.  If found, return
 | |
| 	 * true and note that it is writable.
 | |
| 	 */
 | |
| 	LIST_FOREACH(zd, head, zd_next) {
 | |
| 		len = strlen(zd->zd_dataset);
 | |
| 		if (strlen(dataset) >= len &&
 | |
| 		    memcmp(dataset, zd->zd_dataset, len) == 0 &&
 | |
| 		    (dataset[len] == '\0' || dataset[len] == '/' ||
 | |
| 		    dataset[len] == '@')) {
 | |
| 			if (write)
 | |
| 				*write = 1;
 | |
| 			ret = 1;
 | |
| 			goto end;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Walk the list a second time, searching for datasets which are parents
 | |
| 	 * of exported datasets.  These should be visible, but read-only.
 | |
| 	 *
 | |
| 	 * Note that we also have to support forms such as 'pool/dataset/', with
 | |
| 	 * a trailing slash.
 | |
| 	 */
 | |
| 	LIST_FOREACH(zd, head, zd_next) {
 | |
| 		len = strlen(dataset);
 | |
| 		if (dataset[len - 1] == '/')
 | |
| 			len--;	/* Ignore trailing slash */
 | |
| 		if (len < strlen(zd->zd_dataset) &&
 | |
| 		    memcmp(dataset, zd->zd_dataset, len) == 0 &&
 | |
| 		    zd->zd_dataset[len] == '/') {
 | |
| 			if (write)
 | |
| 				*write = 0;
 | |
| 			ret = 1;
 | |
| 			goto end;
 | |
| 		}
 | |
| 	}
 | |
| end:
 | |
| 	mtx_unlock(&pr->pr_mtx);
 | |
| 	return (ret);
 | |
| }
 | |
| 
 | |
| static void
 | |
| zone_destroy(void *arg)
 | |
| {
 | |
| 	struct zone_dataset_head *head;
 | |
| 	zone_dataset_t *zd;
 | |
| 
 | |
| 	head = arg;
 | |
| 	while ((zd = LIST_FIRST(head)) != NULL) {
 | |
| 		LIST_REMOVE(zd, zd_next);
 | |
| 		free(zd, M_ZONES);
 | |
| 	}
 | |
| 	free(head, M_ZONES);
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| zone_get_hostid(void *ptr)
 | |
| {
 | |
| 
 | |
| 	KASSERT(ptr == NULL, ("only NULL pointer supported in %s", __func__));
 | |
| 
 | |
| 	return ((uint32_t)curthread->td_ucred->cr_prison->pr_hostid);
 | |
| }
 | |
| 
 | |
| static void
 | |
| zone_sysinit(void *arg __unused)
 | |
| {
 | |
| 
 | |
| 	zone_slot = osd_jail_register(zone_destroy, NULL);
 | |
| }
 | |
| 
 | |
| static void
 | |
| zone_sysuninit(void *arg __unused)
 | |
| {
 | |
| 
 | |
| 	osd_jail_deregister(zone_slot);
 | |
| }
 | |
| 
 | |
| SYSINIT(zone_sysinit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysinit, NULL);
 | |
| SYSUNINIT(zone_sysuninit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysuninit, NULL);
 |