/* * Append a mntent structure to the * current mount table. */ void write_mntent(mntent_t *mp, const char *mnttabname) { int retries = 0; FILE *mfp; if (!mtab_is_writable()) { return; } enfile: mfp = open_locked_mtab(mnttabname, "a", mp->mnt_dir); if (mfp) { mtab_stripnl(mp->mnt_opts); if (addmntent(mfp, mp)) plog(XLOG_ERROR, "Couldn't write %s: %m", mnttabname); if (fflush(mfp)) plog(XLOG_ERROR, "Couldn't flush %s: %m", mnttabname); (void) endmntent(mfp); } else { if (errno == ENFILE && retries < NFILE_RETRIES) { sleep(1); goto enfile; } plog(XLOG_ERROR, "setmntent(\"%s\", \"a\"): %m", mnttabname); } unlock_mntlist(); }
void going_down(int rc) { if (foreground) { if (amd_state != Start) { if (amd_state != Done) return; unregister_amq(); } } #ifdef MOUNT_TABLE_ON_FILE /* * Call unlock_mntlist to free any important resources such as an on-disk * lock file (/etc/mtab~). */ unlock_mntlist(); #endif /* MOUNT_TABLE_ON_FILE */ if (foreground) { plog(XLOG_INFO, "Finishing with status %d", rc); } else { dlog("background process exiting with status %d", rc); } /* bye bye... */ exit(rc); }
/* * Throw away a mount list */ void free_mntlist(mntlist *mp) { discard_mntlist(mp); #ifdef MOUNT_TABLE_ON_FILE unlock_mntlist(); #endif /* MOUNT_TABLE_ON_FILE */ }
/* * 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; }
static int amfs_host_mount(am_node *am, mntfs *mf) { struct timeval tv2; CLIENT *client; enum clnt_stat clnt_stat; int n_export; int j, k; exports exlist = 0, ex; exports *ep = NULL; am_nfs_handle_t *fp = NULL; char *host; int error = 0; struct sockaddr_in sin; int sock = RPC_ANYSOCK; int ok = FALSE; mntlist *mlist; char fs_name[MAXPATHLEN], *rfs_dir; char mntpt[MAXPATHLEN]; struct timeval tv; u_long mnt_version; /* * WebNFS servers don't necessarily run mountd. */ if (mf->mf_flags & MFF_WEBNFS) { plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS"); return EIO; } /* * Read the mount list */ 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 */ /* * Take a copy of the server hostname, address, and nfs version * to mount version conversion. */ host = mf->mf_server->fs_host; sin = *mf->mf_server->fs_ip; plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version); #ifdef HAVE_FS_NFS3 if (mf->mf_server->fs_version == NFS_VERSION3) mnt_version = AM_MOUNTVERS3; else #endif /* HAVE_FS_NFS3 */ mnt_version = MOUNTVERS; /* * The original 10 second per try timeout is WAY too large, especially * if we're only waiting 10 or 20 seconds max for the response. * That would mean we'd try only once in 10 seconds, and we could * lose the transmit or receive packet, and never try again. * A 2-second per try timeout here is much more reasonable. * 09/28/92 Mike Mitchell, [email protected] */ tv.tv_sec = 2; tv.tv_usec = 0; /* * Create a client attached to mountd */ client = get_mount_client(host, &sin, &tv, &sock, mnt_version); if (client == NULL) { #ifdef HAVE_CLNT_SPCREATEERROR plog(XLOG_ERROR, "get_mount_client failed for %s: %s", host, clnt_spcreateerror("")); #else /* not HAVE_CLNT_SPCREATEERROR */ plog(XLOG_ERROR, "get_mount_client failed for %s", host); #endif /* not HAVE_CLNT_SPCREATEERROR */ error = EIO; goto out; } if (!nfs_auth) { error = make_nfs_auth(); if (error) goto out; } client->cl_auth = nfs_auth; dlog("Fetching export list from %s", host); /* * Fetch the export list */ tv2.tv_sec = 10; tv2.tv_usec = 0; clnt_stat = clnt_call(client, MOUNTPROC_EXPORT, (XDRPROC_T_TYPE) xdr_void, 0, (XDRPROC_T_TYPE) xdr_exports, (SVC_IN_ARG_TYPE) & exlist, tv2); if (clnt_stat != RPC_SUCCESS) { const char *msg = clnt_sperrno(clnt_stat); plog(XLOG_ERROR, "host_mount rpc failed: %s", msg); /* clnt_perror(client, "rpc"); */ error = EIO; goto out; } /* * Figure out how many exports were returned */ for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) { n_export++; } /* * Allocate an array of pointers into the list * so that they can be sorted. If the filesystem * is already mounted then ignore it. */ ep = (exports *) xmalloc(n_export * sizeof(exports)); for (j = 0, ex = exlist; ex; ex = ex->ex_next) { make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount); if (already_mounted(mlist, mntpt)) /* we have at least one mounted f/s, so don't fail the mount */ ok = TRUE; else ep[j++] = ex; } n_export = j; /* * Sort into order. * This way the mounts are done in order down the tree, * instead of any random order returned by the mount * daemon (the protocol doesn't specify...). */ qsort(ep, n_export, sizeof(exports), sortfun); /* * Allocate an array of filehandles */ fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t)); /* * Try to obtain filehandles for each directory. * If a fetch fails then just zero out the array * reference but discard the error. */ for (j = k = 0; j < n_export; j++) { /* Check and avoid a duplicated export entry */ if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) { dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir); ep[j] = NULL; } else { k = j; error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j], mf->mf_server->fs_version); if (error) ep[j] = NULL; } } /* * Mount each filesystem for which we have a filehandle. * If any of the mounts succeed then mark "ok" and return * error code 0 at the end. If they all fail then return * the last error code. */ xstrlcpy(fs_name, mf->mf_info, sizeof(fs_name)); if ((rfs_dir = strchr(fs_name, ':')) == (char *) NULL) { plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon"); error = EINVAL; goto out; } ++rfs_dir; for (j = 0; j < n_export; j++) { ex = ep[j]; if (ex) { /* * Note: the sizeof space left in rfs_dir is what's left in fs_name * after strchr() above returned a pointer _inside_ fs_name. The * calculation below also takes into account that rfs_dir was * incremented by the ++ above. */ xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name)); make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount); if (do_mount(&fp[j], mntpt, fs_name, mf) == 0) ok = TRUE; } } /* * Clean up and exit */ out: discard_mntlist(mlist); XFREE(ep); XFREE(fp); if (sock != RPC_ANYSOCK) (void) amu_close(sock); if (client) clnt_destroy(client); if (exlist) xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist); if (ok) return 0; return error; }
int umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags) { mntlist *mlist, *mp, *mp_save = NULL; int error = 0; #ifdef HAVE_LOOP_DEVICE char *opt, *xopts = NULL; char loopstr[] = "loop="; char *loopdev; #endif /* HAVE_LOOP_DEVICE */ unsigned int retries = 8; mp = mlist = read_mtab(mntdir, mnttabname); /* * Search the mount table looking for * the correct (ie last) matching entry */ while (mp) { if (STREQ(mp->mnt->mnt_dir, mntdir)) mp_save = mp; mp = mp->mnext; } if (!mp_save) { plog(XLOG_ERROR, "Couldn't find how to unmount %s", mntdir); /* Assume it is already unmounted */ error = 0; goto out; } plog(XLOG_ERROR, "Trying unmount %s, umount_flags 0x%x", mp_save->mnt->mnt_dir, unmount_flags); dlog("Trying unmount(%s)", mp_save->mnt->mnt_dir); #ifdef MOUNT_TABLE_ON_FILE /* * This unmount may hang leaving this process with an exclusive lock on * /etc/mtab. Therefore it is necessary to unlock mtab, do the unmount, * then lock mtab (again) and reread it and finally update it. */ unlock_mntlist(); #endif /* MOUNT_TABLE_ON_FILE */ again: #if defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH) /* * If user asked to try forced unmounts, then do a quick check to see if * the mount point is hung badly. If so, then try to detach it by * force; if the latter works, we're done. */ if (unmount_flags & AMU_UMOUNT_DETACH) { /* * Note: we pass both DETACH and FORCE flags, because umount2_fs below * (on Linux), should try FORCE before DETACH (the latter always * succeeds). */ error = umount2_fs(mp_save->mnt->mnt_dir, unmount_flags & (AMU_UMOUNT_DETACH|AMU_UMOUNT_FORCE)); } else #endif /* defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_DETACH) */ error = UNMOUNT_TRAP(mp_save->mnt); /* Linux kernel can be sluggish for some reason */ if (error == EBUSY && retries--) { struct timespec tm = {0, 200000000}; nanosleep(&tm, NULL); goto again; } if (error < 0) { plog(XLOG_WARNING, "unmount(%s) failed: %m", mp_save->mnt->mnt_dir); switch ((error = errno)) { case EINVAL: case ENOTBLK: plog(XLOG_WARNING, "unmount: %s is not mounted", mp_save->mnt->mnt_dir); error = 0; /* Not really an error */ break; case ENOENT: /* * This could happen if the kernel insists on following symlinks * when we try to unmount a direct mountpoint. We need to propagate * the error up so that the top layers know it failed and don't * try to rmdir() the mountpoint or other silly things. */ plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir); break; #if defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_FORCE) case EBUSY: /* * Caller determines if forced unmounts should be used now (for * EBUSY). If caller asked to force an unmount, *and* the above * "trivial" unmount attempt failed with EBUSY, then try to force * the unmount. */ if (unmount_flags & AMU_UMOUNT_FORCE) { error = umount2_fs(mp_save->mnt->mnt_dir, unmount_flags & AMU_UMOUNT_FORCE); if (error < 0) { plog(XLOG_WARNING, "%s: unmount/force: %m", mp_save->mnt->mnt_dir); error = errno; } } break; #endif /* defined(HAVE_UMOUNT2) && defined(MNT2_GEN_OPT_FORCE) */ default: dlog("%s: unmount: %m", mp_save->mnt->mnt_dir); break; } } else { dlog("unmount(%s) succeeded", mp_save->mnt->mnt_dir); } dlog("Finished unmount(%s)", mp_save->mnt->mnt_dir); /* * If we are successful or there was an ENOENT, remove * the mount entry from the mtab file. */ if (error && error != ENOENT) goto out; #ifdef HAVE_LOOP_DEVICE /* look for loop=/dev/loopX in mnt_opts */ xopts = xstrdup(mp_save->mnt->mnt_opts); /* b/c strtok is destructive */ for (opt = strtok(xopts, ","); opt; opt = strtok(NULL, ",")) if (NSTREQ(opt, loopstr, sizeof(loopstr) - 1)) { loopdev = opt + sizeof(loopstr) - 1; if (delete_loop_device(loopdev) < 0) plog(XLOG_WARNING, "unmount() failed to release loop device %s: %m", loopdev); else plog(XLOG_INFO, "unmount() released loop device %s OK", loopdev); break; } if (xopts) XFREE(xopts); #endif /* HAVE_LOOP_DEVICE */ #ifdef MOUNT_TABLE_ON_FILE free_mntlist(mlist); mp = mlist = read_mtab(mntdir, mnttabname); /* * Search the mount table looking for * the correct (ie last) matching entry */ mp_save = NULL; while (mp) { if (STREQ(mp->mnt->mnt_dir, mntdir)) mp_save = mp; mp = mp->mnext; } if (mp_save) { mnt_free(mp_save->mnt); mp_save->mnt = NULL; rewrite_mtab(mlist, mnttabname); } #endif /* MOUNT_TABLE_ON_FILE */ out: free_mntlist(mlist); return error; }
int umount_fs(char *mntdir, const char *mnttabname, u_int unmount_flags) { mntlist *mlist, *mp, *mp_save = NULL; int error = 0; mp = mlist = read_mtab(mntdir, mnttabname); /* * Search the mount table looking for * the correct (ie last) matching entry */ while (mp) { if (STREQ(mp->mnt->mnt_dir, mntdir)) mp_save = mp; mp = mp->mnext; } if (mp_save) { dlog("Trying unmount(%s)", mp_save->mnt->mnt_dir); #ifdef MOUNT_TABLE_ON_FILE /* * This unmount may hang leaving this process with an exclusive lock on * /etc/mtab. Therefore it is necessary to unlock mtab, do the unmount, * then lock mtab (again) and reread it and finally update it. */ unlock_mntlist(); #endif /* MOUNT_TABLE_ON_FILE */ #ifdef NEED_AUTOFS_SPACE_HACK if (unmount_flags & AMU_UMOUNT_AUTOFS) { char *mnt_dir_save = mp_save->mnt->mnt_dir; mp_save->mnt->mnt_dir = autofs_strdup_space_hack(mnt_dir_save); error = UNMOUNT_TRAP(mp_save->mnt); XFREE(mp_save->mnt->mnt_dir); mp_save->mnt->mnt_dir = mnt_dir_save; } else #endif /* NEED_AUTOFS_SPACE_HACK */ error = UNMOUNT_TRAP(mp_save->mnt); if (error < 0) { switch ((error = errno)) { case EINVAL: case ENOTBLK: plog(XLOG_WARNING, "unmount: %s is not mounted", mp_save->mnt->mnt_dir); error = 0; /* Not really an error */ break; case ENOENT: /* * This could happen if the kernel insists on following symlinks * when we try to unmount a direct mountpoint. We need to propagate * the error up so that the top layers know it failed and don't * try to rmdir() the mountpoint or other silly things. */ plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir); break; #if defined(MNT2_GEN_OPT_FORCE) && defined(HAVE_UVMOUNT) case EBUSY: case EIO: case ESTALE: /* caller determines if forced unmounts should be used */ if (unmount_flags & AMU_UMOUNT_FORCE) { error = umount2_fs(mntdir, unmount_flags); if (error < 0) error = errno; else break; /* all is OK */ } /* fallthrough */ #endif /* MNT2_GEN_OPT_FORCE && HAVE_UVMOUNT */ default: dlog("%s: unmount: %m", mp_save->mnt->mnt_dir); break; } } dlog("Finished unmount(%s)", mp_save->mnt->mnt_dir); if (!error) { #ifdef MOUNT_TABLE_ON_FILE free_mntlist(mlist); mp = mlist = read_mtab(mntdir, mnttabname); /* * Search the mount table looking for * the correct (ie last) matching entry */ mp_save = NULL; while (mp) { if (STREQ(mp->mnt->mnt_dir, mntdir)) mp_save = mp; mp = mp->mnext; } if (mp_save) { mnt_free(mp_save->mnt); mp_save->mnt = NULL; rewrite_mtab(mlist, mnttabname); } #endif /* MOUNT_TABLE_ON_FILE */ } } else { plog(XLOG_ERROR, "Couldn't find how to unmount %s", mntdir); /* * Assume it is already unmounted */ error = 0; } /* end of "if (mp_save)" statement */ free_mntlist(mlist); return error; }