xref: /freebsd/lib/libbe/be_access.c (revision 0957b409)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include "be.h"
35 #include "be_impl.h"
36 
37 struct be_mountcheck_info {
38 	const char *path;
39 	char *name;
40 };
41 
42 struct be_mount_info {
43 	libbe_handle_t *lbh;
44 	const char *be;
45 	const char *mountpoint;
46 	int mntflags;
47 	int deepmount;
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 	if (zfs_prop_get_int(zfs_hdl, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF)
91 		return (0);
92 
93 	if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, zfs_mnt, BE_MAXPATHLEN,
94 	    NULL, NULL, 0, 1))
95 		return (1);
96 
97 	if (strcmp("none", zfs_mnt) != 0) {
98 		char opt = '\0';
99 
100 		mountpoint = be_mountpoint_augmented(info->lbh, zfs_mnt);
101 
102 		snprintf(tmp, BE_MAXPATHLEN, "%s%s", info->mountpoint,
103 		    mountpoint);
104 
105 		if ((err = zmount(zfs_get_name(zfs_hdl), tmp, info->mntflags,
106 		 __DECONST(char *, MNTTYPE_ZFS), NULL, 0, &opt, 1)) != 0) {
107 			switch (errno) {
108 			case ENAMETOOLONG:
109 				return (set_error(info->lbh, BE_ERR_PATHLEN));
110 			case ELOOP:
111 			case ENOENT:
112 			case ENOTDIR:
113 				return (set_error(info->lbh, BE_ERR_BADPATH));
114 			case EPERM:
115 				return (set_error(info->lbh, BE_ERR_PERMS));
116 			case EBUSY:
117 				return (set_error(info->lbh, BE_ERR_PATHBUSY));
118 			default:
119 				return (set_error(info->lbh, BE_ERR_UNKNOWN));
120 			}
121 		}
122 	}
123 
124 	if (!info->deepmount)
125 		return (0);
126 
127 	return (zfs_iter_filesystems(zfs_hdl, be_mount_iter, info));
128 }
129 
130 
131 static int
132 be_umount_iter(zfs_handle_t *zfs_hdl, void *data)
133 {
134 
135 	int err;
136 	char *mountpoint;
137 	struct be_mount_info *info;
138 
139 	info = (struct be_mount_info *)data;
140 
141 	if((err = zfs_iter_filesystems(zfs_hdl, be_umount_iter, info)) != 0) {
142 		return (err);
143 	}
144 
145 	if (!zfs_is_mounted(zfs_hdl, &mountpoint)) {
146 		return (0);
147 	}
148 	free(mountpoint);
149 
150 	if (zfs_unmount(zfs_hdl, NULL, info->mntflags) != 0) {
151 		switch (errno) {
152 		case ENAMETOOLONG:
153 			return (set_error(info->lbh, BE_ERR_PATHLEN));
154 		case ELOOP:
155 		case ENOENT:
156 		case ENOTDIR:
157 			return (set_error(info->lbh, BE_ERR_BADPATH));
158 		case EPERM:
159 			return (set_error(info->lbh, BE_ERR_PERMS));
160 		case EBUSY:
161 			return (set_error(info->lbh, BE_ERR_PATHBUSY));
162 		default:
163 			return (set_error(info->lbh, BE_ERR_UNKNOWN));
164 		}
165 	}
166 	return (0);
167 }
168 
169 /*
170  * usage
171  */
172 int
173 be_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details)
174 {
175 	char be[BE_MAXPATHLEN];
176 	zfs_handle_t *root_hdl;
177 	struct be_mountcheck_info info;
178 	prop_data_t propinfo;
179 
180 	bzero(&be, BE_MAXPATHLEN);
181 	if ((root_hdl = zfs_open(lbh->lzh, lbh->root,
182 	    ZFS_TYPE_FILESYSTEM)) == NULL)
183 		return (BE_ERR_ZFSOPEN);
184 
185 	info.path = path;
186 	info.name = NULL;
187 	zfs_iter_filesystems(root_hdl, be_mountcheck_cb, &info);
188 	zfs_close(root_hdl);
189 
190 	if (info.name != NULL) {
191 		if (details != NULL) {
192 			if ((root_hdl = zfs_open(lbh->lzh, lbh->root,
193 			    ZFS_TYPE_FILESYSTEM)) == NULL) {
194 				free(info.name);
195 				return (BE_ERR_ZFSOPEN);
196 			}
197 
198 			propinfo.lbh = lbh;
199 			propinfo.list = details;
200 			propinfo.single_object = false;
201 			prop_list_builder_cb(root_hdl, &propinfo);
202 			zfs_close(root_hdl);
203 		}
204 		free(info.name);
205 		return (0);
206 	}
207 	return (1);
208 }
209 
210 /*
211  * usage
212  */
213 int
214 be_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags,
215     char *result_loc)
216 {
217 	char be[BE_MAXPATHLEN];
218 	char mnt_temp[BE_MAXPATHLEN];
219 	int mntflags, mntdeep;
220 	int err;
221 	struct be_mount_info info;
222 	zfs_handle_t *zhdl;
223 
224 	if ((err = be_root_concat(lbh, bootenv, be)) != 0)
225 		return (set_error(lbh, err));
226 
227 	if ((err = be_exists(lbh, bootenv)) != 0)
228 		return (set_error(lbh, err));
229 
230 	if (is_mounted(lbh->lzh, be, NULL))
231 		return (set_error(lbh, BE_ERR_MOUNTED));
232 
233 	mntdeep = (flags & BE_MNT_DEEP) ? 1 : 0;
234 	mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0;
235 
236 	/* Create mountpoint if it is not specified */
237 	if (mountpoint == NULL) {
238 		strlcpy(mnt_temp, "/tmp/be_mount.XXXX", sizeof(mnt_temp));
239 		if (mkdtemp(mnt_temp) == NULL)
240 			return (set_error(lbh, BE_ERR_IO));
241 	}
242 
243 	if ((zhdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
244 		return (set_error(lbh, BE_ERR_ZFSOPEN));
245 
246 	info.lbh = lbh;
247 	info.be = be;
248 	info.mountpoint = (mountpoint == NULL) ? mnt_temp : mountpoint;
249 	info.mntflags = mntflags;
250 	info.deepmount = mntdeep;
251 
252 	if((err = be_mount_iter(zhdl, &info) != 0)) {
253 		zfs_close(zhdl);
254 		return (err);
255 	}
256 	zfs_close(zhdl);
257 
258 	if (result_loc != NULL)
259 		strlcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint,
260 		    BE_MAXPATHLEN);
261 
262 	return (BE_ERR_SUCCESS);
263 }
264 
265 /*
266  * usage
267  */
268 int
269 be_unmount(libbe_handle_t *lbh, char *bootenv, int flags)
270 {
271 	int err;
272 	char be[BE_MAXPATHLEN];
273 	zfs_handle_t *root_hdl;
274 	struct be_mount_info info;
275 
276 	if ((err = be_root_concat(lbh, bootenv, be)) != 0)
277 		return (set_error(lbh, err));
278 
279 	if ((root_hdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
280 		return (set_error(lbh, BE_ERR_ZFSOPEN));
281 
282 	info.lbh = lbh;
283 	info.be = be;
284 	info.mountpoint = NULL;
285 	info.mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0;
286 
287 	if ((err = be_umount_iter(root_hdl, &info)) != 0) {
288 		zfs_close(root_hdl);
289 		return (err);
290 	}
291 
292 	zfs_close(root_hdl);
293 	return (BE_ERR_SUCCESS);
294 }
295 
296 /*
297  * This function will blow away the input buffer as needed if we're discovered
298  * to be looking at a root-mount.  If the mountpoint is naturally beyond the
299  * root, however, the buffer may be left intact and a pointer to the section
300  * past altroot will be returned instead for the caller's perusal.
301  */
302 char *
303 be_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint)
304 {
305 
306 	if (lbh->altroot_len == 0)
307 		return (mountpoint);
308 	if (mountpoint == NULL || *mountpoint == '\0')
309 		return (mountpoint);
310 
311 	if (mountpoint[lbh->altroot_len] == '\0') {
312 		*(mountpoint + 1) = '\0';
313 		return (mountpoint);
314 	} else
315 		return (mountpoint + lbh->altroot_len);
316 }
317