Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/autofs/automount.c
105778 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2014 The FreeBSD Foundation
5
*
6
* This software was developed by Edward Tomasz Napierala under sponsorship
7
* from the FreeBSD Foundation.
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 AUTHOR 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 AUTHOR 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
32
#include <sys/types.h>
33
#include <sys/time.h>
34
#include <sys/ioctl.h>
35
#include <sys/param.h>
36
#include <sys/linker.h>
37
#include <sys/mount.h>
38
#include <sys/socket.h>
39
#include <sys/stat.h>
40
#include <sys/wait.h>
41
#include <sys/utsname.h>
42
#include <assert.h>
43
#include <ctype.h>
44
#include <errno.h>
45
#include <fcntl.h>
46
#include <libgen.h>
47
#include <libutil.h>
48
#include <mntopts.h>
49
#include <netdb.h>
50
#include <signal.h>
51
#include <stdbool.h>
52
#include <stdint.h>
53
#include <stdio.h>
54
#include <stdlib.h>
55
#include <string.h>
56
#include <unistd.h>
57
58
#include "common.h"
59
60
static int
61
unmount_by_statfs(const struct statfs *sb, bool force)
62
{
63
char *fsid_str;
64
int error, ret, flags;
65
66
ret = asprintf(&fsid_str, "FSID:%d:%d",
67
sb->f_fsid.val[0], sb->f_fsid.val[1]);
68
if (ret < 0)
69
log_err(1, "asprintf");
70
71
log_debugx("unmounting %s using %s", sb->f_mntonname, fsid_str);
72
73
flags = MNT_BYFSID;
74
if (force)
75
flags |= MNT_FORCE;
76
error = unmount(fsid_str, flags);
77
free(fsid_str);
78
if (error != 0)
79
log_warn("cannot unmount %s", sb->f_mntonname);
80
else
81
rpc_umntall();
82
83
return (error);
84
}
85
86
static const struct statfs *
87
find_statfs(const struct statfs *mntbuf, int nitems, const char *mountpoint)
88
{
89
int i;
90
91
for (i = 0; i < nitems; i++) {
92
if (strcmp(mntbuf[i].f_mntonname, mountpoint) == 0)
93
return (mntbuf + i);
94
}
95
96
return (NULL);
97
}
98
99
static void
100
mount_autofs(const char *from, const char *fspath, const char *options,
101
const char *prefix)
102
{
103
struct iovec *iov = NULL;
104
char errmsg[255];
105
int error, iovlen = 0;
106
107
create_directory(fspath);
108
109
log_debugx("mounting %s on %s, prefix \"%s\", options \"%s\"",
110
from, fspath, prefix, options);
111
memset(errmsg, 0, sizeof(errmsg));
112
113
build_iovec(&iov, &iovlen, "fstype",
114
__DECONST(void *, "autofs"), (size_t)-1);
115
build_iovec(&iov, &iovlen, "fspath",
116
__DECONST(void *, fspath), (size_t)-1);
117
build_iovec(&iov, &iovlen, "from",
118
__DECONST(void *, from), (size_t)-1);
119
build_iovec(&iov, &iovlen, "errmsg",
120
errmsg, sizeof(errmsg));
121
122
/*
123
* Append the options and mountpoint defined in auto_master(5);
124
* this way automountd(8) does not need to parse it.
125
*/
126
build_iovec(&iov, &iovlen, "master_options",
127
__DECONST(void *, options), (size_t)-1);
128
build_iovec(&iov, &iovlen, "master_prefix",
129
__DECONST(void *, prefix), (size_t)-1);
130
131
error = nmount(iov, iovlen, 0);
132
if (error != 0) {
133
if (*errmsg != '\0') {
134
log_err(1, "cannot mount %s on %s: %s",
135
from, fspath, errmsg);
136
} else {
137
log_err(1, "cannot mount %s on %s", from, fspath);
138
}
139
}
140
}
141
142
static void
143
mount_if_not_already(const struct node *n, const char *map, const char *options,
144
const char *prefix, const struct statfs *mntbuf, int nitems)
145
{
146
const struct statfs *sb;
147
char *mountpoint;
148
char *from;
149
int ret;
150
151
ret = asprintf(&from, "map %s", map);
152
if (ret < 0)
153
log_err(1, "asprintf");
154
155
mountpoint = node_path(n);
156
sb = find_statfs(mntbuf, nitems, mountpoint);
157
if (sb != NULL) {
158
if (strcmp(sb->f_fstypename, "autofs") != 0) {
159
log_debugx("unknown filesystem mounted "
160
"on %s; mounting", mountpoint);
161
/*
162
* XXX: Compare options and 'from',
163
* and update the mount if necessary.
164
*/
165
} else {
166
log_debugx("autofs already mounted "
167
"on %s", mountpoint);
168
free(from);
169
free(mountpoint);
170
return;
171
}
172
} else {
173
log_debugx("nothing mounted on %s; mounting",
174
mountpoint);
175
}
176
177
mount_autofs(from, mountpoint, options, prefix);
178
free(from);
179
free(mountpoint);
180
}
181
182
static void
183
mount_unmount(struct node *root)
184
{
185
struct statfs *mntbuf;
186
struct node *n, *n2;
187
int i, nitems;
188
189
nitems = getmntinfo(&mntbuf, MNT_WAIT);
190
if (nitems <= 0)
191
log_err(1, "getmntinfo");
192
193
log_debugx("unmounting stale autofs mounts");
194
195
for (i = 0; i < nitems; i++) {
196
if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) {
197
log_debugx("skipping %s, filesystem type is not autofs",
198
mntbuf[i].f_mntonname);
199
continue;
200
}
201
202
n = node_find(root, mntbuf[i].f_mntonname);
203
if (n != NULL) {
204
log_debugx("leaving autofs mounted on %s",
205
mntbuf[i].f_mntonname);
206
continue;
207
}
208
209
log_debugx("autofs mounted on %s not found "
210
"in new configuration; unmounting", mntbuf[i].f_mntonname);
211
unmount_by_statfs(&(mntbuf[i]), false);
212
}
213
214
log_debugx("mounting new autofs mounts");
215
216
TAILQ_FOREACH(n, &root->n_children, n_next) {
217
if (!node_is_direct_map(n)) {
218
mount_if_not_already(n, n->n_map, n->n_options,
219
n->n_key, mntbuf, nitems);
220
continue;
221
}
222
223
TAILQ_FOREACH(n2, &n->n_children, n_next) {
224
mount_if_not_already(n2, n->n_map, n->n_options,
225
"/", mntbuf, nitems);
226
}
227
}
228
}
229
230
static void
231
flush_autofs(const char *fspath, const fsid_t *fsid)
232
{
233
struct iovec *iov = NULL;
234
char errmsg[255];
235
int error, iovlen = 0;
236
237
log_debugx("flushing %s", fspath);
238
memset(errmsg, 0, sizeof(errmsg));
239
240
build_iovec(&iov, &iovlen, "fstype",
241
__DECONST(void *, "autofs"), (size_t)-1);
242
build_iovec(&iov, &iovlen, "fspath",
243
__DECONST(void *, fspath), (size_t)-1);
244
build_iovec(&iov, &iovlen, "fsid",
245
__DECONST(void *, fsid), sizeof(*fsid));
246
build_iovec(&iov, &iovlen, "errmsg",
247
errmsg, sizeof(errmsg));
248
249
error = nmount(iov, iovlen, MNT_UPDATE);
250
if (error != 0) {
251
if (*errmsg != '\0') {
252
log_err(1, "cannot flush %s: %s",
253
fspath, errmsg);
254
} else {
255
log_err(1, "cannot flush %s", fspath);
256
}
257
}
258
}
259
260
static void
261
flush_caches(void)
262
{
263
struct statfs *mntbuf;
264
struct statfs statbuf;
265
int i, nitems;
266
267
nitems = getmntinfo(&mntbuf, MNT_WAIT);
268
if (nitems <= 0)
269
log_err(1, "getmntinfo");
270
271
log_debugx("flushing autofs caches");
272
273
for (i = 0; i < nitems; i++) {
274
if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) {
275
log_debugx("skipping %s, filesystem type is not autofs",
276
mntbuf[i].f_mntonname);
277
continue;
278
}
279
/*
280
* A direct map mountpoint may have been mounted over, in
281
* which case we can't MNT_UPDATE it. There's an obvious race
282
* condition remaining here, but that has to be fixed in the
283
* kernel.
284
*/
285
if (statfs(mntbuf[i].f_mntonname, &statbuf) != 0) {
286
log_err(1, "cannot statfs %s", mntbuf[i].f_mntonname);
287
continue;
288
}
289
if (strcmp(statbuf.f_fstypename, "autofs") != 0) {
290
log_debugx("skipping %s, filesystem type is not autofs",
291
mntbuf[i].f_mntonname);
292
continue;
293
}
294
295
flush_autofs(mntbuf[i].f_mntonname, &statbuf.f_fsid);
296
}
297
}
298
299
static void
300
unmount_automounted(bool force)
301
{
302
struct statfs *mntbuf;
303
int i, nitems;
304
305
nitems = getmntinfo(&mntbuf, MNT_WAIT);
306
if (nitems <= 0)
307
log_err(1, "getmntinfo");
308
309
log_debugx("unmounting automounted filesystems");
310
311
for (i = 0; i < nitems; i++) {
312
if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) {
313
log_debugx("skipping %s, filesystem type is autofs",
314
mntbuf[i].f_mntonname);
315
continue;
316
}
317
318
if ((mntbuf[i].f_flags & MNT_AUTOMOUNTED) == 0) {
319
log_debugx("skipping %s, not automounted",
320
mntbuf[i].f_mntonname);
321
continue;
322
}
323
324
unmount_by_statfs(&(mntbuf[i]), force);
325
}
326
}
327
328
static void
329
usage_automount(void)
330
{
331
332
fprintf(stderr, "usage: automount [-D name=value][-o opts][-Lcfuv]\n");
333
exit(1);
334
}
335
336
int
337
main_automount(int argc, char **argv)
338
{
339
struct node *root;
340
int ch, debug = 0, show_maps = 0;
341
char *options = NULL;
342
bool do_unmount = false, force_unmount = false, flush = false;
343
344
/*
345
* Note that in automount(8), the only purpose of variable
346
* handling is to aid in debugging maps (automount -L).
347
*/
348
defined_init();
349
350
while ((ch = getopt(argc, argv, "D:Lfco:uv")) != -1) {
351
switch (ch) {
352
case 'D':
353
defined_parse_and_add(optarg);
354
break;
355
case 'L':
356
show_maps++;
357
break;
358
case 'c':
359
flush = true;
360
break;
361
case 'f':
362
force_unmount = true;
363
break;
364
case 'o':
365
options = concat(options, ',', optarg);
366
break;
367
case 'u':
368
do_unmount = true;
369
break;
370
case 'v':
371
debug++;
372
break;
373
case '?':
374
default:
375
usage_automount();
376
}
377
}
378
argc -= optind;
379
if (argc != 0)
380
usage_automount();
381
382
if (force_unmount && !do_unmount)
383
usage_automount();
384
385
log_init(debug);
386
387
if (flush) {
388
flush_caches();
389
return (0);
390
}
391
392
if (do_unmount) {
393
unmount_automounted(force_unmount);
394
return (0);
395
}
396
397
root = node_new_root();
398
parse_master(root, AUTO_MASTER_PATH);
399
400
if (show_maps) {
401
if (show_maps > 1) {
402
node_expand_indirect_maps(root);
403
node_expand_ampersand(root, NULL);
404
}
405
node_expand_defined(root);
406
node_print(root, options);
407
return (0);
408
}
409
410
mount_unmount(root);
411
412
return (0);
413
}
414
415