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, lbh->root, 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 prop_list_builder_cb(root_hdl, &propinfo); 223 zfs_close(root_hdl); 224 } 225 free(info.name); 226 return (0); 227 } 228 return (1); 229 } 230 231 /* 232 * usage 233 */ 234 int 235 be_mount(libbe_handle_t *lbh, const char *bootenv, const char *mountpoint, 236 int flags, char *result_loc) 237 { 238 char be[BE_MAXPATHLEN]; 239 char mnt_temp[BE_MAXPATHLEN]; 240 int mntflags, mntdeep; 241 int err; 242 struct be_mount_info info; 243 zfs_handle_t *zhdl; 244 245 if ((err = be_root_concat(lbh, bootenv, be)) != 0) 246 return (set_error(lbh, err)); 247 248 if ((err = be_exists(lbh, bootenv)) != 0) 249 return (set_error(lbh, err)); 250 251 if (is_mounted(lbh->lzh, be, NULL)) 252 return (set_error(lbh, BE_ERR_MOUNTED)); 253 254 mntdeep = (flags & BE_MNT_DEEP) ? 1 : 0; 255 mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0; 256 257 /* Create mountpoint if it is not specified */ 258 if (mountpoint == NULL) { 259 strlcpy(mnt_temp, "/tmp/be_mount.XXXX", sizeof(mnt_temp)); 260 if (mkdtemp(mnt_temp) == NULL) 261 return (set_error(lbh, BE_ERR_IO)); 262 } 263 264 if ((zhdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL) 265 return (set_error(lbh, BE_ERR_ZFSOPEN)); 266 267 info.lbh = lbh; 268 info.be = be; 269 info.mountpoint = (mountpoint == NULL) ? mnt_temp : mountpoint; 270 info.mntflags = mntflags; 271 info.deepmount = mntdeep; 272 info.depth = 0; 273 274 if((err = be_mount_iter(zhdl, &info) != 0)) { 275 zfs_close(zhdl); 276 return (err); 277 } 278 zfs_close(zhdl); 279 280 if (result_loc != NULL) 281 strlcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint, 282 BE_MAXPATHLEN); 283 284 return (BE_ERR_SUCCESS); 285 } 286 287 /* 288 * usage 289 */ 290 int 291 be_unmount(libbe_handle_t *lbh, const char *bootenv, int flags) 292 { 293 int err; 294 char be[BE_MAXPATHLEN]; 295 zfs_handle_t *root_hdl; 296 struct be_mount_info info; 297 298 if ((err = be_root_concat(lbh, bootenv, be)) != 0) 299 return (set_error(lbh, err)); 300 301 if ((root_hdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL) 302 return (set_error(lbh, BE_ERR_ZFSOPEN)); 303 304 info.lbh = lbh; 305 info.be = be; 306 info.mountpoint = NULL; 307 info.mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0; 308 info.depth = 0; 309 310 if ((err = be_umount_iter(root_hdl, &info)) != 0) { 311 zfs_close(root_hdl); 312 return (err); 313 } 314 315 zfs_close(root_hdl); 316 return (BE_ERR_SUCCESS); 317 } 318 319 /* 320 * This function will blow away the input buffer as needed if we're discovered 321 * to be looking at a root-mount. If the mountpoint is naturally beyond the 322 * root, however, the buffer may be left intact and a pointer to the section 323 * past altroot will be returned instead for the caller's perusal. 324 */ 325 char * 326 be_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint) 327 { 328 329 if (lbh->altroot_len == 0) 330 return (mountpoint); 331 if (mountpoint == NULL || *mountpoint == '\0') 332 return (mountpoint); 333 334 if (mountpoint[lbh->altroot_len] == '\0') { 335 *(mountpoint + 1) = '\0'; 336 return (mountpoint); 337 } else 338 return (mountpoint + lbh->altroot_len); 339 } 340