/* * 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); }
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 {
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; }
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); }
/* * 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); }
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); }
/* * 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); }
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; }
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; }
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; }
/* * 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); }
/* * 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); }
/* * 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); }
/* * 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 *); }
/* * 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; }