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 int depth; 49 }; 50 51 static int 52 be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data) 53 { 54 struct be_mountcheck_info *info; 55 char *mountpoint; 56 57 if (data == NULL) 58 return (1); 59 info = (struct be_mountcheck_info *)data; 60 if (!zfs_is_mounted(zfs_hdl, &mountpoint)) 61 return (0); 62 if (strcmp(mountpoint, info->path) == 0) { 63 info->name = strdup(zfs_get_name(zfs_hdl)); 64 free(mountpoint); 65 return (1); 66 } 67 free(mountpoint); 68 return (0); 69 } 70 71 /* 72 * Called from be_mount, uses the given zfs_handle and attempts to 73 * mount it at the passed mountpoint. If the deepmount flag is set, continue 74 * calling the function for each child dataset. 75 */ 76 static int 77 be_mount_iter(zfs_handle_t *zfs_hdl, void *data) 78 { 79 int err; 80 char *mountpoint; 81 char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN]; 82 struct be_mount_info *info; 83 char opt; 84 85 info = (struct be_mount_info *)data; 86 87 if (zfs_is_mounted(zfs_hdl, &mountpoint)) { 88 free(mountpoint); 89 return (0); 90 } 91 92 /* 93 * canmount and mountpoint are both ignored for the BE dataset, because 94 * the rest of the system (kernel and loader) will effectively do the 95 * same. 96 */ 97 if (info->depth == 0) { 98 snprintf(tmp, BE_MAXPATHLEN, "%s", info->mountpoint); 99 } else { 100 if (zfs_prop_get_int(zfs_hdl, ZFS_PROP_CANMOUNT) == 101 ZFS_CANMOUNT_OFF) 102 return (0); 103 104 if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, zfs_mnt, 105 BE_MAXPATHLEN, NULL, NULL, 0, 1)) 106 return (1); 107 108 /* 109 * We've encountered mountpoint=none at some intermediate 110 * dataset (e.g. zroot/var) that will have children that may 111 * need to be mounted. Skip mounting it, but iterate through 112 * the children. 113 */ 114 if (strcmp("none", zfs_mnt) == 0) 115 goto skipmount; 116 117 mountpoint = be_mountpoint_augmented(info->lbh, zfs_mnt); 118 snprintf(tmp, BE_MAXPATHLEN, "%s%s", info->mountpoint, 119 mountpoint); 120 } 121 122 opt = '\0'; 123 if ((err = zmount(zfs_get_name(zfs_hdl), tmp, info->mntflags, 124 __DECONST(char *, MNTTYPE_ZFS), NULL, 0, &opt, 1)) != 0) { 125 switch (errno) { 126 case ENAMETOOLONG: 127 return (set_error(info->lbh, BE_ERR_PATHLEN)); 128 case ELOOP: 129 case ENOENT: 130 case ENOTDIR: 131 return (set_error(info->lbh, BE_ERR_BADPATH)); 132 case EPERM: 133 return (set_error(info->lbh, BE_ERR_PERMS)); 134 case EBUSY: 135 return (set_error(info->lbh, BE_ERR_PATHBUSY)); 136 default: 137 return (set_error(info->lbh, BE_ERR_UNKNOWN)); 138 } 139 } 140 141 if (!info->deepmount) 142 return (0); 143 144 skipmount: 145 ++info->depth; 146 err = zfs_iter_filesystems(zfs_hdl, be_mount_iter, info); 147 --info->depth; 148 return (err); 149 } 150 151 152 static int 153 be_umount_iter(zfs_handle_t *zfs_hdl, void *data) 154 { 155 156 int err; 157 char *mountpoint; 158 struct be_mount_info *info; 159 160 info = (struct be_mount_info *)data; 161 162 ++info->depth; 163 if((err = zfs_iter_filesystems(zfs_hdl, be_umount_iter, info)) != 0) { 164 return (err); 165 } 166 --info->depth; 167 168 if (!zfs_is_mounted(zfs_hdl, &mountpoint)) { 169 return (0); 170 } 171 free(mountpoint); 172 173 if (zfs_unmount(zfs_hdl, NULL, info->mntflags) != 0) { 174 switch (errno) { 175 case ENAMETOOLONG: 176 return (set_error(info->lbh, BE_ERR_PATHLEN)); 177 case ELOOP: 178 case ENOENT: 179 case ENOTDIR: 180 return (set_error(info->lbh, BE_ERR_BADPATH)); 181 case EPERM: 182 return (set_error(info->lbh, BE_ERR_PERMS)); 183 case EBUSY: 184 return (set_error(info->lbh, BE_ERR_PATHBUSY)); 185 default: 186 return (set_error(info->lbh, BE_ERR_UNKNOWN)); 187 } 188 } 189 return (0); 190 } 191 192 /* 193 * usage 194 */ 195 int 196 be_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details) 197 { 198 char be[BE_MAXPATHLEN]; 199 zfs_handle_t *root_hdl; 200 struct be_mountcheck_info info; 201 prop_data_t propinfo; 202 203 bzero(&be, BE_MAXPATHLEN); 204 if ((root_hdl = zfs_open(lbh->lzh, lbh->root, 205 ZFS_TYPE_FILESYSTEM)) == NULL) 206 return (BE_ERR_ZFSOPEN); 207 208 info.path = path; 209 info.name = NULL; 210 zfs_iter_filesystems(root_hdl, be_mountcheck_cb, &info); 211 zfs_close(root_hdl); 212 213 if (info.name != NULL) { 214 if (details != NULL) { 215 if ((root_hdl = zfs_open(lbh->lzh, lbh->root, 216 ZFS_TYPE_FILESYSTEM)) == NULL) { 217 free(info.name); 218 return (BE_ERR_ZFSOPEN); 219 } 220 221 propinfo.lbh = lbh; 222 propinfo.list = details; 223 propinfo.single_object = false; 224 prop_list_builder_cb(root_hdl, &propinfo); 225 zfs_close(root_hdl); 226 } 227 free(info.name); 228 return (0); 229 } 230 return (1); 231 } 232 233 /* 234 * usage 235 */ 236 int 237 be_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags, 238 char *result_loc) 239 { 240 char be[BE_MAXPATHLEN]; 241 char mnt_temp[BE_MAXPATHLEN]; 242 int mntflags, mntdeep; 243 int err; 244 struct be_mount_info info; 245 zfs_handle_t *zhdl; 246 247 if ((err = be_root_concat(lbh, bootenv, be)) != 0) 248 return (set_error(lbh, err)); 249 250 if ((err = be_exists(lbh, bootenv)) != 0) 251 return (set_error(lbh, err)); 252 253 if (is_mounted(lbh->lzh, be, NULL)) 254 return (set_error(lbh, BE_ERR_MOUNTED)); 255 256 mntdeep = (flags & BE_MNT_DEEP) ? 1 : 0; 257 mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0; 258 259 /* Create mountpoint if it is not specified */ 260 if (mountpoint == NULL) { 261 strlcpy(mnt_temp, "/tmp/be_mount.XXXX", sizeof(mnt_temp)); 262 if (mkdtemp(mnt_temp) == NULL) 263 return (set_error(lbh, BE_ERR_IO)); 264 } 265 266 if ((zhdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL) 267 return (set_error(lbh, BE_ERR_ZFSOPEN)); 268 269 info.lbh = lbh; 270 info.be = be; 271 info.mountpoint = (mountpoint == NULL) ? mnt_temp : mountpoint; 272 info.mntflags = mntflags; 273 info.deepmount = mntdeep; 274 info.depth = 0; 275 276 if((err = be_mount_iter(zhdl, &info) != 0)) { 277 zfs_close(zhdl); 278 return (err); 279 } 280 zfs_close(zhdl); 281 282 if (result_loc != NULL) 283 strlcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint, 284 BE_MAXPATHLEN); 285 286 return (BE_ERR_SUCCESS); 287 } 288 289 /* 290 * usage 291 */ 292 int 293 be_unmount(libbe_handle_t *lbh, char *bootenv, int flags) 294 { 295 int err; 296 char be[BE_MAXPATHLEN]; 297 zfs_handle_t *root_hdl; 298 struct be_mount_info info; 299 300 if ((err = be_root_concat(lbh, bootenv, be)) != 0) 301 return (set_error(lbh, err)); 302 303 if ((root_hdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL) 304 return (set_error(lbh, BE_ERR_ZFSOPEN)); 305 306 info.lbh = lbh; 307 info.be = be; 308 info.mountpoint = NULL; 309 info.mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0; 310 info.depth = 0; 311 312 if ((err = be_umount_iter(root_hdl, &info)) != 0) { 313 zfs_close(root_hdl); 314 return (err); 315 } 316 317 zfs_close(root_hdl); 318 return (BE_ERR_SUCCESS); 319 } 320 321 /* 322 * This function will blow away the input buffer as needed if we're discovered 323 * to be looking at a root-mount. If the mountpoint is naturally beyond the 324 * root, however, the buffer may be left intact and a pointer to the section 325 * past altroot will be returned instead for the caller's perusal. 326 */ 327 char * 328 be_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint) 329 { 330 331 if (lbh->altroot_len == 0) 332 return (mountpoint); 333 if (mountpoint == NULL || *mountpoint == '\0') 334 return (mountpoint); 335 336 if (mountpoint[lbh->altroot_len] == '\0') { 337 *(mountpoint + 1) = '\0'; 338 return (mountpoint); 339 } else 340 return (mountpoint + lbh->altroot_len); 341 } 342