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