Beispiel #1
0
/*
 * nfs version 3 fsinfo rpc call
 */
int
nfs_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
    struct thread *td)
{
	struct nfsv3_fsinfo *fsp;
	u_int32_t pref, max;
	caddr_t bpos, dpos;
	int error = 0, retattr;
	struct mbuf *mreq, *mrep, *md, *mb;
	u_int64_t maxfsize;
	
	nfsstats.rpccnt[NFSPROC_FSINFO]++;
	mreq = nfsm_reqhead(vp, NFSPROC_FSINFO, NFSX_FH(1));
	mb = mreq;
	bpos = mtod(mb, caddr_t);
	nfsm_fhtom(vp, 1);
	nfsm_request(vp, NFSPROC_FSINFO, td, cred);
	nfsm_postop_attr(vp, retattr);
	if (!error) {
		fsp = nfsm_dissect(struct nfsv3_fsinfo *, NFSX_V3FSINFO);
		pref = fxdr_unsigned(u_int32_t, fsp->fs_wtpref);
		mtx_lock(&nmp->nm_mtx);
		if (pref < nmp->nm_wsize && pref >= NFS_FABLKSIZE)
			nmp->nm_wsize = (pref + NFS_FABLKSIZE - 1) &
				~(NFS_FABLKSIZE - 1);
		max = fxdr_unsigned(u_int32_t, fsp->fs_wtmax);
		if (max < nmp->nm_wsize && max > 0) {
			nmp->nm_wsize = max & ~(NFS_FABLKSIZE - 1);
			if (nmp->nm_wsize == 0)
				nmp->nm_wsize = max;
		}
		pref = fxdr_unsigned(u_int32_t, fsp->fs_rtpref);
		if (pref < nmp->nm_rsize && pref >= NFS_FABLKSIZE)
			nmp->nm_rsize = (pref + NFS_FABLKSIZE - 1) &
				~(NFS_FABLKSIZE - 1);
		max = fxdr_unsigned(u_int32_t, fsp->fs_rtmax);
		if (max < nmp->nm_rsize && max > 0) {
			nmp->nm_rsize = max & ~(NFS_FABLKSIZE - 1);
			if (nmp->nm_rsize == 0)
				nmp->nm_rsize = max;
		}
		pref = fxdr_unsigned(u_int32_t, fsp->fs_dtpref);
		if (pref < nmp->nm_readdirsize && pref >= NFS_DIRBLKSIZ)
			nmp->nm_readdirsize = (pref + NFS_DIRBLKSIZ - 1) &
				~(NFS_DIRBLKSIZ - 1);
		if (max < nmp->nm_readdirsize && max > 0) {
			nmp->nm_readdirsize = max & ~(NFS_DIRBLKSIZ - 1);
			if (nmp->nm_readdirsize == 0)
				nmp->nm_readdirsize = max;
		}
		maxfsize = fxdr_hyper(&fsp->fs_maxfilesize);
		if (maxfsize > 0 && maxfsize < nmp->nm_maxfilesize)
			nmp->nm_maxfilesize = maxfsize;
		nmp->nm_mountp->mnt_stat.f_iosize = nfs_iosize(nmp);
		nmp->nm_state |= NFSSTA_GOTFSINFO;
		mtx_unlock(&nmp->nm_mtx);
	}
Beispiel #2
0
int nfs_request(struct nfsmount *nmp, int procnum,
                FAR void *request, size_t reqlen,
                FAR void *response, size_t resplen)
{
  struct rpcclnt *clnt = nmp->nm_rpcclnt;
  struct nfs_reply_header replyh;
  int error;

tryagain:
  error = rpcclnt_request(clnt, procnum, NFS_PROG, NFS_VER3,
                          request, reqlen, response, resplen);
  if (error != 0)
    {
      fdbg("ERROR: rpcclnt_request failed: %d\n", error);
      return error;
    }

  memcpy(&replyh, response, sizeof(struct nfs_reply_header));

  if (replyh.nfs_status != 0)
    {
      if (fxdr_unsigned(uint32_t, replyh.nfs_status) > 32)
        {
          error = EOPNOTSUPP;
        }
      else
        {
          /* NFS_ERRORS are the same as NuttX errno values */

          error = fxdr_unsigned(uint32_t, replyh.nfs_status);
        }

      return error;
    }

  if (replyh.rpc_verfi.authtype != 0)
    {
      error = fxdr_unsigned(int, replyh.rpc_verfi.authtype);

      if (error == EAGAIN)
        {
          error = 0;
          goto tryagain;
        }

      fdbg("ERROR: NFS error %d from server\n", error);
      return error;
    }
/*
 * Load vnode attributes from the xdr file attributes.
 * Returns EBADRPC if they can't be parsed, 0 otherwise.
 */
APPLESTATIC int
nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap)
{
	struct nfs_fattr *fp;
	int error = 0;

	if (nd->nd_flag & ND_NFSV4) {
		error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL,
		    NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
	} else if (nd->nd_flag & ND_NFSV3) {
		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR);
		nap->na_type = nfsv34tov_type(fp->fa_type);
		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
		nap->na_rdev = NFSMAKEDEV(
		    fxdr_unsigned(int, fp->fa3_rdev.specdata1),
		    fxdr_unsigned(int, fp->fa3_rdev.specdata2));
		nap->na_nlink = fxdr_unsigned(uint32_t, fp->fa_nlink);
		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
		nap->na_size = fxdr_hyper(&fp->fa3_size);
		nap->na_blocksize = NFS_FABLKSIZE;
		nap->na_bytes = fxdr_hyper(&fp->fa3_used);
		nap->na_fileid = fxdr_hyper(&fp->fa3_fileid);
		fxdr_nfsv3time(&fp->fa3_atime, &nap->na_atime);
		fxdr_nfsv3time(&fp->fa3_ctime, &nap->na_ctime);
		fxdr_nfsv3time(&fp->fa3_mtime, &nap->na_mtime);
		nap->na_flags = 0;
		nap->na_filerev = 0;
	} else {
Beispiel #4
0
static int
xdr_int_decode(struct mbuf **mptr, int *iptr)
{
	u_int32_t i;

	if (xdr_opaque_decode(mptr, (u_char *) &i, sizeof(u_int32_t)) != 0)
		return EBADRPC;
	*iptr = fxdr_unsigned(u_int32_t, i);
	return 0;
}
Beispiel #5
0
int
nfsm_strsiz_xx(int *s, int m, struct mbuf **mb, caddr_t *bpos)
{
	u_int32_t *tl;

	tl = nfsm_dissect_xx(NFSX_UNSIGNED, mb, bpos);
	if (tl == NULL)
		return (EBADRPC);
	*s = fxdr_unsigned(int32_t, *tl);
	if (*s > m)
		return (EBADRPC);
	return (0);
}
Beispiel #6
0
/*
 * nfs version 3 fsinfo rpc call
 */
int
nfs_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct thread *td)
{
	struct nfsv3_fsinfo *fsp;
	u_int32_t pref, max;
	int error = 0, retattr;
	u_int64_t maxfsize;
	struct nfsm_info info;

	info.v3 = 1;
	nfsstats.rpccnt[NFSPROC_FSINFO]++;
	nfsm_reqhead(&info, vp, NFSPROC_FSINFO, NFSX_FH(1));
	ERROROUT(nfsm_fhtom(&info, vp));
	NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_FSINFO, td,
				nfs_vpcred(vp, ND_READ), &error));
	ERROROUT(nfsm_postop_attr(&info, vp, &retattr, NFS_LATTR_NOSHRINK));
	if (error == 0) {
		NULLOUT(fsp = nfsm_dissect(&info, NFSX_V3FSINFO));
		pref = fxdr_unsigned(u_int32_t, fsp->fs_wtpref);
		if (pref < nmp->nm_wsize && pref >= NFS_FABLKSIZE)
			nmp->nm_wsize = roundup2(pref, NFS_FABLKSIZE);
		max = fxdr_unsigned(u_int32_t, fsp->fs_wtmax);
		if (max < nmp->nm_wsize && max > 0) {
			nmp->nm_wsize = max & ~(NFS_FABLKSIZE - 1);
			if (nmp->nm_wsize == 0)
				nmp->nm_wsize = max;
		}
		pref = fxdr_unsigned(u_int32_t, fsp->fs_rtpref);
		if (pref < nmp->nm_rsize && pref >= NFS_FABLKSIZE)
			nmp->nm_rsize = roundup2(pref, NFS_FABLKSIZE);
		max = fxdr_unsigned(u_int32_t, fsp->fs_rtmax);
		if (max < nmp->nm_rsize && max > 0) {
			nmp->nm_rsize = max & ~(NFS_FABLKSIZE - 1);
			if (nmp->nm_rsize == 0)
				nmp->nm_rsize = max;
		}
		pref = fxdr_unsigned(u_int32_t, fsp->fs_dtpref);
		if (pref < nmp->nm_readdirsize && pref >= NFS_DIRBLKSIZ)
			nmp->nm_readdirsize = roundup2(pref, NFS_DIRBLKSIZ);
		if (max < nmp->nm_readdirsize && max > 0) {
			nmp->nm_readdirsize = max & ~(NFS_DIRBLKSIZ - 1);
			if (nmp->nm_readdirsize == 0)
				nmp->nm_readdirsize = max;
		}
		maxfsize = fxdr_hyper(&fsp->fs_maxfilesize);
		if (maxfsize > 0 && maxfsize < nmp->nm_maxfilesize)
			nmp->nm_maxfilesize = maxfsize;
		nmp->nm_state |= NFSSTA_GOTFSINFO;

		/*
		 * Use the smaller of rsize/wsize for the biosize.
		 */
		if (nmp->nm_rsize < nmp->nm_wsize)
			nmp->nm_mountp->mnt_stat.f_iosize = nmp->nm_rsize;
		else
			nmp->nm_mountp->mnt_stat.f_iosize = nmp->nm_wsize;
	}
	m_freem(info.mrep);
	info.mrep = NULL;
