1337414Skevans/*-
2337414Skevans * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3336668Skevans *
4336668Skevans * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
5337414Skevans * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
6346429Skevans * Copyright (c) 2019 Wes Maag <wes@jwmaag.org>
7336668Skevans *
8336668Skevans * Redistribution and use in source and binary forms, with or without
9336668Skevans * modification, are permitted provided that the following conditions
10336668Skevans * are met:
11336668Skevans * 1. Redistributions of source code must retain the above copyright
12336668Skevans *    notice, this list of conditions and the following disclaimer.
13336668Skevans * 2. Redistributions in binary form must reproduce the above copyright
14336668Skevans *    notice, this list of conditions and the following disclaimer in the
15336668Skevans *    documentation and/or other materials provided with the distribution.
16336668Skevans *
17336668Skevans * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18336668Skevans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19336668Skevans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20336668Skevans * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21336668Skevans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22336668Skevans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23336668Skevans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24336668Skevans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25336668Skevans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26336668Skevans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27336668Skevans * SUCH DAMAGE.
28336668Skevans */
29336668Skevans
30337416Skevans#include <sys/cdefs.h>
31337416Skevans__FBSDID("$FreeBSD: stable/11/lib/libbe/be_access.c 357667 2020-02-07 21:57:27Z kevans $");
32337416Skevans
33355662Skevans#include <sys/mntent.h>
34355662Skevans
35336668Skevans#include "be.h"
36336668Skevans#include "be_impl.h"
37336668Skevans
38336729Skevansstruct be_mountcheck_info {
39336729Skevans	const char *path;
40336729Skevans	char *name;
41336729Skevans};
42336729Skevans
43346429Skevansstruct be_mount_info {
44346429Skevans	libbe_handle_t *lbh;
45346429Skevans	const char *be;
46346429Skevans	const char *mountpoint;
47346429Skevans	int mntflags;
48346429Skevans	int deepmount;
49347170Skevans	int depth;
50346429Skevans};
51346429Skevans
52336729Skevansstatic int
53336729Skevansbe_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data)
54336729Skevans{
55336729Skevans	struct be_mountcheck_info *info;
56336729Skevans	char *mountpoint;
57336729Skevans
58336729Skevans	if (data == NULL)
59336729Skevans		return (1);
60336729Skevans	info = (struct be_mountcheck_info *)data;
61336729Skevans	if (!zfs_is_mounted(zfs_hdl, &mountpoint))
62336729Skevans		return (0);
63336729Skevans	if (strcmp(mountpoint, info->path) == 0) {
64336729Skevans		info->name = strdup(zfs_get_name(zfs_hdl));
65346429Skevans		free(mountpoint);
66336729Skevans		return (1);
67336729Skevans	}
68346429Skevans	free(mountpoint);
69336729Skevans	return (0);
70336729Skevans}
71336729Skevans
72336668Skevans/*
73346429Skevans * Called from be_mount, uses the given zfs_handle and attempts to
74346429Skevans * mount it at the passed mountpoint. If the deepmount flag is set, continue
75346429Skevans * calling the function for each child dataset.
76346429Skevans */
77346429Skevansstatic int
78346429Skevansbe_mount_iter(zfs_handle_t *zfs_hdl, void *data)
79346429Skevans{
80346429Skevans	int err;
81346429Skevans	char *mountpoint;
82346429Skevans	char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN];
83346429Skevans	struct be_mount_info *info;
84346429Skevans
85346429Skevans	info = (struct be_mount_info *)data;
86346429Skevans
87346429Skevans	if (zfs_is_mounted(zfs_hdl, &mountpoint)) {
88346429Skevans		free(mountpoint);
89346429Skevans		return (0);
90346429Skevans	}
91346429Skevans
92350344Skevans	/*
93350344Skevans	 * canmount and mountpoint are both ignored for the BE dataset, because
94350344Skevans	 * the rest of the system (kernel and loader) will effectively do the
95350344Skevans	 * same.
96350344Skevans	 */
97350344Skevans	if (info->depth == 0) {
98350344Skevans		snprintf(tmp, BE_MAXPATHLEN, "%s", info->mountpoint);
99350344Skevans	} else {
100350344Skevans		if (zfs_prop_get_int(zfs_hdl, ZFS_PROP_CANMOUNT) ==
101350344Skevans		    ZFS_CANMOUNT_OFF)
102350344Skevans			return (0);
103346429Skevans
104350344Skevans		if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, zfs_mnt,
105350344Skevans		    BE_MAXPATHLEN, NULL, NULL, 0, 1))
106350344Skevans			return (1);
107346429Skevans
108347170Skevans		/*
109350344Skevans		 * We've encountered mountpoint=none at some intermediate
110350344Skevans		 * dataset (e.g. zroot/var) that will have children that may
111350344Skevans		 * need to be mounted.  Skip mounting it, but iterate through
112350344Skevans		 * the children.
113347170Skevans		 */
114350344Skevans		if (strcmp("none", zfs_mnt) == 0)
115348133Skevans			goto skipmount;
116346429Skevans
117346429Skevans		mountpoint = be_mountpoint_augmented(info->lbh, zfs_mnt);
118346429Skevans		snprintf(tmp, BE_MAXPATHLEN, "%s%s", info->mountpoint,
119346429Skevans		    mountpoint);
120347170Skevans	}
121346429Skevans
122357001Skevans	if ((err = zfs_mount_at(zfs_hdl, NULL, info->mntflags, tmp)) != 0) {
123347170Skevans		switch (errno) {
124347170Skevans		case ENAMETOOLONG:
125347170Skevans			return (set_error(info->lbh, BE_ERR_PATHLEN));
126347170Skevans		case ELOOP:
127347170Skevans		case ENOENT:
128347170Skevans		case ENOTDIR:
129347170Skevans			return (set_error(info->lbh, BE_ERR_BADPATH));
130347170Skevans		case EPERM:
131347170Skevans			return (set_error(info->lbh, BE_ERR_PERMS));
132347170Skevans		case EBUSY:
133347170Skevans			return (set_error(info->lbh, BE_ERR_PATHBUSY));
134347170Skevans		default:
135347170Skevans			return (set_error(info->lbh, BE_ERR_UNKNOWN));
136346429Skevans		}
137346429Skevans	}
138346429Skevans
139346429Skevans	if (!info->deepmount)
140346429Skevans		return (0);
141346429Skevans
142348133Skevansskipmount:
143347170Skevans	++info->depth;
144347170Skevans	err = zfs_iter_filesystems(zfs_hdl, be_mount_iter, info);
145347170Skevans	--info->depth;
146347170Skevans	return (err);
147346429Skevans}
148346429Skevans
149346429Skevans
150346429Skevansstatic int
151346429Skevansbe_umount_iter(zfs_handle_t *zfs_hdl, void *data)
152346429Skevans{
153346429Skevans
154346429Skevans	int err;
155346429Skevans	char *mountpoint;
156346429Skevans	struct be_mount_info *info;
157346429Skevans
158346429Skevans	info = (struct be_mount_info *)data;
159346429Skevans
160347170Skevans	++info->depth;
161346429Skevans	if((err = zfs_iter_filesystems(zfs_hdl, be_umount_iter, info)) != 0) {
162346429Skevans		return (err);
163346429Skevans	}
164347170Skevans	--info->depth;
165346429Skevans
166346429Skevans	if (!zfs_is_mounted(zfs_hdl, &mountpoint)) {
167346429Skevans		return (0);
168346429Skevans	}
169346429Skevans	free(mountpoint);
170346429Skevans
171346429Skevans	if (zfs_unmount(zfs_hdl, NULL, info->mntflags) != 0) {
172346429Skevans		switch (errno) {
173346429Skevans		case ENAMETOOLONG:
174346429Skevans			return (set_error(info->lbh, BE_ERR_PATHLEN));
175346429Skevans		case ELOOP:
176346429Skevans		case ENOENT:
177346429Skevans		case ENOTDIR:
178346429Skevans			return (set_error(info->lbh, BE_ERR_BADPATH));
179346429Skevans		case EPERM:
180346429Skevans			return (set_error(info->lbh, BE_ERR_PERMS));
181346429Skevans		case EBUSY:
182346429Skevans			return (set_error(info->lbh, BE_ERR_PATHBUSY));
183346429Skevans		default:
184346429Skevans			return (set_error(info->lbh, BE_ERR_UNKNOWN));
185346429Skevans		}
186346429Skevans	}
187346429Skevans	return (0);
188346429Skevans}
189346429Skevans
190346429Skevans/*
191336668Skevans * usage
192336668Skevans */
193336668Skevansint
194336729Skevansbe_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details)
195336729Skevans{
196346429Skevans	char be[BE_MAXPATHLEN];
197336729Skevans	zfs_handle_t *root_hdl;
198336729Skevans	struct be_mountcheck_info info;
199336729Skevans	prop_data_t propinfo;
200336729Skevans
201346429Skevans	bzero(&be, BE_MAXPATHLEN);
202336729Skevans	if ((root_hdl = zfs_open(lbh->lzh, lbh->root,
203336729Skevans	    ZFS_TYPE_FILESYSTEM)) == NULL)
204336729Skevans		return (BE_ERR_ZFSOPEN);
205336729Skevans
206336729Skevans	info.path = path;
207336729Skevans	info.name = NULL;
208336729Skevans	zfs_iter_filesystems(root_hdl, be_mountcheck_cb, &info);
209336729Skevans	zfs_close(root_hdl);
210336729Skevans
211336729Skevans	if (info.name != NULL) {
212336729Skevans		if (details != NULL) {
213336729Skevans			if ((root_hdl = zfs_open(lbh->lzh, lbh->root,
214336729Skevans			    ZFS_TYPE_FILESYSTEM)) == NULL) {
215336729Skevans				free(info.name);
216336729Skevans				return (BE_ERR_ZFSOPEN);
217336729Skevans			}
218336729Skevans
219336729Skevans			propinfo.lbh = lbh;
220336729Skevans			propinfo.list = details;
221337228Skevans			propinfo.single_object = false;
222336729Skevans			prop_list_builder_cb(root_hdl, &propinfo);
223336729Skevans			zfs_close(root_hdl);
224336729Skevans		}
225336729Skevans		free(info.name);
226336729Skevans		return (0);
227336729Skevans	}
228336729Skevans	return (1);
229336729Skevans}
230336729Skevans
231336729Skevans/*
232336729Skevans * usage
233336729Skevans */
234336729Skevansint
235336668Skevansbe_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags,
236336668Skevans    char *result_loc)
237336668Skevans{
238336668Skevans	char be[BE_MAXPATHLEN];
239336668Skevans	char mnt_temp[BE_MAXPATHLEN];
240346429Skevans	int mntflags, mntdeep;
241336668Skevans	int err;
242346429Skevans	struct be_mount_info info;
243346429Skevans	zfs_handle_t *zhdl;
244336668Skevans
245336710Skevans	if ((err = be_root_concat(lbh, bootenv, be)) != 0)
246336668Skevans		return (set_error(lbh, err));
247336668Skevans
248346429Skevans	if ((err = be_exists(lbh, bootenv)) != 0)
249346429Skevans		return (set_error(lbh, err));
250336668Skevans
251346429Skevans	if (is_mounted(lbh->lzh, be, NULL))
252336668Skevans		return (set_error(lbh, BE_ERR_MOUNTED));
253336668Skevans
254346429Skevans	mntdeep = (flags & BE_MNT_DEEP) ? 1 : 0;
255336668Skevans	mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0;
256336668Skevans
257336668Skevans	/* Create mountpoint if it is not specified */
258336668Skevans	if (mountpoint == NULL) {
259346429Skevans		strlcpy(mnt_temp, "/tmp/be_mount.XXXX", sizeof(mnt_temp));
260336701Skevans		if (mkdtemp(mnt_temp) == NULL)
261337407Skevans			return (set_error(lbh, BE_ERR_IO));
262336668Skevans	}
263336668Skevans
264346429Skevans	if ((zhdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
265346429Skevans		return (set_error(lbh, BE_ERR_ZFSOPEN));
266346429Skevans
267346429Skevans	info.lbh = lbh;
268346429Skevans	info.be = be;
269346429Skevans	info.mountpoint = (mountpoint == NULL) ? mnt_temp : mountpoint;
270346429Skevans	info.mntflags = mntflags;
271346429Skevans	info.deepmount = mntdeep;
272347170Skevans	info.depth = 0;
273346429Skevans
274346429Skevans	if((err = be_mount_iter(zhdl, &info) != 0)) {
275346429Skevans		zfs_close(zhdl);
276346429Skevans		return (err);
277337407Skevans	}
278346429Skevans	zfs_close(zhdl);
279336668Skevans
280336701Skevans	if (result_loc != NULL)
281346429Skevans		strlcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint,
282346429Skevans		    BE_MAXPATHLEN);
283336668Skevans
284336668Skevans	return (BE_ERR_SUCCESS);
285336668Skevans}
286336668Skevans
287336668Skevans/*
288336668Skevans * usage
289336668Skevans */
290336668Skevansint
291336668Skevansbe_unmount(libbe_handle_t *lbh, char *bootenv, int flags)
292336668Skevans{
293346429Skevans	int err;
294336668Skevans	char be[BE_MAXPATHLEN];
295346429Skevans	zfs_handle_t *root_hdl;
296346429Skevans	struct be_mount_info info;
297336668Skevans
298336710Skevans	if ((err = be_root_concat(lbh, bootenv, be)) != 0)
299336668Skevans		return (set_error(lbh, err));
300336668Skevans
301346429Skevans	if ((root_hdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
302346429Skevans		return (set_error(lbh, BE_ERR_ZFSOPEN));
303336668Skevans
304346429Skevans	info.lbh = lbh;
305346429Skevans	info.be = be;
306346429Skevans	info.mountpoint = NULL;
307346429Skevans	info.mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0;
308347170Skevans	info.depth = 0;
309336668Skevans
310346429Skevans	if ((err = be_umount_iter(root_hdl, &info)) != 0) {
311346429Skevans		zfs_close(root_hdl);
312346429Skevans		return (err);
313336668Skevans	}
314336668Skevans
315346429Skevans	zfs_close(root_hdl);
316346429Skevans	return (BE_ERR_SUCCESS);
317346429Skevans}
318336668Skevans
319346429Skevans/*
320346429Skevans * This function will blow away the input buffer as needed if we're discovered
321346429Skevans * to be looking at a root-mount.  If the mountpoint is naturally beyond the
322346429Skevans * root, however, the buffer may be left intact and a pointer to the section
323346429Skevans * past altroot will be returned instead for the caller's perusal.
324346429Skevans */
325346429Skevanschar *
326346429Skevansbe_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint)
327346429Skevans{
328336668Skevans
329346429Skevans	if (lbh->altroot_len == 0)
330346429Skevans		return (mountpoint);
331346429Skevans	if (mountpoint == NULL || *mountpoint == '\0')
332346429Skevans		return (mountpoint);
333336668Skevans
334346429Skevans	if (mountpoint[lbh->altroot_len] == '\0') {
335346429Skevans		*(mountpoint + 1) = '\0';
336346429Skevans		return (mountpoint);
337346429Skevans	} else
338346429Skevans		return (mountpoint + lbh->altroot_len);
339336668Skevans}
340