void am_mounted(am_node *mp) { mntfs *mf = mp->am_mnt; mf_mounted(mf); #ifdef HAVE_FS_AUTOFS if (mf->mf_flags & MFF_AUTOFS) autofs_mounted(mp); #endif /* HAVE_FS_AUTOFS */ /* * Patch up path for direct mounts */ if (mp->am_parent && mp->am_parent->am_mnt->mf_fsflags & FS_DIRECT) mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", "."); /* * Check whether this mount should be cached permanently */ if (mf->mf_fsflags & FS_NOTIMEOUT) { mp->am_flags |= AMF_NOTIMEOUT; } else if (mf->mf_mount[1] == '\0' && mf->mf_mount[0] == '/') { mp->am_flags |= AMF_NOTIMEOUT; } else { mntent_t mnt; if (mf->mf_mopts) { mnt.mnt_opts = mf->mf_mopts; if (hasmntopt(&mnt, "nounmount")) mp->am_flags |= AMF_NOTIMEOUT; if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0) mp->am_timeo = gopt.am_timeo; } } /* * If this node is a symlink then * compute the length of the returned string. */ if (mp->am_fattr.na_type == NFLNK) mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mp->am_mnt->mf_mount); /* * Record mount time */ mp->am_fattr.na_mtime.nt_seconds = mp->am_stats.s_mtime = clocktime(); new_ttl(mp); /* * Update mtime of parent node */ if (mp->am_parent && mp->am_parent->am_mnt) mp->am_parent->am_fattr.na_mtime.nt_seconds = mp->am_stats.s_mtime; /* * This is ugly, but essentially unavoidable * Sublinks must be treated separately as type==link * when the base type is different. */ if (mp->am_link && mp->am_mnt->mf_ops != &amfs_link_ops) amfs_link_ops.mount_fs(mp, mp->am_mnt); #ifdef HAVE_FS_AUTOFS if (mp->am_flags & AMF_AUTOFS) autofs_mount_succeeded(mp); else #endif /* HAVE_FS_AUTOFS */ /* * Now, if we can, do a reply to our NFS client here * to speed things up. */ nfs_quick_reply(mp, 0); /* * Update stats */ amd_stats.d_mok++; }
/* * Pick a file system to try mounting and * do that in the background if necessary * For each location: discard previous mount location if required fetch next mount location if the filesystem failed to be mounted then this_error = error from filesystem goto failed if the filesystem is mounting or unmounting then goto retry; if the fileserver is down then this_error = EIO continue; if the filesystem is already mounted break fi this_error = initialize mount point if no error on this mount and mount is delayed then this_error = -1 fi if this_error < 0 then retry = true fi if no error on this mount then if mount in background then run mount in background return -1 else this_error = mount in foreground fi fi if an error occurred on this mount then update stats save error in mount point fi endfor */ static int amfs_bgmount(struct continuation *cp) { am_node *mp = cp->mp; am_loc *loc; mntfs *mf; int this_error = -1; /* Per-mount error */ int hard_error = -1; /* Cumulative per-node error */ if (mp->am_al) free_loc(mp->am_al); /* * Try to mount each location. * At the end: * hard_error == 0 indicates something was mounted. * hard_error > 0 indicates everything failed with a hard error * hard_error < 0 indicates nothing could be mounted now */ for (mp->am_al = *cp->al; *cp->al; cp->al++, mp->am_al = *cp->al) { am_ops *p; loc = dup_loc(mp->am_al); mf = loc->al_mnt; p = mf->mf_ops; if (hard_error < 0) hard_error = this_error; this_error = 0; if (mf->mf_error > 0) { this_error = mf->mf_error; goto failed; } if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) { /* * Still mounting - retry later */ dlog("mount of \"%s\" already pending", mf->mf_info); goto retry; } if (FSRV_ISDOWN(mf->mf_server)) { /* * Would just mount from the same place * as a hung mount - so give up */ dlog("%s is already hung - giving up", mf->mf_server->fs_host); this_error = EIO; goto failed; } XFREE(mp->am_link); mp->am_link = NULL; if (loc->al_fo && loc->al_fo->opt_sublink && loc->al_fo->opt_sublink[0]) mp->am_link = xstrdup(loc->al_fo->opt_sublink); /* * Will usually need to play around with the mount nodes * file attribute structure. This must be done here. * Try and get things initialized, even if the fileserver * is not known to be up. In the common case this will * progress things faster. */ /* * Fill in attribute fields. */ if (mf->mf_fsflags & FS_DIRECTORY) mk_fattr(&mp->am_fattr, NFDIR); else mk_fattr(&mp->am_fattr, NFLNK); if (mf->mf_flags & MFF_MOUNTED) { dlog("duplicate mount of \"%s\" ...", mf->mf_info); /* * Skip initial processing of the mountpoint if already mounted. * This could happen if we have multiple sublinks into the same f/s, * or if we are restarting an already-mounted filesystem. */ goto already_mounted; } if (mf->mf_fo && mf->mf_fo->fs_mtab) { plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s", mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type, mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs"); } if (p->fs_init && !(mf->mf_flags & MFF_RESTART)) this_error = p->fs_init(mf); if (this_error > 0) goto failed; if (this_error < 0) goto retry; if (loc->al_fo && loc->al_fo->opt_delay) { /* * If there is a delay timer on the location * then don't try to mount if the timer * has not expired. */ int i = atoi(loc->al_fo->opt_delay); time_t now = clocktime(NULL); if (i > 0 && now < (cp->start + i)) { dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - now + cp->start)); goto retry; } } /* * If the directory is not yet made and it needs to be made, then make it! */ if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) { plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount); this_error = mkdirs(mf->mf_mount, 0555); if (this_error) { plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error)); goto failed; } mf->mf_flags |= MFF_MKMNT; } #ifdef HAVE_FS_AUTOFS if (mf->mf_flags & MFF_IS_AUTOFS) if ((this_error = autofs_get_fh(mp))) goto failed; #endif /* HAVE_FS_AUTOFS */ already_mounted: mf->mf_flags |= MFF_MOUNTING; if (mf->mf_fsflags & FS_MBACKGROUND) { dlog("backgrounding mount of \"%s\"", mf->mf_mount); if (cp->callout) { untimeout(cp->callout); cp->callout = 0; } /* actually run the task, backgrounding as necessary */ run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp); return -1; } else { dlog("foreground mount of \"%s\" ...", mf->mf_mount); this_error = mount_node((opaque_t) mp); } mf->mf_flags &= ~MFF_MOUNTING; if (this_error > 0) goto failed; if (this_error == 0) { am_mounted(mp); break; /* Success */ } retry: if (!cp->retry) continue; dlog("will retry ...\n"); /* * Arrange that amfs_bgmount is called * after anything else happens. */ dlog("Arranging to retry mount of %s", mp->am_path); sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf)); if (cp->callout) untimeout(cp->callout); cp->callout = timeout(RETRY_INTERVAL, wakeup, (opaque_t) get_mntfs_wchan(mf)); mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL; /* * Not done yet - so don't return anything */ return -1; failed: if (!FSRV_ISDOWN(mf->mf_server)) { /* mark the mount as failed unless the server is down */ amd_stats.d_merr++; mf->mf_error = this_error; mf->mf_flags |= MFF_ERROR; #ifdef HAVE_FS_AUTOFS if (mp->am_autofs_fh) autofs_release_fh(mp); #endif /* HAVE_FS_AUTOFS */ if (mf->mf_flags & MFF_MKMNT) { rmdirs(mf->mf_mount); mf->mf_flags &= ~MFF_MKMNT; } } /* * Wakeup anything waiting for this mount */ wakeup(get_mntfs_wchan(mf)); free_loc(loc); /* continue */ } /* * If we get here, then either the mount succeeded or * there is no more mount information available. */ if (this_error) { if (mp->am_al) free_loc(mp->am_al); mp->am_al = loc = new_loc(); mf = loc->al_mnt; #ifdef HAVE_FS_AUTOFS if (mp->am_flags & AMF_AUTOFS) autofs_mount_failed(mp); else #endif /* HAVE_FS_AUTOFS */ nfs_quick_reply(mp, this_error); if (hard_error <= 0) hard_error = this_error; if (hard_error < 0) hard_error = ETIMEDOUT; /* * Set a small(ish) timeout on an error node if * the error was not a time out. */ switch (hard_error) { case ETIMEDOUT: case EWOULDBLOCK: case EIO: mp->am_timeo = 17; break; default: mp->am_timeo = 5; break; } new_ttl(mp); } else { mf = loc->al_mnt; /* * Wakeup anything waiting for this mount */ wakeup(get_mntfs_wchan(mf)); hard_error = 0; } /* * Make sure that the error value in the mntfs has a * reasonable value. */ if (mf->mf_error < 0) { mf->mf_error = hard_error; if (hard_error) mf->mf_flags |= MFF_ERROR; } /* * In any case we don't need the continuation any more */ free_continuation(cp); return hard_error; }
void am_mounted(am_node *mp) { int notimeout = 0; /* assume normal timeouts initially */ mntfs *mf = mp->am_al->al_mnt; /* * This is the parent mntfs which does the mf->mf_fo (am_opts type), and * we're passing TRUE here to tell mf_mounted to actually free the * am_opts. See a related comment in mf_mounted(). */ mf_mounted(mf, TRUE); #ifdef HAVE_FS_AUTOFS if (mf->mf_flags & MFF_IS_AUTOFS) autofs_mounted(mp); #endif /* HAVE_FS_AUTOFS */ /* * Patch up path for direct mounts */ if (mp->am_parent && mp->am_parent->am_al->al_mnt->mf_fsflags & FS_DIRECT) mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", "."); /* * Check whether this mount should be cached permanently or not, * and handle user-requested timeouts. */ /* first check if file system was set to never timeout */ if (mf->mf_fsflags & FS_NOTIMEOUT) notimeout = 1; /* next, alter that decision by map flags */ if (mf->mf_mopts) { mntent_t mnt; mnt.mnt_opts = mf->mf_mopts; /* umount option: user wants to unmount this entry */ if (amu_hasmntopt(&mnt, "unmount") || amu_hasmntopt(&mnt, "umount")) notimeout = 0; /* noumount option: user does NOT want to unmount this entry */ if (amu_hasmntopt(&mnt, "nounmount") || amu_hasmntopt(&mnt, "noumount")) notimeout = 1; /* utimeout=N option: user wants to unmount this option AND set timeout */ if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0) mp->am_timeo = gopt.am_timeo; /* otherwise use default timeout */ else notimeout = 0; /* special case: don't try to unmount "/" (it can never succeed) */ if (mf->mf_mount[0] == '/' && mf->mf_mount[1] == '\0') notimeout = 1; } /* finally set actual flags */ if (notimeout) { mp->am_flags |= AMF_NOTIMEOUT; plog(XLOG_INFO, "%s set to never timeout", mp->am_path); } else { mp->am_flags &= ~AMF_NOTIMEOUT; plog(XLOG_INFO, "%s set to timeout in %d seconds", mp->am_path, mp->am_timeo); } /* * If this node is a symlink then * compute the length of the returned string. */ if (mp->am_fattr.na_type == NFLNK) mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mf->mf_mount); /* * Record mount time, and update am_stats at the same time. */ mp->am_stats.s_mtime = clocktime(&mp->am_fattr.na_mtime); new_ttl(mp); /* * Update mtime of parent node (copying "struct nfstime" in '=' below) */ if (mp->am_parent && mp->am_parent->am_al->al_mnt) mp->am_parent->am_fattr.na_mtime = mp->am_fattr.na_mtime; /* * This is ugly, but essentially unavoidable * Sublinks must be treated separately as type==link * when the base type is different. */ if (mp->am_link && mf->mf_ops != &amfs_link_ops) amfs_link_ops.mount_fs(mp, mf); /* * Now, if we can, do a reply to our client here * to speed things up. */ #ifdef HAVE_FS_AUTOFS if (mp->am_flags & AMF_AUTOFS) autofs_mount_succeeded(mp); else #endif /* HAVE_FS_AUTOFS */ nfs_quick_reply(mp, 0); /* * Update stats */ amd_stats.d_mok++; }