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 * 8336668Skevans * Redistribution and use in source and binary forms, with or without 9336668Skevans * modification, are permitted provided that the following conditions 10336668Skevans * are met: 11336668Skevans * 1. Redistributions of source code must retain the above copyright 12336668Skevans * notice, this list of conditions and the following disclaimer. 13336668Skevans * 2. Redistributions in binary form must reproduce the above copyright 14336668Skevans * notice, this list of conditions and the following disclaimer in the 15336668Skevans * documentation and/or other materials provided with the distribution. 16336668Skevans * 17336668Skevans * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18336668Skevans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19336668Skevans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20336668Skevans * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21336668Skevans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22336668Skevans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23336668Skevans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24336668Skevans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25336668Skevans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26336668Skevans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27336668Skevans * SUCH DAMAGE. 28336668Skevans */ 29336668Skevans 30337416Skevans#include <sys/cdefs.h> 31337416Skevans__FBSDID("$FreeBSD: stable/11/lib/libbe/be_access.c 357667 2020-02-07 21:57:27Z kevans $"); 32337416Skevans 33355662Skevans#include <sys/mntent.h> 34355662Skevans 35336668Skevans#include "be.h" 36336668Skevans#include "be_impl.h" 37336668Skevans 38336729Skevansstruct be_mountcheck_info { 39336729Skevans const char *path; 40336729Skevans char *name; 41336729Skevans}; 42336729Skevans 43346429Skevansstruct be_mount_info { 44346429Skevans libbe_handle_t *lbh; 45346429Skevans const char *be; 46346429Skevans const char *mountpoint; 47346429Skevans int mntflags; 48346429Skevans int deepmount; 49347170Skevans int depth; 50346429Skevans}; 51346429Skevans 52336729Skevansstatic int 53336729Skevansbe_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data) 54336729Skevans{ 55336729Skevans struct be_mountcheck_info *info; 56336729Skevans char *mountpoint; 57336729Skevans 58336729Skevans if (data == NULL) 59336729Skevans return (1); 60336729Skevans info = (struct be_mountcheck_info *)data; 61336729Skevans if (!zfs_is_mounted(zfs_hdl, &mountpoint)) 62336729Skevans return (0); 63336729Skevans if (strcmp(mountpoint, info->path) == 0) { 64336729Skevans info->name = strdup(zfs_get_name(zfs_hdl)); 65346429Skevans free(mountpoint); 66336729Skevans return (1); 67336729Skevans } 68346429Skevans free(mountpoint); 69336729Skevans return (0); 70336729Skevans} 71336729Skevans 72336668Skevans/* 73346429Skevans * Called from be_mount, uses the given zfs_handle and attempts to 74346429Skevans * mount it at the passed mountpoint. If the deepmount flag is set, continue 75346429Skevans * calling the function for each child dataset. 76346429Skevans */ 77346429Skevansstatic int 78346429Skevansbe_mount_iter(zfs_handle_t *zfs_hdl, void *data) 79346429Skevans{ 80346429Skevans int err; 81346429Skevans char *mountpoint; 82346429Skevans char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN]; 83346429Skevans struct be_mount_info *info; 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 122357001Skevans if ((err = zfs_mount_at(zfs_hdl, NULL, info->mntflags, tmp)) != 0) { 123347170Skevans switch (errno) { 124347170Skevans case ENAMETOOLONG: 125347170Skevans return (set_error(info->lbh, BE_ERR_PATHLEN)); 126347170Skevans case ELOOP: 127347170Skevans case ENOENT: 128347170Skevans case ENOTDIR: 129347170Skevans return (set_error(info->lbh, BE_ERR_BADPATH)); 130347170Skevans case EPERM: 131347170Skevans return (set_error(info->lbh, BE_ERR_PERMS)); 132347170Skevans case EBUSY: 133347170Skevans return (set_error(info->lbh, BE_ERR_PATHBUSY)); 134347170Skevans default: 135347170Skevans return (set_error(info->lbh, BE_ERR_UNKNOWN)); 136346429Skevans } 137346429Skevans } 138346429Skevans 139346429Skevans if (!info->deepmount) 140346429Skevans return (0); 141346429Skevans 142348133Skevansskipmount: 143347170Skevans ++info->depth; 144347170Skevans err = zfs_iter_filesystems(zfs_hdl, be_mount_iter, info); 145347170Skevans --info->depth; 146347170Skevans return (err); 147346429Skevans} 148346429Skevans 149346429Skevans 150346429Skevansstatic int 151346429Skevansbe_umount_iter(zfs_handle_t *zfs_hdl, void *data) 152346429Skevans{ 153346429Skevans 154346429Skevans int err; 155346429Skevans char *mountpoint; 156346429Skevans struct be_mount_info *info; 157346429Skevans 158346429Skevans info = (struct be_mount_info *)data; 159346429Skevans 160347170Skevans ++info->depth; 161346429Skevans if((err = zfs_iter_filesystems(zfs_hdl, be_umount_iter, info)) != 0) { 162346429Skevans return (err); 163346429Skevans } 164347170Skevans --info->depth; 165346429Skevans 166346429Skevans if (!zfs_is_mounted(zfs_hdl, &mountpoint)) { 167346429Skevans return (0); 168346429Skevans } 169346429Skevans free(mountpoint); 170346429Skevans 171346429Skevans if (zfs_unmount(zfs_hdl, NULL, info->mntflags) != 0) { 172346429Skevans switch (errno) { 173346429Skevans case ENAMETOOLONG: 174346429Skevans return (set_error(info->lbh, BE_ERR_PATHLEN)); 175346429Skevans case ELOOP: 176346429Skevans case ENOENT: 177346429Skevans case ENOTDIR: 178346429Skevans return (set_error(info->lbh, BE_ERR_BADPATH)); 179346429Skevans case EPERM: 180346429Skevans return (set_error(info->lbh, BE_ERR_PERMS)); 181346429Skevans case EBUSY: 182346429Skevans return (set_error(info->lbh, BE_ERR_PATHBUSY)); 183346429Skevans default: 184346429Skevans return (set_error(info->lbh, BE_ERR_UNKNOWN)); 185346429Skevans } 186346429Skevans } 187346429Skevans return (0); 188346429Skevans} 189346429Skevans 190346429Skevans/* 191336668Skevans * usage 192336668Skevans */ 193336668Skevansint 194336729Skevansbe_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details) 195336729Skevans{ 196346429Skevans char be[BE_MAXPATHLEN]; 197336729Skevans zfs_handle_t *root_hdl; 198336729Skevans struct be_mountcheck_info info; 199336729Skevans prop_data_t propinfo; 200336729Skevans 201346429Skevans bzero(&be, BE_MAXPATHLEN); 202336729Skevans if ((root_hdl = zfs_open(lbh->lzh, lbh->root, 203336729Skevans ZFS_TYPE_FILESYSTEM)) == NULL) 204336729Skevans return (BE_ERR_ZFSOPEN); 205336729Skevans 206336729Skevans info.path = path; 207336729Skevans info.name = NULL; 208336729Skevans zfs_iter_filesystems(root_hdl, be_mountcheck_cb, &info); 209336729Skevans zfs_close(root_hdl); 210336729Skevans 211336729Skevans if (info.name != NULL) { 212336729Skevans if (details != NULL) { 213336729Skevans if ((root_hdl = zfs_open(lbh->lzh, lbh->root, 214336729Skevans ZFS_TYPE_FILESYSTEM)) == NULL) { 215336729Skevans free(info.name); 216336729Skevans return (BE_ERR_ZFSOPEN); 217336729Skevans } 218336729Skevans 219336729Skevans propinfo.lbh = lbh; 220336729Skevans propinfo.list = details; 221337228Skevans propinfo.single_object = false; 222336729Skevans prop_list_builder_cb(root_hdl, &propinfo); 223336729Skevans zfs_close(root_hdl); 224336729Skevans } 225336729Skevans free(info.name); 226336729Skevans return (0); 227336729Skevans } 228336729Skevans return (1); 229336729Skevans} 230336729Skevans 231336729Skevans/* 232336729Skevans * usage 233336729Skevans */ 234336729Skevansint 235336668Skevansbe_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags, 236336668Skevans char *result_loc) 237336668Skevans{ 238336668Skevans char be[BE_MAXPATHLEN]; 239336668Skevans char mnt_temp[BE_MAXPATHLEN]; 240346429Skevans int mntflags, mntdeep; 241336668Skevans int err; 242346429Skevans struct be_mount_info info; 243346429Skevans zfs_handle_t *zhdl; 244336668Skevans 245336710Skevans if ((err = be_root_concat(lbh, bootenv, be)) != 0) 246336668Skevans return (set_error(lbh, err)); 247336668Skevans 248346429Skevans if ((err = be_exists(lbh, bootenv)) != 0) 249346429Skevans return (set_error(lbh, err)); 250336668Skevans 251346429Skevans if (is_mounted(lbh->lzh, be, NULL)) 252336668Skevans return (set_error(lbh, BE_ERR_MOUNTED)); 253336668Skevans 254346429Skevans mntdeep = (flags & BE_MNT_DEEP) ? 1 : 0; 255336668Skevans mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0; 256336668Skevans 257336668Skevans /* Create mountpoint if it is not specified */ 258336668Skevans if (mountpoint == NULL) { 259346429Skevans strlcpy(mnt_temp, "/tmp/be_mount.XXXX", sizeof(mnt_temp)); 260336701Skevans if (mkdtemp(mnt_temp) == NULL) 261337407Skevans return (set_error(lbh, BE_ERR_IO)); 262336668Skevans } 263336668Skevans 264346429Skevans if ((zhdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL) 265346429Skevans return (set_error(lbh, BE_ERR_ZFSOPEN)); 266346429Skevans 267346429Skevans info.lbh = lbh; 268346429Skevans info.be = be; 269346429Skevans info.mountpoint = (mountpoint == NULL) ? mnt_temp : mountpoint; 270346429Skevans info.mntflags = mntflags; 271346429Skevans info.deepmount = mntdeep; 272347170Skevans info.depth = 0; 273346429Skevans 274346429Skevans if((err = be_mount_iter(zhdl, &info) != 0)) { 275346429Skevans zfs_close(zhdl); 276346429Skevans return (err); 277337407Skevans } 278346429Skevans zfs_close(zhdl); 279336668Skevans 280336701Skevans if (result_loc != NULL) 281346429Skevans strlcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint, 282346429Skevans BE_MAXPATHLEN); 283336668Skevans 284336668Skevans return (BE_ERR_SUCCESS); 285336668Skevans} 286336668Skevans 287336668Skevans/* 288336668Skevans * usage 289336668Skevans */ 290336668Skevansint 291336668Skevansbe_unmount(libbe_handle_t *lbh, char *bootenv, int flags) 292336668Skevans{ 293346429Skevans int err; 294336668Skevans char be[BE_MAXPATHLEN]; 295346429Skevans zfs_handle_t *root_hdl; 296346429Skevans struct be_mount_info info; 297336668Skevans 298336710Skevans if ((err = be_root_concat(lbh, bootenv, be)) != 0) 299336668Skevans return (set_error(lbh, err)); 300336668Skevans 301346429Skevans if ((root_hdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL) 302346429Skevans return (set_error(lbh, BE_ERR_ZFSOPEN)); 303336668Skevans 304346429Skevans info.lbh = lbh; 305346429Skevans info.be = be; 306346429Skevans info.mountpoint = NULL; 307346429Skevans info.mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0; 308347170Skevans info.depth = 0; 309336668Skevans 310346429Skevans if ((err = be_umount_iter(root_hdl, &info)) != 0) { 311346429Skevans zfs_close(root_hdl); 312346429Skevans return (err); 313336668Skevans } 314336668Skevans 315346429Skevans zfs_close(root_hdl); 316346429Skevans return (BE_ERR_SUCCESS); 317346429Skevans} 318336668Skevans 319346429Skevans/* 320346429Skevans * This function will blow away the input buffer as needed if we're discovered 321346429Skevans * to be looking at a root-mount. If the mountpoint is naturally beyond the 322346429Skevans * root, however, the buffer may be left intact and a pointer to the section 323346429Skevans * past altroot will be returned instead for the caller's perusal. 324346429Skevans */ 325346429Skevanschar * 326346429Skevansbe_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint) 327346429Skevans{ 328336668Skevans 329346429Skevans if (lbh->altroot_len == 0) 330346429Skevans return (mountpoint); 331346429Skevans if (mountpoint == NULL || *mountpoint == '\0') 332346429Skevans return (mountpoint); 333336668Skevans 334346429Skevans if (mountpoint[lbh->altroot_len] == '\0') { 335346429Skevans *(mountpoint + 1) = '\0'; 336346429Skevans return (mountpoint); 337346429Skevans } else 338346429Skevans return (mountpoint + lbh->altroot_len); 339336668Skevans} 340