RootMount::RootMount( QObject *parent ) : SAbstractImageMounter( parent ) { p = new RootMountPrivate; p->process = new QProcess( this ); p->command = QApplication::applicationDirPath() + "/silicon_rootmount"; p->mount = "--mount"; p->umount = "--unmount"; p->mtab = "/etc/mtab"; p->sudo = "pkexec"; connect( p->process , SIGNAL(finished(int,QProcess::ExitStatus)) , SLOT(finish(int,QProcess::ExitStatus)) ); read_mtab(); }
void RootMount::finish( int exitCode , QProcess::ExitStatus exitStatus ) { p->error_str = p->process->readAllStandardError(); if( exitStatus == QProcess::CrashExit ) p->error_str.append( "\nrootmount crashed" ); QTest::qWait( 137 ); read_mtab(); while( !p->mounted.isEmpty() ) { const QString & file = p->mounted.dequeue(); if( isMount( file ) ) { emit mounted( true ); } else { emit mounted( false ); p->error_str.append( "\n" + tr("Can't mount %1").arg(file) ); } } while( !p->unmounted.isEmpty() ) { const QString & file = p->unmounted.dequeue(); if( !isMount( file ) ) { emit unmounted( true ); } else { emit unmounted( false ); p->error_str.append( "\n" + tr("Can't unmount %1").arg(file) ); } } emit finished( exitCode ); if( !p->error_str.isEmpty() ) emit error( p->error_str ); if( p->queue.isEmpty() ) dropProcess(); }
/* * Handle an amd restart. * * Scan through the mount list finding all "interesting" mount points. * Next hack up partial data structures and add the mounted file * system to the list of known filesystems. * * This module relies on internal details of other components. If * you change something else make *sure* restart() still works. */ void restart(void) { mntlist *ml, *mlp; /* * Read the existing mount table. For each entry, find nfs, ufs or auto * mounts and create a partial am_node to represent it. */ for (mlp = ml = read_mtab("restart", mnttab_file_name); mlp; mlp = mlp->mnext) { mntent_t *me = mlp->mnt; am_ops *fs_ops = NULL; if (STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) { /* * NFS entry, or possibly an Amd entry... * The mnt_fsname for daemon mount points is * host:(pidXXX) * or (seen on Solaris) * host:daemon(pidXXX) */ char *colon = strchr(me->mnt_fsname, ':'); if (colon && strstr(colon, "(pid")) continue; } /* Search for the correct filesystem ops */ fs_ops = ops_search(me->mnt_type); /* * Catch everything else with symlinks to * avoid recursive mounts. This is debatable... */ if (!fs_ops) fs_ops = &amfs_link_ops; restart_fake_mntfs(me, fs_ops); } /* * Free the mount list */ free_mntlist(ml); }
int main(int argc, char **argv) { int ch, keep, success, pathlen; time_t expire, now; char *host, *path; struct mtablist *mtab; expire = 0; host = path = NULL; success = keep = verbose = 0; while ((ch = getopt(argc, argv, "h:kp:ve:")) != -1) switch (ch) { case 'h': host = optarg; break; case 'e': expire = atoi(optarg); break; case 'k': keep = 1; break; case 'p': path = optarg; break; case 'v': verbose = 1; break; case '?': usage(); default: break; } argc -= optind; argv += optind; /* Default expiretime is one day */ if (expire == 0) expire = 86400; time(&now); /* Read PATH_MOUNTTAB. */ if (!read_mtab()) { if (verbose) warnx("no mounttab entries (%s does not exist)", PATH_MOUNTTAB); mtabhead = NULL; } if (host == NULL && path == NULL) { /* Check each entry and do any necessary unmount RPCs. */ for (mtab = mtabhead; mtab != NULL; mtab = mtab->mtab_next) { if (*mtab->mtab_host == '\0') continue; if (mtab->mtab_time + expire < now) { /* Clear expired entry. */ if (verbose) warnx("remove expired entry %s:%s", mtab->mtab_host, mtab->mtab_dirp); bzero(mtab->mtab_host, sizeof(mtab->mtab_host)); continue; } if (keep && is_mounted(mtab->mtab_host, mtab->mtab_dirp)) { if (verbose) warnx("skip entry %s:%s", mtab->mtab_host, mtab->mtab_dirp); continue; } if (do_umount(mtab->mtab_host, mtab->mtab_dirp)) { if (verbose) warnx("umount RPC for %s:%s succeeded", mtab->mtab_host, mtab->mtab_dirp); /* Remove all entries for this host + path. */ clean_mtab(mtab->mtab_host, mtab->mtab_dirp, verbose); } } success = 1; } else { if (host == NULL && path != NULL) /* Missing hostname. */ usage(); if (path == NULL) { /* Do a RPC UMNTALL for this specific host */ success = do_umntall(host); if (verbose && success) warnx("umntall RPC for %s succeeded", host); } else { /* Do a RPC UMNTALL for this specific mount */ for (pathlen = strlen(path); pathlen > 1 && path[pathlen - 1] == '/'; pathlen--) path[pathlen - 1] = '\0'; success = do_umount(host, path); if (verbose && success) warnx("umount RPC for %s:%s succeeded", host, path); } /* If successful, remove any corresponding mounttab entries. */ if (success) clean_mtab(host, path, verbose); } /* Write and unlink PATH_MOUNTTAB if necessary */ if (success) success = write_mtab(verbose); free_mtab(); exit (success ? 0 : 1); }
/* * Handle an amd restart. * * Scan through the mount list finding all "interesting" mount points. * Next hack up partial data structures and add the mounted file * system to the list of known filesystems. This will leave a * dangling reference to that filesystems, so when the filesystem is * finally inherited, an extra "free" must be done on it. * * This module relies on internal details of other components. If * you change something else make *sure* restart() still works. */ void restart(void) { /* * Read the existing mount table */ mntlist *ml, *mlp; /* * For each entry, find nfs, ufs or auto mounts * and create a partial am_node to represent it. */ for (mlp = ml = read_mtab("restart", mnttab_file_name); mlp; mlp = mlp->mnext) { mntent_t *me = mlp->mnt; am_ops *fs_ops = 0; if (STREQ(me->mnt_type, MNTTAB_TYPE_UFS)) { /* * UFS entry */ fs_ops = &ufs_ops; } else if (STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) { /* * NFS entry, or possibly an Amd entry... * The mnt_fsname for daemon mount points is * host:(pidXXX) * or (seen on Solaris) * host:daemon(pidXXX) */ char *colon = strchr(me->mnt_fsname, ':'); if (colon && strstr(colon, "(pid")) { plog(XLOG_WARNING, "%s is an existing automount point", me->mnt_dir); fs_ops = &amfs_link_ops; } else { fs_ops = &nfs_ops; } #ifdef MNTTAB_TYPE_NFS3 } else if (STREQ(me->mnt_type, MNTTAB_TYPE_NFS3)) { fs_ops = &nfs_ops; #endif /* MNTTAB_TYPE_NFS3 */ #ifdef MNTTAB_TYPE_LOFS } else if (STREQ(me->mnt_type, MNTTAB_TYPE_LOFS)) { fs_ops = &lofs_ops; #endif /* MNTTAB_TYPE_LOFS */ #ifdef MNTTAB_TYPE_CDFS } else if (STREQ(me->mnt_type, MNTTAB_TYPE_CDFS)) { fs_ops = &cdfs_ops; #endif /* MNTTAB_TYPE_CDFS */ #ifdef MNTTAB_TYPE_PCFS } else if (STREQ(me->mnt_type, MNTTAB_TYPE_PCFS)) { fs_ops = &pcfs_ops; #endif /* MNTTAB_TYPE_PCFS */ #ifdef MNTTAB_TYPE_MFS } else if (STREQ(me->mnt_type, MNTTAB_TYPE_MFS)) { /* * MFS entry. Fake with a symlink. */ fs_ops = &amfs_link_ops; #endif /* MNTTAB_TYPE_MFS */ } else { /* * Catch everything else with symlinks to * avoid recursive mounts. This is debatable... */ fs_ops = &amfs_link_ops; } /* * If we found something to do */ if (fs_ops) { mntfs *mf; am_opts mo; char *cp; cp = strchr(me->mnt_fsname, ':'); /* * Partially fake up an opts structure */ memset(&mo, 0, sizeof(mo)); mo.opt_rhost = 0; mo.opt_rfs = 0; if (cp) { *cp = '\0'; mo.opt_rhost = strdup(me->mnt_fsname); mo.opt_rfs = strdup(cp + 1); *cp = ':'; } else if (fs_ops->ffserver == find_nfs_srvr) { /* * Prototype 4.4 BSD used to end up here - * might as well keep the workaround for now */ plog(XLOG_WARNING, "NFS server entry assumed to be %s:/", me->mnt_fsname); mo.opt_rhost = strdup(me->mnt_fsname); mo.opt_rfs = strdup("/"); me->mnt_fsname = str3cat(me->mnt_fsname, mo.opt_rhost, ":", "/"); } mo.opt_fs = me->mnt_dir; mo.opt_opts = me->mnt_opts; /* * Make a new mounted filesystem */ mf = find_mntfs(fs_ops, &mo, me->mnt_dir, me->mnt_fsname, "", me->mnt_opts, ""); if (mf->mf_refc == 1) { mf->mf_flags |= MFF_RESTART | MFF_MOUNTED; mf->mf_error = 0; /* Already mounted correctly */ mf->mf_fo = 0; /* * If the restarted type is a link then * don't time out. */ if (fs_ops == &amfs_link_ops || fs_ops == &ufs_ops) mf->mf_flags |= MFF_RSTKEEP; if (fs_ops->fs_init) { /* * Don't care whether this worked since * it is checked again when the fs is * inherited. */ (void) (*fs_ops->fs_init) (mf); } plog(XLOG_INFO, "%s restarted fstype %s on %s", me->mnt_fsname, fs_ops->fs_type, me->mnt_dir); } else { /* Something strange happened - two mounts at the same place! */ free_mntfs(mf); } /* * Clean up mo */ if (mo.opt_rhost) XFREE(mo.opt_rhost); if (mo.opt_rfs) XFREE(mo.opt_rfs); } } /* * Free the mount list */ free_mntlist(ml); }
/* * 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; }
/* * NFS stuff and unmount(2) call */ int umountfs(struct statfs *sfs) { char fsidbuf[64]; enum clnt_stat clnt_stat; struct timeval try; struct addrinfo *ai, hints; int do_rpc; CLIENT *clp; char *nfsdirname, *orignfsdirname; char *hostp, *delimp; ai = NULL; do_rpc = 0; hostp = NULL; nfsdirname = delimp = orignfsdirname = NULL; memset(&hints, 0, sizeof hints); if (strcmp(sfs->f_fstypename, "nfs") == 0) { if ((nfsdirname = strdup(sfs->f_mntfromname)) == NULL) err(1, "strdup"); orignfsdirname = nfsdirname; if (*nfsdirname == '[' && (delimp = strchr(nfsdirname + 1, ']')) != NULL && *(delimp + 1) == ':') { hostp = nfsdirname + 1; nfsdirname = delimp + 2; } else if ((delimp = strrchr(nfsdirname, ':')) != NULL) { hostp = nfsdirname; nfsdirname = delimp + 1; } if (hostp != NULL) { *delimp = '\0'; getaddrinfo(hostp, NULL, &hints, &ai); if (ai == NULL) { warnx("can't get net id for host"); } } /* * Check if we have to start the rpc-call later. * If there are still identical nfs-names mounted, * we skip the rpc-call. Obviously this has to * happen before unmount(2), but it should happen * after the previous namecheck. * A non-NULL return means that this is the last * mount from mntfromname that is still mounted. */ if (getmntentry(sfs->f_mntfromname, NULL, NULL, CHECKUNIQUE) != NULL) do_rpc = 1; } if (!namematch(ai)) { free(orignfsdirname); return (1); } /* First try to unmount using the file system ID. */ snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", sfs->f_fsid.val[0], sfs->f_fsid.val[1]); if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) { /* XXX, non-root users get a zero fsid, so don't warn. */ if (errno != ENOENT || sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0) warn("unmount of %s failed", sfs->f_mntonname); if (errno != ENOENT) { free(orignfsdirname); return (1); } /* Compatibility for old kernels. */ if (sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0) warnx("retrying using path instead of file system ID"); if (unmount(sfs->f_mntonname, fflag) != 0) { warn("unmount of %s failed", sfs->f_mntonname); free(orignfsdirname); return (1); } } /* Mark this this file system as unmounted. */ getmntentry(NULL, NULL, &sfs->f_fsid, REMOVE); if (vflag) (void)printf("%s: unmount from %s\n", sfs->f_mntfromname, sfs->f_mntonname); /* * Report to mountd-server which nfsname * has been unmounted. */ if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) { clp = clnt_create(hostp, MOUNTPROG, MOUNTVERS, "udp"); if (clp == NULL) { warnx("%s: %s", hostp, clnt_spcreateerror("MOUNTPROG")); free(orignfsdirname); return (1); } clp->cl_auth = authsys_create_default(); try.tv_sec = 20; try.tv_usec = 0; clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, (xdrproc_t)xdr_dir, nfsdirname, (xdrproc_t)xdr_void, (caddr_t)0, try); if (clnt_stat != RPC_SUCCESS) { warnx("%s: %s", hostp, clnt_sperror(clp, "RPCMNT_UMOUNT")); free(orignfsdirname); return (1); } /* * Remove the unmounted entry from /var/db/mounttab. */ if (read_mtab()) { clean_mtab(hostp, nfsdirname, vflag); if(!write_mtab(vflag)) warnx("cannot remove mounttab entry %s:%s", hostp, nfsdirname); free_mtab(); } auth_destroy(clp->cl_auth); clnt_destroy(clp); }
/* * Handle an amd restart for amd's own mount points. * * Scan through the mount list finding all daemon mount points * (determined by the presence of a pid inside the mount info). * Next hack up partial data structures and add the mounted file * system to the list of known filesystems. * * This module relies on internal details of other components. If * you change something else make *sure* restart() still works. */ void restart_automounter_nodes(void) { mntlist *ml, *mlp; /* reasonably sized list of restarted nfs ports */ u_short old_ports[256]; memset((voidp) &old_ports, 0, sizeof(u_short) * 256); /* * Read the existing mount table. For each entry, find nfs, ufs or auto * mounts and create a partial am_node to represent it. */ for (mlp = ml = read_mtab("restart", mnttab_file_name); mlp; mlp = mlp->mnext) { mntent_t *me = mlp->mnt; am_ops *fs_ops = NULL; char *colon; long pid; u_short port; int err; if (!STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) continue; /* to next mlp */ /* * NFS entry, or possibly an Amd entry... * The mnt_fsname for daemon mount points is * host:(pidXXX) * or (seen on Solaris) * host:daemon(pidXXX) */ colon = strchr(me->mnt_fsname, ':'); if (!colon || !strstr(colon, "(pid")) continue; /* if got here, then we matched an existing Amd mount point */ err = 1; plog(XLOG_WARNING, "%s is an existing automount point", me->mnt_dir); /* Is the old automounter still alive? */ if (sscanf(colon, "%*[^(](pid%ld%*[,)]", &pid) != 1) { plog(XLOG_WARNING, "Can't parse pid in %s", me->mnt_fsname); goto give_up; } if (kill(pid, 0) != -1 || errno != ESRCH) { plog(XLOG_WARNING, "Automounter (pid: %ld) still alive", pid); goto give_up; } /* * Do we have a map for this mount point? Who cares, we'll restart * anyway -- getting ESTALE is way better than hanging. */ /* Can we restart it? Only if it tells us what port it was using... */ if (sscanf(colon, "%*[^,],port%hu)", &port) != 1) { plog(XLOG_WARNING, "No port specified for %s", me->mnt_fsname); goto give_up; } /* Maybe we already own that port... */ if (port != nfs_port) { int i; for (i = 0; i < 256; i++) { if (old_ports[i] == port || old_ports[i] == 0) break; } if (i == 256) { plog(XLOG_WARNING, "Too many open ports (256)"); goto give_up; } if (old_ports[i] == 0) { int soNFS; SVCXPRT *nfsxprt; if (create_nfs_service(&soNFS, &port, &nfsxprt, nfs_program_2) != 0) { plog(XLOG_WARNING, "Can't bind to port %u", port); goto give_up; } old_ports[i] = nfs_port = port; } } err = 0; give_up: if (err) { plog(XLOG_WARNING, "Can't restart %s, leaving it alone", me->mnt_dir); fs_ops = &amfs_link_ops; } else { fs_ops = &amfs_toplvl_ops; } restart_fake_mntfs(me, fs_ops); } /* end of "for (mlp" */ /* free the mount list */ free_mntlist(ml); }
void RootMount::reload() { read_mtab(); }
int umountfs(char *name, char **typelist) { enum clnt_stat clnt_stat; struct hostent *hp; struct mtablist *mtab; struct sockaddr_in saddr; struct timeval pertry, try; CLIENT *clp; size_t len; int so, speclen, do_rpc; char *mntonname, *mntfromname; char *mntfromnamerev; char *nfsdirname, *orignfsdirname; char *resolved, realname[MAXPATHLEN]; char *type, *delimp, *hostp, *origname; len = 0; mtab = NULL; mntfromname = mntonname = delimp = hostp = orignfsdirname = NULL; /* * 1. Check if the name exists in the mounttable. */ (void)checkmntlist(name, &mntfromname, &mntonname, &type); /* * 2. Remove trailing slashes if there are any. After that * we look up the name in the mounttable again. */ if (mntfromname == NULL && mntonname == NULL) { speclen = strlen(name); for (speclen = strlen(name); speclen > 1 && name[speclen - 1] == '/'; speclen--) name[speclen - 1] = '\0'; (void)checkmntlist(name, &mntfromname, &mntonname, &type); resolved = name; /* Save off original name in origname */ if ((origname = strdup(name)) == NULL) err(1, "strdup"); /* * 3. Check if the deprecated nfs-syntax with an '@' * has been used and translate it to the ':' syntax. * Look up the name in the mounttable again. */ if (mntfromname == NULL && mntonname == NULL) { if ((delimp = strrchr(name, '@')) != NULL) { hostp = delimp + 1; if (*hostp != '\0') { /* * Make both '@' and ':' * notations equal */ char *host = strdup(hostp); len = strlen(hostp); if (host == NULL) err(1, "strdup"); memmove(name + len + 1, name, (size_t)(delimp - name)); name[len] = ':'; memmove(name, host, len); free(host); } for (speclen = strlen(name); speclen > 1 && name[speclen - 1] == '/'; speclen--) name[speclen - 1] = '\0'; name[len + speclen + 1] = '\0'; (void)checkmntlist(name, &mntfromname, &mntonname, &type); resolved = name; } /* * 4. Check if a relative mountpoint has been * specified. This should happen as last check, * the order is important. To prevent possible * nfs-hangs, we just call realpath(3) on the * basedir of mountpoint and add the dirname again. * Check the name in mounttable one last time. */ if (mntfromname == NULL && mntonname == NULL) { (void)strcpy(name, origname); if ((getrealname(name, realname)) != NULL) { (void)checkmntlist(realname, &mntfromname, &mntonname, &type); resolved = realname; } /* * All tests failed, return to main() */ if (mntfromname == NULL && mntonname == NULL) { (void)strcpy(name, origname); warnx("%s: not currently mounted", origname); free(origname); return (1); } } } free(origname); } else resolved = name; if (checkvfsname(type, typelist)) return (1); hp = NULL; nfsdirname = NULL; if (!strcmp(type, "nfs")) { if ((nfsdirname = strdup(mntfromname)) == NULL) err(1, "strdup"); orignfsdirname = nfsdirname; if ((delimp = strchr(nfsdirname, ':')) != NULL) { *delimp = '\0'; hostp = nfsdirname; if ((hp = gethostbyname(hostp)) == NULL) { warnx("can't get net id for host"); } nfsdirname = delimp + 1; } } /* * Check if the reverse entrys of the mounttable are really the * same as the normal ones. */ if ((mntfromnamerev = strdup(getmntname(getmntname(mntfromname, NULL, MNTON, &type, NAME), NULL, MNTFROM, &type, NAME))) == NULL) err(1, "strdup"); /* * Mark the uppermost mount as unmounted. */ (void)getmntname(mntfromname, mntonname, NOTHING, &type, MARK); /* * If several equal mounts are in the mounttable, check the order * and warn the user if necessary. */ if (strcmp(mntfromnamerev, mntfromname ) != 0 && strcmp(resolved, mntonname) != 0) { warnx("cannot umount %s, %s\n " "is mounted there, umount it first", mntonname, mntfromnamerev); /* call getmntname again to set mntcheck[i] to 0 */ (void)getmntname(mntfromname, mntonname, NOTHING, &type, UNMARK); return (1); } free(mntfromnamerev); /* * Check if we have to start the rpc-call later. * If there are still identical nfs-names mounted, * we skip the rpc-call. Obviously this has to * happen before unmount(2), but it should happen * after the previous namecheck. */ if (strcmp(type, "nfs") == 0 && getmntname(mntfromname, NULL, NOTHING, &type, COUNT) != NULL) do_rpc = 1; else do_rpc = 0; if (!namematch(hp)) return (1); if (unmount(mntonname, fflag) != 0 ) { warn("unmount of %s failed", mntonname); return (1); } if (vflag) (void)printf("%s: unmount from %s\n", mntfromname, mntonname); /* * Report to mountd-server which nfsname * has been unmounted. */ if (hp != NULL && !(fflag & MNT_FORCE) && do_rpc) { memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = 0; memmove(&saddr.sin_addr, hp->h_addr, MIN(hp->h_length, sizeof(saddr.sin_addr))); pertry.tv_sec = 3; pertry.tv_usec = 0; so = RPC_ANYSOCK; if ((clp = clntudp_create(&saddr, RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) { clnt_pcreateerror("Cannot MNT PRC"); return (1); } clp->cl_auth = authunix_create_default(); try.tv_sec = 20; try.tv_usec = 0; clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir, nfsdirname, xdr_void, (caddr_t)0, try); if (clnt_stat != RPC_SUCCESS) { clnt_perror(clp, "Bad MNT RPC"); return (1); } /* * Remove the unmounted entry from /var/db/mounttab. */ if (read_mtab(mtab)) { mtab = mtabhead; clean_mtab(hostp, nfsdirname); if(!write_mtab()) warnx("cannot remove entry %s:%s", hostp, nfsdirname); free_mtab(); } free(orignfsdirname); auth_destroy(clp->cl_auth); clnt_destroy(clp); }