nfsmout:
	return (error);
}
Beispiel #7
0
static int
nfs_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
{
	struct vnode *vp;
	struct nfs_statfs *sfp;
	struct nfsmount *nmp = VFSTONFS(mp);
	thread_t td = curthread;
	int error = 0, retattr;
	struct nfsnode *np;
	struct nfsm_info info;

	info.mrep = NULL;
	info.v3 = (nmp->nm_flag & NFSMNT_NFSV3);
	lwkt_gettoken(&nmp->nm_token);

#ifndef nolint
	sfp = NULL;
#endif
	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np, NULL);
	if (error) {
		lwkt_reltoken(&nmp->nm_token);
		return (error);
	}
	vp = NFSTOV(np);
	/* ignore the passed cred */
	cred = crget();
	cred->cr_ngroups = 1;
	if (info.v3 && (nmp->nm_state & NFSSTA_GOTFSINFO) == 0)
		(void)nfs_fsinfo(nmp, vp, td);
	nfsstats.rpccnt[NFSPROC_FSSTAT]++;
	nfsm_reqhead(&info, vp, NFSPROC_FSSTAT, NFSX_FH(info.v3));
	ERROROUT(nfsm_fhtom(&info, vp));
	NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_FSSTAT, td, cred, &error));
	if (info.v3) {
		ERROROUT(nfsm_postop_attr(&info, vp, &retattr,
					 NFS_LATTR_NOSHRINK));
	}
	if (error) {
		if (info.mrep != NULL)
			m_freem(info.mrep);
		goto nfsmout;
	}
	NULLOUT(sfp = nfsm_dissect(&info, NFSX_STATFS(info.v3)));
	sbp->f_flag = nmp->nm_flag;
	sbp->f_owner = nmp->nm_cred->cr_ruid;

	if (info.v3) {
		sbp->f_bsize = NFS_FABLKSIZE;
		sbp->f_frsize = NFS_FABLKSIZE;
		sbp->f_blocks = (fxdr_hyper(&sfp->sf_tbytes) /
				((u_quad_t)NFS_FABLKSIZE));
		sbp->f_bfree = (fxdr_hyper(&sfp->sf_fbytes) /
				((u_quad_t)NFS_FABLKSIZE));
		sbp->f_bavail = (fxdr_hyper(&sfp->sf_abytes) /
				((u_quad_t)NFS_FABLKSIZE));
		sbp->f_files = fxdr_hyper(&sfp->sf_tfiles);
		sbp->f_ffree = fxdr_hyper(&sfp->sf_ffiles);
		sbp->f_favail = fxdr_hyper(&sfp->sf_afiles);
	} else {
		sbp->f_bsize = fxdr_unsigned(int32_t, sfp->sf_bsize);
		sbp->f_blocks = fxdr_unsigned(int32_t, sfp->sf_blocks);
		sbp->f_bfree = fxdr_unsigned(int32_t, sfp->sf_bfree);
		sbp->f_bavail = fxdr_unsigned(int32_t, sfp->sf_bavail);
		sbp->f_files = 0;
		sbp->f_ffree = 0;
		sbp->f_favail = 0;
	}
	sbp->f_syncreads = 0;
	sbp->f_syncwrites = 0;
	sbp->f_asyncreads = 0;
	sbp->f_asyncwrites = 0;
	sbp->f_type = mp->mnt_vfc->vfc_typenum;

	m_freem(info.mrep);
	info.mrep = NULL;
