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