amfs_toplvl.c revision 310490
1162271Srwatson/*
2162271Srwatson * Copyright (c) 1997-2014 Erez Zadok
3172106Srwatson * Copyright (c) 1990 Jan-Simon Pendry
4162271Srwatson * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5162271Srwatson * Copyright (c) 1990 The Regents of the University of California.
6162271Srwatson * All rights reserved.
7162271Srwatson *
8162271Srwatson * This code is derived from software contributed to Berkeley by
9162271Srwatson * Jan-Simon Pendry at Imperial College, London.
10162271Srwatson *
11162271Srwatson * Redistribution and use in source and binary forms, with or without
12162271Srwatson * modification, are permitted provided that the following conditions
13162271Srwatson * are met:
14162271Srwatson * 1. Redistributions of source code must retain the above copyright
15162271Srwatson *    notice, this list of conditions and the following disclaimer.
16162271Srwatson * 2. Redistributions in binary form must reproduce the above copyright
17162271Srwatson *    notice, this list of conditions and the following disclaimer in the
18162271Srwatson *    documentation and/or other materials provided with the distribution.
19162271Srwatson * 3. Neither the name of the University nor the names of its contributors
20162271Srwatson *    may be used to endorse or promote products derived from this software
21162271Srwatson *    without specific prior written permission.
22162271Srwatson *
23162271Srwatson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24162271Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25162271Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26162271Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27162271Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28162271Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29162271Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30162271Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31162271Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32162271Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33162271Srwatson * SUCH DAMAGE.
34172106Srwatson *
35162271Srwatson *
36162271Srwatson * File: am-utils/amd/amfs_toplvl.c
37162271Srwatson *
38162271Srwatson */
39162271Srwatson
40162271Srwatson/*
41162271Srwatson * Top-level file system
42162271Srwatson */
43162271Srwatson
44162271Srwatson#ifdef HAVE_CONFIG_H
45162271Srwatson# include <config.h>
46172106Srwatson#endif /* HAVE_CONFIG_H */
47172106Srwatson#include <am_defs.h>
48172106Srwatson#include <amd.h>
49172106Srwatson
50172106Srwatson/****************************************************************************
51172106Srwatson *** FORWARD DEFINITIONS                                                  ***
52172106Srwatson ****************************************************************************/
53162271Srwatsonstatic int amfs_toplvl_init(mntfs *mf);
54172106Srwatson
55162271Srwatson/****************************************************************************
56162271Srwatson *** OPS STRUCTURES                                                       ***
57162271Srwatson ****************************************************************************/
58162271Srwatsonam_ops amfs_toplvl_ops =
59172106Srwatson{
60172106Srwatson  "toplvl",
61172106Srwatson  amfs_generic_match,
62172106Srwatson  amfs_toplvl_init,		/* amfs_toplvl_init */
63172106Srwatson  amfs_toplvl_mount,
64172106Srwatson  amfs_toplvl_umount,
65172106Srwatson  amfs_generic_lookup_child,
66172106Srwatson  amfs_generic_mount_child,
67172106Srwatson  amfs_generic_readdir,
68172106Srwatson  0,				/* amfs_toplvl_readlink */
69172106Srwatson  amfs_generic_mounted,
70172106Srwatson  0,				/* amfs_toplvl_umounted */
71162271Srwatson  amfs_generic_find_srvr,
72172106Srwatson  0,				/* amfs_toplvl_get_wchan */
73172106Srwatson  FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND |
74172106Srwatson	  FS_AMQINFO | FS_DIRECTORY, /* nfs_fs_flags */
75162271Srwatson#ifdef HAVE_FS_AUTOFS
76162271Srwatson  AUTOFS_TOPLVL_FS_FLAGS,
77#endif /* HAVE_FS_AUTOFS */
78};
79
80
81/****************************************************************************
82 *** FUNCTIONS                                                             ***
83 ****************************************************************************/
84
85static void
86set_auto_attrcache_timeout(char *preopts, char *opts, size_t l)
87{
88
89#ifdef MNTTAB_OPT_NOAC
90  /*
91   * Don't cache attributes - they are changing under the kernel's feet.
92   * For example, IRIX5.2 will dispense with nfs lookup calls and hand stale
93   * filehandles to getattr unless we disable attribute caching on the
94   * automount points.
95   */
96  if (gopt.auto_attrcache == 0) {
97    xsnprintf(preopts, l, ",%s", MNTTAB_OPT_NOAC);
98    xstrlcat(opts, preopts, l);
99  }
100#endif /* MNTTAB_OPT_NOAC */
101
102  /*
103   * XXX: note that setting these to 0 in the past resulted in an error on
104   * some systems, which is why it's better to use "noac" if possible.  For
105   * now, we're setting everything possible, but if this will cause trouble,
106   * then we'll have to condition the remainder of this on OPT_NOAC.
107   */
108#ifdef MNTTAB_OPT_ACTIMEO
109  xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTIMEO, gopt.auto_attrcache);
110  xstrlcat(opts, preopts, l);
111#else /* MNTTAB_OPT_ACTIMEO */
112# ifdef MNTTAB_OPT_ACDIRMIN
113  xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTDIRMIN, gopt.auto_attrcache);
114  xstrlcat(opts, preopts, l);
115# endif /* MNTTAB_OPT_ACDIRMIN */
116# ifdef MNTTAB_OPT_ACDIRMAX
117  xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTDIRMAX, gopt.auto_attrcache);
118  xstrlcat(opts, preopts, l);
119# endif /* MNTTAB_OPT_ACDIRMAX */
120# ifdef MNTTAB_OPT_ACREGMIN
121  xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTREGMIN, gopt.auto_attrcache);
122  xstrlcat(opts, preopts, l);
123# endif /* MNTTAB_OPT_ACREGMIN */
124# ifdef MNTTAB_OPT_ACREGMAX
125  xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTREGMAX, gopt.auto_attrcache);
126  xstrlcat(opts, preopts, l);
127# endif /* MNTTAB_OPT_ACREGMAX */
128#endif /* MNTTAB_OPT_ACTIMEO */
129}
130
131
132/*
133 * Initialize a top-level mount.  In our case, if the user asked for
134 * forced_unmounts, and the OS supports it, then we try forced/lazy unmounts
135 * on any previous toplvl mounts.  This is useful if a previous Amd died and
136 * left behind toplvl mount points (this Amd will clean them up).
137 *
138 * WARNING: Don't use forced/lazy unmounts if you have another valid Amd
139 * running, because this code WILL force those valid toplvl mount points to
140 * be detached as well!
141 */
142static int
143amfs_toplvl_init(mntfs *mf)
144{
145  int error = 0;
146
147#if (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH)) && (defined(HAVE_UVMOUNT) || defined(HAVE_UMOUNT2))
148  if (gopt.flags & CFM_FORCED_UNMOUNTS) {
149    plog(XLOG_INFO, "amfs_toplvl_init: trying forced/lazy unmount of %s",
150	 mf->mf_mount);
151    error = umount2_fs(mf->mf_mount, AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH);
152    if (error)
153      plog(XLOG_INFO, "amfs_toplvl_init: forced/lazy unmount failed: %m");
154    else
155      dlog("amfs_toplvl_init: forced/lazy unmount succeeded");
156  }
157#endif /* (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) && (HAVE_UVMOUNT || HAVE_UMOUNT2) */
158  return error;
159}
160
161
162/*
163 * Mount the top-level
164 */
165int
166amfs_toplvl_mount(am_node *mp, mntfs *mf)
167{
168  struct stat stb;
169  char opts[SIZEOF_OPTS], preopts[SIZEOF_OPTS], toplvl_opts[40];
170  int error;
171
172  /*
173   * Mounting the automounter.
174   * Make sure the mount directory exists, construct
175   * the mount options and call the mount_amfs_toplvl routine.
176   */
177
178  if (stat(mp->am_path, &stb) < 0) {
179    return errno;
180  } else if ((stb.st_mode & S_IFMT) != S_IFDIR) {
181    plog(XLOG_WARNING, "%s is not a directory", mp->am_path);
182    return ENOTDIR;
183  }
184
185  /*
186   * Construct some mount options:
187   *
188   * Tack on magic map=<mapname> option in mtab to emulate
189   * SunOS automounter behavior.
190   */
191
192#ifdef HAVE_FS_AUTOFS
193  if (mf->mf_flags & MFF_IS_AUTOFS) {
194    autofs_get_opts(opts, sizeof(opts), mp->am_autofs_fh);
195  } else
196#endif /* HAVE_FS_AUTOFS */
197  {
198    preopts[0] = '\0';
199#ifdef MNTTAB_OPT_INTR
200    xstrlcat(preopts, MNTTAB_OPT_INTR, sizeof(preopts));
201    xstrlcat(preopts, ",", sizeof(preopts));
202#endif /* MNTTAB_OPT_INTR */
203#ifdef MNTTAB_OPT_IGNORE
204    xstrlcat(preopts, MNTTAB_OPT_IGNORE, sizeof(preopts));
205    xstrlcat(preopts, ",", sizeof(preopts));
206#endif /* MNTTAB_OPT_IGNORE */
207    /* write most of the initial options + preopts */
208    xsnprintf(opts, sizeof(opts), "%s%s,%s=%d,%s,map=%s",
209	      preopts,
210	      MNTTAB_OPT_RW,
211	      MNTTAB_OPT_PORT, nfs_port,
212	      mf->mf_ops->fs_type, mf->mf_info);
213
214    /* process toplvl timeo/retrans options, if any */
215    if (gopt.amfs_auto_timeo[AMU_TYPE_TOPLVL] > 0) {
216      xsnprintf(toplvl_opts, sizeof(toplvl_opts), ",%s=%d",
217		MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo[AMU_TYPE_TOPLVL]);
218      xstrlcat(opts, toplvl_opts, sizeof(opts));
219    }
220    if (gopt.amfs_auto_retrans[AMU_TYPE_TOPLVL] > 0) {
221      xsnprintf(toplvl_opts, sizeof(toplvl_opts), ",%s=%d",
222		MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans[AMU_TYPE_TOPLVL]);
223      xstrlcat(opts, toplvl_opts, sizeof(opts));
224    }
225
226#ifdef MNTTAB_OPT_NOLOCK
227    xstrlcat(opts, ",", sizeof(opts));
228    xstrlcat(opts, MNTTAB_OPT_NOLOCK, sizeof(opts));
229#endif /* MNTTAB_OPT_NOLOCK */
230
231#ifdef MNTTAB_OPT_NOAC
232    if (gopt.auto_attrcache == 0) {
233      xstrlcat(opts, ",", sizeof(opts));
234      xstrlcat(opts, MNTTAB_OPT_NOAC, sizeof(opts));
235    } else
236#endif /* MNTTAB_OPT_NOAC */
237      set_auto_attrcache_timeout(preopts, opts, sizeof(preopts));
238  }
239
240  /* now do the mount */
241  error = amfs_mount(mp, mf, opts);
242  if (error) {
243    errno = error;
244    plog(XLOG_FATAL, "amfs_toplvl_mount: amfs_mount failed: %m");
245    return error;
246  }
247  return 0;
248}
249
250
251/*
252 * Unmount a top-level automount node
253 */
254int
255amfs_toplvl_umount(am_node *mp, mntfs *mf)
256{
257  struct stat stb;
258  int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
259  int error;
260  int count = 0;		/* how many times did we try to unmount? */
261
262again:
263  /*
264   * The lstat is needed if this mount is type=direct.
265   * When that happens, the kernel cache gets confused
266   * between the underlying type (dir) and the mounted
267   * type (link) and so needs to be re-synced before
268   * the unmount.  This is all because the unmount system
269   * call follows links and so can't actually unmount
270   * a link (stupid!).  It was noted that doing an ls -ld
271   * of the mount point to see why things were not working
272   * actually fixed the problem - so simulate an ls -ld here.
273   */
274  if (lstat(mp->am_path, &stb) < 0) {
275    error = errno;
276    dlog("lstat(%s): %m", mp->am_path);
277    goto out;
278  }
279  if ((stb.st_mode & S_IFMT) != S_IFDIR) {
280    plog(XLOG_ERROR, "amfs_toplvl_umount: %s is not a directory, aborting.", mp->am_path);
281    error = ENOTDIR;
282    goto out;
283  }
284
285  error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags);
286  if (error == EBUSY) {
287#ifdef HAVE_FS_AUTOFS
288    /*
289     * autofs mounts are "in place", so it is possible
290     * that we can't just unmount our mount points and go away.
291     * If that's the case, just give up.
292     */
293    if (mf->mf_flags & MFF_IS_AUTOFS)
294      return error;
295#endif /* HAVE_FS_AUTOFS */
296    plog(XLOG_WARNING, "amfs_toplvl_unmount retrying %s in 1s", mp->am_path);
297    count++;
298    sleep(1);
299    /*
300     * If user wants forced/lazy unmount semantics, then set those flags,
301     * but only after we've tried normal lstat/umount a few times --
302     * otherwise forced unmounts may hang this very same Amd (by preventing
303     * it from achieving a clean unmount).
304     */
305    if (gopt.flags & CFM_FORCED_UNMOUNTS) {
306      if (count == 5) {		/* after 5 seconds, try MNT_FORCE */
307	dlog("enabling forced unmounts for toplvl node %s", mp->am_path);
308	unmount_flags |= AMU_UMOUNT_FORCE;
309      }
310      if (count == 10) {	/* after 10 seconds, try MNT_DETACH */
311	dlog("enabling detached unmounts for toplvl node %s", mp->am_path);
312	unmount_flags |= AMU_UMOUNT_DETACH;
313      }
314    }
315    goto again;
316  }
317out:
318  return error;
319}
320