nfsmout:
	vput(vp);
	crfree(cred);
	lwkt_reltoken(&nmp->nm_token);
	return (error);
}
Beispiel #8
0
/*
 * nfs statfs call
 */
int
nfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
{
	struct vnode *vp;
	struct nfs_statfs *sfp;
	struct nfsmount *nmp = VFSTONFS(mp);
	thread_t td = curthread;
	int error = 0, retattr;
	struct nfsnode *np;
	u_quad_t tquad;
	struct nfsm_info info;

	info.mrep = NULL;
	info.v3 = (nmp->nm_flag & NFSMNT_NFSV3);

	lwkt_gettoken(&nmp->nm_token);

#ifndef nolint
	sfp = NULL;
#endif
	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np, NULL);
	if (error) {
		lwkt_reltoken(&nmp->nm_token);
		return (error);
	}
	vp = NFSTOV(np);
	/* ignore the passed cred */
	cred = crget();
	cred->cr_ngroups = 1;
	if (info.v3 && (nmp->nm_state & NFSSTA_GOTFSINFO) == 0)
		(void)nfs_fsinfo(nmp, vp, td);
	nfsstats.rpccnt[NFSPROC_FSSTAT]++;
	nfsm_reqhead(&info, vp, NFSPROC_FSSTAT, NFSX_FH(info.v3));
	ERROROUT(nfsm_fhtom(&info, vp));
	NEGKEEPOUT(nfsm_request(&info, vp, NFSPROC_FSSTAT, td, cred, &error));
	if (info.v3) {
		ERROROUT(nfsm_postop_attr(&info, vp, &retattr,
					 NFS_LATTR_NOSHRINK));
	}
	if (error) {
		if (info.mrep != NULL)
			m_freem(info.mrep);
		goto nfsmout;
	}
	NULLOUT(sfp = nfsm_dissect(&info, NFSX_STATFS(info.v3)));
	sbp->f_flags = nmp->nm_flag;

	if (info.v3) {
		sbp->f_bsize = NFS_FABLKSIZE;
		tquad = fxdr_hyper(&sfp->sf_tbytes);
		sbp->f_blocks = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
		tquad = fxdr_hyper(&sfp->sf_fbytes);
		sbp->f_bfree = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
		tquad = fxdr_hyper(&sfp->sf_abytes);
		sbp->f_bavail = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
		sbp->f_files = (fxdr_unsigned(int32_t,
		    sfp->sf_tfiles.nfsuquad[1]) & 0x7fffffff);
		sbp->f_ffree = (fxdr_unsigned(int32_t,
		    sfp->sf_ffiles.nfsuquad[1]) & 0x7fffffff);
	} else {
		sbp->f_bsize = fxdr_unsigned(int32_t, sfp->sf_bsize);
		sbp->f_blocks = fxdr_unsigned(int32_t, sfp->sf_blocks);
		sbp->f_bfree = fxdr_unsigned(int32_t, sfp->sf_bfree);
		sbp->f_bavail = fxdr_unsigned(int32_t, sfp->sf_bavail);
		sbp->f_files = 0;
		sbp->f_ffree = 0;
	}

	/*
	 * Some values are pre-set in mnt_stat.  Note in particular f_iosize
	 * cannot be changed once the filesystem is mounted as it is used
	 * as the basis for BIOs.
	 */
	if (sbp != &mp->mnt_stat) {
		sbp->f_type = mp->mnt_vfc->vfc_typenum;
		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
		sbp->f_iosize = mp->mnt_stat.f_iosize;
	}
	m_freem(info.mrep);
	info.mrep = NULL;
