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