xref: /netbsd-src/external/bsd/am-utils/dist/amd/amfs_generic.c (revision 4da6d876cd9670a0f55b4857d574006fa142db45)
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