Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libbe/be_access.c
34820 views
1
/*
2
* Copyright (c) 2017 Kyle J. Kneitinger <[email protected]>
3
* Copyright (c) 2018 Kyle Evans <[email protected]>
4
* Copyright (c) 2019 Wes Maag <[email protected]>
5
*
6
* SPDX-License-Identifier: BSD-2-Clause
7
*/
8
9
#include <sys/cdefs.h>
10
#include <sys/mntent.h>
11
12
#include "be.h"
13
#include "be_impl.h"
14
15
#define LIBBE_MOUNT_PREFIX "be_mount."
16
17
struct be_mountcheck_info {
18
const char *path;
19
char *name;
20
};
21
22
struct be_mount_info {
23
libbe_handle_t *lbh;
24
const char *be;
25
const char *mountpoint;
26
int mntflags;
27
int deepmount;
28
int depth;
29
};
30
31
static int
32
be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data)
33
{
34
struct be_mountcheck_info *info;
35
char *mountpoint;
36
37
if (data == NULL)
38
return (1);
39
info = (struct be_mountcheck_info *)data;
40
if (!zfs_is_mounted(zfs_hdl, &mountpoint))
41
return (0);
42
if (strcmp(mountpoint, info->path) == 0) {
43
info->name = strdup(zfs_get_name(zfs_hdl));
44
free(mountpoint);
45
return (1);
46
}
47
free(mountpoint);
48
return (0);
49
}
50
51
/*
52
* Called from be_mount, uses the given zfs_handle and attempts to
53
* mount it at the passed mountpoint. If the deepmount flag is set, continue
54
* calling the function for each child dataset.
55
*/
56
static int
57
be_mount_iter(zfs_handle_t *zfs_hdl, void *data)
58
{
59
int err;
60
char *mountpoint;
61
char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN];
62
struct be_mount_info *info;
63
64
info = (struct be_mount_info *)data;
65
66
if (zfs_is_mounted(zfs_hdl, &mountpoint)) {
67
free(mountpoint);
68
return (0);
69
}
70
71
/*
72
* canmount and mountpoint are both ignored for the BE dataset, because
73
* the rest of the system (kernel and loader) will effectively do the
74
* same.
75
*/
76
if (info->depth == 0) {
77
snprintf(tmp, BE_MAXPATHLEN, "%s", info->mountpoint);
78
} else {
79
if (zfs_prop_get_int(zfs_hdl, ZFS_PROP_CANMOUNT) ==
80
ZFS_CANMOUNT_OFF)
81
return (0);
82
83
if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, zfs_mnt,
84
BE_MAXPATHLEN, NULL, NULL, 0, 1))
85
return (1);
86
87
/*
88
* We've encountered mountpoint=none at some intermediate
89
* dataset (e.g. zroot/var) that will have children that may
90
* need to be mounted. Skip mounting it, but iterate through
91
* the children.
92
*/
93
if (strcmp("none", zfs_mnt) == 0)
94
goto skipmount;
95
96
mountpoint = be_mountpoint_augmented(info->lbh, zfs_mnt);
97
snprintf(tmp, BE_MAXPATHLEN, "%s%s", info->mountpoint,
98
mountpoint);
99
}
100
101
if ((err = zfs_mount_at(zfs_hdl, NULL, info->mntflags, tmp)) != 0) {
102
switch (errno) {
103
case ENAMETOOLONG:
104
return (set_error(info->lbh, BE_ERR_PATHLEN));
105
case ELOOP:
106
case ENOENT:
107
case ENOTDIR:
108
return (set_error(info->lbh, BE_ERR_BADPATH));
109
case EPERM:
110
return (set_error(info->lbh, BE_ERR_PERMS));
111
case EBUSY:
112
return (set_error(info->lbh, BE_ERR_PATHBUSY));
113
default:
114
return (set_error(info->lbh, BE_ERR_UNKNOWN));
115
}
116
}
117
118
if (!info->deepmount)
119
return (0);
120
121
skipmount:
122
++info->depth;
123
err = zfs_iter_filesystems(zfs_hdl, be_mount_iter, info);
124
--info->depth;
125
return (err);
126
}
127
128
129
static int
130
be_umount_iter(zfs_handle_t *zfs_hdl, void *data)
131
{
132
133
int err;
134
char *mountpoint;
135
struct be_mount_info *info;
136
137
info = (struct be_mount_info *)data;
138
139
++info->depth;
140
if((err = zfs_iter_filesystems(zfs_hdl, be_umount_iter, info)) != 0) {
141
return (err);
142
}
143
--info->depth;
144
145
if (!zfs_is_mounted(zfs_hdl, &mountpoint)) {
146
return (0);
147
}
148
149
if (info->depth == 0 && info->mountpoint == NULL)
150
info->mountpoint = mountpoint;
151
else
152
free(mountpoint);
153
154
if (zfs_unmount(zfs_hdl, NULL, info->mntflags) != 0) {
155
switch (errno) {
156
case ENAMETOOLONG:
157
return (set_error(info->lbh, BE_ERR_PATHLEN));
158
case ELOOP:
159
case ENOENT:
160
case ENOTDIR:
161
return (set_error(info->lbh, BE_ERR_BADPATH));
162
case EPERM:
163
return (set_error(info->lbh, BE_ERR_PERMS));
164
case EBUSY:
165
return (set_error(info->lbh, BE_ERR_PATHBUSY));
166
default:
167
return (set_error(info->lbh, BE_ERR_UNKNOWN));
168
}
169
}
170
return (0);
171
}
172
173
/*
174
* usage
175
*/
176
int
177
be_mounted_at(libbe_handle_t *lbh, const char *path, nvlist_t *details)
178
{
179
char be[BE_MAXPATHLEN];
180
zfs_handle_t *root_hdl;
181
struct be_mountcheck_info info;
182
prop_data_t propinfo;
183
184
bzero(&be, BE_MAXPATHLEN);
185
if ((root_hdl = zfs_open(lbh->lzh, lbh->root,
186
ZFS_TYPE_FILESYSTEM)) == NULL)
187
return (BE_ERR_ZFSOPEN);
188
189
info.path = path;
190
info.name = NULL;
191
zfs_iter_filesystems(root_hdl, be_mountcheck_cb, &info);
192
zfs_close(root_hdl);
193
194
if (info.name != NULL) {
195
if (details != NULL) {
196
if ((root_hdl = zfs_open(lbh->lzh, info.name,
197
ZFS_TYPE_FILESYSTEM)) == NULL) {
198
free(info.name);
199
return (BE_ERR_ZFSOPEN);
200
}
201
202
propinfo.lbh = lbh;
203
propinfo.list = details;
204
propinfo.single_object = false;
205
propinfo.bootonce = NULL;
206
prop_list_builder_cb(root_hdl, &propinfo);
207
zfs_close(root_hdl);
208
}
209
free(info.name);
210
return (0);
211
}
212
return (1);
213
}
214
215
/*
216
* usage
217
*/
218
int
219
be_mount(libbe_handle_t *lbh, const char *bootenv, const char *mountpoint,
220
int flags, char *result_loc)
221
{
222
char be[BE_MAXPATHLEN];
223
char mnt_temp[BE_MAXPATHLEN];
224
int mntflags, mntdeep;
225
int err;
226
struct be_mount_info info;
227
zfs_handle_t *zhdl;
228
229
if ((err = be_root_concat(lbh, bootenv, be)) != 0)
230
return (set_error(lbh, err));
231
232
if ((err = be_exists(lbh, bootenv)) != 0)
233
return (set_error(lbh, err));
234
235
if (is_mounted(lbh->lzh, be, NULL))
236
return (set_error(lbh, BE_ERR_MOUNTED));
237
238
mntdeep = (flags & BE_MNT_DEEP) ? 1 : 0;
239
mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0;
240
241
/* Create mountpoint if it is not specified */
242
if (mountpoint == NULL) {
243
const char *tmpdir;
244
245
tmpdir = getenv("TMPDIR");
246
if (tmpdir == NULL)
247
tmpdir = _PATH_TMP;
248
249
if (snprintf(mnt_temp, sizeof(mnt_temp), "%s%s%sXXXX", tmpdir,
250
tmpdir[strlen(tmpdir) - 1] == '/' ? "" : "/",
251
LIBBE_MOUNT_PREFIX) >= (int)sizeof(mnt_temp))
252
return (set_error(lbh, BE_ERR_PATHLEN));
253
254
if (mkdtemp(mnt_temp) == NULL)
255
return (set_error(lbh, BE_ERR_IO));
256
}
257
258
if ((zhdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
259
return (set_error(lbh, BE_ERR_ZFSOPEN));
260
261
info.lbh = lbh;
262
info.be = be;
263
info.mountpoint = (mountpoint == NULL) ? mnt_temp : mountpoint;
264
info.mntflags = mntflags;
265
info.deepmount = mntdeep;
266
info.depth = 0;
267
268
if((err = be_mount_iter(zhdl, &info) != 0)) {
269
zfs_close(zhdl);
270
return (err);
271
}
272
zfs_close(zhdl);
273
274
if (result_loc != NULL)
275
strlcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint,
276
BE_MAXPATHLEN);
277
278
return (BE_ERR_SUCCESS);
279
}
280
281
/*
282
* usage
283
*/
284
int
285
be_unmount(libbe_handle_t *lbh, const char *bootenv, int flags)
286
{
287
int err;
288
char be[BE_MAXPATHLEN];
289
zfs_handle_t *root_hdl;
290
struct be_mount_info info;
291
292
if ((err = be_root_concat(lbh, bootenv, be)) != 0)
293
return (set_error(lbh, err));
294
295
if ((root_hdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
296
return (set_error(lbh, BE_ERR_ZFSOPEN));
297
298
info.lbh = lbh;
299
info.be = be;
300
info.mountpoint = NULL;
301
info.mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0;
302
info.depth = 0;
303
304
if ((err = be_umount_iter(root_hdl, &info)) != 0) {
305
free(__DECONST(char *, info.mountpoint));
306
zfs_close(root_hdl);
307
return (err);
308
}
309
310
/*
311
* We'll attempt to remove the directory if we created it on a
312
* best-effort basis. rmdir(2) failure will not be reported.
313
*/
314
if (info.mountpoint != NULL) {
315
const char *mdir;
316
317
mdir = strrchr(info.mountpoint, '/');
318
if (mdir == NULL)
319
mdir = info.mountpoint;
320
else
321
mdir++;
322
323
if (strncmp(mdir, LIBBE_MOUNT_PREFIX,
324
sizeof(LIBBE_MOUNT_PREFIX) - 1) == 0) {
325
(void)rmdir(info.mountpoint);
326
}
327
}
328
329
free(__DECONST(char *, info.mountpoint));
330
331
zfs_close(root_hdl);
332
return (BE_ERR_SUCCESS);
333
}
334
335
/*
336
* This function will blow away the input buffer as needed if we're discovered
337
* to be looking at a root-mount. If the mountpoint is naturally beyond the
338
* root, however, the buffer may be left intact and a pointer to the section
339
* past altroot will be returned instead for the caller's perusal.
340
*/
341
char *
342
be_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint)
343
{
344
345
if (lbh->altroot_len == 0)
346
return (mountpoint);
347
if (mountpoint == NULL || *mountpoint == '\0')
348
return (mountpoint);
349
350
if (mountpoint[lbh->altroot_len] == '\0') {
351
*(mountpoint + 1) = '\0';
352
return (mountpoint);
353
} else
354
return (mountpoint + lbh->altroot_len);
355
}
356
357