nfsmout:
	vput(vp);
	crfree(cred);
	lwkt_reltoken(&nmp->nm_token);
	return (error);
}
Beispiel #9
0
int rpcclnt_request(FAR struct rpcclnt *rpc, int procnum, int prog,
                    int version, FAR void *request, size_t reqlen,
                    FAR void *response, size_t resplen)
{
  struct rpc_reply_header *replymsg;
  uint32_t tmp;
  uint32_t xid;
  int retries;
  int error = 0;

  /* Get a new (non-zero) xid */

  xid = rpcclnt_newxid();

  /* Initialize the RPC header fields */

  rpcclnt_fmtheader((FAR struct rpc_call_header *)request,
                    xid, prog, version, procnum);

  /* Get the full size of the message (the size of variable data plus the size of
   * the messages header).
   */

  reqlen += sizeof(struct rpc_call_header);

  /* Send the RPC call messsages and receive the RPC response.  A limited
   * number of re-tries will be attempted, but only for the case of response
   * timeouts.
   */

  retries = 0;
  do
    {
      /* Do the client side RPC. */

      rpc_statistics(rpcrequests);
      rpc->rc_timeout = false;

      /* Send the RPC CALL message */

      error = rpcclnt_send(rpc, procnum, prog, request, reqlen);
      if (error != OK)
        {
          fvdbg("ERROR rpcclnt_send failed: %d\n", error);
        }

      /* Wait for the reply from our send */

      else
        {
          error = rpcclnt_reply(rpc, procnum, prog, response, resplen);
          if (error != OK)
            {
              fvdbg("ERROR rpcclnt_reply failed: %d\n", error);
            }
        }

      retries++;
    }
  while (rpc->rc_timeout && retries <= rpc->rc_retry);

  if (error != OK)
    {
      fdbg("ERROR: RPC failed: %d\n", error);
      return error;
    }

  /* Break down the RPC header and check if it is OK */

  replymsg = (FAR struct rpc_reply_header *)response;

  tmp = fxdr_unsigned(uint32_t, replymsg->type);
  if (tmp == RPC_MSGDENIED)
    {
      tmp = fxdr_unsigned(uint32_t, replymsg->status);
      switch (tmp)
        {
        case RPC_MISMATCH:
          fdbg("RPC_MSGDENIED: RPC_MISMATCH error\n");
          return EOPNOTSUPP;

        case RPC_AUTHERR:
          fdbg("RPC_MSGDENIED: RPC_AUTHERR error\n");
          return EACCES;

        default:
          return EOPNOTSUPP;
        }
    }
  else if (tmp != RPC_MSGACCEPTED)
    {
      return EOPNOTSUPP;
    }

  tmp = fxdr_unsigned(uint32_t, replymsg->status);
  if (tmp == RPC_SUCCESS)
    {
      fvdbg("RPC_SUCCESS\n");
    }
  else if (tmp == RPC_PROGMISMATCH)
    {
      fdbg("RPC_MSGACCEPTED: RPC_PROGMISMATCH error\n");
      return EOPNOTSUPP;
    }
  else if (tmp > 5)
    {
      fdbg("ERROR:  Other RPC type: %d\n", tmp);
      return EOPNOTSUPP;
    }

  return OK;
}
Beispiel #10
0
int rpcclnt_umount(struct rpcclnt *rpc)
{
  struct sockaddr *saddr;
  struct sockaddr_in *sa;

  union
  {
    struct rpc_call_pmap   sdata;
    struct rpc_call_umount mountd;
  } request;

  union
  {
    struct rpc_reply_pmap   rdata;
    struct rpc_reply_umount mdata;
  } response;

  int error;
  int ret;

  saddr = rpc->rc_name;
  sa = (FAR struct sockaddr_in *)saddr;

  /* Do the RPC to get a dynamic bounding with the server using ppmap.
   * Get port number for MOUNTD.
   */

  sa->sin_port = htons(PMAPPORT);

  ret = psock_connect(rpc->rc_so, saddr, sizeof(*saddr));
  if (ret < 0)
    {
      error = get_errno();
      fdbg("ERROR: psock_connect failed [port=%d]: %d\n",
            ntohs(sa->sin_port), error);
      goto bad;
    }

  request.sdata.pmap.prog = txdr_unsigned(RPCPROG_MNT);
  request.sdata.pmap.vers = txdr_unsigned(RPCMNT_VER1);
  request.sdata.pmap.proc = txdr_unsigned(IPPROTO_UDP);
  request.sdata.pmap.port = 0;

  error = rpcclnt_request(rpc, PMAPPROC_GETPORT, PMAPPROG, PMAPVERS,
                          (FAR void *)&request.sdata, sizeof(struct call_args_pmap),
                          (FAR void *)&response.rdata, sizeof(struct rpc_reply_pmap));
  if (error != 0)
    {
      fdbg("ERROR: rpcclnt_request failed: %d\n", error);
      goto bad;
    }

  sa->sin_port = htons(fxdr_unsigned(uint32_t, response.rdata.pmap.port));

  ret = psock_connect(rpc->rc_so, saddr, sizeof(*saddr));
  if (ret < 0)
    {
      error = get_errno();
      fdbg("ERROR: psock_connect failed [port=%d]: %d\n",
            ntohs(sa->sin_port), error);
      goto bad;
    }

  /* Do RPC to umountd. */

  strncpy(request.mountd.umount.rpath, rpc->rc_path, 92);
  request.mountd.umount.len =  txdr_unsigned(sizeof(request.mountd.umount.rpath));

  error = rpcclnt_request(rpc, RPCMNT_UMOUNT, RPCPROG_MNT, RPCMNT_VER1,
                          (FAR void *)&request.mountd, sizeof(struct call_args_umount),
                          (FAR void *)&response.mdata, sizeof(struct rpc_reply_umount));
  if (error != 0)
    {
      fdbg("ERROR: rpcclnt_request failed: %d\n", error);
      goto bad;
    }

  return OK;

bad:
  rpcclnt_disconnect(rpc);
  return error;
}
Beispiel #11
0
int rpcclnt_connect(struct rpcclnt *rpc)
{
  struct socket *so;
  int error;
  struct sockaddr *saddr;
  struct sockaddr_in sin;
  struct sockaddr_in *sa;

  union
  {
    struct rpc_call_pmap  sdata;
    struct rpc_call_mount mountd;
  } request;

  union
  {
    struct rpc_reply_pmap  rdata;
    struct rpc_reply_mount mdata;
  } response;

  struct timeval tv;
  uint16_t tport;
  int errval;

  fvdbg("Connecting\n");

  /* Create the socket */

  saddr = rpc->rc_name;

  /* Create an instance of the socket state structure */

  so = (struct socket *)kmm_zalloc(sizeof(struct socket));
  if (!so)
    {
      fdbg("ERROR: Failed to allocate socket structure\n");
      return ENOMEM;
    }

  error = psock_socket(saddr->sa_family, rpc->rc_sotype, IPPROTO_UDP, so);
  if (error < 0)
    {
      errval = get_errno();
      fdbg("ERROR: psock_socket failed: %d", errval);
      return error;
    }

  so->s_crefs     = 1;
  rpc->rc_so      = so;

  /* Always set receive timeout to detect server crash and reconnect.
   * Otherwise, we can get stuck in psock_receive forever.
   */

  tv.tv_sec  = 1;
  tv.tv_usec = 0;

  error = psock_setsockopt(rpc->rc_so, SOL_SOCKET, SO_RCVTIMEO,
                          (const void *)&tv, sizeof(tv));
  if (error < 0)
    {
      errval = get_errno();
      fdbg("ERROR: psock_setsockopt failed: %d\n", errval);
      goto bad;
    }

  /* Some servers require that the client port be a reserved port
   * number. We always allocate a reserved port, as this prevents
   * filehandle disclosure through UDP port capture.
   */

  sin.sin_family      = AF_INET;
  sin.sin_addr.s_addr = INADDR_ANY;
  tport               = 1024;

  errval = 0;
  do
    {
      tport--;
      sin.sin_port = htons(tport);
      error = psock_bind(rpc->rc_so, (struct sockaddr *)&sin, sizeof(sin));
      if (error < 0)
        {
          errval = get_errno();
          fdbg("ERROR: psock_bind failed: %d\n", errval);
        }
    }
  while (errval == EADDRINUSE && tport > 1024 / 2);

  if (error)
    {
      fdbg("ERROR: psock_bind failed: %d\n", errval);
      goto bad;
    }

  /* Protocols that do not require connections may be optionally left
   * unconnected for servers that reply from a port other than
   * NFS_PORT.
   */

  error = psock_connect(rpc->rc_so, saddr, sizeof(*saddr));
  if (error < 0)
    {
      errval = get_errno();
      fdbg("ERROR: psock_connect to PMAP port failed: %d", errval);
      goto bad;
    }

  /* Do the RPC to get a dynamic bounding with the server using ppmap.
   * Get port number for MOUNTD.
   */

  request.sdata.pmap.prog = txdr_unsigned(RPCPROG_MNT);
  request.sdata.pmap.vers = txdr_unsigned(RPCMNT_VER1);
  request.sdata.pmap.proc = txdr_unsigned(IPPROTO_UDP);
  request.sdata.pmap.port = 0;

  error = rpcclnt_request(rpc, PMAPPROC_GETPORT, PMAPPROG, PMAPVERS,
                          (FAR void *)&request.sdata, sizeof(struct call_args_pmap),
                          (FAR void *)&response.rdata, sizeof(struct rpc_reply_pmap));
  if (error != 0)
    {
      fdbg("ERROR: rpcclnt_request failed: %d\n", error);
      goto bad;
    }

  sa = (FAR struct sockaddr_in *)saddr;
  sa->sin_port = htons(fxdr_unsigned(uint32_t, response.rdata.pmap.port));

  error = psock_connect(rpc->rc_so, saddr, sizeof(*saddr));
  if (error < 0)
    {
      errval = get_errno();
      fdbg("ERROR: psock_connect MOUNTD port failed: %d\n", errval);
      goto bad;
    }

  /* Do RPC to mountd. */

  strncpy(request.mountd.mount.rpath, rpc->rc_path, 90);
  request.mountd.mount.len =  txdr_unsigned(sizeof(request.mountd.mount.rpath));

  error = rpcclnt_request(rpc, RPCMNT_MOUNT, RPCPROG_MNT, RPCMNT_VER1,
                          (FAR void *)&request.mountd, sizeof(struct call_args_mount),
                          (FAR void *)&response.mdata, sizeof(struct rpc_reply_mount));
  if (error != 0)
    {
      fdbg("ERROR: rpcclnt_request failed: %d\n", error);
      goto bad;
    }

  error = fxdr_unsigned(uint32_t, response.mdata.mount.status);
  if (error != 0)
    {
      fdbg("ERROR: Bad mount status: %d\n", error);
      goto bad;
    }

  memcpy(&rpc->rc_fh, &response.mdata.mount.fhandle, sizeof(nfsfh_t));

  /* Do the RPC to get a dynamic bounding with the server using PMAP.
   * NFS port in the socket.
   */

  sa->sin_port = htons(PMAPPORT);

  error = psock_connect(rpc->rc_so, saddr, sizeof(*saddr));
  if (error < 0)
    {
      errval = get_errno();
      fdbg("ERROR: psock_connect PMAP port failed: %d\n", errval);
      goto bad;
    }

  request.sdata.pmap.prog = txdr_unsigned(NFS_PROG);
  request.sdata.pmap.vers = txdr_unsigned(NFS_VER3);
  request.sdata.pmap.proc = txdr_unsigned(IPPROTO_UDP);
  request.sdata.pmap.port = 0;

  error = rpcclnt_request(rpc, PMAPPROC_GETPORT, PMAPPROG, PMAPVERS,
                          (FAR void *)&request.sdata, sizeof(struct call_args_pmap),
                          (FAR void *)&response.rdata, sizeof(struct rpc_reply_pmap));
  if (error != 0)
    {
      fdbg("ERROR: rpcclnt_request failed: %d\n", error);
      goto bad;
    }

  sa->sin_port = htons(fxdr_unsigned(uint32_t, response.rdata.pmap.port));

  error = psock_connect(rpc->rc_so, saddr, sizeof(*saddr));
  if (error)
    {
      fdbg("psock_connect NFS port returns %d\n", error);
      goto bad;
    }

  return OK;

bad:
  rpcclnt_disconnect(rpc);
  return error;
}
Beispiel #12
0
/*
 * nfs statfs call
 */
