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