1*4da6d876Schristos /* $NetBSD: amfs_generic.c,v 1.2 2015/01/17 17:46:31 christos Exp $ */
2a53f50b9Schristos
3a53f50b9Schristos /*
48bae5d40Schristos * Copyright (c) 1997-2014 Erez Zadok
5a53f50b9Schristos * Copyright (c) 1990 Jan-Simon Pendry
6a53f50b9Schristos * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7a53f50b9Schristos * Copyright (c) 1990 The Regents of the University of California.
8a53f50b9Schristos * All rights reserved.
9a53f50b9Schristos *
10a53f50b9Schristos * This code is derived from software contributed to Berkeley by
11a53f50b9Schristos * Jan-Simon Pendry at Imperial College, London.
12a53f50b9Schristos *
13a53f50b9Schristos * Redistribution and use in source and binary forms, with or without
14a53f50b9Schristos * modification, are permitted provided that the following conditions
15a53f50b9Schristos * are met:
16a53f50b9Schristos * 1. Redistributions of source code must retain the above copyright
17a53f50b9Schristos * notice, this list of conditions and the following disclaimer.
18a53f50b9Schristos * 2. Redistributions in binary form must reproduce the above copyright
19a53f50b9Schristos * notice, this list of conditions and the following disclaimer in the
20a53f50b9Schristos * documentation and/or other materials provided with the distribution.
218bae5d40Schristos * 3. Neither the name of the University nor the names of its contributors
22a53f50b9Schristos * may be used to endorse or promote products derived from this software
23a53f50b9Schristos * without specific prior written permission.
24a53f50b9Schristos *
25a53f50b9Schristos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26a53f50b9Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27a53f50b9Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28a53f50b9Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29a53f50b9Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30a53f50b9Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31a53f50b9Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32a53f50b9Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33a53f50b9Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34a53f50b9Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35a53f50b9Schristos * SUCH DAMAGE.
36a53f50b9Schristos *
37a53f50b9Schristos *
38a53f50b9Schristos * File: am-utils/amd/amfs_generic.c
39a53f50b9Schristos *
40a53f50b9Schristos */
41a53f50b9Schristos
42a53f50b9Schristos /*
43a53f50b9Schristos * generic functions used by amfs filesystems, ripped out of amfs_auto.c.
44a53f50b9Schristos */
45a53f50b9Schristos
46a53f50b9Schristos #ifdef HAVE_CONFIG_H
47a53f50b9Schristos # include <config.h>
48a53f50b9Schristos #endif /* HAVE_CONFIG_H */
49a53f50b9Schristos #include <am_defs.h>
50a53f50b9Schristos #include <amd.h>
51a53f50b9Schristos
52a53f50b9Schristos
53a53f50b9Schristos /****************************************************************************
54a53f50b9Schristos *** MACROS ***
55a53f50b9Schristos ****************************************************************************/
568bae5d40Schristos #define IN_PROGRESS(cp) ((cp)->mp->am_al->al_mnt->mf_flags & MFF_MOUNTING)
57a53f50b9Schristos
58a53f50b9Schristos
59a53f50b9Schristos /****************************************************************************
60a53f50b9Schristos *** STRUCTURES ***
61a53f50b9Schristos ****************************************************************************/
62a53f50b9Schristos /*
63a53f50b9Schristos * Mounting a file system may take a significant period of time. The
64a53f50b9Schristos * problem is that if this is done in the main process thread then the
65a53f50b9Schristos * entire automounter could be blocked, possibly hanging lots of processes
66a53f50b9Schristos * on the system. Instead we use a continuation scheme to allow mounts to
67a53f50b9Schristos * be attempted in a sub-process. When the sub-process exits we pick up the
68a53f50b9Schristos * exit status (by convention a UN*X error number) and continue in a
69a53f50b9Schristos * notifier. The notifier gets handed a data structure and can then
70a53f50b9Schristos * determine whether the mount was successful or not. If not, it updates
71a53f50b9Schristos * the data structure and tries again until there are no more ways to try
72a53f50b9Schristos * the mount, or some other permanent error occurs. In the mean time no RPC
73a53f50b9Schristos * reply is sent, even after the mount is successful. We rely on the RPC
74a53f50b9Schristos * retry mechanism to resend the lookup request which can then be handled.
75a53f50b9Schristos */
76a53f50b9Schristos struct continuation {
77a53f50b9Schristos am_node *mp; /* Node we are trying to mount */
78a53f50b9Schristos int retry; /* Try again? */
79a53f50b9Schristos time_t start; /* Time we started this mount */
80a53f50b9Schristos int callout; /* Callout identifier */
818bae5d40Schristos am_loc **al; /* Current location */
82a53f50b9Schristos };
83a53f50b9Schristos
84a53f50b9Schristos
85a53f50b9Schristos /****************************************************************************
86a53f50b9Schristos *** FORWARD DEFINITIONS ***
87a53f50b9Schristos ****************************************************************************/
88a53f50b9Schristos static am_node *amfs_lookup_node(am_node *mp, char *fname, int *error_return);
898bae5d40Schristos static am_loc *amfs_lookup_one_location(am_node *new_mp, mntfs *mf, char *ivec,
90a53f50b9Schristos char *def_opts, char *pfname);
918bae5d40Schristos static am_loc **amfs_lookup_loc(am_node *new_mp, int *error_return);
92a53f50b9Schristos static void amfs_cont(int rc, int term, opaque_t arg);
93a53f50b9Schristos static void amfs_retry(int rc, int term, opaque_t arg);
94a53f50b9Schristos static void free_continuation(struct continuation *cp);
95a53f50b9Schristos static int amfs_bgmount(struct continuation *cp);
96a53f50b9Schristos static char *amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts);
97a53f50b9Schristos
98a53f50b9Schristos
99a53f50b9Schristos /****************************************************************************
100a53f50b9Schristos *** FUNCTIONS ***
101a53f50b9Schristos ****************************************************************************/
102a53f50b9Schristos static am_node *
amfs_lookup_node(am_node * mp,char * fname,int * error_return)103a53f50b9Schristos amfs_lookup_node(am_node *mp, char *fname, int *error_return)
104a53f50b9Schristos {
105a53f50b9Schristos am_node *new_mp;
106a53f50b9Schristos int error = 0; /* Error so far */
107a53f50b9Schristos int in_progress = 0; /* # of (un)mount in progress */
108a53f50b9Schristos mntfs *mf;
109a53f50b9Schristos char *expanded_fname = NULL;
110a53f50b9Schristos
111a53f50b9Schristos dlog("in amfs_lookup_node");
112a53f50b9Schristos
113a53f50b9Schristos /*
114a53f50b9Schristos * If the server is shutting down
115a53f50b9Schristos * then don't return information
116a53f50b9Schristos * about the mount point.
117a53f50b9Schristos */
118a53f50b9Schristos if (amd_state == Finishing) {
1198bae5d40Schristos if (mp->am_al == NULL || mp->am_al->al_mnt == NULL || mp->am_al->al_mnt->mf_fsflags & FS_DIRECT) {
120a53f50b9Schristos dlog("%s mount ignored - going down", fname);
121a53f50b9Schristos } else {
122a53f50b9Schristos dlog("%s/%s mount ignored - going down", mp->am_path, fname);
123a53f50b9Schristos }
124a53f50b9Schristos ereturn(ENOENT);
125a53f50b9Schristos }
126a53f50b9Schristos
127a53f50b9Schristos /*
128a53f50b9Schristos * Handle special case of "." and ".."
129a53f50b9Schristos */
130a53f50b9Schristos if (fname[0] == '.') {
131a53f50b9Schristos if (fname[1] == '\0')
132a53f50b9Schristos return mp; /* "." is the current node */
133a53f50b9Schristos if (fname[1] == '.' && fname[2] == '\0') {
134a53f50b9Schristos if (mp->am_parent) {
135a53f50b9Schristos dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
136a53f50b9Schristos return mp->am_parent; /* ".." is the parent node */
137a53f50b9Schristos }
138a53f50b9Schristos ereturn(ESTALE);
139a53f50b9Schristos }
140a53f50b9Schristos }
141a53f50b9Schristos
142a53f50b9Schristos /*
143a53f50b9Schristos * Check for valid key name.
144a53f50b9Schristos * If it is invalid then pretend it doesn't exist.
145a53f50b9Schristos */
146a53f50b9Schristos if (!valid_key(fname)) {
147a53f50b9Schristos plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
148a53f50b9Schristos ereturn(ENOENT);
149a53f50b9Schristos }
150a53f50b9Schristos
151a53f50b9Schristos /*
152a53f50b9Schristos * Expand key name.
153a53f50b9Schristos * expanded_fname is now a private copy.
154a53f50b9Schristos */
155a53f50b9Schristos expanded_fname = expand_selectors(fname);
156a53f50b9Schristos
157a53f50b9Schristos /*
158a53f50b9Schristos * Search children of this node
159a53f50b9Schristos */
160a53f50b9Schristos for (new_mp = mp->am_child; new_mp; new_mp = new_mp->am_osib) {
161a53f50b9Schristos if (FSTREQ(new_mp->am_name, expanded_fname)) {
162a53f50b9Schristos if (new_mp->am_error) {
163a53f50b9Schristos error = new_mp->am_error;
164a53f50b9Schristos continue;
165a53f50b9Schristos }
166a53f50b9Schristos
167a53f50b9Schristos /*
168a53f50b9Schristos * If the error code is undefined then it must be
169a53f50b9Schristos * in progress.
170a53f50b9Schristos */
1718bae5d40Schristos mf = new_mp->am_al->al_mnt;
172a53f50b9Schristos if (mf->mf_error < 0)
173a53f50b9Schristos goto in_progrss;
174a53f50b9Schristos
175a53f50b9Schristos /*
176a53f50b9Schristos * If there was a previous error with this node
177a53f50b9Schristos * then return that error code.
178a53f50b9Schristos */
179a53f50b9Schristos if (mf->mf_flags & MFF_ERROR) {
180a53f50b9Schristos error = mf->mf_error;
181a53f50b9Schristos continue;
182a53f50b9Schristos }
183a53f50b9Schristos if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
184a53f50b9Schristos in_progrss:
185a53f50b9Schristos /*
186a53f50b9Schristos * If the fs is not mounted or it is unmounting then there
187a53f50b9Schristos * is a background (un)mount in progress. In this case
188a53f50b9Schristos * we just drop the RPC request (return nil) and
189a53f50b9Schristos * wait for a retry, by which time the (un)mount may
190a53f50b9Schristos * have completed.
191a53f50b9Schristos */
192a53f50b9Schristos dlog("ignoring mount of %s in %s -- %smounting in progress, flags %x",
193a53f50b9Schristos expanded_fname, mf->mf_mount,
194a53f50b9Schristos (mf->mf_flags & MFF_UNMOUNTING) ? "un" : "", mf->mf_flags);
195a53f50b9Schristos in_progress++;
196a53f50b9Schristos if (mf->mf_flags & MFF_UNMOUNTING) {
197a53f50b9Schristos dlog("will remount later");
198a53f50b9Schristos new_mp->am_flags |= AMF_REMOUNT;
199a53f50b9Schristos }
200a53f50b9Schristos continue;
201a53f50b9Schristos }
202a53f50b9Schristos
203a53f50b9Schristos /*
204a53f50b9Schristos * Otherwise we have a hit: return the current mount point.
205a53f50b9Schristos */
206a53f50b9Schristos dlog("matched %s in %s", expanded_fname, new_mp->am_path);
207a53f50b9Schristos XFREE(expanded_fname);
208a53f50b9Schristos return new_mp;
209a53f50b9Schristos }
210a53f50b9Schristos }
211a53f50b9Schristos
212a53f50b9Schristos if (in_progress) {
213a53f50b9Schristos dlog("Waiting while %d mount(s) in progress", in_progress);
214a53f50b9Schristos XFREE(expanded_fname);
215a53f50b9Schristos ereturn(-1);
216a53f50b9Schristos }
217a53f50b9Schristos
218a53f50b9Schristos /*
219a53f50b9Schristos * If an error occurred then return it.
220a53f50b9Schristos */
221a53f50b9Schristos if (error) {
222a53f50b9Schristos dlog("Returning error: %s", strerror(error));
223a53f50b9Schristos XFREE(expanded_fname);
224a53f50b9Schristos ereturn(error);
225a53f50b9Schristos }
226a53f50b9Schristos
227a53f50b9Schristos /*
228a53f50b9Schristos * If the server is going down then just return,
229a53f50b9Schristos * don't try to mount any more file systems
230a53f50b9Schristos */
231a53f50b9Schristos if ((int) amd_state >= (int) Finishing) {
232a53f50b9Schristos dlog("not found - server going down anyway");
233a53f50b9Schristos ereturn(ENOENT);
234a53f50b9Schristos }
235a53f50b9Schristos
236a53f50b9Schristos /*
237a53f50b9Schristos * Allocate a new map
238a53f50b9Schristos */
239a53f50b9Schristos new_mp = get_ap_child(mp, expanded_fname);
240a53f50b9Schristos XFREE(expanded_fname);
241a53f50b9Schristos if (new_mp == NULL)
242a53f50b9Schristos ereturn(ENOSPC);
243a53f50b9Schristos
244a53f50b9Schristos *error_return = -1;
245a53f50b9Schristos return new_mp;
246a53f50b9Schristos }
247a53f50b9Schristos
248a53f50b9Schristos
249a53f50b9Schristos
2508bae5d40Schristos static am_loc *
amfs_lookup_one_location(am_node * new_mp,mntfs * mf,char * ivec,char * def_opts,char * pfname)2518bae5d40Schristos amfs_lookup_one_location(am_node *new_mp, mntfs *mf, char *ivec,
252a53f50b9Schristos char *def_opts, char *pfname)
253a53f50b9Schristos {
254a53f50b9Schristos am_ops *p;
255a53f50b9Schristos am_opts *fs_opts;
2568bae5d40Schristos am_loc *new_al;
257a53f50b9Schristos mntfs *new_mf;
258a53f50b9Schristos char *mp_dir = NULL;
259a53f50b9Schristos #ifdef HAVE_FS_AUTOFS
260a53f50b9Schristos int on_autofs = 1;
261a53f50b9Schristos #endif /* HAVE_FS_AUTOFS */
262a53f50b9Schristos
263a53f50b9Schristos /* match the operators */
2648bae5d40Schristos /*
2658bae5d40Schristos * although we alloc the fs_opts here, the pointer is 'owned' by the am_loc and will
2668bae5d40Schristos * be free'd on destruction of the am_loc. If we don't allocate a loc, then we need
2678bae5d40Schristos * to free this.
2688bae5d40Schristos */
269a53f50b9Schristos fs_opts = CALLOC(am_opts);
270a53f50b9Schristos p = ops_match(fs_opts, ivec, def_opts, new_mp->am_path,
271a53f50b9Schristos pfname, mf->mf_info);
272a53f50b9Schristos #ifdef HAVE_FS_AUTOFS
273a53f50b9Schristos /* XXX: this should be factored out into an autofs-specific function */
274a53f50b9Schristos if (new_mp->am_flags & AMF_AUTOFS) {
275a53f50b9Schristos /* ignore user-provided fs if we're using autofs */
2764bcd344eSchristos if (fs_opts->opt_sublink && fs_opts->opt_sublink[0]) {
277a53f50b9Schristos /*
278a53f50b9Schristos * For sublinks we need to use a hack with autofs:
279a53f50b9Schristos * mount the filesystem on the original opt_fs (which is NOT an
280a53f50b9Schristos * autofs mountpoint) and symlink (or lofs-mount) to it from
281a53f50b9Schristos * the autofs mountpoint.
282a53f50b9Schristos */
283a53f50b9Schristos on_autofs = 0;
284a53f50b9Schristos mp_dir = fs_opts->opt_fs;
285a53f50b9Schristos } else {
286a53f50b9Schristos if (p->autofs_fs_flags & FS_ON_AUTOFS) {
287a53f50b9Schristos mp_dir = new_mp->am_path;
288a53f50b9Schristos } else {
289a53f50b9Schristos mp_dir = fs_opts->opt_fs;
290a53f50b9Schristos on_autofs = 0;
291a53f50b9Schristos }
292a53f50b9Schristos }
293a53f50b9Schristos } else
294a53f50b9Schristos #endif /* HAVE_FS_AUTOFS */
295a53f50b9Schristos mp_dir = fs_opts->opt_fs;
296a53f50b9Schristos
297a53f50b9Schristos /*
298a53f50b9Schristos * Find or allocate a filesystem for this node.
2998bae5d40Schristos * we search for a matching backend share, since
3008bae5d40Schristos * we will construct our own al_loc to handle
3018bae5d40Schristos * any customisations for this usage.
302a53f50b9Schristos */
303a53f50b9Schristos new_mf = find_mntfs(p, fs_opts,
304a53f50b9Schristos mp_dir,
305a53f50b9Schristos fs_opts->fs_mtab,
306a53f50b9Schristos def_opts,
307a53f50b9Schristos fs_opts->opt_opts,
308a53f50b9Schristos fs_opts->opt_remopts);
309a53f50b9Schristos
3108bae5d40Schristos
311a53f50b9Schristos /*
312a53f50b9Schristos * See whether this is a real filesystem
313a53f50b9Schristos */
314a53f50b9Schristos p = new_mf->mf_ops;
315a53f50b9Schristos if (p == &amfs_error_ops) {
316a53f50b9Schristos plog(XLOG_MAP, "Map entry %s for %s did not match", ivec, new_mp->am_path);
317a53f50b9Schristos free_mntfs(new_mf);
3188bae5d40Schristos free_opts(fs_opts);
3198bae5d40Schristos XFREE(fs_opts);
320a53f50b9Schristos return NULL;
321a53f50b9Schristos }
322a53f50b9Schristos
323a53f50b9Schristos dlog("Got a hit with %s", p->fs_type);
3248bae5d40Schristos new_al = new_loc();
3258bae5d40Schristos free_mntfs(new_al->al_mnt);
3268bae5d40Schristos new_al->al_mnt = new_mf;
3278bae5d40Schristos new_al->al_fo = fs_opts; /* now the loc is in charge of free'ing this mem */
328a53f50b9Schristos
329a53f50b9Schristos #ifdef HAVE_FS_AUTOFS
330a53f50b9Schristos if (new_mp->am_flags & AMF_AUTOFS && on_autofs) {
331a53f50b9Schristos new_mf->mf_flags |= MFF_ON_AUTOFS;
332a53f50b9Schristos new_mf->mf_fsflags = new_mf->mf_ops->autofs_fs_flags;
333a53f50b9Schristos }
334a53f50b9Schristos /*
335a53f50b9Schristos * A new filesystem is an autofs filesystems if:
336a53f50b9Schristos * 1. it claims it can be one (has the FS_AUTOFS flag)
337a53f50b9Schristos * 2. autofs is enabled system-wide
338a53f50b9Schristos * 3. either has an autofs parent,
339a53f50b9Schristos * or it is explicitly requested to be autofs.
340a53f50b9Schristos */
341a53f50b9Schristos if (new_mf->mf_ops->autofs_fs_flags & FS_AUTOFS &&
342a53f50b9Schristos amd_use_autofs &&
343a53f50b9Schristos ((mf->mf_flags & MFF_IS_AUTOFS) ||
344a53f50b9Schristos (new_mf->mf_fo && new_mf->mf_fo->opt_mount_type &&
345a53f50b9Schristos STREQ(new_mf->mf_fo->opt_mount_type, "autofs"))))
346a53f50b9Schristos new_mf->mf_flags |= MFF_IS_AUTOFS;
347a53f50b9Schristos #endif /* HAVE_FS_AUTOFS */
348a53f50b9Schristos
3498bae5d40Schristos return new_al;
350a53f50b9Schristos }
351a53f50b9Schristos
352a53f50b9Schristos
3538bae5d40Schristos static am_loc **
amfs_lookup_loc(am_node * new_mp,int * error_return)3548bae5d40Schristos amfs_lookup_loc(am_node *new_mp, int *error_return)
355a53f50b9Schristos {
356a53f50b9Schristos am_node *mp;
357a53f50b9Schristos char *info; /* Mount info - where to get the file system */
358a53f50b9Schristos char **ivecs, **cur_ivec; /* Split version of info */
359a53f50b9Schristos int num_ivecs;
360a53f50b9Schristos char *orig_def_opts; /* Original Automount options */
361a53f50b9Schristos char *def_opts; /* Automount options */
362a53f50b9Schristos int error = 0; /* Error so far */
363a53f50b9Schristos char path_name[MAXPATHLEN]; /* General path name buffer */
364a53f50b9Schristos char *pfname; /* Path for database lookup */
3658bae5d40Schristos mntfs* mf; /* The mntfs for the map of our parent */
3668bae5d40Schristos am_loc **al_array; /* the generated list of locations */
367a53f50b9Schristos int count;
368a53f50b9Schristos
3698bae5d40Schristos dlog("in amfs_lookup_loc");
370a53f50b9Schristos
371a53f50b9Schristos mp = new_mp->am_parent;
372a53f50b9Schristos
373a53f50b9Schristos /*
374a53f50b9Schristos * If we get here then this is a reference to an,
375a53f50b9Schristos * as yet, unknown name so we need to search the mount
376a53f50b9Schristos * map for it.
377a53f50b9Schristos */
378a53f50b9Schristos if (mp->am_pref) {
379a53f50b9Schristos if (strlen(mp->am_pref) + strlen(new_mp->am_name) >= sizeof(path_name))
380a53f50b9Schristos ereturn(ENAMETOOLONG);
381a53f50b9Schristos xsnprintf(path_name, sizeof(path_name), "%s%s", mp->am_pref, new_mp->am_name);
382a53f50b9Schristos pfname = path_name;
383a53f50b9Schristos } else {
384a53f50b9Schristos pfname = new_mp->am_name;
385a53f50b9Schristos }
386a53f50b9Schristos
3878bae5d40Schristos mf = mp->am_al->al_mnt;
388a53f50b9Schristos
389a53f50b9Schristos dlog("will search map info in %s to find %s", mf->mf_info, pfname);
390a53f50b9Schristos /*
391a53f50b9Schristos * Consult the oracle for some mount information.
392a53f50b9Schristos * info is malloc'ed and belongs to this routine.
393a53f50b9Schristos * It ends up being free'd in free_continuation().
394a53f50b9Schristos *
395a53f50b9Schristos * Note that this may return -1 indicating that information
396a53f50b9Schristos * is not yet available.
397a53f50b9Schristos */
398a53f50b9Schristos error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
399a53f50b9Schristos if (error) {
400a53f50b9Schristos if (error > 0)
401a53f50b9Schristos plog(XLOG_MAP, "No map entry for %s", pfname);
402a53f50b9Schristos else
403a53f50b9Schristos plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
404a53f50b9Schristos ereturn(error);
405a53f50b9Schristos }
406a53f50b9Schristos dlog("mount info is %s", info);
407a53f50b9Schristos
408a53f50b9Schristos /*
409a53f50b9Schristos * Split info into an argument vector.
410a53f50b9Schristos * The vector is malloc'ed and belongs to
411a53f50b9Schristos * this routine. It is free'd further down.
412a53f50b9Schristos *
413a53f50b9Schristos * Note: the vector pointers point into info, so don't free it!
414a53f50b9Schristos */
415a53f50b9Schristos ivecs = strsplit(info, ' ', '\"');
416a53f50b9Schristos
417a53f50b9Schristos if (mf->mf_auto)
418a53f50b9Schristos def_opts = mf->mf_auto;
419a53f50b9Schristos else
420a53f50b9Schristos def_opts = "";
421a53f50b9Schristos
4228bae5d40Schristos orig_def_opts = amfs_parse_defaults(mp, mf, xstrdup(def_opts));
4238bae5d40Schristos def_opts = xstrdup(orig_def_opts);
424a53f50b9Schristos
425a53f50b9Schristos /* first build our defaults */
426a53f50b9Schristos num_ivecs = 0;
427a53f50b9Schristos for (cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
428a53f50b9Schristos if (**cur_ivec == '-') {
429a53f50b9Schristos /*
430a53f50b9Schristos * Pick up new defaults
431a53f50b9Schristos */
432a53f50b9Schristos char *new_def_opts = str3cat(NULL, def_opts, ";", *cur_ivec + 1);
433a53f50b9Schristos XFREE(def_opts);
434a53f50b9Schristos def_opts = new_def_opts;
435a53f50b9Schristos dlog("Setting def_opts to \"%s\"", def_opts);
436a53f50b9Schristos continue;
437a53f50b9Schristos } else
438a53f50b9Schristos num_ivecs++;
439a53f50b9Schristos }
440a53f50b9Schristos
4418bae5d40Schristos al_array = calloc(num_ivecs + 1, sizeof(am_loc *));
442a53f50b9Schristos
4438bae5d40Schristos /* construct the array of struct locations for this key */
444a53f50b9Schristos for (count = 0, cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
4458bae5d40Schristos am_loc *new_al;
446a53f50b9Schristos
447a53f50b9Schristos if (**cur_ivec == '-') {
448a53f50b9Schristos XFREE(def_opts);
449a53f50b9Schristos if ((*cur_ivec)[1] == '\0') {
450a53f50b9Schristos /*
451a53f50b9Schristos * If we have a single dash '-' than we need to reset the
452a53f50b9Schristos * default options.
453a53f50b9Schristos */
4548bae5d40Schristos def_opts = xstrdup(orig_def_opts);
455a53f50b9Schristos dlog("Resetting the default options, a single dash '-' was found.");
456a53f50b9Schristos } else {
457a53f50b9Schristos /* append options to /default options */
458a53f50b9Schristos def_opts = str3cat((char *) NULL, orig_def_opts, ";", *cur_ivec + 1);
459a53f50b9Schristos dlog("Resetting def_opts to \"%s\"", def_opts);
460a53f50b9Schristos }
461a53f50b9Schristos continue;
462a53f50b9Schristos }
463a53f50b9Schristos
464a53f50b9Schristos /*
4658bae5d40Schristos * If a loc has already been found, and we find
466a53f50b9Schristos * a cut then don't try any more locations.
467a53f50b9Schristos *
468a53f50b9Schristos * XXX: we do not know when the "/" was added as an equivalent for "||".
469a53f50b9Schristos * It's undocumented, it might go away at any time. Caveat emptor.
470a53f50b9Schristos */
471a53f50b9Schristos if (STREQ(*cur_ivec, "/") || STREQ(*cur_ivec, "||")) {
472a53f50b9Schristos if (count > 0) {
4738bae5d40Schristos dlog("Cut: not trying any more locations for %s", pfname);
474a53f50b9Schristos break;
475a53f50b9Schristos }
476a53f50b9Schristos continue;
477a53f50b9Schristos }
478a53f50b9Schristos
4798bae5d40Schristos new_al = amfs_lookup_one_location(new_mp, mf, *cur_ivec, def_opts, pfname);
4808bae5d40Schristos if (new_al == NULL)
481a53f50b9Schristos continue;
4828bae5d40Schristos al_array[count++] = new_al;
483a53f50b9Schristos }
484a53f50b9Schristos
485a53f50b9Schristos /* We're done with ivecs */
486a53f50b9Schristos XFREE(ivecs);
487a53f50b9Schristos XFREE(info);
488a53f50b9Schristos XFREE(orig_def_opts);
489a53f50b9Schristos XFREE(def_opts);
490a53f50b9Schristos if (count == 0) { /* no match */
4918bae5d40Schristos XFREE(al_array);
492a53f50b9Schristos ereturn(ENOENT);
493a53f50b9Schristos }
494a53f50b9Schristos
4958bae5d40Schristos return al_array;
496a53f50b9Schristos }
497a53f50b9Schristos
498a53f50b9Schristos
499a53f50b9Schristos /*
500a53f50b9Schristos * The continuation function. This is called by
501a53f50b9Schristos * the task notifier when a background mount attempt
502a53f50b9Schristos * completes.
503a53f50b9Schristos */
504a53f50b9Schristos static void
amfs_cont(int rc,int term,opaque_t arg)505a53f50b9Schristos amfs_cont(int rc, int term, opaque_t arg)
506a53f50b9Schristos {
507a53f50b9Schristos struct continuation *cp = (struct continuation *) arg;
508a53f50b9Schristos am_node *mp = cp->mp;
5098bae5d40Schristos mntfs *mf = mp->am_al->al_mnt;
510a53f50b9Schristos
511a53f50b9Schristos dlog("amfs_cont: '%s'", mp->am_path);
512a53f50b9Schristos
513a53f50b9Schristos /*
514a53f50b9Schristos * Definitely not trying to mount at the moment
515a53f50b9Schristos */
516a53f50b9Schristos mf->mf_flags &= ~MFF_MOUNTING;
517a53f50b9Schristos
518a53f50b9Schristos /*
519a53f50b9Schristos * While we are mounting - try to avoid race conditions
520a53f50b9Schristos */
521a53f50b9Schristos new_ttl(mp);
522a53f50b9Schristos
523a53f50b9Schristos /*
524a53f50b9Schristos * Wakeup anything waiting for this mount
525a53f50b9Schristos */
526a53f50b9Schristos wakeup(get_mntfs_wchan(mf));
527a53f50b9Schristos
528a53f50b9Schristos /*
529a53f50b9Schristos * Check for termination signal or exit status...
530a53f50b9Schristos */
531a53f50b9Schristos if (rc || term) {
532a53f50b9Schristos #ifdef HAVE_FS_AUTOFS
533a53f50b9Schristos if (mf->mf_flags & MFF_IS_AUTOFS &&
534a53f50b9Schristos !(mf->mf_flags & MFF_MOUNTED))
535a53f50b9Schristos autofs_release_fh(mp);
536a53f50b9Schristos #endif /* HAVE_FS_AUTOFS */
537a53f50b9Schristos
538a53f50b9Schristos if (term) {
539a53f50b9Schristos /*
540a53f50b9Schristos * Not sure what to do for an error code.
541a53f50b9Schristos */
542a53f50b9Schristos mf->mf_error = EIO; /* XXX ? */
543a53f50b9Schristos mf->mf_flags |= MFF_ERROR;
544a53f50b9Schristos plog(XLOG_ERROR, "mount for %s got signal %d", mp->am_path, term);
545a53f50b9Schristos } else {
546a53f50b9Schristos /*
547a53f50b9Schristos * Check for exit status...
548a53f50b9Schristos */
549a53f50b9Schristos #ifdef __linux__
550a53f50b9Schristos /*
551a53f50b9Schristos * HACK ALERT!
552a53f50b9Schristos *
553a53f50b9Schristos * On Linux (and maybe not only) it's possible to run
554a53f50b9Schristos * an amd which "knows" how to mount certain combinations
555a53f50b9Schristos * of nfs_proto/nfs_version which the kernel doesn't grok.
556a53f50b9Schristos * So if we got an EINVAL and we have a server that's not
557a53f50b9Schristos * using NFSv2/UDP, try again with NFSv2/UDP.
558a53f50b9Schristos *
559a53f50b9Schristos * Too bad that there is no way to dynamically determine
560a53f50b9Schristos * what combinations the _client_ supports, as opposed to
561a53f50b9Schristos * what the _server_ supports...
562a53f50b9Schristos */
563a53f50b9Schristos if (rc == EINVAL &&
564a53f50b9Schristos mf->mf_server &&
565a53f50b9Schristos (mf->mf_server->fs_version != 2 ||
566a53f50b9Schristos !STREQ(mf->mf_server->fs_proto, "udp")))
567a53f50b9Schristos mf->mf_flags |= MFF_NFS_SCALEDOWN;
568a53f50b9Schristos else
569a53f50b9Schristos #endif /* __linux__ */
570a53f50b9Schristos {
571a53f50b9Schristos mf->mf_error = rc;
572a53f50b9Schristos mf->mf_flags |= MFF_ERROR;
573a53f50b9Schristos errno = rc; /* XXX */
5748bae5d40Schristos if (!STREQ(mp->am_al->al_mnt->mf_ops->fs_type, "linkx"))
575a53f50b9Schristos plog(XLOG_ERROR, "%s: mount (amfs_cont): %m", mp->am_path);
576a53f50b9Schristos }
577a53f50b9Schristos }
578a53f50b9Schristos
579a53f50b9Schristos if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) {
580a53f50b9Schristos /*
581a53f50b9Schristos * If we get here then that attempt didn't work, so
582a53f50b9Schristos * move the info vector pointer along by one and
583a53f50b9Schristos * call the background mount routine again
584a53f50b9Schristos */
585a53f50b9Schristos amd_stats.d_merr++;
5868bae5d40Schristos cp->al++;
587a53f50b9Schristos }
588a53f50b9Schristos amfs_bgmount(cp);
589a53f50b9Schristos if (mp->am_error > 0)
590a53f50b9Schristos assign_error_mntfs(mp);
591a53f50b9Schristos } else {
592a53f50b9Schristos /*
593a53f50b9Schristos * The mount worked.
594a53f50b9Schristos */
595a53f50b9Schristos dlog("Mounting %s returned success", cp->mp->am_path);
596a53f50b9Schristos am_mounted(cp->mp);
597a53f50b9Schristos free_continuation(cp);
598a53f50b9Schristos }
599a53f50b9Schristos
600a53f50b9Schristos reschedule_timeout_mp();
601a53f50b9Schristos }
602a53f50b9Schristos
603a53f50b9Schristos
604a53f50b9Schristos /*
605a53f50b9Schristos * Retry a mount
606a53f50b9Schristos */
607a53f50b9Schristos static void
amfs_retry(int rc,int term,opaque_t arg)608a53f50b9Schristos amfs_retry(int rc, int term, opaque_t arg)
609a53f50b9Schristos {
610a53f50b9Schristos struct continuation *cp = (struct continuation *) arg;
611a53f50b9Schristos am_node *mp = cp->mp;
612a53f50b9Schristos int error = 0;
613a53f50b9Schristos
614a53f50b9Schristos dlog("Commencing retry for mount of %s", mp->am_path);
615a53f50b9Schristos
616a53f50b9Schristos new_ttl(mp);
617a53f50b9Schristos
618a53f50b9Schristos if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime(NULL)) {
619a53f50b9Schristos /*
620a53f50b9Schristos * The entire mount has timed out. Set the error code and skip past all
621a53f50b9Schristos * the mntfs's so that amfs_bgmount will not have any more
622a53f50b9Schristos * ways to try the mount, thus causing an error.
623a53f50b9Schristos */
624a53f50b9Schristos plog(XLOG_INFO, "mount of \"%s\" has timed out", mp->am_path);
625a53f50b9Schristos error = ETIMEDOUT;
6268bae5d40Schristos while (*cp->al)
6278bae5d40Schristos cp->al++;
628a53f50b9Schristos /* explicitly forbid further retries after timeout */
629a53f50b9Schristos cp->retry = FALSE;
630a53f50b9Schristos }
631a53f50b9Schristos if (error || !IN_PROGRESS(cp))
632a53f50b9Schristos error = amfs_bgmount(cp);
6338bae5d40Schristos else
6348bae5d40Schristos /* Normally it's amfs_bgmount() which frees the continuation. However, if
6358bae5d40Schristos * the mount is already in progress and we're in amfs_retry() for another
6368bae5d40Schristos * node we don't try mounting the filesystem once again. Still, we have
6378bae5d40Schristos * to free the continuation as we won't get called again and thus would
6388bae5d40Schristos * leak the continuation structure and our am_loc references.
6398bae5d40Schristos */
6408bae5d40Schristos free_continuation(cp);
641a53f50b9Schristos
642a53f50b9Schristos reschedule_timeout_mp();
643a53f50b9Schristos }
644a53f50b9Schristos
645a53f50b9Schristos
646a53f50b9Schristos /*
647a53f50b9Schristos * Discard an old continuation
648a53f50b9Schristos */
649a53f50b9Schristos static void
free_continuation(struct continuation * cp)650a53f50b9Schristos free_continuation(struct continuation *cp)
651a53f50b9Schristos {
6528bae5d40Schristos am_loc **alp;
653a53f50b9Schristos
654a53f50b9Schristos dlog("free_continuation");
655a53f50b9Schristos if (cp->callout)
656a53f50b9Schristos untimeout(cp->callout);
657a53f50b9Schristos /*
658a53f50b9Schristos * we must free the mntfs's in the list.
659a53f50b9Schristos * so free all of them if there was an error,
660a53f50b9Schristos */
6618bae5d40Schristos for (alp = cp->mp->am_alarray; *alp; alp++) {
6628bae5d40Schristos free_loc(*alp);
663a53f50b9Schristos }
6648bae5d40Schristos XFREE(cp->mp->am_alarray);
6658bae5d40Schristos cp->mp->am_alarray = 0;
666a53f50b9Schristos XFREE(cp);
667a53f50b9Schristos }
668a53f50b9Schristos
669a53f50b9Schristos
670a53f50b9Schristos /*
671a53f50b9Schristos * Pick a file system to try mounting and
672a53f50b9Schristos * do that in the background if necessary
673a53f50b9Schristos *
674a53f50b9Schristos For each location:
675a53f50b9Schristos discard previous mount location if required
676a53f50b9Schristos fetch next mount location
677a53f50b9Schristos if the filesystem failed to be mounted then
678a53f50b9Schristos this_error = error from filesystem
679a53f50b9Schristos goto failed
680a53f50b9Schristos if the filesystem is mounting or unmounting then
681a53f50b9Schristos goto retry;
682a53f50b9Schristos if the fileserver is down then
683a53f50b9Schristos this_error = EIO
684a53f50b9Schristos continue;
685a53f50b9Schristos if the filesystem is already mounted
686a53f50b9Schristos break
687a53f50b9Schristos fi
688a53f50b9Schristos
689a53f50b9Schristos this_error = initialize mount point
690a53f50b9Schristos
691a53f50b9Schristos if no error on this mount and mount is delayed then
692a53f50b9Schristos this_error = -1
693a53f50b9Schristos fi
694a53f50b9Schristos if this_error < 0 then
695a53f50b9Schristos retry = true
696a53f50b9Schristos fi
697a53f50b9Schristos if no error on this mount then
698a53f50b9Schristos if mount in background then
699a53f50b9Schristos run mount in background
700a53f50b9Schristos return -1
701a53f50b9Schristos else
702a53f50b9Schristos this_error = mount in foreground
703a53f50b9Schristos fi
704a53f50b9Schristos fi
705a53f50b9Schristos if an error occurred on this mount then
706a53f50b9Schristos update stats
707a53f50b9Schristos save error in mount point
708a53f50b9Schristos fi
709a53f50b9Schristos endfor
710a53f50b9Schristos */
711a53f50b9Schristos static int
amfs_bgmount(struct continuation * cp)712a53f50b9Schristos amfs_bgmount(struct continuation *cp)
713a53f50b9Schristos {
714a53f50b9Schristos am_node *mp = cp->mp;
7158bae5d40Schristos am_loc *loc;
7168bae5d40Schristos mntfs *mf;
717a53f50b9Schristos int this_error = -1; /* Per-mount error */
718a53f50b9Schristos int hard_error = -1; /* Cumulative per-node error */
719a53f50b9Schristos
7208bae5d40Schristos if (mp->am_al)
7218bae5d40Schristos free_loc(mp->am_al);
722a53f50b9Schristos
723a53f50b9Schristos /*
724a53f50b9Schristos * Try to mount each location.
725a53f50b9Schristos * At the end:
726a53f50b9Schristos * hard_error == 0 indicates something was mounted.
727a53f50b9Schristos * hard_error > 0 indicates everything failed with a hard error
728a53f50b9Schristos * hard_error < 0 indicates nothing could be mounted now
729a53f50b9Schristos */
7308bae5d40Schristos for (mp->am_al = *cp->al; *cp->al; cp->al++, mp->am_al = *cp->al) {
731a53f50b9Schristos am_ops *p;
732a53f50b9Schristos
7338bae5d40Schristos loc = dup_loc(mp->am_al);
7348bae5d40Schristos mf = loc->al_mnt;
735a53f50b9Schristos p = mf->mf_ops;
736a53f50b9Schristos
737a53f50b9Schristos if (hard_error < 0)
738a53f50b9Schristos hard_error = this_error;
739a53f50b9Schristos this_error = 0;
740a53f50b9Schristos
741a53f50b9Schristos if (mf->mf_error > 0) {
742a53f50b9Schristos this_error = mf->mf_error;
743a53f50b9Schristos goto failed;
744a53f50b9Schristos }
745a53f50b9Schristos
746a53f50b9Schristos if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
747a53f50b9Schristos /*
748a53f50b9Schristos * Still mounting - retry later
749a53f50b9Schristos */
750a53f50b9Schristos dlog("mount of \"%s\" already pending", mf->mf_info);
751a53f50b9Schristos goto retry;
752a53f50b9Schristos }
753a53f50b9Schristos
754a53f50b9Schristos if (FSRV_ISDOWN(mf->mf_server)) {
755a53f50b9Schristos /*
756a53f50b9Schristos * Would just mount from the same place
757a53f50b9Schristos * as a hung mount - so give up
758a53f50b9Schristos */
759a53f50b9Schristos dlog("%s is already hung - giving up", mf->mf_server->fs_host);
760a53f50b9Schristos this_error = EIO;
761a53f50b9Schristos goto failed;
762a53f50b9Schristos }
763a53f50b9Schristos
764a53f50b9Schristos XFREE(mp->am_link);
765a53f50b9Schristos mp->am_link = NULL;
7668bae5d40Schristos
7678bae5d40Schristos if (loc->al_fo && loc->al_fo->opt_sublink && loc->al_fo->opt_sublink[0])
7688bae5d40Schristos mp->am_link = xstrdup(loc->al_fo->opt_sublink);
769a53f50b9Schristos
770a53f50b9Schristos /*
771a53f50b9Schristos * Will usually need to play around with the mount nodes
772a53f50b9Schristos * file attribute structure. This must be done here.
773a53f50b9Schristos * Try and get things initialized, even if the fileserver
774a53f50b9Schristos * is not known to be up. In the common case this will
775a53f50b9Schristos * progress things faster.
776a53f50b9Schristos */
777a53f50b9Schristos
778a53f50b9Schristos /*
779a53f50b9Schristos * Fill in attribute fields.
780a53f50b9Schristos */
781a53f50b9Schristos if (mf->mf_fsflags & FS_DIRECTORY)
782a53f50b9Schristos mk_fattr(&mp->am_fattr, NFDIR);
783a53f50b9Schristos else
784a53f50b9Schristos mk_fattr(&mp->am_fattr, NFLNK);
785a53f50b9Schristos
786a53f50b9Schristos if (mf->mf_flags & MFF_MOUNTED) {
787a53f50b9Schristos dlog("duplicate mount of \"%s\" ...", mf->mf_info);
788a53f50b9Schristos /*
789a53f50b9Schristos * Skip initial processing of the mountpoint if already mounted.
790a53f50b9Schristos * This could happen if we have multiple sublinks into the same f/s,
791a53f50b9Schristos * or if we are restarting an already-mounted filesystem.
792a53f50b9Schristos */
793a53f50b9Schristos goto already_mounted;
794a53f50b9Schristos }
795a53f50b9Schristos
796a53f50b9Schristos if (mf->mf_fo && mf->mf_fo->fs_mtab) {
797a53f50b9Schristos plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s",
798a53f50b9Schristos mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type,
799a53f50b9Schristos mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs");
800a53f50b9Schristos }
801a53f50b9Schristos
802a53f50b9Schristos if (p->fs_init && !(mf->mf_flags & MFF_RESTART))
803a53f50b9Schristos this_error = p->fs_init(mf);
804a53f50b9Schristos
805a53f50b9Schristos if (this_error > 0)
806a53f50b9Schristos goto failed;
807a53f50b9Schristos if (this_error < 0)
808a53f50b9Schristos goto retry;
809a53f50b9Schristos
8108bae5d40Schristos if (loc->al_fo && loc->al_fo->opt_delay) {
811a53f50b9Schristos /*
8128bae5d40Schristos * If there is a delay timer on the location
813a53f50b9Schristos * then don't try to mount if the timer
814a53f50b9Schristos * has not expired.
815a53f50b9Schristos */
8168bae5d40Schristos int i = atoi(loc->al_fo->opt_delay);
817a53f50b9Schristos time_t now = clocktime(NULL);
818a53f50b9Schristos if (i > 0 && now < (cp->start + i)) {
819a53f50b9Schristos dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - now + cp->start));
820a53f50b9Schristos goto retry;
821a53f50b9Schristos }
822a53f50b9Schristos }
823a53f50b9Schristos
824a53f50b9Schristos /*
825a53f50b9Schristos * If the directory is not yet made and it needs to be made, then make it!
826a53f50b9Schristos */
827a53f50b9Schristos if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) {
828a53f50b9Schristos plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount);
829a53f50b9Schristos this_error = mkdirs(mf->mf_mount, 0555);
830a53f50b9Schristos if (this_error) {
831a53f50b9Schristos plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error));
832a53f50b9Schristos goto failed;
833a53f50b9Schristos }
834a53f50b9Schristos mf->mf_flags |= MFF_MKMNT;
835a53f50b9Schristos }
836a53f50b9Schristos
837a53f50b9Schristos #ifdef HAVE_FS_AUTOFS
838a53f50b9Schristos if (mf->mf_flags & MFF_IS_AUTOFS)
839a53f50b9Schristos if ((this_error = autofs_get_fh(mp)))
840a53f50b9Schristos goto failed;
841a53f50b9Schristos #endif /* HAVE_FS_AUTOFS */
842a53f50b9Schristos
843a53f50b9Schristos already_mounted:
844a53f50b9Schristos mf->mf_flags |= MFF_MOUNTING;
845a53f50b9Schristos if (mf->mf_fsflags & FS_MBACKGROUND) {
846a53f50b9Schristos dlog("backgrounding mount of \"%s\"", mf->mf_mount);
847a53f50b9Schristos if (cp->callout) {
848a53f50b9Schristos untimeout(cp->callout);
849a53f50b9Schristos cp->callout = 0;
850a53f50b9Schristos }
851a53f50b9Schristos
852a53f50b9Schristos /* actually run the task, backgrounding as necessary */
853a53f50b9Schristos run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp);
854a53f50b9Schristos return -1;
855a53f50b9Schristos } else {
856a53f50b9Schristos dlog("foreground mount of \"%s\" ...", mf->mf_mount);
857a53f50b9Schristos this_error = mount_node((opaque_t) mp);
858a53f50b9Schristos }
859a53f50b9Schristos
860a53f50b9Schristos mf->mf_flags &= ~MFF_MOUNTING;
861a53f50b9Schristos if (this_error > 0)
862a53f50b9Schristos goto failed;
863a53f50b9Schristos if (this_error == 0) {
864a53f50b9Schristos am_mounted(mp);
865a53f50b9Schristos break; /* Success */
866a53f50b9Schristos }
867a53f50b9Schristos
868a53f50b9Schristos retry:
869a53f50b9Schristos if (!cp->retry)
870a53f50b9Schristos continue;
871a53f50b9Schristos dlog("will retry ...\n");
872a53f50b9Schristos
873a53f50b9Schristos /*
874a53f50b9Schristos * Arrange that amfs_bgmount is called
875a53f50b9Schristos * after anything else happens.
876a53f50b9Schristos */
877a53f50b9Schristos dlog("Arranging to retry mount of %s", mp->am_path);
878a53f50b9Schristos sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf));
879a53f50b9Schristos if (cp->callout)
880a53f50b9Schristos untimeout(cp->callout);
881a53f50b9Schristos cp->callout = timeout(RETRY_INTERVAL, wakeup,
882a53f50b9Schristos (opaque_t) get_mntfs_wchan(mf));
883a53f50b9Schristos
884a53f50b9Schristos mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL;
885a53f50b9Schristos
886a53f50b9Schristos /*
887a53f50b9Schristos * Not done yet - so don't return anything
888a53f50b9Schristos */
889a53f50b9Schristos return -1;
890a53f50b9Schristos
891a53f50b9Schristos failed:
8928bae5d40Schristos if (!FSRV_ISDOWN(mf->mf_server)) {
8938bae5d40Schristos /* mark the mount as failed unless the server is down */
894a53f50b9Schristos amd_stats.d_merr++;
895a53f50b9Schristos mf->mf_error = this_error;
896a53f50b9Schristos mf->mf_flags |= MFF_ERROR;
897a53f50b9Schristos #ifdef HAVE_FS_AUTOFS
898a53f50b9Schristos if (mp->am_autofs_fh)
899a53f50b9Schristos autofs_release_fh(mp);
900a53f50b9Schristos #endif /* HAVE_FS_AUTOFS */
901a53f50b9Schristos if (mf->mf_flags & MFF_MKMNT) {
902a53f50b9Schristos rmdirs(mf->mf_mount);
903a53f50b9Schristos mf->mf_flags &= ~MFF_MKMNT;
904a53f50b9Schristos }
9058bae5d40Schristos }
906a53f50b9Schristos /*
907a53f50b9Schristos * Wakeup anything waiting for this mount
908a53f50b9Schristos */
909a53f50b9Schristos wakeup(get_mntfs_wchan(mf));
9108bae5d40Schristos free_loc(loc);
911a53f50b9Schristos /* continue */
912a53f50b9Schristos }
913a53f50b9Schristos
914a53f50b9Schristos /*
915a53f50b9Schristos * If we get here, then either the mount succeeded or
916a53f50b9Schristos * there is no more mount information available.
917a53f50b9Schristos */
918a53f50b9Schristos if (this_error) {
9198bae5d40Schristos if (mp->am_al)
9208bae5d40Schristos free_loc(mp->am_al);
9218bae5d40Schristos mp->am_al = loc = new_loc();
9228bae5d40Schristos mf = loc->al_mnt;
923a53f50b9Schristos
924a53f50b9Schristos #ifdef HAVE_FS_AUTOFS
925a53f50b9Schristos if (mp->am_flags & AMF_AUTOFS)
926a53f50b9Schristos autofs_mount_failed(mp);
927a53f50b9Schristos else
928a53f50b9Schristos #endif /* HAVE_FS_AUTOFS */
929a53f50b9Schristos nfs_quick_reply(mp, this_error);
930a53f50b9Schristos
931a53f50b9Schristos if (hard_error <= 0)
932a53f50b9Schristos hard_error = this_error;
933a53f50b9Schristos if (hard_error < 0)
934a53f50b9Schristos hard_error = ETIMEDOUT;
935a53f50b9Schristos
936a53f50b9Schristos /*
937a53f50b9Schristos * Set a small(ish) timeout on an error node if
938a53f50b9Schristos * the error was not a time out.
939a53f50b9Schristos */
940a53f50b9Schristos switch (hard_error) {
941a53f50b9Schristos case ETIMEDOUT:
942a53f50b9Schristos case EWOULDBLOCK:
943a53f50b9Schristos case EIO:
944a53f50b9Schristos mp->am_timeo = 17;
945a53f50b9Schristos break;
946a53f50b9Schristos default:
947a53f50b9Schristos mp->am_timeo = 5;
948a53f50b9Schristos break;
949a53f50b9Schristos }
950a53f50b9Schristos new_ttl(mp);
951a53f50b9Schristos } else {
9528bae5d40Schristos mf = loc->al_mnt;
953a53f50b9Schristos /*
954a53f50b9Schristos * Wakeup anything waiting for this mount
955a53f50b9Schristos */
956a53f50b9Schristos wakeup(get_mntfs_wchan(mf));
957a53f50b9Schristos hard_error = 0;
958a53f50b9Schristos }
959a53f50b9Schristos
960a53f50b9Schristos /*
961a53f50b9Schristos * Make sure that the error value in the mntfs has a
962a53f50b9Schristos * reasonable value.
963a53f50b9Schristos */
964a53f50b9Schristos if (mf->mf_error < 0) {
965a53f50b9Schristos mf->mf_error = hard_error;
966a53f50b9Schristos if (hard_error)
967a53f50b9Schristos mf->mf_flags |= MFF_ERROR;
968a53f50b9Schristos }
969a53f50b9Schristos
970a53f50b9Schristos /*
971a53f50b9Schristos * In any case we don't need the continuation any more
972a53f50b9Schristos */
973a53f50b9Schristos free_continuation(cp);
974a53f50b9Schristos
975a53f50b9Schristos return hard_error;
976a53f50b9Schristos }
977a53f50b9Schristos
978a53f50b9Schristos
979a53f50b9Schristos static char *
amfs_parse_defaults(am_node * mp,mntfs * mf,char * def_opts)980a53f50b9Schristos amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts)
981a53f50b9Schristos {
982a53f50b9Schristos char *dflts;
983a53f50b9Schristos char *dfl;
984a53f50b9Schristos char **rvec = NULL;
985a53f50b9Schristos struct mnt_map *mm = (mnt_map *) mf->mf_private;
986a53f50b9Schristos
987a53f50b9Schristos dlog("determining /defaults entry value");
988a53f50b9Schristos
989a53f50b9Schristos /*
990a53f50b9Schristos * Find out if amd.conf overrode any map-specific /defaults.
991a53f50b9Schristos */
992a53f50b9Schristos if (mm->cfm && mm->cfm->cfm_defaults) {
993a53f50b9Schristos dlog("map %s map_defaults override: %s", mf->mf_mount, mm->cfm->cfm_defaults);
9948bae5d40Schristos dflts = xstrdup(mm->cfm->cfm_defaults);
995a53f50b9Schristos } else if (mapc_search(mm, "/defaults", &dflts) == 0) {
996a53f50b9Schristos dlog("/defaults gave %s", dflts);
997a53f50b9Schristos } else {
998a53f50b9Schristos return def_opts; /* if nothing found */
999a53f50b9Schristos }
1000a53f50b9Schristos
1001a53f50b9Schristos /* trim leading '-' in case thee's one */
1002a53f50b9Schristos if (*dflts == '-')
1003a53f50b9Schristos dfl = dflts + 1;
1004a53f50b9Schristos else
1005a53f50b9Schristos dfl = dflts;
1006a53f50b9Schristos
1007a53f50b9Schristos /*
1008a53f50b9Schristos * Chop the defaults up
1009a53f50b9Schristos */
1010a53f50b9Schristos rvec = strsplit(dfl, ' ', '\"');
1011a53f50b9Schristos
1012a53f50b9Schristos if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) {
1013a53f50b9Schristos /*
1014a53f50b9Schristos * Pick whichever first entry matched the list of selectors.
1015a53f50b9Schristos * Strip the selectors from the string, and assign to dfl the
1016a53f50b9Schristos * rest of the string.
1017a53f50b9Schristos */
1018a53f50b9Schristos if (rvec) {
1019a53f50b9Schristos am_opts ap;
1020a53f50b9Schristos am_ops *pt;
1021a53f50b9Schristos char **sp = rvec;
1022a53f50b9Schristos while (*sp) { /* loop until you find something, if any */
1023a53f50b9Schristos memset((char *) &ap, 0, sizeof(am_opts));
1024a53f50b9Schristos /*
1025a53f50b9Schristos * This next routine cause many spurious "expansion of ... is"
1026a53f50b9Schristos * messages, which are ignored, b/c all we need out of this
1027a53f50b9Schristos * routine is to match selectors. These spurious messages may
1028a53f50b9Schristos * be wrong, esp. if they try to expand ${key} b/c it will
1029a53f50b9Schristos * get expanded to "/defaults"
1030a53f50b9Schristos */
1031a53f50b9Schristos pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
10328bae5d40Schristos mp->am_parent->am_al->al_mnt->mf_info);
1033a53f50b9Schristos free_opts(&ap); /* don't leak */
1034a53f50b9Schristos if (pt == &amfs_error_ops) {
1035a53f50b9Schristos plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp);
1036a53f50b9Schristos } else {
1037a53f50b9Schristos dfl = strip_selectors(*sp, "/defaults");
1038a53f50b9Schristos plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1039a53f50b9Schristos break;
1040a53f50b9Schristos }
1041a53f50b9Schristos ++sp;
1042a53f50b9Schristos }
1043a53f50b9Schristos }
1044a53f50b9Schristos } else { /* not selectors_in_defaults */
1045a53f50b9Schristos /*
1046a53f50b9Schristos * Extract first value
1047a53f50b9Schristos */
1048a53f50b9Schristos dfl = rvec[0];
1049a53f50b9Schristos }
1050a53f50b9Schristos
1051a53f50b9Schristos /*
1052a53f50b9Schristos * If there were any values at all...
1053a53f50b9Schristos */
1054a53f50b9Schristos if (dfl) {
1055a53f50b9Schristos /*
1056a53f50b9Schristos * Log error if there were other values
1057a53f50b9Schristos */
1058a53f50b9Schristos if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) {
1059a53f50b9Schristos dlog("/defaults chopped into %s", dfl);
1060a53f50b9Schristos plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1061a53f50b9Schristos }
1062a53f50b9Schristos
1063a53f50b9Schristos /*
1064a53f50b9Schristos * Prepend to existing defaults if they exist,
1065a53f50b9Schristos * otherwise just use these defaults.
1066a53f50b9Schristos */
1067a53f50b9Schristos if (*def_opts && *dfl) {
1068a53f50b9Schristos size_t l = strlen(def_opts) + strlen(dfl) + 2;
1069a53f50b9Schristos char *nopts = (char *) xmalloc(l);
1070a53f50b9Schristos xsnprintf(nopts, l, "%s;%s", dfl, def_opts);
1071a53f50b9Schristos XFREE(def_opts);
1072a53f50b9Schristos def_opts = nopts;
1073a53f50b9Schristos } else if (*dfl) {
1074a53f50b9Schristos def_opts = strealloc(def_opts, dfl);
1075a53f50b9Schristos }
1076a53f50b9Schristos }
1077a53f50b9Schristos
1078a53f50b9Schristos XFREE(dflts);
1079a53f50b9Schristos
1080a53f50b9Schristos /* don't need info vector any more */
1081a53f50b9Schristos if (rvec)
1082a53f50b9Schristos XFREE(rvec);
1083a53f50b9Schristos
1084a53f50b9Schristos return def_opts;
1085a53f50b9Schristos }
1086a53f50b9Schristos
1087a53f50b9Schristos
1088a53f50b9Schristos am_node *
amfs_generic_mount_child(am_node * new_mp,int * error_return)1089a53f50b9Schristos amfs_generic_mount_child(am_node *new_mp, int *error_return)
1090a53f50b9Schristos {
1091a53f50b9Schristos int error;
1092a53f50b9Schristos struct continuation *cp; /* Continuation structure if need to mount */
1093a53f50b9Schristos
1094a53f50b9Schristos dlog("in amfs_generic_mount_child");
1095a53f50b9Schristos
1096a53f50b9Schristos *error_return = error = 0; /* Error so far */
1097a53f50b9Schristos
1098a53f50b9Schristos /* we have an errorfs attached to the am_node, free it */
10998bae5d40Schristos if (new_mp->am_al)
11008bae5d40Schristos free_loc(new_mp->am_al);
11018bae5d40Schristos new_mp->am_al = NULL;
1102a53f50b9Schristos
1103a53f50b9Schristos /*
1104a53f50b9Schristos * Construct a continuation
1105a53f50b9Schristos */
1106a53f50b9Schristos cp = ALLOC(struct continuation);
1107a53f50b9Schristos cp->callout = 0;
1108a53f50b9Schristos cp->mp = new_mp;
1109a53f50b9Schristos cp->retry = TRUE;
1110a53f50b9Schristos cp->start = clocktime(NULL);
11118bae5d40Schristos cp->al = new_mp->am_alarray;
1112a53f50b9Schristos
1113a53f50b9Schristos /*
1114a53f50b9Schristos * Try and mount the file system. If this succeeds immediately (possible
1115a53f50b9Schristos * for a ufs file system) then return the attributes, otherwise just
1116a53f50b9Schristos * return an error.
1117a53f50b9Schristos */
1118a53f50b9Schristos error = amfs_bgmount(cp);
1119a53f50b9Schristos reschedule_timeout_mp();
1120a53f50b9Schristos if (!error)
1121a53f50b9Schristos return new_mp;
1122a53f50b9Schristos
1123a53f50b9Schristos /*
1124a53f50b9Schristos * Code for quick reply. If current_transp is set, then it's the
11258bae5d40Schristos * transp that's been passed down from nfs_dispatcher() or from
1126a53f50b9Schristos * autofs_program_[123]().
1127a53f50b9Schristos * If new_mp->am_transp is not already set, set it by copying in
1128a53f50b9Schristos * current_transp. Once am_transp is set, nfs_quick_reply() and
1129a53f50b9Schristos * autofs_mount_succeeded() can use it to send a reply to the
1130a53f50b9Schristos * client that requested this mount.
1131a53f50b9Schristos */
1132a53f50b9Schristos if (current_transp && !new_mp->am_transp) {
1133a53f50b9Schristos dlog("Saving RPC transport for %s", new_mp->am_path);
1134a53f50b9Schristos new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1135a53f50b9Schristos *(new_mp->am_transp) = *current_transp;
1136a53f50b9Schristos }
11378bae5d40Schristos if (error && new_mp->am_al && new_mp->am_al->al_mnt &&
11388bae5d40Schristos (new_mp->am_al->al_mnt->mf_ops == &amfs_error_ops))
1139a53f50b9Schristos new_mp->am_error = error;
1140a53f50b9Schristos
1141a53f50b9Schristos if (new_mp->am_error > 0)
1142a53f50b9Schristos assign_error_mntfs(new_mp);
1143a53f50b9Schristos
1144a53f50b9Schristos ereturn(error);
1145a53f50b9Schristos }
1146a53f50b9Schristos
1147a53f50b9Schristos
1148a53f50b9Schristos /*
1149a53f50b9Schristos * Automount interface to RPC lookup routine
1150a53f50b9Schristos * Find the corresponding entry and return
1151a53f50b9Schristos * the file handle for it.
1152a53f50b9Schristos */
1153a53f50b9Schristos am_node *
amfs_generic_lookup_child(am_node * mp,char * fname,int * error_return,int op)1154a53f50b9Schristos amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op)
1155a53f50b9Schristos {
1156a53f50b9Schristos am_node *new_mp;
11578bae5d40Schristos am_loc **al_array;
1158a53f50b9Schristos
1159a53f50b9Schristos dlog("in amfs_generic_lookup_child");
1160a53f50b9Schristos
1161a53f50b9Schristos *error_return = 0;
1162a53f50b9Schristos new_mp = amfs_lookup_node(mp, fname, error_return);
1163a53f50b9Schristos
1164a53f50b9Schristos /* return if we got an error */
1165a53f50b9Schristos if (!new_mp || *error_return > 0)
1166a53f50b9Schristos return new_mp;
1167a53f50b9Schristos
1168a53f50b9Schristos /* also return if it's already mounted and known to be up */
11698bae5d40Schristos if (*error_return == 0 && FSRV_ISUP(new_mp->am_al->al_mnt->mf_server))
1170a53f50b9Schristos return new_mp;
1171a53f50b9Schristos
1172a53f50b9Schristos switch (op) {
1173a53f50b9Schristos case VLOOK_DELETE:
1174a53f50b9Schristos /*
1175a53f50b9Schristos * If doing a delete then don't create again!
1176a53f50b9Schristos */
1177a53f50b9Schristos ereturn(ENOENT);
1178a53f50b9Schristos case VLOOK_LOOKUP:
1179a53f50b9Schristos return new_mp;
1180a53f50b9Schristos }
1181a53f50b9Schristos
11828bae5d40Schristos al_array = amfs_lookup_loc(new_mp, error_return);
11838bae5d40Schristos if (!al_array) {
11848bae5d40Schristos new_mp->am_error = new_mp->am_al->al_mnt->mf_error = *error_return;
1185a53f50b9Schristos free_map(new_mp);
1186a53f50b9Schristos return NULL;
1187a53f50b9Schristos }
1188a53f50b9Schristos
1189a53f50b9Schristos /* store the array inside the am_node */
11908bae5d40Schristos new_mp->am_alarray = al_array;
1191a53f50b9Schristos
1192a53f50b9Schristos /*
1193a53f50b9Schristos * Note: while it might seem like a good idea to prioritize
1194a53f50b9Schristos * the list of mntfs's we got here, it probably isn't.
1195a53f50b9Schristos * It would ignore the ordering of entries specified by the user,
1196a53f50b9Schristos * which is counterintuitive and confusing.
1197a53f50b9Schristos */
1198a53f50b9Schristos return new_mp;
1199a53f50b9Schristos }
1200a53f50b9Schristos
1201a53f50b9Schristos
1202a53f50b9Schristos void
amfs_generic_mounted(mntfs * mf)1203a53f50b9Schristos amfs_generic_mounted(mntfs *mf)
1204a53f50b9Schristos {
1205a53f50b9Schristos amfs_mkcacheref(mf);
1206a53f50b9Schristos }
1207a53f50b9Schristos
1208a53f50b9Schristos
1209a53f50b9Schristos /*
1210a53f50b9Schristos * Unmount an automount sub-node
1211a53f50b9Schristos */
1212a53f50b9Schristos int
amfs_generic_umount(am_node * mp,mntfs * mf)1213a53f50b9Schristos amfs_generic_umount(am_node *mp, mntfs *mf)
1214a53f50b9Schristos {
1215a53f50b9Schristos int error = 0;
1216a53f50b9Schristos
1217a53f50b9Schristos #ifdef HAVE_FS_AUTOFS
1218a53f50b9Schristos int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
1219a53f50b9Schristos if (mf->mf_flags & MFF_IS_AUTOFS)
1220a53f50b9Schristos error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags);
1221a53f50b9Schristos #endif /* HAVE_FS_AUTOFS */
1222a53f50b9Schristos
1223a53f50b9Schristos return error;
1224a53f50b9Schristos }
1225a53f50b9Schristos
1226a53f50b9Schristos
1227a53f50b9Schristos char *
amfs_generic_match(am_opts * fo)1228a53f50b9Schristos amfs_generic_match(am_opts *fo)
1229a53f50b9Schristos {
1230a53f50b9Schristos char *p;
1231a53f50b9Schristos
1232a53f50b9Schristos if (!fo->opt_rfs) {
1233a53f50b9Schristos plog(XLOG_USER, "amfs_generic_match: no mount point named (rfs:=)");
1234a53f50b9Schristos return 0;
1235a53f50b9Schristos }
1236a53f50b9Schristos if (!fo->opt_fs) {
1237a53f50b9Schristos plog(XLOG_USER, "amfs_generic_match: no map named (fs:=)");
1238a53f50b9Schristos return 0;
1239a53f50b9Schristos }
1240a53f50b9Schristos
1241a53f50b9Schristos /*
1242a53f50b9Schristos * Swap round fs:= and rfs:= options
1243a53f50b9Schristos * ... historical (jsp)
1244a53f50b9Schristos */
1245a53f50b9Schristos p = fo->opt_rfs;
1246a53f50b9Schristos fo->opt_rfs = fo->opt_fs;
1247a53f50b9Schristos fo->opt_fs = p;
1248a53f50b9Schristos
1249a53f50b9Schristos /*
1250a53f50b9Schristos * mtab entry turns out to be the name of the mount map
1251a53f50b9Schristos */
12528bae5d40Schristos return xstrdup(fo->opt_rfs ? fo->opt_rfs : ".");
1253a53f50b9Schristos }
1254