static int pcfs_umount(am_node *am, mntfs *mf) { int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); }
int autofs_umount_fs(am_node *mp, mntfs *mf) { int err = 0; char *space_hack = autofs_strdup_space_hack(mp->am_path); /* * Autofs v1 doesn't support symlinks, * so we ignore the CFM_AUTOFS_USE_LOFS flag */ if (!(mf->mf_flags & MFF_ON_AUTOFS)) { err = UMOUNT_FS(mp->am_path, mnttab_file_name, 1); if (err) goto out; rmdir(space_hack); } /* * Multiple sublinks could reference this f/s. * Don't actually unmount it unless we're holding the last reference. */ if (mf->mf_refc == 1) { if ((err = mf->mf_ops->umount_fs(mp, mf))) goto out; if (mf->mf_flags & MFF_ON_AUTOFS) rmdir(space_hack); } out: XFREE(space_hack); return err; }
static int cachefs_umount(am_node *am, mntfs *mf) { int error; error = UMOUNT_FS(mf->mf_mount, mf->mf_real_mount, mnttab_file_name); /* * In the case of cachefs, we must fsck the cache directory. Otherwise, * it will remain inconsistent, and the next cachefs mount will fail * with the error "no space left on device" (ENOSPC). * * XXX: this is hacky! use fork/exec/wait instead... */ if (!error) { char *cachedir = NULL; char cmd[128]; cachedir = (char *) mf->mf_private; plog(XLOG_INFO, "running fsck on cache directory \"%s\"", cachedir); sprintf(cmd, "fsck -F cachefs %s", cachedir); system(cmd); } return error; }
/* * Unmount a top-level automount node */ int amfs_toplvl_umount(am_node *mp) { int error; struct stat stb; again: /* * The lstat is needed if this mount is type=direct. * When that happens, the kernel cache gets confused * between the underlying type (dir) and the mounted * type (link) and so needs to be re-synced before * the unmount. This is all because the unmount system * call follows links and so can't actually unmount * a link (stupid!). It was noted that doing an ls -ld * of the mount point to see why things were not working * actually fixed the problem - so simulate an ls -ld here. */ if (lstat(mp->am_path, &stb) < 0) { #ifdef DEBUG dlog("lstat(%s): %m", mp->am_path); #endif /* DEBUG */ } error = UMOUNT_FS(mp->am_path, mnttab_file_name); if (error == EBUSY) { plog(XLOG_WARNING, "amfs_toplvl_unmount retrying %s in 1s", mp->am_path); sleep(1); /* XXX */ goto again; } return error; }
/* * Unmount an automount sub-node */ int amfs_generic_umount(am_node *mp, mntfs *mf) { int error = 0; #ifdef HAVE_FS_AUTOFS int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; if (mf->mf_flags & MFF_IS_AUTOFS) error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags); #endif /* HAVE_FS_AUTOFS */ return error; }
RETSIGTYPE cleanup(int signum) { struct stat stbuf; int umount_result; if (amuDebug(D_DAEMON)) { if (getpid() != masterpid) return; if (fork() != 0) { masterpid = 0; am_set_mypid(); return; } } am_set_mypid(); for (;;) { while ((umount_result = UMOUNT_FS(dir_name, mnttab_file_name, 0)) == EBUSY) { dlog("cleanup(): umount delaying for 10 seconds"); sleep(10); } if (stat(dir_name, &stbuf) == 0 && stbuf.st_ino == ROOTID) { plog(XLOG_ERROR, "unable to unmount %s", dir_name); plog(XLOG_ERROR, "suspending, unmount before terminating"); kill(am_mypid, SIGSTOP); continue; /* retry unmount */ } break; } if (amuDebug(D_DAEMON)) { plog(XLOG_INFO, "cleanup(): killing processes and terminating"); kill(masterpid, SIGKILL); kill(serverpid, SIGKILL); } plog(XLOG_INFO, "hlfsd terminating with status 0\n"); _exit(0); }
/* * Unmount a mount tree */ static int amfs_host_umount(am_node *am, mntfs *mf) { mntlist *ml, *mprev; int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; int xerror = 0; /* * Read the mount list */ mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name); #ifdef MOUNT_TABLE_ON_FILE /* * Unlock the mount list */ unlock_mntlist(); #endif /* MOUNT_TABLE_ON_FILE */ /* * Reverse list... */ ml = mlist; mprev = NULL; while (ml) { mntlist *ml2 = ml->mnext; ml->mnext = mprev; mprev = ml; ml = ml2; } mlist = mprev; /* * Unmount all filesystems... */ for (ml = mlist; ml && !xerror; ml = ml->mnext) { char *dir = ml->mnt->mnt_dir; if (directory_prefix(mf->mf_mount, dir)) { int error; dlog("amfs_host: unmounts %s", dir); /* * Unmount "dir" */ error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags); /* * Keep track of errors */ if (error) { /* * If we have not already set xerror and error is not ENOENT, * then set xerror equal to error and log it. * 'xerror' is the return value for this function. * * We do not want to pass ENOENT as an error because if the * directory does not exists our work is done anyway. */ if (!xerror && error != ENOENT) xerror = error; if (error != EBUSY) { errno = error; plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir); } } else { (void) rmdirs(dir); } } } /* * Throw away mount list */ discard_mntlist(mlist); /* * Try to remount, except when we are shutting down. */ if (xerror && amd_state != Finishing) { xerror = amfs_host_mount(am, mf); if (!xerror) { /* * Don't log this - it's usually too verbose plog(XLOG_INFO, "Remounted host %s", mf->mf_info); */ xerror = EBUSY; } } return xerror; }
/* * Unmount a top-level automount node */ int amfs_toplvl_umount(am_node *mp, mntfs *mf) { struct stat stb; int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; int error; int count = 0; /* how many times did we try to unmount? */ again: /* * The lstat is needed if this mount is type=direct. * When that happens, the kernel cache gets confused * between the underlying type (dir) and the mounted * type (link) and so needs to be re-synced before * the unmount. This is all because the unmount system * call follows links and so can't actually unmount * a link (stupid!). It was noted that doing an ls -ld * of the mount point to see why things were not working * actually fixed the problem - so simulate an ls -ld here. */ if (lstat(mp->am_path, &stb) < 0) { error = errno; dlog("lstat(%s): %m", mp->am_path); goto out; } if ((stb.st_mode & S_IFMT) != S_IFDIR) { plog(XLOG_ERROR, "amfs_toplvl_umount: %s is not a directory, aborting.", mp->am_path); error = ENOTDIR; goto out; } error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags); if (error == EBUSY) { #ifdef HAVE_FS_AUTOFS /* * autofs mounts are "in place", so it is possible * that we can't just unmount our mount points and go away. * If that's the case, just give up. */ if (mf->mf_flags & MFF_IS_AUTOFS) return error; #endif /* HAVE_FS_AUTOFS */ plog(XLOG_WARNING, "amfs_toplvl_unmount retrying %s in 1s", mp->am_path); count++; sleep(1); /* * If user wants forced/lazy unmount semantics, then set those flags, * but only after we've tried normal lstat/umount a few times -- * otherwise forced unmounts may hang this very same Amd (by preventing * it from achieving a clean unmount). */ if (gopt.flags & CFM_FORCED_UNMOUNTS) { if (count == 5) { /* after 5 seconds, try MNT_FORCE */ dlog("enabling forced unmounts for toplvl node %s", mp->am_path); unmount_flags |= AMU_UMOUNT_FORCE; } if (count == 10) { /* after 10 seconds, try MNT_DETACH */ dlog("enabling detached unmounts for toplvl node %s", mp->am_path); unmount_flags |= AMU_UMOUNT_DETACH; } } goto again; } out: return error; }
/* * Mount an automounter directory. * The automounter is connected into the system * as a user-level NFS server. amfs_mount constructs * the necessary NFS parameters to be given to the * kernel so that it will talk back to us. * * NOTE: automounter mounts in themselves are using NFS Version 2 (UDP). * * NEW: on certain systems, mounting can be done using the * kernel-level automount (autofs) support. In that case, * we don't need NFS at all here. */ int amfs_mount(am_node *mp, mntfs *mf, char *opts) { char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1]; int retry, error = 0, genflags; int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; char *dir = mf->mf_mount; mntent_t mnt; MTYPE_TYPE type; int forced_unmount = 0; /* are we using forced unmounts? */ u_long nfs_version = get_nfs_dispatcher_version(nfs_dispatcher); memset(&mnt, 0, sizeof(mnt)); mnt.mnt_dir = dir; mnt.mnt_fsname = pid_fsname; mnt.mnt_opts = opts; #ifdef HAVE_FS_AUTOFS if (mf->mf_flags & MFF_IS_AUTOFS) { type = MOUNT_TYPE_AUTOFS; /* * Make sure that amd's top-level autofs mounts are hidden by default * from df. * XXX: It works ok on Linux, might not work on other systems. */ mnt.mnt_type = "autofs"; } else #endif /* HAVE_FS_AUTOFS */ { type = MOUNT_TYPE_NFS; /* * Make sure that amd's top-level NFS mounts are hidden by default * from df. * If they don't appear to support the either the "ignore" mnttab * option entry, or the "auto" one, set the mount type to "nfs". */ mnt.mnt_type = HIDE_MOUNT_TYPE; } retry = hasmntval(&mnt, MNTTAB_OPT_RETRY); if (retry <= 0) retry = 2; /* XXX: default to 2 retries */ /* * SET MOUNT ARGS */ /* * Make a ``hostname'' string for the kernel */ xsnprintf(fs_hostname, sizeof(fs_hostname), "pid%ld@%s:%s", get_server_pid(), am_get_hostname(), dir); /* * Most kernels have a name length restriction (64 bytes)... */ if (strlen(fs_hostname) >= MAXHOSTNAMELEN) xstrlcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..", sizeof(fs_hostname) - MAXHOSTNAMELEN + 3); #ifdef HOSTNAMESZ /* * ... and some of these restrictions are 32 bytes (HOSTNAMESZ) * If you need to get the definition for HOSTNAMESZ found, you may * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file. */ if (strlen(fs_hostname) >= HOSTNAMESZ) xstrlcpy(fs_hostname + HOSTNAMESZ - 3, "..", sizeof(fs_hostname) - HOSTNAMESZ + 3); #endif /* HOSTNAMESZ */ /* * Finally we can compute the mount genflags set above, * and add any automounter specific flags. */ genflags = compute_mount_flags(&mnt); #ifdef HAVE_FS_AUTOFS if (on_autofs) genflags |= autofs_compute_mount_flags(&mnt); #endif /* HAVE_FS_AUTOFS */ genflags |= compute_automounter_mount_flags(&mnt); again: if (!(mf->mf_flags & MFF_IS_AUTOFS)) { nfs_args_t nfs_args; am_nfs_handle_t *fhp, anh; #ifndef HAVE_TRANSPORT_TYPE_TLI u_short port; struct sockaddr_in sin; #endif /* not HAVE_TRANSPORT_TYPE_TLI */ /* * get fhandle of remote path for automount point */ fhp = get_root_nfs_fh(dir, &anh); if (!fhp) { plog(XLOG_FATAL, "Can't find root file handle for %s", dir); return EINVAL; } #ifndef HAVE_TRANSPORT_TYPE_TLI /* * Create sockaddr to point to the local machine. */ memset(&sin, 0, sizeof(sin)); /* as per POSIX, sin_len need not be set (used internally by kernel) */ sin.sin_family = AF_INET; sin.sin_addr = myipaddr; port = hasmntval(&mnt, MNTTAB_OPT_PORT); if (port) { sin.sin_port = htons(port); } else { plog(XLOG_ERROR, "no port number specified for %s", dir); return EINVAL; } #endif /* not HAVE_TRANSPORT_TYPE_TLI */ /* setup the many fields and flags within nfs_args */ #ifdef HAVE_TRANSPORT_TYPE_TLI compute_nfs_args(&nfs_args, &mnt, genflags, nfsncp, NULL, /* remote host IP addr is set below */ nfs_version, "udp", fhp, fs_hostname, pid_fsname); /* * IMPORTANT: set the correct IP address AFTERWARDS. It cannot * be done using the normal mechanism of compute_nfs_args(), because * that one will allocate a new address and use NFS_SA_DREF() to copy * parts to it, while assuming that the ip_addr passed is always * a "struct sockaddr_in". That assumption is incorrect on TLI systems, * because they define a special macro HOST_SELF which is DIFFERENT * than localhost (127.0.0.1)! */ nfs_args.addr = &nfsxprt->xp_ltaddr; #else /* not HAVE_TRANSPORT_TYPE_TLI */ compute_nfs_args(&nfs_args, &mnt, genflags, NULL, &sin, nfs_version, "udp", fhp, fs_hostname, pid_fsname); #endif /* not HAVE_TRANSPORT_TYPE_TLI */ /************************************************************************* * NOTE: while compute_nfs_args() works ok for regular NFS mounts * * the toplvl one is not quite regular, and so some options must be * * corrected by hand more carefully, *after* compute_nfs_args() runs. * *************************************************************************/ compute_automounter_nfs_args(&nfs_args, &mnt); if (amuDebug(D_TRACE)) { print_nfs_args(&nfs_args, 0); plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags); } /* This is it! Here we try to mount amd on its mount points */ error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type, 0, NULL, mnttab_file_name, on_autofs); #ifdef HAVE_TRANSPORT_TYPE_TLI free_knetconfig(nfs_args.knconf); /* * local automounter mounts do not allocate a special address, so * no need to XFREE(nfs_args.addr) under TLI. */ #endif /* HAVE_TRANSPORT_TYPE_TLI */ #ifdef HAVE_FS_AUTOFS } else { /* This is it! Here we try to mount amd on its mount points */ error = mount_fs(&mnt, genflags, (caddr_t) mp->am_autofs_fh, retry, type, 0, NULL, mnttab_file_name, on_autofs); #endif /* HAVE_FS_AUTOFS */ } if (error == 0 || forced_unmount) return error; /* * If user wants forced/lazy unmount semantics, then try it iff the * current mount failed with EIO or ESTALE. */ if (gopt.flags & CFM_FORCED_UNMOUNTS) { switch (errno) { case ESTALE: case EIO: forced_unmount = errno; plog(XLOG_WARNING, "Mount %s failed (%m); force unmount.", mp->am_path); if ((error = UMOUNT_FS(mp->am_path, mnttab_file_name, AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH)) < 0) { plog(XLOG_WARNING, "Forced umount %s failed: %m.", mp->am_path); errno = forced_unmount; } else goto again; default: break; } } return error; }