138494Sobrien/*
2310490Scy * Copyright (c) 1997-2014 Erez Zadok
338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1990 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
19310490Scy * 3. Neither the name of the University nor the names of its contributors
2038494Sobrien *    may be used to endorse or promote products derived from this software
2138494Sobrien *    without specific prior written permission.
2238494Sobrien *
2338494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2438494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2538494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2638494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2738494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2838494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2938494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3038494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3138494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3238494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3338494Sobrien * SUCH DAMAGE.
3438494Sobrien *
3538494Sobrien *
36174294Sobrien * File: am-utils/amd/amfs_toplvl.c
3738494Sobrien *
3838494Sobrien */
3938494Sobrien
4038494Sobrien/*
4138494Sobrien * Top-level file system
4238494Sobrien */
4338494Sobrien
4438494Sobrien#ifdef HAVE_CONFIG_H
4538494Sobrien# include <config.h>
4638494Sobrien#endif /* HAVE_CONFIG_H */
4738494Sobrien#include <am_defs.h>
4838494Sobrien#include <amd.h>
4938494Sobrien
5038494Sobrien/****************************************************************************
5138494Sobrien *** FORWARD DEFINITIONS                                                  ***
5238494Sobrien ****************************************************************************/
53174294Sobrienstatic int amfs_toplvl_init(mntfs *mf);
5438494Sobrien
5538494Sobrien/****************************************************************************
5638494Sobrien *** OPS STRUCTURES                                                       ***
5738494Sobrien ****************************************************************************/
5838494Sobrienam_ops amfs_toplvl_ops =
5938494Sobrien{
6038494Sobrien  "toplvl",
61174294Sobrien  amfs_generic_match,
62174294Sobrien  amfs_toplvl_init,		/* amfs_toplvl_init */
6338494Sobrien  amfs_toplvl_mount,
6438494Sobrien  amfs_toplvl_umount,
65174294Sobrien  amfs_generic_lookup_child,
66174294Sobrien  amfs_generic_mount_child,
67174294Sobrien  amfs_generic_readdir,
6838494Sobrien  0,				/* amfs_toplvl_readlink */
69174294Sobrien  amfs_generic_mounted,
7038494Sobrien  0,				/* amfs_toplvl_umounted */
71174294Sobrien  amfs_generic_find_srvr,
72174294Sobrien  0,				/* amfs_toplvl_get_wchan */
73174294Sobrien  FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND |
74174294Sobrien	  FS_AMQINFO | FS_DIRECTORY, /* nfs_fs_flags */
75174294Sobrien#ifdef HAVE_FS_AUTOFS
76174294Sobrien  AUTOFS_TOPLVL_FS_FLAGS,
77174294Sobrien#endif /* HAVE_FS_AUTOFS */
7838494Sobrien};
7938494Sobrien
8038494Sobrien
8138494Sobrien/****************************************************************************
8238494Sobrien *** FUNCTIONS                                                             ***
8338494Sobrien ****************************************************************************/
8438494Sobrien
85174294Sobrienstatic void
86174294Sobrienset_auto_attrcache_timeout(char *preopts, char *opts, size_t l)
8738494Sobrien{
8838494Sobrien
89174294Sobrien#ifdef MNTTAB_OPT_NOAC
9038494Sobrien  /*
91174294Sobrien   * Don't cache attributes - they are changing under the kernel's feet.
92174294Sobrien   * For example, IRIX5.2 will dispense with nfs lookup calls and hand stale
93174294Sobrien   * filehandles to getattr unless we disable attribute caching on the
94174294Sobrien   * automount points.
9538494Sobrien   */
96174294Sobrien  if (gopt.auto_attrcache == 0) {
97174294Sobrien    xsnprintf(preopts, l, ",%s", MNTTAB_OPT_NOAC);
98174294Sobrien    xstrlcat(opts, preopts, l);
9938494Sobrien  }
100174294Sobrien#endif /* MNTTAB_OPT_NOAC */
10138494Sobrien
10238494Sobrien  /*
103174294Sobrien   * XXX: note that setting these to 0 in the past resulted in an error on
104174294Sobrien   * some systems, which is why it's better to use "noac" if possible.  For
105174294Sobrien   * now, we're setting everything possible, but if this will cause trouble,
106174294Sobrien   * then we'll have to condition the remainder of this on OPT_NOAC.
10738494Sobrien   */
108174294Sobrien#ifdef MNTTAB_OPT_ACTIMEO
109174294Sobrien  xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTIMEO, gopt.auto_attrcache);
110174294Sobrien  xstrlcat(opts, preopts, l);
111174294Sobrien#else /* MNTTAB_OPT_ACTIMEO */
112174294Sobrien# ifdef MNTTAB_OPT_ACDIRMIN
113174294Sobrien  xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTDIRMIN, gopt.auto_attrcache);
114174294Sobrien  xstrlcat(opts, preopts, l);
115174294Sobrien# endif /* MNTTAB_OPT_ACDIRMIN */
116174294Sobrien# ifdef MNTTAB_OPT_ACDIRMAX
117174294Sobrien  xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTDIRMAX, gopt.auto_attrcache);
118174294Sobrien  xstrlcat(opts, preopts, l);
119174294Sobrien# endif /* MNTTAB_OPT_ACDIRMAX */
120174294Sobrien# ifdef MNTTAB_OPT_ACREGMIN
121174294Sobrien  xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTREGMIN, gopt.auto_attrcache);
122174294Sobrien  xstrlcat(opts, preopts, l);
123174294Sobrien# endif /* MNTTAB_OPT_ACREGMIN */
124174294Sobrien# ifdef MNTTAB_OPT_ACREGMAX
125174294Sobrien  xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTREGMAX, gopt.auto_attrcache);
126174294Sobrien  xstrlcat(opts, preopts, l);
127174294Sobrien# endif /* MNTTAB_OPT_ACREGMAX */
128174294Sobrien#endif /* MNTTAB_OPT_ACTIMEO */
129174294Sobrien}
13038494Sobrien
13138494Sobrien
132174294Sobrien/*
133174294Sobrien * Initialize a top-level mount.  In our case, if the user asked for
134174294Sobrien * forced_unmounts, and the OS supports it, then we try forced/lazy unmounts
135174294Sobrien * on any previous toplvl mounts.  This is useful if a previous Amd died and
136174294Sobrien * left behind toplvl mount points (this Amd will clean them up).
137174294Sobrien *
138174294Sobrien * WARNING: Don't use forced/lazy unmounts if you have another valid Amd
139174294Sobrien * running, because this code WILL force those valid toplvl mount points to
140174294Sobrien * be detached as well!
141174294Sobrien */
142174294Sobrienstatic int
143174294Sobrienamfs_toplvl_init(mntfs *mf)
144174294Sobrien{
145174294Sobrien  int error = 0;
14638494Sobrien
147310490Scy#if (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH)) && (defined(HAVE_UVMOUNT) || defined(HAVE_UMOUNT2))
148174294Sobrien  if (gopt.flags & CFM_FORCED_UNMOUNTS) {
149174294Sobrien    plog(XLOG_INFO, "amfs_toplvl_init: trying forced/lazy unmount of %s",
150174294Sobrien	 mf->mf_mount);
151174294Sobrien    error = umount2_fs(mf->mf_mount, AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH);
152174294Sobrien    if (error)
153174294Sobrien      plog(XLOG_INFO, "amfs_toplvl_init: forced/lazy unmount failed: %m");
154174294Sobrien    else
155174294Sobrien      dlog("amfs_toplvl_init: forced/lazy unmount succeeded");
15651292Sobrien  }
157310490Scy#endif /* (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) && (HAVE_UVMOUNT || HAVE_UMOUNT2) */
15838494Sobrien  return error;
15938494Sobrien}
16038494Sobrien
16138494Sobrien
16238494Sobrien/*
16338494Sobrien * Mount the top-level
16438494Sobrien */
16538494Sobrienint
166174294Sobrienamfs_toplvl_mount(am_node *mp, mntfs *mf)
16738494Sobrien{
16838494Sobrien  struct stat stb;
169174294Sobrien  char opts[SIZEOF_OPTS], preopts[SIZEOF_OPTS], toplvl_opts[40];
17038494Sobrien  int error;
17138494Sobrien
17238494Sobrien  /*
17338494Sobrien   * Mounting the automounter.
17438494Sobrien   * Make sure the mount directory exists, construct
17538494Sobrien   * the mount options and call the mount_amfs_toplvl routine.
17638494Sobrien   */
17738494Sobrien
17838494Sobrien  if (stat(mp->am_path, &stb) < 0) {
17938494Sobrien    return errno;
18038494Sobrien  } else if ((stb.st_mode & S_IFMT) != S_IFDIR) {
18138494Sobrien    plog(XLOG_WARNING, "%s is not a directory", mp->am_path);
18238494Sobrien    return ENOTDIR;
18338494Sobrien  }
18438494Sobrien
18538494Sobrien  /*
18638494Sobrien   * Construct some mount options:
18738494Sobrien   *
18838494Sobrien   * Tack on magic map=<mapname> option in mtab to emulate
18938494Sobrien   * SunOS automounter behavior.
19038494Sobrien   */
191174294Sobrien
192174294Sobrien#ifdef HAVE_FS_AUTOFS
193174294Sobrien  if (mf->mf_flags & MFF_IS_AUTOFS) {
194174294Sobrien    autofs_get_opts(opts, sizeof(opts), mp->am_autofs_fh);
195174294Sobrien  } else
196174294Sobrien#endif /* HAVE_FS_AUTOFS */
197174294Sobrien  {
198174294Sobrien    preopts[0] = '\0';
19938494Sobrien#ifdef MNTTAB_OPT_INTR
200174294Sobrien    xstrlcat(preopts, MNTTAB_OPT_INTR, sizeof(preopts));
201174294Sobrien    xstrlcat(preopts, ",", sizeof(preopts));
20238494Sobrien#endif /* MNTTAB_OPT_INTR */
20338494Sobrien#ifdef MNTTAB_OPT_IGNORE
204174294Sobrien    xstrlcat(preopts, MNTTAB_OPT_IGNORE, sizeof(preopts));
205174294Sobrien    xstrlcat(preopts, ",", sizeof(preopts));
20638494Sobrien#endif /* MNTTAB_OPT_IGNORE */
207174294Sobrien    /* write most of the initial options + preopts */
208174294Sobrien    xsnprintf(opts, sizeof(opts), "%s%s,%s=%d,%s,map=%s",
209174294Sobrien	      preopts,
210174294Sobrien	      MNTTAB_OPT_RW,
211174294Sobrien	      MNTTAB_OPT_PORT, nfs_port,
212174294Sobrien	      mf->mf_ops->fs_type, mf->mf_info);
21338494Sobrien
214174294Sobrien    /* process toplvl timeo/retrans options, if any */
215174294Sobrien    if (gopt.amfs_auto_timeo[AMU_TYPE_TOPLVL] > 0) {
216174294Sobrien      xsnprintf(toplvl_opts, sizeof(toplvl_opts), ",%s=%d",
217174294Sobrien		MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo[AMU_TYPE_TOPLVL]);
218174294Sobrien      xstrlcat(opts, toplvl_opts, sizeof(opts));
219174294Sobrien    }
220174294Sobrien    if (gopt.amfs_auto_retrans[AMU_TYPE_TOPLVL] > 0) {
221174294Sobrien      xsnprintf(toplvl_opts, sizeof(toplvl_opts), ",%s=%d",
222174294Sobrien		MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans[AMU_TYPE_TOPLVL]);
223174294Sobrien      xstrlcat(opts, toplvl_opts, sizeof(opts));
224174294Sobrien    }
225174294Sobrien
226310490Scy#ifdef MNTTAB_OPT_NOLOCK
227310490Scy    xstrlcat(opts, ",", sizeof(opts));
228310490Scy    xstrlcat(opts, MNTTAB_OPT_NOLOCK, sizeof(opts));
229310490Scy#endif /* MNTTAB_OPT_NOLOCK */
230310490Scy
231174294Sobrien#ifdef MNTTAB_OPT_NOAC
232174294Sobrien    if (gopt.auto_attrcache == 0) {
233174294Sobrien      xstrlcat(opts, ",", sizeof(opts));
234174294Sobrien      xstrlcat(opts, MNTTAB_OPT_NOAC, sizeof(opts));
235174294Sobrien    } else
236174294Sobrien#endif /* MNTTAB_OPT_NOAC */
237174294Sobrien      set_auto_attrcache_timeout(preopts, opts, sizeof(preopts));
238174294Sobrien  }
239174294Sobrien
24038494Sobrien  /* now do the mount */
241174294Sobrien  error = amfs_mount(mp, mf, opts);
24238494Sobrien  if (error) {
24338494Sobrien    errno = error;
244174294Sobrien    plog(XLOG_FATAL, "amfs_toplvl_mount: amfs_mount failed: %m");
24538494Sobrien    return error;
24638494Sobrien  }
24738494Sobrien  return 0;
24838494Sobrien}
24938494Sobrien
25038494Sobrien
25138494Sobrien/*
25238494Sobrien * Unmount a top-level automount node
25338494Sobrien */
25438494Sobrienint
255174294Sobrienamfs_toplvl_umount(am_node *mp, mntfs *mf)
25638494Sobrien{
257174294Sobrien  struct stat stb;
258174294Sobrien  int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
25938494Sobrien  int error;
260174294Sobrien  int count = 0;		/* how many times did we try to unmount? */
26138494Sobrien
26238494Sobrienagain:
26338494Sobrien  /*
26438494Sobrien   * The lstat is needed if this mount is type=direct.
26538494Sobrien   * When that happens, the kernel cache gets confused
26638494Sobrien   * between the underlying type (dir) and the mounted
26738494Sobrien   * type (link) and so needs to be re-synced before
26838494Sobrien   * the unmount.  This is all because the unmount system
26938494Sobrien   * call follows links and so can't actually unmount
27038494Sobrien   * a link (stupid!).  It was noted that doing an ls -ld
27138494Sobrien   * of the mount point to see why things were not working
27238494Sobrien   * actually fixed the problem - so simulate an ls -ld here.
27338494Sobrien   */
27438494Sobrien  if (lstat(mp->am_path, &stb) < 0) {
275174294Sobrien    error = errno;
27638494Sobrien    dlog("lstat(%s): %m", mp->am_path);
277174294Sobrien    goto out;
27838494Sobrien  }
279174294Sobrien  if ((stb.st_mode & S_IFMT) != S_IFDIR) {
280174294Sobrien    plog(XLOG_ERROR, "amfs_toplvl_umount: %s is not a directory, aborting.", mp->am_path);
281174294Sobrien    error = ENOTDIR;
282174294Sobrien    goto out;
283174294Sobrien  }
284174294Sobrien
285174294Sobrien  error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags);
28638494Sobrien  if (error == EBUSY) {
287174294Sobrien#ifdef HAVE_FS_AUTOFS
288174294Sobrien    /*
289174294Sobrien     * autofs mounts are "in place", so it is possible
290174294Sobrien     * that we can't just unmount our mount points and go away.
291174294Sobrien     * If that's the case, just give up.
292174294Sobrien     */
293174294Sobrien    if (mf->mf_flags & MFF_IS_AUTOFS)
294174294Sobrien      return error;
295174294Sobrien#endif /* HAVE_FS_AUTOFS */
29638494Sobrien    plog(XLOG_WARNING, "amfs_toplvl_unmount retrying %s in 1s", mp->am_path);
297174294Sobrien    count++;
298174294Sobrien    sleep(1);
299174294Sobrien    /*
300174294Sobrien     * If user wants forced/lazy unmount semantics, then set those flags,
301174294Sobrien     * but only after we've tried normal lstat/umount a few times --
302174294Sobrien     * otherwise forced unmounts may hang this very same Amd (by preventing
303174294Sobrien     * it from achieving a clean unmount).
304174294Sobrien     */
305174294Sobrien    if (gopt.flags & CFM_FORCED_UNMOUNTS) {
306174294Sobrien      if (count == 5) {		/* after 5 seconds, try MNT_FORCE */
307174294Sobrien	dlog("enabling forced unmounts for toplvl node %s", mp->am_path);
308174294Sobrien	unmount_flags |= AMU_UMOUNT_FORCE;
309174294Sobrien      }
310174294Sobrien      if (count == 10) {	/* after 10 seconds, try MNT_DETACH */
311174294Sobrien	dlog("enabling detached unmounts for toplvl node %s", mp->am_path);
312174294Sobrien	unmount_flags |= AMU_UMOUNT_DETACH;
313174294Sobrien      }
314174294Sobrien    }
31538494Sobrien    goto again;
31638494Sobrien  }
317174294Sobrienout:
31838494Sobrien  return error;
31938494Sobrien}
320