static int
nfs_statfs(struct mount *mp, struct statfs *sbp)
{
	struct vnode *vp;
	struct thread *td;
	struct nfs_statfs *sfp;
	caddr_t bpos, dpos;
	struct nfsmount *nmp = VFSTONFS(mp);
	int error = 0, v3 = (nmp->nm_flag & NFSMNT_NFSV3), retattr;
	struct mbuf *mreq, *mrep, *md, *mb;
	struct nfsnode *np;
	u_quad_t tquad;

	td = curthread;
#ifndef nolint
	sfp = NULL;
#endif
	error = vfs_busy(mp, MBF_NOWAIT);
	if (error)
		return (error);
	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE);
	if (error) {
		vfs_unbusy(mp);
		return (error);
	}
	vp = NFSTOV(np);
	mtx_lock(&nmp->nm_mtx);
	if (v3 && (nmp->nm_state & NFSSTA_GOTFSINFO) == 0) {
		mtx_unlock(&nmp->nm_mtx);		
		(void)nfs_fsinfo(nmp, vp, td->td_ucred, td);
	} else
		mtx_unlock(&nmp->nm_mtx);
	nfsstats.rpccnt[NFSPROC_FSSTAT]++;
	mreq = nfsm_reqhead(vp, NFSPROC_FSSTAT, NFSX_FH(v3));
	mb = mreq;
	bpos = mtod(mb, caddr_t);
	nfsm_fhtom(vp, v3);
	nfsm_request(vp, NFSPROC_FSSTAT, td, td->td_ucred);
	if (v3)
		nfsm_postop_attr(vp, retattr);
	if (error) {
		if (mrep != NULL)
			m_freem(mrep);
		goto nfsmout;
	}
	sfp = nfsm_dissect(struct nfs_statfs *, NFSX_STATFS(v3));
	mtx_lock(&nmp->nm_mtx);
	sbp->f_iosize = nfs_iosize(nmp);
	mtx_unlock(&nmp->nm_mtx);
	if (v3) {
		sbp->f_bsize = NFS_FABLKSIZE;
		tquad = fxdr_hyper(&sfp->sf_tbytes);
		sbp->f_blocks = tquad / NFS_FABLKSIZE;
		tquad = fxdr_hyper(&sfp->sf_fbytes);
		sbp->f_bfree = tquad / NFS_FABLKSIZE;
		tquad = fxdr_hyper(&sfp->sf_abytes);
		sbp->f_bavail = tquad / NFS_FABLKSIZE;
		sbp->f_files = (fxdr_unsigned(int32_t,
		    sfp->sf_tfiles.nfsuquad[1]) & 0x7fffffff);
		sbp->f_ffree = (fxdr_unsigned(int32_t,
		    sfp->sf_ffiles.nfsuquad[1]) & 0x7fffffff);
	} else {
		sbp->f_bsize = fxdr_unsigned(int32_t, sfp->sf_bsize);
		sbp->f_blocks = fxdr_unsigned(int32_t, sfp->sf_blocks);
		sbp->f_bfree = fxdr_unsigned(int32_t, sfp->sf_bfree);
		sbp->f_bavail = fxdr_unsigned(int32_t, sfp->sf_bavail);
		sbp->f_files = 0;
		sbp->f_ffree = 0;
	}
	m_freem(mrep);
