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 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/mntent.h> 34 35 #include "be.h" 36 #include "be_impl.h" 37 38 struct be_mountcheck_info { 39 const char *path; 40 char *name; 41 }; 42 43 struct be_mount_info { 44 libbe_handle_t *lbh; 45 const char *be; 46 const char *mountpoint; 47 int mntflags; 48 int deepmount; 49 int depth; 50 }; 51 52 static int 53 be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data) 54 { 55 struct be_mountcheck_info *info; 56 char *mountpoint; 57 58 if (data == NULL) 59 return (1); 60 info = (struct be_mountcheck_info *)data; 61 if (!zfs_is_mounted(zfs_hdl, &mountpoint)) 62 return (0); 63 if (strcmp(mountpoint, info->path) == 0) { 64 info->name = strdup(zfs_get_name(zfs_hdl)); 65 free(mountpoint); 66 return (1); 67 } 68 free(mountpoint); 69 return (0); 70 } 71 72 /* 73 * Called from be_mount, uses the given zfs_handle and attempts to 74 * mount it at the passed mountpoint. If the deepmount flag is set, continue 75 * calling the function for each child dataset. 76 */ 77 static int 78 be_mount_iter(zfs_handle_t *zfs_hdl, void *data) 79 { 80 int err; 81 char *mountpoint; 82 char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN]; 83 struct be_mount_info *info; 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 if ((err = zfs_mount_at(zfs_hdl, NULL, info->mntflags, tmp)) != 0) { 123 switch (errno) { 124 case ENAMETOOLONG: 125 return (set_error(info->lbh, BE_ERR_PATHLEN)); 126 case ELOOP: 127 case ENOENT: 128 case ENOTDIR: 129 return (set_error(info->lbh, BE_ERR_BADPATH)); 130 case EPERM: 131 return (set_error(info->lbh, BE_ERR_PERMS)); 132 case EBUSY: 133 return (set_error(info->lbh, BE_ERR_PATHBUSY)); 134 default: 135 return (set_error(info->lbh, BE_ERR_UNKNOWN)); 136 } 137 } 138 139 if (!info->deepmount) 140 return (0); 141 142 skipmount: 143 ++info->depth; 144 err = zfs_iter_filesystems(zfs_hdl, be_mount_iter, info); 145 --info->depth; 146 return (err); 147 } 148 149 150 static int 151 be_umount_iter(zfs_handle_t *zfs_hdl, void *data) 152 { 153 154 int err; 155 char *mountpoint; 156 struct be_mount_info *info; 157 158 info = (struct be_mount_info *)data; 159 160 ++info->depth; 161 if((err = zfs_iter_filesystems(zfs_hdl, be_umount_iter, info)) != 0) { 162 return (err); 163 } 164 --info->depth; 165 166 if (!zfs_is_mounted(zfs_hdl, &mountpoint)) { 167 return (0); 168 } 169 free(mountpoint); 170 171 if (zfs_unmount(zfs_hdl, NULL, info->mntflags) != 0) { 172 switch (errno) { 173 case ENAMETOOLONG: 174 return (set_error(info->lbh, BE_ERR_PATHLEN)); 175 case ELOOP: 176 case ENOENT: 177 case ENOTDIR: 178 return (set_error(info->lbh, BE_ERR_BADPATH)); 179 case EPERM: 180 return (set_error(info->lbh, BE_ERR_PERMS)); 181 case EBUSY: 182 return (set_error(info->lbh, BE_ERR_PATHBUSY)); 183 default: 184 return (set_error(info->lbh, BE_ERR_UNKNOWN)); 185 } 186 } 187 return (0); 188 } 189 190 /* 191 * usage 192 */ 193 int 194 be_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details) 195 { 196 char be[BE_MAXPATHLEN]; 197 zfs_handle_t *root_hdl; 198 struct be_mountcheck_info info; 199 prop_data_t propinfo; 200 201 bzero(&be, BE_MAXPATHLEN); 202 if ((root_hdl = zfs_open(lbh->lzh, lbh->root, 203 ZFS_TYPE_FILESYSTEM)) == NULL) 204 return (BE_ERR_ZFSOPEN); 205 206 info.path = path; 207 info.name = NULL; 208 zfs_iter_filesystems(root_hdl, be_mountcheck_cb, &info); 209 zfs_close(root_hdl); 210 211 if (info.name != NULL) { 212 if (details != NULL) { 213 if ((root_hdl = zfs_open(lbh->lzh, info.name, 214 ZFS_TYPE_FILESYSTEM)) == NULL) { 215 free(info.name); 216 return (BE_ERR_ZFSOPEN); 217 } 218 219 propinfo.lbh = lbh; 220 propinfo.list = details; 221 propinfo.single_object = false; 222 propinfo.bootonce = NULL; 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, const char *bootenv, const char *mountpoint, 237 int flags, 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, const 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