be_access.c revision 350344
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 * All rights reserved.
8336668Skevans *
9336668Skevans * Redistribution and use in source and binary forms, with or without
10336668Skevans * modification, are permitted provided that the following conditions
11336668Skevans * are met:
12336668Skevans * 1. Redistributions of source code must retain the above copyright
13336668Skevans *    notice, this list of conditions and the following disclaimer.
14336668Skevans * 2. Redistributions in binary form must reproduce the above copyright
15336668Skevans *    notice, this list of conditions and the following disclaimer in the
16336668Skevans *    documentation and/or other materials provided with the distribution.
17336668Skevans *
18336668Skevans * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19336668Skevans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20336668Skevans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21336668Skevans * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22336668Skevans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23336668Skevans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24336668Skevans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25336668Skevans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26336668Skevans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27336668Skevans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28336668Skevans * SUCH DAMAGE.
29336668Skevans */
30336668Skevans
31337416Skevans#include <sys/cdefs.h>
32337416Skevans__FBSDID("$FreeBSD: stable/11/lib/libbe/be_access.c 350344 2019-07-26 01:49:58Z kevans $");
33337416Skevans
34336668Skevans#include "be.h"
35336668Skevans#include "be_impl.h"
36336668Skevans
37336729Skevansstruct be_mountcheck_info {
38336729Skevans	const char *path;
39336729Skevans	char *name;
40336729Skevans};
41336729Skevans
42346429Skevansstruct be_mount_info {
43346429Skevans	libbe_handle_t *lbh;
44346429Skevans	const char *be;
45346429Skevans	const char *mountpoint;
46346429Skevans	int mntflags;
47346429Skevans	int deepmount;
48347170Skevans	int depth;
49346429Skevans};
50346429Skevans
51336729Skevansstatic int
52336729Skevansbe_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data)
53336729Skevans{
54336729Skevans	struct be_mountcheck_info *info;
55336729Skevans	char *mountpoint;
56336729Skevans
57336729Skevans	if (data == NULL)
58336729Skevans		return (1);
59336729Skevans	info = (struct be_mountcheck_info *)data;
60336729Skevans	if (!zfs_is_mounted(zfs_hdl, &mountpoint))
61336729Skevans		return (0);
62336729Skevans	if (strcmp(mountpoint, info->path) == 0) {
63336729Skevans		info->name = strdup(zfs_get_name(zfs_hdl));
64346429Skevans		free(mountpoint);
65336729Skevans		return (1);
66336729Skevans	}
67346429Skevans	free(mountpoint);
68336729Skevans	return (0);
69336729Skevans}
70336729Skevans
71336668Skevans/*
72346429Skevans * Called from be_mount, uses the given zfs_handle and attempts to
73346429Skevans * mount it at the passed mountpoint. If the deepmount flag is set, continue
74346429Skevans * calling the function for each child dataset.
75346429Skevans */
76346429Skevansstatic int
77346429Skevansbe_mount_iter(zfs_handle_t *zfs_hdl, void *data)
78346429Skevans{
79346429Skevans	int err;
80346429Skevans	char *mountpoint;
81346429Skevans	char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN];
82346429Skevans	struct be_mount_info *info;
83347170Skevans	char opt;
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
122347170Skevans	opt = '\0';
123347170Skevans	if ((err = zmount(zfs_get_name(zfs_hdl), tmp, info->mntflags,
124347170Skevans	    __DECONST(char *, MNTTYPE_ZFS), NULL, 0, &opt, 1)) != 0) {
125347170Skevans		switch (errno) {
126347170Skevans		case ENAMETOOLONG:
127347170Skevans			return (set_error(info->lbh, BE_ERR_PATHLEN));
128347170Skevans		case ELOOP:
129347170Skevans		case ENOENT:
130347170Skevans		case ENOTDIR:
131347170Skevans			return (set_error(info->lbh, BE_ERR_BADPATH));
132347170Skevans		case EPERM:
133347170Skevans			return (set_error(info->lbh, BE_ERR_PERMS));
134347170Skevans		case EBUSY:
135347170Skevans			return (set_error(info->lbh, BE_ERR_PATHBUSY));
136347170Skevans		default:
137347170Skevans			return (set_error(info->lbh, BE_ERR_UNKNOWN));
138346429Skevans		}
139346429Skevans	}
140346429Skevans
141346429Skevans	if (!info->deepmount)
142346429Skevans		return (0);
143346429Skevans
144348133Skevansskipmount:
145347170Skevans	++info->depth;
146347170Skevans	err = zfs_iter_filesystems(zfs_hdl, be_mount_iter, info);
147347170Skevans	--info->depth;
148347170Skevans	return (err);
149346429Skevans}
150346429Skevans
151346429Skevans
152346429Skevansstatic int
153346429Skevansbe_umount_iter(zfs_handle_t *zfs_hdl, void *data)
154346429Skevans{
155346429Skevans
156346429Skevans	int err;
157346429Skevans	char *mountpoint;
158346429Skevans	struct be_mount_info *info;
159346429Skevans
160346429Skevans	info = (struct be_mount_info *)data;
161346429Skevans
162347170Skevans	++info->depth;
163346429Skevans	if((err = zfs_iter_filesystems(zfs_hdl, be_umount_iter, info)) != 0) {
164346429Skevans		return (err);
165346429Skevans	}
166347170Skevans	--info->depth;
167346429Skevans
168346429Skevans	if (!zfs_is_mounted(zfs_hdl, &mountpoint)) {
169346429Skevans		return (0);
170346429Skevans	}
171346429Skevans	free(mountpoint);
172346429Skevans
173346429Skevans	if (zfs_unmount(zfs_hdl, NULL, info->mntflags) != 0) {
174346429Skevans		switch (errno) {
175346429Skevans		case ENAMETOOLONG:
176346429Skevans			return (set_error(info->lbh, BE_ERR_PATHLEN));
177346429Skevans		case ELOOP:
178346429Skevans		case ENOENT:
179346429Skevans		case ENOTDIR:
180346429Skevans			return (set_error(info->lbh, BE_ERR_BADPATH));
181346429Skevans		case EPERM:
182346429Skevans			return (set_error(info->lbh, BE_ERR_PERMS));
183346429Skevans		case EBUSY:
184346429Skevans			return (set_error(info->lbh, BE_ERR_PATHBUSY));
185346429Skevans		default:
186346429Skevans			return (set_error(info->lbh, BE_ERR_UNKNOWN));
187346429Skevans		}
188346429Skevans	}
189346429Skevans	return (0);
190346429Skevans}
191346429Skevans
192346429Skevans/*
193336668Skevans * usage
194336668Skevans */
195336668Skevansint
196336729Skevansbe_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details)
197336729Skevans{
198346429Skevans	char be[BE_MAXPATHLEN];
199336729Skevans	zfs_handle_t *root_hdl;
200336729Skevans	struct be_mountcheck_info info;
201336729Skevans	prop_data_t propinfo;
202336729Skevans
203346429Skevans	bzero(&be, BE_MAXPATHLEN);
204336729Skevans	if ((root_hdl = zfs_open(lbh->lzh, lbh->root,
205336729Skevans	    ZFS_TYPE_FILESYSTEM)) == NULL)
206336729Skevans		return (BE_ERR_ZFSOPEN);
207336729Skevans
208336729Skevans	info.path = path;
209336729Skevans	info.name = NULL;
210336729Skevans	zfs_iter_filesystems(root_hdl, be_mountcheck_cb, &info);
211336729Skevans	zfs_close(root_hdl);
212336729Skevans
213336729Skevans	if (info.name != NULL) {
214336729Skevans		if (details != NULL) {
215336729Skevans			if ((root_hdl = zfs_open(lbh->lzh, lbh->root,
216336729Skevans			    ZFS_TYPE_FILESYSTEM)) == NULL) {
217336729Skevans				free(info.name);
218336729Skevans				return (BE_ERR_ZFSOPEN);
219336729Skevans			}
220336729Skevans
221336729Skevans			propinfo.lbh = lbh;
222336729Skevans			propinfo.list = details;
223337228Skevans			propinfo.single_object = false;
224336729Skevans			prop_list_builder_cb(root_hdl, &propinfo);
225336729Skevans			zfs_close(root_hdl);
226336729Skevans		}
227336729Skevans		free(info.name);
228336729Skevans		return (0);
229336729Skevans	}
230336729Skevans	return (1);
231336729Skevans}
232336729Skevans
233336729Skevans/*
234336729Skevans * usage
235336729Skevans */
236336729Skevansint
237336668Skevansbe_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags,
238336668Skevans    char *result_loc)
239336668Skevans{
240336668Skevans	char be[BE_MAXPATHLEN];
241336668Skevans	char mnt_temp[BE_MAXPATHLEN];
242346429Skevans	int mntflags, mntdeep;
243336668Skevans	int err;
244346429Skevans	struct be_mount_info info;
245346429Skevans	zfs_handle_t *zhdl;
246336668Skevans
247336710Skevans	if ((err = be_root_concat(lbh, bootenv, be)) != 0)
248336668Skevans		return (set_error(lbh, err));
249336668Skevans
250346429Skevans	if ((err = be_exists(lbh, bootenv)) != 0)
251346429Skevans		return (set_error(lbh, err));
252336668Skevans
253346429Skevans	if (is_mounted(lbh->lzh, be, NULL))
254336668Skevans		return (set_error(lbh, BE_ERR_MOUNTED));
255336668Skevans
256346429Skevans	mntdeep = (flags & BE_MNT_DEEP) ? 1 : 0;
257336668Skevans	mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0;
258336668Skevans
259336668Skevans	/* Create mountpoint if it is not specified */
260336668Skevans	if (mountpoint == NULL) {
261346429Skevans		strlcpy(mnt_temp, "/tmp/be_mount.XXXX", sizeof(mnt_temp));
262336701Skevans		if (mkdtemp(mnt_temp) == NULL)
263337407Skevans			return (set_error(lbh, BE_ERR_IO));
264336668Skevans	}
265336668Skevans
266346429Skevans	if ((zhdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
267346429Skevans		return (set_error(lbh, BE_ERR_ZFSOPEN));
268346429Skevans
269346429Skevans	info.lbh = lbh;
270346429Skevans	info.be = be;
271346429Skevans	info.mountpoint = (mountpoint == NULL) ? mnt_temp : mountpoint;
272346429Skevans	info.mntflags = mntflags;
273346429Skevans	info.deepmount = mntdeep;
274347170Skevans	info.depth = 0;
275346429Skevans
276346429Skevans	if((err = be_mount_iter(zhdl, &info) != 0)) {
277346429Skevans		zfs_close(zhdl);
278346429Skevans		return (err);
279337407Skevans	}
280346429Skevans	zfs_close(zhdl);
281336668Skevans
282336701Skevans	if (result_loc != NULL)
283346429Skevans		strlcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint,
284346429Skevans		    BE_MAXPATHLEN);
285336668Skevans
286336668Skevans	return (BE_ERR_SUCCESS);
287336668Skevans}
288336668Skevans
289336668Skevans/*
290336668Skevans * usage
291336668Skevans */
292336668Skevansint
293336668Skevansbe_unmount(libbe_handle_t *lbh, char *bootenv, int flags)
294336668Skevans{
295346429Skevans	int err;
296336668Skevans	char be[BE_MAXPATHLEN];
297346429Skevans	zfs_handle_t *root_hdl;
298346429Skevans	struct be_mount_info info;
299336668Skevans
300336710Skevans	if ((err = be_root_concat(lbh, bootenv, be)) != 0)
301336668Skevans		return (set_error(lbh, err));
302336668Skevans
303346429Skevans	if ((root_hdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
304346429Skevans		return (set_error(lbh, BE_ERR_ZFSOPEN));
305336668Skevans
306346429Skevans	info.lbh = lbh;
307346429Skevans	info.be = be;
308346429Skevans	info.mountpoint = NULL;
309346429Skevans	info.mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0;
310347170Skevans	info.depth = 0;
311336668Skevans
312346429Skevans	if ((err = be_umount_iter(root_hdl, &info)) != 0) {
313346429Skevans		zfs_close(root_hdl);
314346429Skevans		return (err);
315336668Skevans	}
316336668Skevans
317346429Skevans	zfs_close(root_hdl);
318346429Skevans	return (BE_ERR_SUCCESS);
319346429Skevans}
320336668Skevans
321346429Skevans/*
322346429Skevans * This function will blow away the input buffer as needed if we're discovered
323346429Skevans * to be looking at a root-mount.  If the mountpoint is naturally beyond the
324346429Skevans * root, however, the buffer may be left intact and a pointer to the section
325346429Skevans * past altroot will be returned instead for the caller's perusal.
326346429Skevans */
327346429Skevanschar *
328346429Skevansbe_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint)
329346429Skevans{
330336668Skevans
331346429Skevans	if (lbh->altroot_len == 0)
332346429Skevans		return (mountpoint);
333346429Skevans	if (mountpoint == NULL || *mountpoint == '\0')
334346429Skevans		return (mountpoint);
335336668Skevans
336346429Skevans	if (mountpoint[lbh->altroot_len] == '\0') {
337346429Skevans		*(mountpoint + 1) = '\0';
338346429Skevans		return (mountpoint);
339346429Skevans	} else
340346429Skevans		return (mountpoint + lbh->altroot_len);
341336668Skevans}
342