nfsmout:
	vput(vp);
	vfs_unbusy(mp);
	return (error);
}
Beispiel #13
0
/*
 * nfs version 3 fsinfo rpc call
 */
int
nfs_fsinfo(struct nfsmount *nmp, struct vnode *vp, kauth_cred_t cred, struct lwp *l)
{
	struct nfsv3_fsinfo *fsp;
	char *cp;
	int32_t t1, t2;
	u_int32_t *tl, pref, xmax;
	char *bpos, *dpos, *cp2;
	int error = 0, retattr;
	struct mbuf *mreq, *mrep, *md, *mb;
	u_int64_t maxfsize;
	struct nfsnode *np = VTONFS(vp);

	nfsstats.rpccnt[NFSPROC_FSINFO]++;
	nfsm_reqhead(np, NFSPROC_FSINFO, NFSX_FH(1));
	nfsm_fhtom(np, 1);
	nfsm_request(np, NFSPROC_FSINFO, l, cred);
	nfsm_postop_attr(vp, retattr, 0);
	if (!error) {
		nfsm_dissect(fsp, struct nfsv3_fsinfo *, NFSX_V3FSINFO);
		pref = fxdr_unsigned(u_int32_t, fsp->fs_wtpref);
		if ((nmp->nm_flag & NFSMNT_WSIZE) == 0 &&
		    pref < nmp->nm_wsize && pref >= NFS_FABLKSIZE)
			nmp->nm_wsize = (pref + NFS_FABLKSIZE - 1) &
				~(NFS_FABLKSIZE - 1);
		xmax = fxdr_unsigned(u_int32_t, fsp->fs_wtmax);
		if (xmax < nmp->nm_wsize && xmax > 0) {
			nmp->nm_wsize = xmax & ~(NFS_FABLKSIZE - 1);
			if (nmp->nm_wsize == 0)
				nmp->nm_wsize = xmax;
		}
		pref = fxdr_unsigned(u_int32_t, fsp->fs_rtpref);
		if ((nmp->nm_flag & NFSMNT_RSIZE) == 0 &&
		    pref < nmp->nm_rsize && pref >= NFS_FABLKSIZE)
			nmp->nm_rsize = (pref + NFS_FABLKSIZE - 1) &
				~(NFS_FABLKSIZE - 1);
		xmax = fxdr_unsigned(u_int32_t, fsp->fs_rtmax);
		if (xmax < nmp->nm_rsize && xmax > 0) {
			nmp->nm_rsize = xmax & ~(NFS_FABLKSIZE - 1);
			if (nmp->nm_rsize == 0)
				nmp->nm_rsize = xmax;
		}
		pref = fxdr_unsigned(u_int32_t, fsp->fs_dtpref);
		if (pref < nmp->nm_readdirsize && pref >= NFS_DIRFRAGSIZ)
			nmp->nm_readdirsize = (pref + NFS_DIRFRAGSIZ - 1) &
				~(NFS_DIRFRAGSIZ - 1);
		if (xmax < nmp->nm_readdirsize && xmax > 0) {
			nmp->nm_readdirsize = xmax & ~(NFS_DIRFRAGSIZ - 1);
			if (nmp->nm_readdirsize == 0)
				nmp->nm_readdirsize = xmax;
		}
		/* XXX */
		nmp->nm_maxfilesize = (u_int64_t)0x80000000 * DEV_BSIZE - 1;
		maxfsize = fxdr_hyper(&fsp->fs_maxfilesize);
		if (maxfsize > 0 && maxfsize < nmp->nm_maxfilesize)
			nmp->nm_maxfilesize = maxfsize;
		nmp->nm_mountp->mnt_fs_bshift =
		    ffs(MIN(nmp->nm_rsize, nmp->nm_wsize)) - 1;
		nmp->nm_iflag |= NFSMNT_GOTFSINFO;
	}
	nfsm_reqdone;
	return (error);
}
Beispiel #14
0
/*
 * nfs statvfs call
 */
