autofs.c revision 1.1 1 /* $NetBSD: autofs.c,v 1.1 2018/01/09 03:31:14 christos Exp $ */
2
3 /*-
4 * Copyright (c) 2017 The NetBSD Foundation, Inc.
5 * Copyright (c) 2016 The DragonFly Project
6 * Copyright (c) 2014 The FreeBSD Foundation
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to The NetBSD Foundation
10 * by Tomohiro Kusumi <kusumi.tomohiro (at) gmail.com>.
11 *
12 * This software was developed by Edward Tomasz Napierala under sponsorship
13 * from the FreeBSD Foundation.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 */
37 /*-
38 * Copyright (c) 1989, 1991, 1993, 1995
39 * The Regents of the University of California. All rights reserved.
40 *
41 * This code is derived from software contributed to Berkeley by
42 * Rick Macklem at The University of Guelph.
43 *
44 * Redistribution and use in source and binary forms, with or without
45 * modification, are permitted provided that the following conditions
46 * are met:
47 * 1. Redistributions of source code must retain the above copyright
48 * notice, this list of conditions and the following disclaimer.
49 * 2. Redistributions in binary form must reproduce the above copyright
50 * notice, this list of conditions and the following disclaimer in the
51 * documentation and/or other materials provided with the distribution.
52 * 3. Neither the name of the University nor the names of its contributors
53 * may be used to endorse or promote products derived from this software
54 * without specific prior written permission.
55 *
56 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
57 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
58 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
59 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
60 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
61 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
62 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
63 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
64 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
65 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
66 * SUCH DAMAGE.
67 *
68 */
69
70 #include <sys/cdefs.h>
71 __KERNEL_RCSID(0, "$NetBSD: autofs.c,v 1.1 2018/01/09 03:31:14 christos Exp $");
72
73 #include "autofs.h"
74
75 #include <sys/queue.h>
76 #include <sys/signalvar.h>
77
78 static int autofs_open(dev_t dev, int flags, int mode, struct lwp *l);
79 static int autofs_close(dev_t dev, int flags, int mode, struct lwp *l);
80 static int autofs_ioctl(dev_t dev, const u_long cmd, void *data, int flag,
81 struct lwp *l);
82
83 struct cdevsw autofs_ops = {
84 .d_open = autofs_open,
85 .d_close = autofs_close,
86 .d_ioctl = autofs_ioctl,
87 };
88
89 /*
90 * List of signals that can interrupt an autofs trigger.
91 */
92 static int autofs_sig_set[] = {
93 SIGINT,
94 SIGTERM,
95 SIGHUP,
96 SIGKILL,
97 SIGQUIT,
98 };
99
100 struct pool autofs_request_pool;
101 struct pool autofs_node_pool;
102 struct autofs_softc *autofs_softc = NULL;
103 struct workqueue *autofs_tmo_wq = NULL;
104
105 int autofs_debug = 1;
106 int autofs_mount_on_stat = 0;
107 int autofs_timeout = 30;
108 int autofs_cache = 600;
109 int autofs_retry_attempts = 3;
110 int autofs_retry_delay = 1;
111 int autofs_interruptible = 1;
112
113 static int
114 autofs_node_cmp(const struct autofs_node *a, const struct autofs_node *b)
115 {
116
117 return strcmp(a->an_name, b->an_name);
118 }
119
120 RB_GENERATE(autofs_node_tree, autofs_node, an_entry, autofs_node_cmp);
121
122 bool
123 autofs_ignore_thread(void)
124 {
125
126 if (autofs_softc->sc_dev_opened == false)
127 return false;
128
129 mutex_enter(proc_lock);
130 if (autofs_softc->sc_dev_sid == curproc->p_pgrp->pg_id) {
131 mutex_exit(proc_lock);
132 return true;
133 }
134 mutex_exit(proc_lock);
135
136 return false;
137 }
138
139 static char *
140 autofs_path(struct autofs_node *anp)
141 {
142 struct autofs_mount *amp = anp->an_mount;
143 size_t len;
144 char *path, *tmp;
145
146 path = kmem_strdup("", KM_SLEEP);
147 for (; anp->an_parent; anp = anp->an_parent) {
148 len = strlen(anp->an_name) + strlen(path) + 2;
149 tmp = kmem_alloc(len, KM_SLEEP);
150 snprintf(tmp, len, "%s/%s", anp->an_name, path);
151 kmem_strfree(path);
152 path = tmp;
153 }
154
155 len = strlen(amp->am_on) + strlen(path) + 2;
156 tmp = kmem_alloc(len, KM_SLEEP);
157 snprintf(tmp, len, "%s/%s", amp->am_on, path);
158 kmem_strfree(path);
159
160 return tmp;
161 }
162
163 void
164 autofs_timeout_wq(struct work *wk, void *arg)
165 {
166 struct autofs_request *ar = (void *)wk;
167
168 mutex_enter(&autofs_softc->sc_lock);
169 AUTOFS_WARN("request %d for %s timed out after %d seconds",
170 ar->ar_id, ar->ar_path, autofs_timeout);
171
172 ar->ar_error = ETIMEDOUT;
173 ar->ar_wildcards = true;
174 ar->ar_done = true;
175 ar->ar_in_progress = false;
176 cv_broadcast(&autofs_softc->sc_cv);
177 mutex_exit(&autofs_softc->sc_lock);
178 }
179
180 static void
181 autofs_timeout_callout(void *context)
182 {
183 struct autofs_request *ar = context;
184
185 workqueue_enqueue(autofs_tmo_wq, &ar->ar_wk, NULL);
186 }
187
188 bool
189 autofs_cached(struct autofs_node *anp, const char *component, int componentlen)
190 {
191 struct autofs_mount *amp = anp->an_mount;
192
193 KASSERT(!mutex_owned(&->am_lock));
194
195 /*
196 * For root node we need to request automountd(8) assistance even
197 * if the node is marked as cached, but the requested top-level
198 * directory does not exist. This is necessary for wildcard indirect
199 * map keys to work. We don't do this if we know that there are
200 * no wildcards.
201 */
202 if (!anp->an_parent && componentlen && anp->an_wildcards) {
203 int error;
204 KASSERT(amp->am_root == anp);
205 mutex_enter(&->am_lock);
206 error = autofs_node_find(anp, component, componentlen, NULL);
207 mutex_exit(&->am_lock);
208 if (error)
209 return false;
210 }
211
212 return anp->an_cached;
213 }
214
215 static void
216 autofs_cache_callout(void *context)
217 {
218 struct autofs_node *anp = context;
219
220 autofs_node_uncache(anp);
221 }
222
223 void
224 autofs_flush(struct autofs_mount *amp)
225 {
226 struct autofs_node *anp = amp->am_root;
227 struct autofs_node *child;
228
229 mutex_enter(&->am_lock);
230 RB_FOREACH(child, autofs_node_tree, &anp->an_children) {
231 autofs_node_uncache(child);
232 }
233 autofs_node_uncache(amp->am_root);
234 mutex_exit(&->am_lock);
235
236 AUTOFS_DEBUG("%s flushed", amp->am_on);
237 }
238
239 /*
240 * The set/restore sigmask functions are used to (temporarily) overwrite
241 * the thread sigmask during triggering.
242 */
243 static void
244 autofs_set_sigmask(sigset_t *oldset)
245 {
246 sigset_t newset;
247 int i;
248
249 sigfillset(&newset);
250 /* Remove the autofs set of signals from newset */
251 mutex_enter(proc_lock);
252 mutex_enter(curproc->p_lock);
253
254 for (i = 0; i < __arraycount(autofs_sig_set); i++) {
255 /*
256 * But make sure we leave the ones already masked
257 * by the process, i.e. remove the signal from the
258 * temporary signalmask only if it wasn't already
259 * in sigmask.
260 */
261 if (!sigismasked(curlwp, autofs_sig_set[i]))
262 sigdelset(&newset, autofs_sig_set[i]);
263 }
264 sigprocmask1(curlwp, SIG_SETMASK, &newset, oldset);
265
266 mutex_exit(curproc->p_lock);
267 mutex_exit(proc_lock);
268 }
269
270 static void
271 autofs_restore_sigmask(sigset_t *set)
272 {
273
274 mutex_enter(proc_lock);
275 mutex_enter(curproc->p_lock);
276
277 sigprocmask1(curlwp, SIG_SETMASK, set, NULL);
278
279 mutex_exit(curproc->p_lock);
280 mutex_exit(proc_lock);
281 }
282
283 static int
284 autofs_trigger_one(struct autofs_node *anp, const char *component,
285 int componentlen)
286 {
287 struct autofs_mount *amp = anp->an_mount;
288 struct autofs_request *ar;
289 char *key, *path;
290 int error = 0, request_error;
291 bool wildcards;
292
293 KASSERT(mutex_owned(&autofs_softc->sc_lock));
294
295 if (!anp->an_parent) {
296 key = autofs_strndup(component, componentlen, KM_SLEEP);
297 } else {
298 struct autofs_node *firstanp;
299 for (firstanp = anp; firstanp->an_parent->an_parent;
300 firstanp = firstanp->an_parent)
301 continue;
302 key = kmem_strdup(firstanp->an_name, KM_SLEEP);
303 }
304
305 path = autofs_path(anp);
306
307 TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) {
308 if (strcmp(ar->ar_path, path))
309 continue;
310 if (strcmp(ar->ar_key, key))
311 continue;
312
313 KASSERT(!strcmp(ar->ar_from, amp->am_from));
314 KASSERT(!strcmp(ar->ar_prefix, amp->am_prefix));
315 KASSERT(!strcmp(ar->ar_options, amp->am_options));
316 break;
317 }
318
319 if (ar) {
320 atomic_add_int(&ar->ar_refcount, 1);
321 } else {
322 ar = pool_get(&autofs_request_pool, PR_WAITOK);
323 ar->ar_mount = amp;
324 ar->ar_id = autofs_softc->sc_last_request_id++;
325 ar->ar_done = false;
326 ar->ar_error = 0;
327 ar->ar_wildcards = false;
328 ar->ar_in_progress = false;
329 strlcpy(ar->ar_from, amp->am_from, sizeof(ar->ar_from));
330 strlcpy(ar->ar_path, path, sizeof(ar->ar_path));
331 strlcpy(ar->ar_prefix, amp->am_prefix, sizeof(ar->ar_prefix));
332 strlcpy(ar->ar_key, key, sizeof(ar->ar_key));
333 strlcpy(ar->ar_options,
334 amp->am_options, sizeof(ar->ar_options));
335
336 callout_init(&ar->ar_callout, 0);
337 callout_reset(&ar->ar_callout, autofs_timeout * hz,
338 autofs_timeout_callout, ar);
339 ar->ar_refcount = 1;
340 TAILQ_INSERT_TAIL(&autofs_softc->sc_requests, ar, ar_next);
341 }
342
343 cv_broadcast(&autofs_softc->sc_cv);
344 while (ar->ar_done == false) {
345 if (autofs_interruptible) {
346 sigset_t oldset;
347 autofs_set_sigmask(&oldset);
348 error = cv_wait_sig(&autofs_softc->sc_cv,
349 &autofs_softc->sc_lock);
350 autofs_restore_sigmask(&oldset);
351 if (error) {
352 AUTOFS_WARN("cv_wait_sig for %s failed "
353 "with error %d", ar->ar_path, error);
354 break;
355 }
356 } else {
357 cv_wait(&autofs_softc->sc_cv, &autofs_softc->sc_lock);
358 }
359 }
360
361 request_error = ar->ar_error;
362 if (request_error)
363 AUTOFS_WARN("request for %s completed with error %d",
364 ar->ar_path, request_error);
365
366 wildcards = ar->ar_wildcards;
367
368 /*
369 * Check if this is the last reference.
370 */
371 if (!atomic_add_int_nv(&ar->ar_refcount, -1)) {
372 TAILQ_REMOVE(&autofs_softc->sc_requests, ar, ar_next);
373 mutex_exit(&autofs_softc->sc_lock);
374 callout_halt(&ar->ar_callout, NULL);
375 pool_put(&autofs_request_pool, ar);
376 mutex_enter(&autofs_softc->sc_lock);
377 }
378
379 /*
380 * Note that we do not do negative caching on purpose. This
381 * way the user can retry access at any time, e.g. after fixing
382 * the failure reason, without waiting for cache timer to expire.
383 */
384 if (!error && !request_error && autofs_cache > 0) {
385 autofs_node_cache(anp);
386 anp->an_wildcards = wildcards;
387 callout_reset(&anp->an_callout, autofs_cache * hz,
388 autofs_cache_callout, anp);
389 }
390
391 kmem_strfree(key);
392 kmem_strfree(path);
393
394 if (error)
395 return error;
396 return request_error;
397 }
398
399 int
400 autofs_trigger(struct autofs_node *anp, const char *component,
401 int componentlen)
402 {
403 for (;;) {
404 int error, dummy;
405
406 error = autofs_trigger_one(anp, component, componentlen);
407 if (!error) {
408 anp->an_retries = 0;
409 return 0;
410 }
411 if (error == EINTR || error == ERESTART) {
412 AUTOFS_DEBUG("trigger interrupted by signal, "
413 "not retrying");
414 anp->an_retries = 0;
415 return error;
416 }
417 anp->an_retries++;
418 if (anp->an_retries >= autofs_retry_attempts) {
419 AUTOFS_DEBUG("trigger failed %d times; returning "
420 "error %d", anp->an_retries, error);
421 anp->an_retries = 0;
422 return error;
423
424 }
425 AUTOFS_DEBUG("trigger failed with error %d; will retry in "
426 "%d seconds, %d attempts left", error, autofs_retry_delay,
427 autofs_retry_attempts - anp->an_retries);
428 mutex_exit(&autofs_softc->sc_lock);
429 tsleep(&dummy, 0, "autofs_retry", autofs_retry_delay * hz);
430 mutex_enter(&autofs_softc->sc_lock);
431 }
432 }
433
434 static int
435 autofs_ioctl_request(struct autofs_daemon_request *adr)
436 {
437 struct autofs_request *ar;
438
439 mutex_enter(&autofs_softc->sc_lock);
440 for (;;) {
441 int error;
442 TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) {
443 if (ar->ar_done)
444 continue;
445 if (ar->ar_in_progress)
446 continue;
447 break;
448 }
449
450 if (ar)
451 break;
452
453 error = cv_wait_sig(&autofs_softc->sc_cv,
454 &autofs_softc->sc_lock);
455 if (error) {
456 mutex_exit(&autofs_softc->sc_lock);
457 return error;
458 }
459 }
460
461 ar->ar_in_progress = true;
462 mutex_exit(&autofs_softc->sc_lock);
463
464 adr->adr_id = ar->ar_id;
465 strlcpy(adr->adr_from, ar->ar_from, sizeof(adr->adr_from));
466 strlcpy(adr->adr_path, ar->ar_path, sizeof(adr->adr_path));
467 strlcpy(adr->adr_prefix, ar->ar_prefix, sizeof(adr->adr_prefix));
468 strlcpy(adr->adr_key, ar->ar_key, sizeof(adr->adr_key));
469 strlcpy(adr->adr_options, ar->ar_options, sizeof(adr->adr_options));
470
471 mutex_enter(proc_lock);
472 autofs_softc->sc_dev_sid = curproc->p_pgrp->pg_id;
473 mutex_exit(proc_lock);
474
475 return 0;
476 }
477
478 static int
479 autofs_ioctl_done(struct autofs_daemon_done *add)
480 {
481 struct autofs_request *ar;
482
483 mutex_enter(&autofs_softc->sc_lock);
484 TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) {
485 if (ar->ar_id == add->add_id)
486 break;
487 }
488
489 if (!ar) {
490 mutex_exit(&autofs_softc->sc_lock);
491 AUTOFS_DEBUG("id %d not found", add->add_id);
492 return ESRCH;
493 }
494
495 ar->ar_error = add->add_error;
496 ar->ar_wildcards = add->add_wildcards;
497 ar->ar_done = true;
498 ar->ar_in_progress = false;
499 cv_broadcast(&autofs_softc->sc_cv);
500
501 mutex_exit(&autofs_softc->sc_lock);
502
503 return 0;
504 }
505
506 static int
507 autofs_open(dev_t dev, int flags, int mode, struct lwp *l)
508 {
509
510 mutex_enter(&autofs_softc->sc_lock);
511 /*
512 * We must never block automountd(8) and its descendants, and we use
513 * session ID to determine that: we store session id of the process
514 * that opened the device, and then compare it with session ids
515 * of triggering processes. This means running a second automountd(8)
516 * instance would break the previous one. The check below prevents
517 * it from happening.
518 */
519 if (autofs_softc->sc_dev_opened) {
520 mutex_exit(&autofs_softc->sc_lock);
521 return EBUSY;
522 }
523
524 autofs_softc->sc_dev_opened = true;
525 mutex_exit(&autofs_softc->sc_lock);
526
527 return 0;
528 }
529
530 static int
531 autofs_close(dev_t dev, int flags, int mode, struct lwp *l)
532 {
533
534 mutex_enter(&autofs_softc->sc_lock);
535 KASSERT(autofs_softc->sc_dev_opened);
536 autofs_softc->sc_dev_opened = false;
537 mutex_exit(&autofs_softc->sc_lock);
538
539 return 0;
540 }
541
542 static int
543 autofs_ioctl(dev_t dev, const u_long cmd, void *data, int flag, struct lwp *l)
544 {
545
546 KASSERT(autofs_softc->sc_dev_opened);
547
548 switch (cmd) {
549 case AUTOFSREQUEST:
550 return autofs_ioctl_request(
551 (struct autofs_daemon_request *)data);
552 case AUTOFSDONE:
553 return autofs_ioctl_done(
554 (struct autofs_daemon_done *)data);
555 default:
556 AUTOFS_DEBUG("invalid cmd %lx", cmd);
557 return EINVAL;
558 }
559 return EINVAL;
560 }
561