1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in> 5 * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> 6 * Copyright (c) 2019 Wes Maag <wes@jwmaag.org> 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include "be.h" 35 #include "be_impl.h" 36 37 struct be_mountcheck_info { 38 const char *path; 39 char *name; 40 }; 41 42 struct be_mount_info { 43 libbe_handle_t *lbh; 44 const char *be; 45 const char *mountpoint; 46 int mntflags; 47 int deepmount; 48 }; 49 50 static int 51 be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data) 52 { 53 struct be_mountcheck_info *info; 54 char *mountpoint; 55 56 if (data == NULL) 57 return (1); 58 info = (struct be_mountcheck_info *)data; 59 if (!zfs_is_mounted(zfs_hdl, &mountpoint)) 60 return (0); 61 if (strcmp(mountpoint, info->path) == 0) { 62 info->name = strdup(zfs_get_name(zfs_hdl)); 63 free(mountpoint); 64 return (1); 65 } 66 free(mountpoint); 67 return (0); 68 } 69 70 /* 71 * Called from be_mount, uses the given zfs_handle and attempts to 72 * mount it at the passed mountpoint. If the deepmount flag is set, continue 73 * calling the function for each child dataset. 74 */ 75 static int 76 be_mount_iter(zfs_handle_t *zfs_hdl, void *data) 77 { 78 int err; 79 char *mountpoint; 80 char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN]; 81 struct be_mount_info *info; 82 83 info = (struct be_mount_info *)data; 84 85 if (zfs_is_mounted(zfs_hdl, &mountpoint)) { 86 free(mountpoint); 87 return (0); 88 } 89 90 if (zfs_prop_get_int(zfs_hdl, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF) 91 return (0); 92 93 if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, zfs_mnt, BE_MAXPATHLEN, 94 NULL, NULL, 0, 1)) 95 return (1); 96 97 if (strcmp("none", zfs_mnt) != 0) { 98 char opt = '\0'; 99 100 mountpoint = be_mountpoint_augmented(info->lbh, zfs_mnt); 101 102 snprintf(tmp, BE_MAXPATHLEN, "%s%s", info->mountpoint, 103 mountpoint); 104 105 if ((err = zmount(zfs_get_name(zfs_hdl), tmp, info->mntflags, 106 __DECONST(char *, MNTTYPE_ZFS), NULL, 0, &opt, 1)) != 0) { 107 switch (errno) { 108 case ENAMETOOLONG: 109 return (set_error(info->lbh, BE_ERR_PATHLEN)); 110 case ELOOP: 111 case ENOENT: 112 case ENOTDIR: 113 return (set_error(info->lbh, BE_ERR_BADPATH)); 114 case EPERM: 115 return (set_error(info->lbh, BE_ERR_PERMS)); 116 case EBUSY: 117 return (set_error(info->lbh, BE_ERR_PATHBUSY)); 118 default: 119 return (set_error(info->lbh, BE_ERR_UNKNOWN)); 120 } 121 } 122 } 123 124 if (!info->deepmount) 125 return (0); 126 127 return (zfs_iter_filesystems(zfs_hdl, be_mount_iter, info)); 128 } 129 130 131 static int 132 be_umount_iter(zfs_handle_t *zfs_hdl, void *data) 133 { 134 135 int err; 136 char *mountpoint; 137 struct be_mount_info *info; 138 139 info = (struct be_mount_info *)data; 140 141 if((err = zfs_iter_filesystems(zfs_hdl, be_umount_iter, info)) != 0) { 142 return (err); 143 } 144 145 if (!zfs_is_mounted(zfs_hdl, &mountpoint)) { 146 return (0); 147 } 148 free(mountpoint); 149 150 if (zfs_unmount(zfs_hdl, NULL, info->mntflags) != 0) { 151 switch (errno) { 152 case ENAMETOOLONG: 153 return (set_error(info->lbh, BE_ERR_PATHLEN)); 154 case ELOOP: 155 case ENOENT: 156 case ENOTDIR: 157 return (set_error(info->lbh, BE_ERR_BADPATH)); 158 case EPERM: 159 return (set_error(info->lbh, BE_ERR_PERMS)); 160 case EBUSY: 161 return (set_error(info->lbh, BE_ERR_PATHBUSY)); 162 default: 163 return (set_error(info->lbh, BE_ERR_UNKNOWN)); 164 } 165 } 166 return (0); 167 } 168 169 /* 170 * usage 171 */ 172 int 173 be_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details) 174 { 175 char be[BE_MAXPATHLEN]; 176 zfs_handle_t *root_hdl; 177 struct be_mountcheck_info info; 178 prop_data_t propinfo; 179 180 bzero(&be, BE_MAXPATHLEN); 181 if ((root_hdl = zfs_open(lbh->lzh, lbh->root, 182 ZFS_TYPE_FILESYSTEM)) == NULL) 183 return (BE_ERR_ZFSOPEN); 184 185 info.path = path; 186 info.name = NULL; 187 zfs_iter_filesystems(root_hdl, be_mountcheck_cb, &info); 188 zfs_close(root_hdl); 189 190 if (info.name != NULL) { 191 if (details != NULL) { 192 if ((root_hdl = zfs_open(lbh->lzh, lbh->root, 193 ZFS_TYPE_FILESYSTEM)) == NULL) { 194 free(info.name); 195 return (BE_ERR_ZFSOPEN); 196 } 197 198 propinfo.lbh = lbh; 199 propinfo.list = details; 200 propinfo.single_object = false; 201 prop_list_builder_cb(root_hdl, &propinfo); 202 zfs_close(root_hdl); 203 } 204 free(info.name); 205 return (0); 206 } 207 return (1); 208 } 209 210 /* 211 * usage 212 */ 213 int 214 be_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags, 215 char *result_loc) 216 { 217 char be[BE_MAXPATHLEN]; 218 char mnt_temp[BE_MAXPATHLEN]; 219 int mntflags, mntdeep; 220 int err; 221 struct be_mount_info info; 222 zfs_handle_t *zhdl; 223 224 if ((err = be_root_concat(lbh, bootenv, be)) != 0) 225 return (set_error(lbh, err)); 226 227 if ((err = be_exists(lbh, bootenv)) != 0) 228 return (set_error(lbh, err)); 229 230 if (is_mounted(lbh->lzh, be, NULL)) 231 return (set_error(lbh, BE_ERR_MOUNTED)); 232 233 mntdeep = (flags & BE_MNT_DEEP) ? 1 : 0; 234 mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0; 235 236 /* Create mountpoint if it is not specified */ 237 if (mountpoint == NULL) { 238 strlcpy(mnt_temp, "/tmp/be_mount.XXXX", sizeof(mnt_temp)); 239 if (mkdtemp(mnt_temp) == NULL) 240 return (set_error(lbh, BE_ERR_IO)); 241 } 242 243 if ((zhdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL) 244 return (set_error(lbh, BE_ERR_ZFSOPEN)); 245 246 info.lbh = lbh; 247 info.be = be; 248 info.mountpoint = (mountpoint == NULL) ? mnt_temp : mountpoint; 249 info.mntflags = mntflags; 250 info.deepmount = mntdeep; 251 252 if((err = be_mount_iter(zhdl, &info) != 0)) { 253 zfs_close(zhdl); 254 return (err); 255 } 256 zfs_close(zhdl); 257 258 if (result_loc != NULL) 259 strlcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint, 260 BE_MAXPATHLEN); 261 262 return (BE_ERR_SUCCESS); 263 } 264 265 /* 266 * usage 267 */ 268 int 269 be_unmount(libbe_handle_t *lbh, char *bootenv, int flags) 270 { 271 int err; 272 char be[BE_MAXPATHLEN]; 273 zfs_handle_t *root_hdl; 274 struct be_mount_info info; 275 276 if ((err = be_root_concat(lbh, bootenv, be)) != 0) 277 return (set_error(lbh, err)); 278 279 if ((root_hdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL) 280 return (set_error(lbh, BE_ERR_ZFSOPEN)); 281 282 info.lbh = lbh; 283 info.be = be; 284 info.mountpoint = NULL; 285 info.mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0; 286 287 if ((err = be_umount_iter(root_hdl, &info)) != 0) { 288 zfs_close(root_hdl); 289 return (err); 290 } 291 292 zfs_close(root_hdl); 293 return (BE_ERR_SUCCESS); 294 } 295 296 /* 297 * This function will blow away the input buffer as needed if we're discovered 298 * to be looking at a root-mount. If the mountpoint is naturally beyond the 299 * root, however, the buffer may be left intact and a pointer to the section 300 * past altroot will be returned instead for the caller's perusal. 301 */ 302 char * 303 be_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint) 304 { 305 306 if (lbh->altroot_len == 0) 307 return (mountpoint); 308 if (mountpoint == NULL || *mountpoint == '\0') 309 return (mountpoint); 310 311 if (mountpoint[lbh->altroot_len] == '\0') { 312 *(mountpoint + 1) = '\0'; 313 return (mountpoint); 314 } else 315 return (mountpoint + lbh->altroot_len); 316 } 317