int
nfs_statvfs(struct mount *mp, struct statvfs *sbp)
{
	struct lwp *l = curlwp;
	struct vnode *vp;
	struct nfs_statfs *sfp;
	char *cp;
	u_int32_t *tl;
	int32_t t1, t2;
	char *bpos, *dpos, *cp2;
	struct nfsmount *nmp = VFSTONFS(mp);
	int error = 0, retattr;
#ifdef NFS_V2_ONLY
	const int v3 = 0;
#else
	int v3 = (nmp->nm_flag & NFSMNT_NFSV3);
#endif
	struct mbuf *mreq, *mrep = NULL, *md, *mb;
	kauth_cred_t cred;
	u_quad_t tquad;
	struct nfsnode *np;

#ifndef nolint
	sfp = (struct nfs_statfs *)0;
#endif
	vp = nmp->nm_vnode;
	np = VTONFS(vp);
	cred = kauth_cred_alloc();
#ifndef NFS_V2_ONLY
	if (v3 && (nmp->nm_iflag & NFSMNT_GOTFSINFO) == 0)
		(void)nfs_fsinfo(nmp, vp, cred, l);
#endif
	nfsstats.rpccnt[NFSPROC_FSSTAT]++;
	nfsm_reqhead(np, NFSPROC_FSSTAT, NFSX_FH(v3));
	nfsm_fhtom(np, v3);
	nfsm_request(np, NFSPROC_FSSTAT, l, cred);
	if (v3)
		nfsm_postop_attr(vp, retattr, 0);
	if (error) {
		if (mrep != NULL) {
			if (mrep->m_next != NULL)
				printf("nfs_vfsops: nfs_statvfs would lose buffers\n");
			m_freem(mrep);
		}
		goto nfsmout;
	}
	nfsm_dissect(sfp, struct nfs_statfs *, NFSX_STATFS(v3));
	sbp->f_flag = nmp->nm_flag;
	sbp->f_iosize = min(nmp->nm_rsize, nmp->nm_wsize);
	if (v3) {
		sbp->f_frsize = sbp->f_bsize = NFS_FABLKSIZE;
		tquad = fxdr_hyper(&sfp->sf_tbytes);
		sbp->f_blocks = ((quad_t)tquad / (quad_t)NFS_FABLKSIZE);
		tquad = fxdr_hyper(&sfp->sf_fbytes);
		sbp->f_bfree = ((quad_t)tquad / (quad_t)NFS_FABLKSIZE);
		tquad = fxdr_hyper(&sfp->sf_abytes);
		tquad = ((quad_t)tquad / (quad_t)NFS_FABLKSIZE);
		sbp->f_bresvd = sbp->f_bfree - tquad;
		sbp->f_bavail = tquad;
		/* Handle older NFS servers returning negative values */
		if ((quad_t)sbp->f_bavail < 0)
			sbp->f_bavail = 0;
		tquad = fxdr_hyper(&sfp->sf_tfiles);
		sbp->f_files = tquad;
		tquad = fxdr_hyper(&sfp->sf_ffiles);
		sbp->f_ffree = tquad;
		sbp->f_favail = tquad;
		sbp->f_fresvd = 0;
		sbp->f_namemax = NFS_MAXNAMLEN;
	} else {
		sbp->f_bsize = NFS_FABLKSIZE;
		sbp->f_frsize = fxdr_unsigned(int32_t, sfp->sf_bsize);
		sbp->f_blocks = fxdr_unsigned(int32_t, sfp->sf_blocks);
		sbp->f_bfree = fxdr_unsigned(int32_t, sfp->sf_bfree);
		sbp->f_bavail = fxdr_unsigned(int32_t, sfp->sf_bavail);
		sbp->f_fresvd = 0;
		sbp->f_files = 0;
		sbp->f_ffree = 0;
		sbp->f_favail = 0;
		sbp->f_fresvd = 0;
		sbp->f_namemax = NFS_MAXNAMLEN;
	}
	copy_statvfs_info(sbp, mp);
	nfsm_reqdone;
	kauth_cred_free(cred);
	return (error);
}
Beispiel #15
0
/*
 * RPC: mountd/mount
 * Given a server pathname, get an NFS file handle.
 * Also, sets sin->sin_port to the NFS service port.
 *
 * mdsin   mountd server address
 */
static int
md_mount(struct sockaddr_in *mdsin, char *path,
	 struct nfs_args *argp, struct lwp *lwp)
{
	/* The RPC structures */
	struct rdata {
		u_int32_t errno;
		union {
			u_int8_t  v2fh[NFSX_V2FH];
			struct {
				u_int32_t fhlen;
				u_int8_t  fh[1];
			} v3fh;
		} fh;
	} *rdata;
	struct mbuf *m;
	u_int8_t *fh;
	int minlen, error;
	int mntver;

	mntver = (argp->flags & NFSMNT_NFSV3) ? 3 : 2;
	do {
		/*
		 * Get port number for MOUNTD.
		 */
		error = krpc_portmap(mdsin, RPCPROG_MNT, mntver,
		                    IPPROTO_UDP, &mdsin->sin_port, lwp);
		if (error)
			continue;

		/* This mbuf is consumed by krpc_call. */
		m = xdr_string_encode(path, strlen(path));
		if (m == NULL)
			return ENOMEM;

		/* Do RPC to mountd. */
		error = krpc_call(mdsin, RPCPROG_MNT, mntver,
		                  RPCMNT_MOUNT, &m, NULL, lwp);
		if (error != EPROGMISMATCH)
			break;
		/* Try lower version of mountd. */
	} while (--mntver >= 1);
	if (error) {
		printf("nfs_boot: mountd error=%d\n", error);
		return error;
	}
	if (mntver != 3)
		argp->flags &= ~NFSMNT_NFSV3;

	/* The reply might have only the errno. */
	if (m->m_len < 4)
		goto bad;
	/* Have at least errno, so check that. */
	rdata = mtod(m, struct rdata *);
	error = fxdr_unsigned(u_int32_t, rdata->errno);
	if (error)
		goto out;

	/* Have errno==0, so the fh must be there. */
	if (mntver == 3) {
		argp->fhsize   = fxdr_unsigned(u_int32_t, rdata->fh.v3fh.fhlen);
		if (argp->fhsize > NFSX_V3FHMAX)
			goto bad;
		minlen = 2 * sizeof(u_int32_t) + argp->fhsize;
	} else {
		argp->fhsize   = NFSX_V2FH;
		minlen = sizeof(u_int32_t) + argp->fhsize;
	}

	if (m->m_len < minlen) {
		m = m_pullup(m, minlen);
		if (m == NULL)
			return(EBADRPC);
		rdata = mtod(m, struct rdata *);
	}
Beispiel #16
0
/*
 * Do a remote procedure call (RPC) and wait for its reply.
 * If from_p is non-null, then we are doing broadcast, and
 * the address from whence the response came is saved there.
 * data:	input/output
 * from_p:	output
 */
int
krpc_call(struct sockaddr_in *sa, u_int prog, u_int vers, u_int func,
    struct mbuf **data, struct mbuf **from_p, int retries)
{
	struct socket *so;
	struct sockaddr_in *sin;
	struct mbuf *m, *nam, *mhead, *from, *mopt;
	struct rpc_call *call;
	struct rpc_reply *reply;
	struct uio auio;
	int error, rcvflg, timo, secs, len;
	static u_int32_t xid = 0;
	char addr[INET_ADDRSTRLEN];
	int *ip;
	struct timeval tv;

	/*
	 * Validate address family.
	 * Sorry, this is INET specific...
	 */
	if (sa->sin_family != AF_INET)
		return (EAFNOSUPPORT);

	/* Free at end if not null. */
	nam = mhead = NULL;
	from = NULL;

