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