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