	/*
	 * Create socket and set its receive timeout.
	 */
	if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)))
		goto out;

	m = m_get(M_WAIT, MT_SOOPTS);
	tv.tv_sec = 1;
	tv.tv_usec = 0;
	memcpy(mtod(m, struct timeval *), &tv, sizeof tv);
	m->m_len = sizeof(tv);
	if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m)))
		goto out;

	/*
	 * Enable broadcast if necessary.
	 */
	if (from_p) {
		int32_t *on;
		m = m_get(M_WAIT, MT_SOOPTS);
		on = mtod(m, int32_t *);
		m->m_len = sizeof(*on);
		*on = 1;
		if ((error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m)))
			goto out;
	}

	/*
	 * Bind the local endpoint to a reserved port,
	 * because some NFS servers refuse requests from
	 * non-reserved (non-privileged) ports.
	 */
	MGET(mopt, M_WAIT, MT_SOOPTS);
	mopt->m_len = sizeof(int);
	ip = mtod(mopt, int *);
	*ip = IP_PORTRANGE_LOW;
	error = sosetopt(so, IPPROTO_IP, IP_PORTRANGE, mopt);
	if (error)
		goto out;

	MGET(m, M_WAIT, MT_SONAME);
	sin = mtod(m, struct sockaddr_in *);
	sin->sin_len = m->m_len = sizeof (struct sockaddr_in);
	sin->sin_family = AF_INET;
	sin->sin_addr.s_addr = INADDR_ANY;
	sin->sin_port = htons(0);
	error = sobind(so, m, &proc0);
	m_freem(m);
	if (error) {
		printf("bind failed\n");
		goto out;
	}

	MGET(mopt, M_WAIT, MT_SOOPTS);
	mopt->m_len = sizeof(int);
	ip = mtod(mopt, int *);
	*ip = IP_PORTRANGE_DEFAULT;
	error = sosetopt(so, IPPROTO_IP, IP_PORTRANGE, mopt);
	if (error)
		goto out;

	/*
	 * Setup socket address for the server.
	 */
	nam = m_get(M_WAIT, MT_SONAME);
	sin = mtod(nam, struct sockaddr_in *);
	bcopy((caddr_t)sa, (caddr_t)sin, (nam->m_len = sa->sin_len));

	/*
	 * Prepend RPC message header.
	 */
	mhead = m_gethdr(M_WAIT, MT_DATA);
	mhead->m_next = *data;
	call = mtod(mhead, struct rpc_call *);
	mhead->m_len = sizeof(*call);
	bzero((caddr_t)call, sizeof(*call));
	/* rpc_call part */
	xid = krpc_get_xid();
	call->rp_xid = txdr_unsigned(xid);
	/* call->rp_direction = 0; */
	call->rp_rpcvers = txdr_unsigned(2);
	call->rp_prog = txdr_unsigned(prog);
	call->rp_vers = txdr_unsigned(vers);
	call->rp_proc = txdr_unsigned(func);
	/* rpc_auth part (auth_unix as root) */
	call->rpc_auth.authtype = txdr_unsigned(RPCAUTH_UNIX);
	call->rpc_auth.authlen  = txdr_unsigned(sizeof(struct auth_unix));
	/* rpc_verf part (auth_null) */
	call->rpc_verf.authtype = 0;
	call->rpc_verf.authlen  = 0;

	/*
	 * Setup packet header
	 */
	len = 0;
	m = mhead;
	while (m) {
		len += m->m_len;
		m = m->m_next;
	}
	mhead->m_pkthdr.len = len;
	mhead->m_pkthdr.rcvif = NULL;

	/*
	 * Send it, repeatedly, until a reply is received,
	 * but delay each re-send by an increasing amount.
	 * If the delay hits the maximum, start complaining.
	 */
	for (timo = 0; retries; retries--) {
		/* Send RPC request (or re-send). */
		m = m_copym(mhead, 0, M_COPYALL, M_WAIT);
		if (m == NULL) {
			error = ENOBUFS;
			goto out;
		}
		error = sosend(so, nam, NULL, m, NULL, 0);
		if (error) {
			printf("krpc_call: sosend: %d\n", error);
			goto out;
		}
		m = NULL;

		/* Determine new timeout. */
		if (timo < MAX_RESEND_DELAY)
			timo++;
		else
			printf("RPC timeout for server %s (0x%x) prog %u\n",
			    inet_ntop(AF_INET, &sin->sin_addr,
				addr, sizeof(addr)),
			    ntohl(sin->sin_addr.s_addr), prog);

		/*
		 * Wait for up to timo seconds for a reply.
		 * The socket receive timeout was set to 1 second.
		 */
		secs = timo;
		while (secs > 0) {
			if (from) {
				m_freem(from);
				from = NULL;
			}
			if (m) {
				m_freem(m);
				m = NULL;
			}
			auio.uio_resid = len = 1<<16;
			auio.uio_procp = NULL;
			rcvflg = 0;
			error = soreceive(so, &from, &auio, &m, NULL, &rcvflg,
			    0);
			if (error == EWOULDBLOCK) {
				secs--;
				continue;
			}
			if (error)
				goto out;
			len -= auio.uio_resid;

			/* Does the reply contain at least a header? */
			if (len < MIN_REPLY_HDR)
				continue;
			if (m->m_len < MIN_REPLY_HDR)
				continue;
			reply = mtod(m, struct rpc_reply *);

			/* Is it the right reply? */
			if (reply->rp_direction != txdr_unsigned(RPC_REPLY))
				continue;

			if (reply->rp_xid != txdr_unsigned(xid))
				continue;

			/* Was RPC accepted? (authorization OK) */
			if (reply->rp_astatus != 0) {
				error = fxdr_unsigned(u_int32_t, reply->rp_errno);
				printf("rpc denied, error=%d\n", error);
				continue;
			}

			/* Did the call succeed? */
			if (reply->rp_status != 0) {
				error = fxdr_unsigned(u_int32_t, reply->rp_status);
				printf("rpc denied, status=%d\n", error);
				continue;
			}

			goto gotreply;	/* break two levels */

		} /* while secs */
	} /* forever send/receive */

	error = ETIMEDOUT;
	goto out;

 gotreply:

	/*
	 * Get RPC reply header into first mbuf,
	 * get its length, then strip it off.
	 */
	len = sizeof(*reply);
	if (m->m_len < len) {
		m = m_pullup(m, len);
		if (m == NULL) {
			error = ENOBUFS;
			goto out;
		}
	}
	reply = mtod(m, struct rpc_reply *);
	if (reply->rp_auth.authtype != 0) {
		len += fxdr_unsigned(u_int32_t, reply->rp_auth.authlen);
		len = (len + 3) & ~3; /* XXX? */
	}
	m_adj(m, len);

	/* result */
	*data = m;
	if (from_p && error == 0) {
		*from_p = from;
		from = NULL;
	}

 out:
	if (nam) m_freem(nam);
	if (mhead) m_freem(mhead);
	if (from) m_freem(from);
	soclose(so);
	return error;
}