be_access.c revision 350344
1337414Skevans/*- 2337414Skevans * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3336668Skevans * 4336668Skevans * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in> 5337414Skevans * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> 6346429Skevans * Copyright (c) 2019 Wes Maag <wes@jwmaag.org> 7336668Skevans * All rights reserved. 8336668Skevans * 9336668Skevans * Redistribution and use in source and binary forms, with or without 10336668Skevans * modification, are permitted provided that the following conditions 11336668Skevans * are met: 12336668Skevans * 1. Redistributions of source code must retain the above copyright 13336668Skevans * notice, this list of conditions and the following disclaimer. 14336668Skevans * 2. Redistributions in binary form must reproduce the above copyright 15336668Skevans * notice, this list of conditions and the following disclaimer in the 16336668Skevans * documentation and/or other materials provided with the distribution. 17336668Skevans * 18336668Skevans * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19336668Skevans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20336668Skevans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21336668Skevans * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22336668Skevans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23336668Skevans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24336668Skevans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25336668Skevans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26336668Skevans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27336668Skevans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28336668Skevans * SUCH DAMAGE. 29336668Skevans */ 30336668Skevans 31337416Skevans#include <sys/cdefs.h> 32337416Skevans__FBSDID("$FreeBSD: stable/11/lib/libbe/be_access.c 350344 2019-07-26 01:49:58Z kevans $"); 33337416Skevans 34336668Skevans#include "be.h" 35336668Skevans#include "be_impl.h" 36336668Skevans 37336729Skevansstruct be_mountcheck_info { 38336729Skevans const char *path; 39336729Skevans char *name; 40336729Skevans}; 41336729Skevans 42346429Skevansstruct be_mount_info { 43346429Skevans libbe_handle_t *lbh; 44346429Skevans const char *be; 45346429Skevans const char *mountpoint; 46346429Skevans int mntflags; 47346429Skevans int deepmount; 48347170Skevans int depth; 49346429Skevans}; 50346429Skevans 51336729Skevansstatic int 52336729Skevansbe_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data) 53336729Skevans{ 54336729Skevans struct be_mountcheck_info *info; 55336729Skevans char *mountpoint; 56336729Skevans 57336729Skevans if (data == NULL) 58336729Skevans return (1); 59336729Skevans info = (struct be_mountcheck_info *)data; 60336729Skevans if (!zfs_is_mounted(zfs_hdl, &mountpoint)) 61336729Skevans return (0); 62336729Skevans if (strcmp(mountpoint, info->path) == 0) { 63336729Skevans info->name = strdup(zfs_get_name(zfs_hdl)); 64346429Skevans free(mountpoint); 65336729Skevans return (1); 66336729Skevans } 67346429Skevans free(mountpoint); 68336729Skevans return (0); 69336729Skevans} 70336729Skevans 71336668Skevans/* 72346429Skevans * Called from be_mount, uses the given zfs_handle and attempts to 73346429Skevans * mount it at the passed mountpoint. If the deepmount flag is set, continue 74346429Skevans * calling the function for each child dataset. 75346429Skevans */ 76346429Skevansstatic int 77346429Skevansbe_mount_iter(zfs_handle_t *zfs_hdl, void *data) 78346429Skevans{ 79346429Skevans int err; 80346429Skevans char *mountpoint; 81346429Skevans char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN]; 82346429Skevans struct be_mount_info *info; 83347170Skevans char opt; 84346429Skevans 85346429Skevans info = (struct be_mount_info *)data; 86346429Skevans 87346429Skevans if (zfs_is_mounted(zfs_hdl, &mountpoint)) { 88346429Skevans free(mountpoint); 89346429Skevans return (0); 90346429Skevans } 91346429Skevans 92350344Skevans /* 93350344Skevans * canmount and mountpoint are both ignored for the BE dataset, because 94350344Skevans * the rest of the system (kernel and loader) will effectively do the 95350344Skevans * same. 96350344Skevans */ 97350344Skevans if (info->depth == 0) { 98350344Skevans snprintf(tmp, BE_MAXPATHLEN, "%s", info->mountpoint); 99350344Skevans } else { 100350344Skevans if (zfs_prop_get_int(zfs_hdl, ZFS_PROP_CANMOUNT) == 101350344Skevans ZFS_CANMOUNT_OFF) 102350344Skevans return (0); 103346429Skevans 104350344Skevans if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, zfs_mnt, 105350344Skevans BE_MAXPATHLEN, NULL, NULL, 0, 1)) 106350344Skevans return (1); 107346429Skevans 108347170Skevans /* 109350344Skevans * We've encountered mountpoint=none at some intermediate 110350344Skevans * dataset (e.g. zroot/var) that will have children that may 111350344Skevans * need to be mounted. Skip mounting it, but iterate through 112350344Skevans * the children. 113347170Skevans */ 114350344Skevans if (strcmp("none", zfs_mnt) == 0) 115348133Skevans goto skipmount; 116346429Skevans 117346429Skevans mountpoint = be_mountpoint_augmented(info->lbh, zfs_mnt); 118346429Skevans snprintf(tmp, BE_MAXPATHLEN, "%s%s", info->mountpoint, 119346429Skevans mountpoint); 120347170Skevans } 121346429Skevans 122347170Skevans opt = '\0'; 123347170Skevans if ((err = zmount(zfs_get_name(zfs_hdl), tmp, info->mntflags, 124347170Skevans __DECONST(char *, MNTTYPE_ZFS), NULL, 0, &opt, 1)) != 0) { 125347170Skevans switch (errno) { 126347170Skevans case ENAMETOOLONG: 127347170Skevans return (set_error(info->lbh, BE_ERR_PATHLEN)); 128347170Skevans case ELOOP: 129347170Skevans case ENOENT: 130347170Skevans case ENOTDIR: 131347170Skevans return (set_error(info->lbh, BE_ERR_BADPATH)); 132347170Skevans case EPERM: 133347170Skevans return (set_error(info->lbh, BE_ERR_PERMS)); 134347170Skevans case EBUSY: 135347170Skevans return (set_error(info->lbh, BE_ERR_PATHBUSY)); 136347170Skevans default: 137347170Skevans return (set_error(info->lbh, BE_ERR_UNKNOWN)); 138346429Skevans } 139346429Skevans } 140346429Skevans 141346429Skevans if (!info->deepmount) 142346429Skevans return (0); 143346429Skevans 144348133Skevansskipmount: 145347170Skevans ++info->depth; 146347170Skevans err = zfs_iter_filesystems(zfs_hdl, be_mount_iter, info); 147347170Skevans --info->depth; 148347170Skevans return (err); 149346429Skevans} 150346429Skevans 151346429Skevans 152346429Skevansstatic int 153346429Skevansbe_umount_iter(zfs_handle_t *zfs_hdl, void *data) 154346429Skevans{ 155346429Skevans 156346429Skevans int err; 157346429Skevans char *mountpoint; 158346429Skevans struct be_mount_info *info; 159346429Skevans 160346429Skevans info = (struct be_mount_info *)data; 161346429Skevans 162347170Skevans ++info->depth; 163346429Skevans if((err = zfs_iter_filesystems(zfs_hdl, be_umount_iter, info)) != 0) { 164346429Skevans return (err); 165346429Skevans } 166347170Skevans --info->depth; 167346429Skevans 168346429Skevans if (!zfs_is_mounted(zfs_hdl, &mountpoint)) { 169346429Skevans return (0); 170346429Skevans } 171346429Skevans free(mountpoint); 172346429Skevans 173346429Skevans if (zfs_unmount(zfs_hdl, NULL, info->mntflags) != 0) { 174346429Skevans switch (errno) { 175346429Skevans case ENAMETOOLONG: 176346429Skevans return (set_error(info->lbh, BE_ERR_PATHLEN)); 177346429Skevans case ELOOP: 178346429Skevans case ENOENT: 179346429Skevans case ENOTDIR: 180346429Skevans return (set_error(info->lbh, BE_ERR_BADPATH)); 181346429Skevans case EPERM: 182346429Skevans return (set_error(info->lbh, BE_ERR_PERMS)); 183346429Skevans case EBUSY: 184346429Skevans return (set_error(info->lbh, BE_ERR_PATHBUSY)); 185346429Skevans default: 186346429Skevans return (set_error(info->lbh, BE_ERR_UNKNOWN)); 187346429Skevans } 188346429Skevans } 189346429Skevans return (0); 190346429Skevans} 191346429Skevans 192346429Skevans/* 193336668Skevans * usage 194336668Skevans */ 195336668Skevansint 196336729Skevansbe_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details) 197336729Skevans{ 198346429Skevans char be[BE_MAXPATHLEN]; 199336729Skevans zfs_handle_t *root_hdl; 200336729Skevans struct be_mountcheck_info info; 201336729Skevans prop_data_t propinfo; 202336729Skevans 203346429Skevans bzero(&be, BE_MAXPATHLEN); 204336729Skevans if ((root_hdl = zfs_open(lbh->lzh, lbh->root, 205336729Skevans ZFS_TYPE_FILESYSTEM)) == NULL) 206336729Skevans return (BE_ERR_ZFSOPEN); 207336729Skevans 208336729Skevans info.path = path; 209336729Skevans info.name = NULL; 210336729Skevans zfs_iter_filesystems(root_hdl, be_mountcheck_cb, &info); 211336729Skevans zfs_close(root_hdl); 212336729Skevans 213336729Skevans if (info.name != NULL) { 214336729Skevans if (details != NULL) { 215336729Skevans if ((root_hdl = zfs_open(lbh->lzh, lbh->root, 216336729Skevans ZFS_TYPE_FILESYSTEM)) == NULL) { 217336729Skevans free(info.name); 218336729Skevans return (BE_ERR_ZFSOPEN); 219336729Skevans } 220336729Skevans 221336729Skevans propinfo.lbh = lbh; 222336729Skevans propinfo.list = details; 223337228Skevans propinfo.single_object = false; 224336729Skevans prop_list_builder_cb(root_hdl, &propinfo); 225336729Skevans zfs_close(root_hdl); 226336729Skevans } 227336729Skevans free(info.name); 228336729Skevans return (0); 229336729Skevans } 230336729Skevans return (1); 231336729Skevans} 232336729Skevans 233336729Skevans/* 234336729Skevans * usage 235336729Skevans */ 236336729Skevansint 237336668Skevansbe_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags, 238336668Skevans char *result_loc) 239336668Skevans{ 240336668Skevans char be[BE_MAXPATHLEN]; 241336668Skevans char mnt_temp[BE_MAXPATHLEN]; 242346429Skevans int mntflags, mntdeep; 243336668Skevans int err; 244346429Skevans struct be_mount_info info; 245346429Skevans zfs_handle_t *zhdl; 246336668Skevans 247336710Skevans if ((err = be_root_concat(lbh, bootenv, be)) != 0) 248336668Skevans return (set_error(lbh, err)); 249336668Skevans 250346429Skevans if ((err = be_exists(lbh, bootenv)) != 0) 251346429Skevans return (set_error(lbh, err)); 252336668Skevans 253346429Skevans if (is_mounted(lbh->lzh, be, NULL)) 254336668Skevans return (set_error(lbh, BE_ERR_MOUNTED)); 255336668Skevans 256346429Skevans mntdeep = (flags & BE_MNT_DEEP) ? 1 : 0; 257336668Skevans mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0; 258336668Skevans 259336668Skevans /* Create mountpoint if it is not specified */ 260336668Skevans if (mountpoint == NULL) { 261346429Skevans strlcpy(mnt_temp, "/tmp/be_mount.XXXX", sizeof(mnt_temp)); 262336701Skevans if (mkdtemp(mnt_temp) == NULL) 263337407Skevans return (set_error(lbh, BE_ERR_IO)); 264336668Skevans } 265336668Skevans 266346429Skevans if ((zhdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL) 267346429Skevans return (set_error(lbh, BE_ERR_ZFSOPEN)); 268346429Skevans 269346429Skevans info.lbh = lbh; 270346429Skevans info.be = be; 271346429Skevans info.mountpoint = (mountpoint == NULL) ? mnt_temp : mountpoint; 272346429Skevans info.mntflags = mntflags; 273346429Skevans info.deepmount = mntdeep; 274347170Skevans info.depth = 0; 275346429Skevans 276346429Skevans if((err = be_mount_iter(zhdl, &info) != 0)) { 277346429Skevans zfs_close(zhdl); 278346429Skevans return (err); 279337407Skevans } 280346429Skevans zfs_close(zhdl); 281336668Skevans 282336701Skevans if (result_loc != NULL) 283346429Skevans strlcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint, 284346429Skevans BE_MAXPATHLEN); 285336668Skevans 286336668Skevans return (BE_ERR_SUCCESS); 287336668Skevans} 288336668Skevans 289336668Skevans/* 290336668Skevans * usage 291336668Skevans */ 292336668Skevansint 293336668Skevansbe_unmount(libbe_handle_t *lbh, char *bootenv, int flags) 294336668Skevans{ 295346429Skevans int err; 296336668Skevans char be[BE_MAXPATHLEN]; 297346429Skevans zfs_handle_t *root_hdl; 298346429Skevans struct be_mount_info info; 299336668Skevans 300336710Skevans if ((err = be_root_concat(lbh, bootenv, be)) != 0) 301336668Skevans return (set_error(lbh, err)); 302336668Skevans 303346429Skevans if ((root_hdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL) 304346429Skevans return (set_error(lbh, BE_ERR_ZFSOPEN)); 305336668Skevans 306346429Skevans info.lbh = lbh; 307346429Skevans info.be = be; 308346429Skevans info.mountpoint = NULL; 309346429Skevans info.mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0; 310347170Skevans info.depth = 0; 311336668Skevans 312346429Skevans if ((err = be_umount_iter(root_hdl, &info)) != 0) { 313346429Skevans zfs_close(root_hdl); 314346429Skevans return (err); 315336668Skevans } 316336668Skevans 317346429Skevans zfs_close(root_hdl); 318346429Skevans return (BE_ERR_SUCCESS); 319346429Skevans} 320336668Skevans 321346429Skevans/* 322346429Skevans * This function will blow away the input buffer as needed if we're discovered 323346429Skevans * to be looking at a root-mount. If the mountpoint is naturally beyond the 324346429Skevans * root, however, the buffer may be left intact and a pointer to the section 325346429Skevans * past altroot will be returned instead for the caller's perusal. 326346429Skevans */ 327346429Skevanschar * 328346429Skevansbe_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint) 329346429Skevans{ 330336668Skevans 331346429Skevans if (lbh->altroot_len == 0) 332346429Skevans return (mountpoint); 333346429Skevans if (mountpoint == NULL || *mountpoint == '\0') 334346429Skevans return (mountpoint); 335336668Skevans 336346429Skevans if (mountpoint[lbh->altroot_len] == '\0') { 337346429Skevans *(mountpoint + 1) = '\0'; 338346429Skevans return (mountpoint); 339346429Skevans } else 340346429Skevans return (mountpoint + lbh->altroot_len); 341336668Skevans} 342