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