bool_t xdr_amq_mount_info_qelem(XDR *xdrs, qelem *qhead) { mntfs *mf; u_int len = 0; /* * Compute length of list */ for (mf = AM_LAST(mntfs, qhead); mf != HEAD(mntfs, qhead); mf = PREV(mntfs, mf)) { if (!(mf->mf_fsflags & FS_AMQINFO)) continue; len++; } xdr_u_int(xdrs, &len); /* * Send individual data items */ for (mf = AM_LAST(mntfs, qhead); mf != HEAD(mntfs, qhead); mf = PREV(mntfs, mf)) { int up; if (!(mf->mf_fsflags & FS_AMQINFO)) continue; if (!xdr_amq_string(xdrs, &mf->mf_ops->fs_type)) { return (FALSE); } if (!xdr_amq_string(xdrs, &mf->mf_mount)) { return (FALSE); } if (!xdr_amq_string(xdrs, &mf->mf_info)) { return (FALSE); } if (!xdr_amq_string(xdrs, &mf->mf_server->fs_host)) { return (FALSE); } if (!xdr_int(xdrs, &mf->mf_error)) { return (FALSE); } if (!xdr_int(xdrs, &mf->mf_refc)) { return (FALSE); } if (FSRV_ERROR(mf->mf_server) || FSRV_ISDOWN(mf->mf_server)) up = 0; else if (FSRV_ISUP(mf->mf_server)) up = 1; else up = -1; if (!xdr_int(xdrs, &up)) { return (FALSE); } } return (TRUE); }
/* * Convert from file handle to automount node. */ static am_node * fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop) { struct am_fh *fp = (struct am_fh *) fhp; am_node *ap = NULL; if (fp->u.s.fhh_type != 0) { /* New filehandle type */ int len = sizeof(*fhp) - sizeof(fp->fhh_gen); char *path = xmalloc(len+1); /* * Because fhp is treated as a filehandle we use memcpy * instead of xstrlcpy. */ memcpy(path, (char *) fp->u.fhh_path, len); path[len] = '\0'; /* dlog("fh_to_mp3: new filehandle: %s", path); */ ap = path_to_exported_ap(path); XFREE(path); } else { /* dlog("fh_to_mp3: old filehandle: %d", fp->u.s.fhh_id); */ /* * Check process id matches * If it doesn't then it is probably * from an old kernel-cached filehandle * which is now out of date. */ if (fp->u.s.fhh_pid != get_server_pid()) { dlog("fh_to_mp3: wrong pid %ld != my pid %ld", (long) fp->u.s.fhh_pid, get_server_pid()); goto drop; } /* * Get hold of the supposed mount node */ ap = get_exported_ap(fp->u.s.fhh_id); } /* * Check the generation number in the node * matches the one from the kernel. If not * then the old node has been timed out and * a new one allocated. */ if (ap != NULL && ap->am_gen != fp->fhh_gen) ap = NULL; /* * If it doesn't exists then drop the request */ if (!ap) goto drop; #if 0 /* * If the node is hung then locate a new node * for it. This implements the replicated filesystem * retries. */ if (ap->am_al->al_mnt && FSRV_ISDOWN(ap->am_al->al_mnt->mf_server) && ap->am_parent) { int error; am_node *orig_ap = ap; dlog("fh_to_mp3: %s (%s) is hung: lookup alternative file server", orig_ap->am_path, orig_ap->am_al->al_mnt->mf_info); /* * Update modify time of parent node. * With any luck the kernel will re-stat * the child node and get new information. */ clocktime(&orig_ap->am_fattr.na_mtime); /* * Call the parent's lookup routine for an object * with the same name. This may return -1 in error * if a mount is in progress. In any case, if no * mount node is returned the error code is propagated * to the caller. */ if (vop == VLOOK_CREATE) { ap = orig_ap->am_parent->am_al->al_mnt->mf_ops->lookup_child(orig_ap->am_parent, orig_ap->am_name, &error, vop); if (ap && error < 0) ap = orig_ap->am_parent->am_al->al_mnt->mf_ops->mount_child(ap, &error); } else { ap = NULL; error = ESTALE; } if (ap == 0) { if (error < 0 && amd_state == Finishing) error = ENOENT; *rp = error; return 0; } /* * Update last access to original node. This * avoids timing it out and so sending ESTALE * back to the kernel. * XXX - Not sure we need this anymore (jsp, 90/10/6). */ new_ttl(orig_ap); } #endif /* 0 */ /* * Disallow references to objects being unmounted, unless * they are automount points. */ if (ap->am_al->al_mnt && (ap->am_al->al_mnt->mf_flags & MFF_UNMOUNTING) && !(ap->am_flags & AMF_ROOT)) { if (amd_state == Finishing) *rp = ENOENT; else *rp = -1; return 0; } new_ttl(ap); drop: if (!ap || !ap->am_al->al_mnt) { /* * If we are shutting down then it is likely * that this node has disappeared because of * a fast timeout. To avoid things thrashing * just pretend it doesn't exist at all. If * ESTALE is returned, some NFS clients just * keep retrying (stupid or what - if it's * stale now, what's it going to be in 5 minutes?) */ if (amd_state == Finishing) *rp = ENOENT; else { *rp = ESTALE; amd_stats.d_stale++; } } return ap; }
/* * 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; }