xref: /freebsd/lib/libbe/be_access.c (revision 54d2737e7fe48226c908dcccfbda2ca1c08e07fc)
1*54d2737eSAlexander Ziaee /*
228f16a0fSKyle Evans  * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
3b179da01SKyle Evans  * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
40a603a6eSKyle Evans  * Copyright (c) 2019 Wes Maag <wes@jwmaag.org>
528f16a0fSKyle Evans  *
6*54d2737eSAlexander Ziaee  * SPDX-License-Identifier: BSD-2-Clause
728f16a0fSKyle Evans  */
828f16a0fSKyle Evans 
9b6e7c421SKyle Evans #include <sys/cdefs.h>
10485172f5SKyle Evans #include <sys/mntent.h>
11485172f5SKyle Evans 
1228f16a0fSKyle Evans #include "be.h"
1328f16a0fSKyle Evans #include "be_impl.h"
1428f16a0fSKyle Evans 
152f11393fSKyle Evans #define	LIBBE_MOUNT_PREFIX	"be_mount."
16d6fbae08SKyle Evans 
17843e39ceSKyle Evans struct be_mountcheck_info {
18843e39ceSKyle Evans 	const char *path;
19843e39ceSKyle Evans 	char *name;
20843e39ceSKyle Evans };
21843e39ceSKyle Evans 
220a603a6eSKyle Evans struct be_mount_info {
230a603a6eSKyle Evans 	libbe_handle_t *lbh;
240a603a6eSKyle Evans 	const char *be;
250a603a6eSKyle Evans 	const char *mountpoint;
260a603a6eSKyle Evans 	int mntflags;
270a603a6eSKyle Evans 	int deepmount;
28011fdcbfSKyle Evans 	int depth;
290a603a6eSKyle Evans };
300a603a6eSKyle Evans 
31843e39ceSKyle Evans static int
be_mountcheck_cb(zfs_handle_t * zfs_hdl,void * data)32843e39ceSKyle Evans be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data)
33843e39ceSKyle Evans {
34843e39ceSKyle Evans 	struct be_mountcheck_info *info;
35843e39ceSKyle Evans 	char *mountpoint;
36843e39ceSKyle Evans 
37843e39ceSKyle Evans 	if (data == NULL)
38843e39ceSKyle Evans 		return (1);
39843e39ceSKyle Evans 	info = (struct be_mountcheck_info *)data;
40843e39ceSKyle Evans 	if (!zfs_is_mounted(zfs_hdl, &mountpoint))
41843e39ceSKyle Evans 		return (0);
42843e39ceSKyle Evans 	if (strcmp(mountpoint, info->path) == 0) {
43843e39ceSKyle Evans 		info->name = strdup(zfs_get_name(zfs_hdl));
44cc4deabcSKyle Evans 		free(mountpoint);
45843e39ceSKyle Evans 		return (1);
46843e39ceSKyle Evans 	}
47cc4deabcSKyle Evans 	free(mountpoint);
48843e39ceSKyle Evans 	return (0);
49843e39ceSKyle Evans }
50843e39ceSKyle Evans 
51843e39ceSKyle Evans /*
520a603a6eSKyle Evans  * Called from be_mount, uses the given zfs_handle and attempts to
530a603a6eSKyle Evans  * mount it at the passed mountpoint. If the deepmount flag is set, continue
540a603a6eSKyle Evans  * calling the function for each child dataset.
550a603a6eSKyle Evans  */
560a603a6eSKyle Evans static int
be_mount_iter(zfs_handle_t * zfs_hdl,void * data)570a603a6eSKyle Evans be_mount_iter(zfs_handle_t *zfs_hdl, void *data)
580a603a6eSKyle Evans {
590a603a6eSKyle Evans 	int err;
600a603a6eSKyle Evans 	char *mountpoint;
610a603a6eSKyle Evans 	char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN];
620a603a6eSKyle Evans 	struct be_mount_info *info;
630a603a6eSKyle Evans 
640a603a6eSKyle Evans 	info = (struct be_mount_info *)data;
650a603a6eSKyle Evans 
660a603a6eSKyle Evans 	if (zfs_is_mounted(zfs_hdl, &mountpoint)) {
670a603a6eSKyle Evans 		free(mountpoint);
680a603a6eSKyle Evans 		return (0);
690a603a6eSKyle Evans 	}
700a603a6eSKyle Evans 
71011fdcbfSKyle Evans 	/*
723afb4dc2SKyle Evans 	 * canmount and mountpoint are both ignored for the BE dataset, because
733afb4dc2SKyle Evans 	 * the rest of the system (kernel and loader) will effectively do the
743afb4dc2SKyle Evans 	 * same.
75011fdcbfSKyle Evans 	 */
763afb4dc2SKyle Evans 	if (info->depth == 0) {
77011fdcbfSKyle Evans 		snprintf(tmp, BE_MAXPATHLEN, "%s", info->mountpoint);
78011fdcbfSKyle Evans 	} else {
793afb4dc2SKyle Evans 		if (zfs_prop_get_int(zfs_hdl, ZFS_PROP_CANMOUNT) ==
803afb4dc2SKyle Evans 		    ZFS_CANMOUNT_OFF)
813afb4dc2SKyle Evans 			return (0);
823afb4dc2SKyle Evans 
833afb4dc2SKyle Evans 		if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, zfs_mnt,
843afb4dc2SKyle Evans 		    BE_MAXPATHLEN, NULL, NULL, 0, 1))
853afb4dc2SKyle Evans 			return (1);
863afb4dc2SKyle Evans 
873afb4dc2SKyle Evans 		/*
883afb4dc2SKyle Evans 		 * We've encountered mountpoint=none at some intermediate
893afb4dc2SKyle Evans 		 * dataset (e.g. zroot/var) that will have children that may
903afb4dc2SKyle Evans 		 * need to be mounted.  Skip mounting it, but iterate through
913afb4dc2SKyle Evans 		 * the children.
923afb4dc2SKyle Evans 		 */
933afb4dc2SKyle Evans 		if (strcmp("none", zfs_mnt) == 0)
943afb4dc2SKyle Evans 			goto skipmount;
953afb4dc2SKyle Evans 
960a603a6eSKyle Evans 		mountpoint = be_mountpoint_augmented(info->lbh, zfs_mnt);
970a603a6eSKyle Evans 		snprintf(tmp, BE_MAXPATHLEN, "%s%s", info->mountpoint,
980a603a6eSKyle Evans 		    mountpoint);
99011fdcbfSKyle Evans 	}
1000a603a6eSKyle Evans 
101c7a19fd7SKyle Evans 	if ((err = zfs_mount_at(zfs_hdl, NULL, info->mntflags, tmp)) != 0) {
1020a603a6eSKyle Evans 		switch (errno) {
1030a603a6eSKyle Evans 		case ENAMETOOLONG:
1040a603a6eSKyle Evans 			return (set_error(info->lbh, BE_ERR_PATHLEN));
1050a603a6eSKyle Evans 		case ELOOP:
1060a603a6eSKyle Evans 		case ENOENT:
1070a603a6eSKyle Evans 		case ENOTDIR:
1080a603a6eSKyle Evans 			return (set_error(info->lbh, BE_ERR_BADPATH));
1090a603a6eSKyle Evans 		case EPERM:
1100a603a6eSKyle Evans 			return (set_error(info->lbh, BE_ERR_PERMS));
1110a603a6eSKyle Evans 		case EBUSY:
1120a603a6eSKyle Evans 			return (set_error(info->lbh, BE_ERR_PATHBUSY));
1130a603a6eSKyle Evans 		default:
1140a603a6eSKyle Evans 			return (set_error(info->lbh, BE_ERR_UNKNOWN));
1150a603a6eSKyle Evans 		}
1160a603a6eSKyle Evans 	}
1170a603a6eSKyle Evans 
1180a603a6eSKyle Evans 	if (!info->deepmount)
1190a603a6eSKyle Evans 		return (0);
1200a603a6eSKyle Evans 
12188a95076SKyle Evans skipmount:
122011fdcbfSKyle Evans 	++info->depth;
123d411c1d6SMartin Matuska 	err = zfs_iter_filesystems(zfs_hdl, be_mount_iter, info);
124011fdcbfSKyle Evans 	--info->depth;
125011fdcbfSKyle Evans 	return (err);
1260a603a6eSKyle Evans }
1270a603a6eSKyle Evans 
1280a603a6eSKyle Evans 
1290a603a6eSKyle Evans static int
be_umount_iter(zfs_handle_t * zfs_hdl,void * data)1300a603a6eSKyle Evans be_umount_iter(zfs_handle_t *zfs_hdl, void *data)
1310a603a6eSKyle Evans {
1320a603a6eSKyle Evans 
1330a603a6eSKyle Evans 	int err;
1340a603a6eSKyle Evans 	char *mountpoint;
1350a603a6eSKyle Evans 	struct be_mount_info *info;
1360a603a6eSKyle Evans 
1370a603a6eSKyle Evans 	info = (struct be_mount_info *)data;
1380a603a6eSKyle Evans 
139011fdcbfSKyle Evans 	++info->depth;
140d411c1d6SMartin Matuska 	if((err = zfs_iter_filesystems(zfs_hdl, be_umount_iter, info)) != 0) {
1410a603a6eSKyle Evans 		return (err);
1420a603a6eSKyle Evans 	}
143011fdcbfSKyle Evans 	--info->depth;
1440a603a6eSKyle Evans 
1450a603a6eSKyle Evans 	if (!zfs_is_mounted(zfs_hdl, &mountpoint)) {
1460a603a6eSKyle Evans 		return (0);
1470a603a6eSKyle Evans 	}
148d6fbae08SKyle Evans 
149d6fbae08SKyle Evans 	if (info->depth == 0 && info->mountpoint == NULL)
150d6fbae08SKyle Evans 		info->mountpoint = mountpoint;
151d6fbae08SKyle Evans 	else
1520a603a6eSKyle Evans 		free(mountpoint);
1530a603a6eSKyle Evans 
1540a603a6eSKyle Evans 	if (zfs_unmount(zfs_hdl, NULL, info->mntflags) != 0) {
1550a603a6eSKyle Evans 		switch (errno) {
1560a603a6eSKyle Evans 		case ENAMETOOLONG:
1570a603a6eSKyle Evans 			return (set_error(info->lbh, BE_ERR_PATHLEN));
1580a603a6eSKyle Evans 		case ELOOP:
1590a603a6eSKyle Evans 		case ENOENT:
1600a603a6eSKyle Evans 		case ENOTDIR:
1610a603a6eSKyle Evans 			return (set_error(info->lbh, BE_ERR_BADPATH));
1620a603a6eSKyle Evans 		case EPERM:
1630a603a6eSKyle Evans 			return (set_error(info->lbh, BE_ERR_PERMS));
1640a603a6eSKyle Evans 		case EBUSY:
1650a603a6eSKyle Evans 			return (set_error(info->lbh, BE_ERR_PATHBUSY));
1660a603a6eSKyle Evans 		default:
1670a603a6eSKyle Evans 			return (set_error(info->lbh, BE_ERR_UNKNOWN));
1680a603a6eSKyle Evans 		}
1690a603a6eSKyle Evans 	}
1700a603a6eSKyle Evans 	return (0);
1710a603a6eSKyle Evans }
1720a603a6eSKyle Evans 
1730a603a6eSKyle Evans /*
174843e39ceSKyle Evans  * usage
175843e39ceSKyle Evans  */
176843e39ceSKyle Evans int
be_mounted_at(libbe_handle_t * lbh,const char * path,nvlist_t * details)177843e39ceSKyle Evans be_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details)
178843e39ceSKyle Evans {
17955b0e92bSKyle Evans 	char be[BE_MAXPATHLEN];
180843e39ceSKyle Evans 	zfs_handle_t *root_hdl;
181843e39ceSKyle Evans 	struct be_mountcheck_info info;
182843e39ceSKyle Evans 	prop_data_t propinfo;
183843e39ceSKyle Evans 
18455b0e92bSKyle Evans 	bzero(&be, BE_MAXPATHLEN);
185843e39ceSKyle Evans 	if ((root_hdl = zfs_open(lbh->lzh, lbh->root,
186843e39ceSKyle Evans 	    ZFS_TYPE_FILESYSTEM)) == NULL)
187843e39ceSKyle Evans 		return (BE_ERR_ZFSOPEN);
188843e39ceSKyle Evans 
189843e39ceSKyle Evans 	info.path = path;
190843e39ceSKyle Evans 	info.name = NULL;
191d411c1d6SMartin Matuska 	zfs_iter_filesystems(root_hdl, be_mountcheck_cb, &info);
192843e39ceSKyle Evans 	zfs_close(root_hdl);
193843e39ceSKyle Evans 
194843e39ceSKyle Evans 	if (info.name != NULL) {
195843e39ceSKyle Evans 		if (details != NULL) {
19631190aa0SKyle Evans 			if ((root_hdl = zfs_open(lbh->lzh, info.name,
197843e39ceSKyle Evans 			    ZFS_TYPE_FILESYSTEM)) == NULL) {
198843e39ceSKyle Evans 				free(info.name);
199843e39ceSKyle Evans 				return (BE_ERR_ZFSOPEN);
200843e39ceSKyle Evans 			}
201843e39ceSKyle Evans 
202843e39ceSKyle Evans 			propinfo.lbh = lbh;
203843e39ceSKyle Evans 			propinfo.list = details;
2044146029bSKyle Evans 			propinfo.single_object = false;
205d155d8e1SKyle Evans 			propinfo.bootonce = NULL;
206843e39ceSKyle Evans 			prop_list_builder_cb(root_hdl, &propinfo);
207843e39ceSKyle Evans 			zfs_close(root_hdl);
208843e39ceSKyle Evans 		}
209843e39ceSKyle Evans 		free(info.name);
210843e39ceSKyle Evans 		return (0);
211843e39ceSKyle Evans 	}
212843e39ceSKyle Evans 	return (1);
213843e39ceSKyle Evans }
214843e39ceSKyle Evans 
21528f16a0fSKyle Evans /*
21628f16a0fSKyle Evans  * usage
21728f16a0fSKyle Evans  */
21828f16a0fSKyle Evans int
be_mount(libbe_handle_t * lbh,const char * bootenv,const char * mountpoint,int flags,char * result_loc)2195773e924SKyle Evans be_mount(libbe_handle_t *lbh, const char *bootenv, const char *mountpoint,
2205773e924SKyle Evans     int flags, char *result_loc)
22128f16a0fSKyle Evans {
22228f16a0fSKyle Evans 	char be[BE_MAXPATHLEN];
22328f16a0fSKyle Evans 	char mnt_temp[BE_MAXPATHLEN];
2240a603a6eSKyle Evans 	int mntflags, mntdeep;
22528f16a0fSKyle Evans 	int err;
2260a603a6eSKyle Evans 	struct be_mount_info info;
2270a603a6eSKyle Evans 	zfs_handle_t *zhdl;
22828f16a0fSKyle Evans 
229b29bf2f8SKyle Evans 	if ((err = be_root_concat(lbh, bootenv, be)) != 0)
23028f16a0fSKyle Evans 		return (set_error(lbh, err));
23128f16a0fSKyle Evans 
232162ec569SKyle Evans 	if ((err = be_exists(lbh, bootenv)) != 0)
233162ec569SKyle Evans 		return (set_error(lbh, err));
23428f16a0fSKyle Evans 
235cc4deabcSKyle Evans 	if (is_mounted(lbh->lzh, be, NULL))
23628f16a0fSKyle Evans 		return (set_error(lbh, BE_ERR_MOUNTED));
23728f16a0fSKyle Evans 
2380a603a6eSKyle Evans 	mntdeep = (flags & BE_MNT_DEEP) ? 1 : 0;
23928f16a0fSKyle Evans 	mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0;
24028f16a0fSKyle Evans 
24128f16a0fSKyle Evans 	/* Create mountpoint if it is not specified */
24228f16a0fSKyle Evans 	if (mountpoint == NULL) {
2432f11393fSKyle Evans 		const char *tmpdir;
2442f11393fSKyle Evans 
2452f11393fSKyle Evans 		tmpdir = getenv("TMPDIR");
2462f11393fSKyle Evans 		if (tmpdir == NULL)
2472f11393fSKyle Evans 			tmpdir = _PATH_TMP;
2482f11393fSKyle Evans 
2492f11393fSKyle Evans 		if (snprintf(mnt_temp, sizeof(mnt_temp), "%s%s%sXXXX", tmpdir,
2502f11393fSKyle Evans 		    tmpdir[strlen(tmpdir) - 1] == '/' ? "" : "/",
2512f11393fSKyle Evans 		     LIBBE_MOUNT_PREFIX) >= (int)sizeof(mnt_temp))
2522f11393fSKyle Evans 			return (set_error(lbh, BE_ERR_PATHLEN));
2532f11393fSKyle Evans 
254bfe0869cSKyle Evans 		if (mkdtemp(mnt_temp) == NULL)
255f1ca70d3SKyle Evans 			return (set_error(lbh, BE_ERR_IO));
25628f16a0fSKyle Evans 	}
25728f16a0fSKyle Evans 
2580a603a6eSKyle Evans 	if ((zhdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
2590a603a6eSKyle Evans 		return (set_error(lbh, BE_ERR_ZFSOPEN));
2600a603a6eSKyle Evans 
2610a603a6eSKyle Evans 	info.lbh = lbh;
2620a603a6eSKyle Evans 	info.be = be;
2630a603a6eSKyle Evans 	info.mountpoint = (mountpoint == NULL) ? mnt_temp : mountpoint;
2640a603a6eSKyle Evans 	info.mntflags = mntflags;
2650a603a6eSKyle Evans 	info.deepmount = mntdeep;
266011fdcbfSKyle Evans 	info.depth = 0;
2670a603a6eSKyle Evans 
2680a603a6eSKyle Evans 	if((err = be_mount_iter(zhdl, &info) != 0)) {
2690a603a6eSKyle Evans 		zfs_close(zhdl);
2700a603a6eSKyle Evans 		return (err);
271f1ca70d3SKyle Evans 	}
2720a603a6eSKyle Evans 	zfs_close(zhdl);
27328f16a0fSKyle Evans 
274bfe0869cSKyle Evans 	if (result_loc != NULL)
275a8e44f4dSKyle Evans 		strlcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint,
276a8e44f4dSKyle Evans 		    BE_MAXPATHLEN);
27728f16a0fSKyle Evans 
27828f16a0fSKyle Evans 	return (BE_ERR_SUCCESS);
27928f16a0fSKyle Evans }
28028f16a0fSKyle Evans 
28128f16a0fSKyle Evans /*
28228f16a0fSKyle Evans  * usage
28328f16a0fSKyle Evans  */
28428f16a0fSKyle Evans int
be_unmount(libbe_handle_t * lbh,const char * bootenv,int flags)2855773e924SKyle Evans be_unmount(libbe_handle_t *lbh, const char *bootenv, int flags)
28628f16a0fSKyle Evans {
2870a603a6eSKyle Evans 	int err;
28828f16a0fSKyle Evans 	char be[BE_MAXPATHLEN];
289efd6500dSKyle Evans 	zfs_handle_t *root_hdl;
2900a603a6eSKyle Evans 	struct be_mount_info info;
29128f16a0fSKyle Evans 
292b29bf2f8SKyle Evans 	if ((err = be_root_concat(lbh, bootenv, be)) != 0)
29328f16a0fSKyle Evans 		return (set_error(lbh, err));
29428f16a0fSKyle Evans 
295efd6500dSKyle Evans 	if ((root_hdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
296efd6500dSKyle Evans 		return (set_error(lbh, BE_ERR_ZFSOPEN));
29728f16a0fSKyle Evans 
2980a603a6eSKyle Evans 	info.lbh = lbh;
2990a603a6eSKyle Evans 	info.be = be;
3000a603a6eSKyle Evans 	info.mountpoint = NULL;
3010a603a6eSKyle Evans 	info.mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0;
302011fdcbfSKyle Evans 	info.depth = 0;
30328f16a0fSKyle Evans 
3040a603a6eSKyle Evans 	if ((err = be_umount_iter(root_hdl, &info)) != 0) {
305d6fbae08SKyle Evans 		free(__DECONST(char *, info.mountpoint));
306efd6500dSKyle Evans 		zfs_close(root_hdl);
3070a603a6eSKyle Evans 		return (err);
308f1ca70d3SKyle Evans 	}
30928f16a0fSKyle Evans 
310d6fbae08SKyle Evans 	/*
311d6fbae08SKyle Evans 	 * We'll attempt to remove the directory if we created it on a
312d6fbae08SKyle Evans 	 * best-effort basis.  rmdir(2) failure will not be reported.
313d6fbae08SKyle Evans 	 */
314d6fbae08SKyle Evans 	if (info.mountpoint != NULL) {
315d6fbae08SKyle Evans 		const char *mdir;
316d6fbae08SKyle Evans 
317d6fbae08SKyle Evans 		mdir = strrchr(info.mountpoint, '/');
318d6fbae08SKyle Evans 		if (mdir == NULL)
319d6fbae08SKyle Evans 			mdir = info.mountpoint;
320d6fbae08SKyle Evans 		else
321d6fbae08SKyle Evans 			mdir++;
322d6fbae08SKyle Evans 
323d6fbae08SKyle Evans 		if (strncmp(mdir, LIBBE_MOUNT_PREFIX,
324d6fbae08SKyle Evans 		    sizeof(LIBBE_MOUNT_PREFIX) - 1) == 0) {
325d6fbae08SKyle Evans 			(void)rmdir(info.mountpoint);
326d6fbae08SKyle Evans 		}
327d6fbae08SKyle Evans 	}
328d6fbae08SKyle Evans 
329d6fbae08SKyle Evans 	free(__DECONST(char *, info.mountpoint));
330d6fbae08SKyle Evans 
3310a603a6eSKyle Evans 	zfs_close(root_hdl);
332efd6500dSKyle Evans 	return (BE_ERR_SUCCESS);
33328f16a0fSKyle Evans }
334fc13fc1cSKyle Evans 
335fc13fc1cSKyle Evans /*
336fc13fc1cSKyle Evans  * This function will blow away the input buffer as needed if we're discovered
337fc13fc1cSKyle Evans  * to be looking at a root-mount.  If the mountpoint is naturally beyond the
338fc13fc1cSKyle Evans  * root, however, the buffer may be left intact and a pointer to the section
339fc13fc1cSKyle Evans  * past altroot will be returned instead for the caller's perusal.
340fc13fc1cSKyle Evans  */
341fc13fc1cSKyle Evans char *
be_mountpoint_augmented(libbe_handle_t * lbh,char * mountpoint)342fc13fc1cSKyle Evans be_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint)
343fc13fc1cSKyle Evans {
344fc13fc1cSKyle Evans 
345fc13fc1cSKyle Evans 	if (lbh->altroot_len == 0)
346fc13fc1cSKyle Evans 		return (mountpoint);
347fc13fc1cSKyle Evans 	if (mountpoint == NULL || *mountpoint == '\0')
348fc13fc1cSKyle Evans 		return (mountpoint);
349fc13fc1cSKyle Evans 
350fc13fc1cSKyle Evans 	if (mountpoint[lbh->altroot_len] == '\0') {
351fc13fc1cSKyle Evans 		*(mountpoint + 1) = '\0';
352fc13fc1cSKyle Evans 		return (mountpoint);
353fc13fc1cSKyle Evans 	} else
354fc13fc1cSKyle Evans 		return (mountpoint + lbh->altroot_len);
355fc13fc1cSKyle Evans }
356