/* * Encode RENAME arguments */ static int nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args) { p = xdr_encode_fhandle(p, args->fromfh); p = xdr_encode_array(p, args->fromname, args->fromlen); p = xdr_encode_fhandle(p, args->tofh); p = xdr_encode_array(p, args->toname, args->tolen); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; }
/* * Encode SYMLINK arguments */ static int nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args) { p = xdr_encode_fhandle(p, args->fromfh); p = xdr_encode_array(p, args->fromname, args->fromlen); p = xdr_encode_array(p, args->topath, args->tolen); p = xdr_encode_sattr(p, args->sattr); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; }
/* * Encode RENAME arguments */ static int nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args) { p = xdr_encode_fhandle(p, args->old_dir); p = xdr_encode_array(p, args->old_name->name, args->old_name->len); p = xdr_encode_fhandle(p, args->new_dir); p = xdr_encode_array(p, args->new_name->name, args->new_name->len); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; }
/* * Encode SETATTR arguments */ static int nfs3_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs3_sattrargs *args) { p = xdr_encode_fhandle(p, args->fh); p = xdr_encode_sattr(p, args->sattr); *p++ = htonl(args->guard); if (args->guard) p = xdr_encode_time3(p, &args->guardtime); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; }
static int nlm4clt_encode_unlockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp) { struct nlm_lock *lock = &argp->lock; if (!(p = nlm4_encode_cookie(p, &argp->cookie))) return -EIO; if (!(p = nlm4_encode_lock(p, lock))) return -EIO; req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; }
/* * Encode SYMLINK arguments */ static int nfs3_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_symlinkargs *args) { p = xdr_encode_fhandle(p, args->fromfh); p = xdr_encode_array(p, args->fromname, args->fromlen); p = xdr_encode_sattr(p, args->sattr); *p++ = htonl(args->pathlen); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); /* Copy the page */ xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen); return 0; }
static int xdr_encode_mon(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp) { p = xdr_encode_common(rqstp, p, argp); if (IS_ERR(p)) return PTR_ERR(p); *p++ = argp->addr; *p++ = argp->vers; *p++ = argp->proto; *p++ = 0; rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p); return 0; }
static int nlm4clt_encode_testargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp) { struct nlm_lock *lock = &argp->lock; if (!(p = nlm4_encode_cookie(p, &argp->cookie))) return -EIO; *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; if (!(p = nlm4_encode_lock(p, lock))) return -EIO; req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; }
/* * Encode READLINK args */ static int nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args) { struct rpc_auth *auth = req->rq_task->tk_auth; unsigned int replen; p = xdr_encode_fhandle(p, args->fh); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); /* Inline the page array */ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2; xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen); return 0; }
/* * Encode MKNOD arguments */ static int nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args) { p = xdr_encode_fhandle(p, args->fh); p = xdr_encode_array(p, args->name, args->len); *p++ = htonl(args->type); p = xdr_encode_sattr(p, args->sattr); if (args->type == NF3CHR || args->type == NF3BLK) { *p++ = htonl(MAJOR(args->rdev)); *p++ = htonl(MINOR(args->rdev)); } req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; }
/* * Encode CREATE arguments */ static int nfs3_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs3_createargs *args) { p = xdr_encode_fhandle(p, args->fh); p = xdr_encode_array(p, args->name, args->len); *p++ = htonl(args->createmode); if (args->createmode == NFS3_CREATE_EXCLUSIVE) { *p++ = args->verifier[0]; *p++ = args->verifier[1]; } else p = xdr_encode_sattr(p, args->sattr); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; }
/* * Encode arguments to readdir call */ static int nfs_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs_readdirargs *args) { struct rpc_auth *auth = req->rq_cred->cr_auth; unsigned int replen; u32 count = args->count; p = xdr_encode_fhandle(p, args->fh); *p++ = htonl(args->cookie); *p++ = htonl(count); /* see above */ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); /* Inline the page array */ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2; xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count); return 0; }
/* * Write arguments. Splice the buffer to be written into the iovec. */ static int nfs3_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args) { struct xdr_buf *sndbuf = &req->rq_snd_buf; u32 count = args->count; p = xdr_encode_fhandle(p, args->fh); p = xdr_encode_hyper(p, args->offset); *p++ = htonl(count); *p++ = htonl(args->stable); *p++ = htonl(count); sndbuf->len = xdr_adjust_iovec(sndbuf->head, p); /* Copy the page array */ xdr_encode_pages(sndbuf, args->pages, args->pgbase, count); return 0; }
/* * Write arguments. Splice the buffer to be written into the iovec. */ static int nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args) { unsigned int nr; u32 count = args->count; p = xdr_encode_fhandle(p, args->fh); *p++ = htonl(args->offset); *p++ = htonl(args->offset); *p++ = htonl(count); *p++ = htonl(count); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); /* Get the number of buffers in the send iovec */ nr = args->nriov; if (nr+2 > MAX_IOVEC) { printk(KERN_ERR "NFS: Bad number of iov's in xdr_writeargs " "(nr %d max %d)\n", nr, MAX_IOVEC); return -EINVAL; } /* Copy the iovec */ memcpy(req->rq_svec + 1, args->iov, nr * sizeof(struct iovec)); #ifdef NFS_PAD_WRITES /* * Some old servers require that the message length * be a multiple of 4, so we pad it here if needed. */ if (count & 3) { struct iovec *iov = req->rq_svec + nr + 1; int pad = 4 - (count & 3); iov->iov_base = (void *) "\0\0\0"; iov->iov_len = pad; count += pad; nr++; } #endif req->rq_slen += count; req->rq_snr += nr; return 0; }
/* * Arguments to a READ call. Since we read data directly into the page * cache, we also set up the reply iovec here so that iov[1] points * exactly to the page we want to fetch. */ static int nfs3_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args) { struct rpc_auth *auth = req->rq_task->tk_auth; unsigned int replen; u32 count = args->count; p = xdr_encode_fhandle(p, args->fh); p = xdr_encode_hyper(p, args->offset); *p++ = htonl(count); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); /* Inline the page array */ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2; xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, count); return 0; }
static int nfs_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args) { struct xdr_buf *sndbuf = &req->rq_snd_buf; u32 offset = (u32)args->offset; u32 count = args->count; p = xdr_encode_fhandle(p, args->fh); *p++ = htonl(offset); *p++ = htonl(offset); *p++ = htonl(count); *p++ = htonl(count); sndbuf->len = xdr_adjust_iovec(sndbuf->head, p); /* Copy the page array */ xdr_encode_pages(sndbuf, args->pages, args->pgbase, count); sndbuf->flags |= XDRBUF_WRITE; return 0; }
/* * Encode READLINK args */ static int nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args) { struct rpc_task *task = req->rq_task; struct rpc_auth *auth = task->tk_auth; int buflen, replen; p = xdr_encode_fhandle(p, args->fh); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2; buflen = req->rq_rvec[0].iov_len; req->rq_rvec[0].iov_len = replen; req->rq_rvec[1].iov_base = args->buffer; req->rq_rvec[1].iov_len = args->bufsiz; req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen; req->rq_rvec[2].iov_len = buflen - replen; req->rq_rlen = buflen + args->bufsiz; req->rq_rnr += 2; return 0; }
/* * Arguments to a READ call. Since we read data directly into the page * cache, we also set up the reply iovec here so that iov[1] points * exactly to the page we want to fetch. */ static int nfs_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args) { struct rpc_auth *auth = req->rq_cred->cr_auth; unsigned int replen; u32 offset = (u32)args->offset; u32 count = args->count; p = xdr_encode_fhandle(p, args->fh); *p++ = htonl(offset); *p++ = htonl(count); *p++ = htonl(count); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); /* Inline the page array */ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2; xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, count); req->rq_rcv_buf.flags |= XDRBUF_READ; return 0; }
static int xdr_encode_mon(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp) { char buffer[20]; u32 addr = ntohl(argp->addr); dprintk("nsm: xdr_encode_mon(%08x, %d, %d, %d)\n", htonl(argp->addr), htonl(argp->proc), htonl(argp->vers), htonl(argp->proc)); /* * Use the dotted-quad IP address of the remote host as * identifier. Linux statd always looks up the canonical * hostname first for whatever remote hostname it receives, * so this works alright. */ sprintf(buffer, "%d.%d.%d.%d", (addr>>24) & 0xff, (addr>>16) & 0xff, (addr>>8) & 0xff, (addr) & 0xff); if (!(p = xdr_encode_string(p, buffer)) || !(p = xdr_encode_string(p, system_utsname.nodename))) return -EIO; *p++ = htonl(argp->prog); *p++ = htonl(argp->vers); *p++ = htonl(argp->proc); /* This is the private part. Needed only for SM_MON call */ if (rqstp->rq_task->tk_proc == SM_MON) { *p++ = argp->addr; *p++ = 0; *p++ = 0; *p++ = 0; } rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p); return 0; }
/* * Create NSM client for the local host */ static struct rpc_clnt * nsm_create(void) { struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK), .sin_port = 0, }; struct rpc_create_args args = { .protocol = IPPROTO_UDP, .address = (struct sockaddr *)&sin, .addrsize = sizeof(sin), .servername = "localhost", .program = &nsm_program, .version = SM_VERSION, .authflavor = RPC_AUTH_NULL, .flags = (RPC_CLNT_CREATE_ONESHOT), }; return rpc_create(&args); } /* * XDR functions for NSM. */ static __be32 * xdr_encode_common(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp) { char buffer[20], *name; /* * Use the dotted-quad IP address of the remote host as * identifier. Linux statd always looks up the canonical * hostname first for whatever remote hostname it receives, * so this works alright. */ if (nsm_use_hostnames) { name = argp->mon_name; } else { sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(argp->addr)); name = buffer; } if (!(p = xdr_encode_string(p, name)) || !(p = xdr_encode_string(p, utsname()->nodename))) return ERR_PTR(-EIO); *p++ = htonl(argp->prog); *p++ = htonl(argp->vers); *p++ = htonl(argp->proc); return p; } static int xdr_encode_mon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp) { p = xdr_encode_common(rqstp, p, argp); if (IS_ERR(p)) return PTR_ERR(p); /* Surprise - there may even be room for an IPv6 address now */ *p++ = argp->addr; *p++ = 0; *p++ = 0; *p++ = 0; rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p); return 0; }
/* * Encode void argument */ static int nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy) { req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; }
/* * Obtain the port for a given RPC service on a given host. This one can * be called for an ongoing RPC request. */ void rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) { struct rpc_portmap *map = clnt->cl_pmap; struct sockaddr_in *sap = &clnt->cl_xprt->addr; struct rpc_message msg = { .rpc_proc = &pmap_procedures[PMAP_GETPORT], .rpc_argp = map, .rpc_resp = &clnt->cl_port, .rpc_cred = NULL }; struct rpc_clnt *pmap_clnt; struct rpc_task *child; dprintk("RPC: %4d rpc_getport(%s, %d, %d, %d)\n", task->tk_pid, clnt->cl_server, map->pm_prog, map->pm_vers, map->pm_prot); /* Autobind on cloned rpc clients is discouraged */ BUG_ON(clnt->cl_parent != clnt); spin_lock(&pmap_lock); if (map->pm_binding) { rpc_sleep_on(&map->pm_bindwait, task, NULL, NULL); spin_unlock(&pmap_lock); return; } map->pm_binding = 1; spin_unlock(&pmap_lock); pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot, 0); if (IS_ERR(pmap_clnt)) { task->tk_status = PTR_ERR(pmap_clnt); goto bailout; } task->tk_status = 0; /* * Note: rpc_new_child will release client after a failure. */ if (!(child = rpc_new_child(pmap_clnt, task))) goto bailout; /* Setup the call info struct */ rpc_call_setup(child, &msg, 0); /* ... and run the child task */ rpc_run_child(task, child, pmap_getport_done); return; bailout: spin_lock(&pmap_lock); map->pm_binding = 0; rpc_wake_up(&map->pm_bindwait); spin_unlock(&pmap_lock); rpc_exit(task, -EIO); } #ifdef CONFIG_ROOT_NFS int rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot) { struct rpc_portmap map = { .pm_prog = prog, .pm_vers = vers, .pm_prot = prot, .pm_port = 0 }; struct rpc_clnt *pmap_clnt; char hostname[32]; int status; dprintk("RPC: rpc_getport_external(%u.%u.%u.%u, %d, %d, %d)\n", NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot); sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr)); pmap_clnt = pmap_create(hostname, sin, prot, 0); if (IS_ERR(pmap_clnt)) return PTR_ERR(pmap_clnt); /* Setup the call info struct */ status = rpc_call(pmap_clnt, PMAP_GETPORT, &map, &map.pm_port, 0); if (status >= 0) { if (map.pm_port != 0) return map.pm_port; status = -EACCES; } return status; } #endif static void pmap_getport_done(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; struct rpc_xprt *xprt = task->tk_xprt; struct rpc_portmap *map = clnt->cl_pmap; dprintk("RPC: %4d pmap_getport_done(status %d, port %d)\n", task->tk_pid, task->tk_status, clnt->cl_port); xprt->ops->set_port(xprt, 0); if (task->tk_status < 0) { /* Make the calling task exit with an error */ task->tk_action = rpc_exit_task; } else if (clnt->cl_port == 0) { /* Program not registered */ rpc_exit(task, -EACCES); } else { xprt->ops->set_port(xprt, clnt->cl_port); clnt->cl_port = htons(clnt->cl_port); } spin_lock(&pmap_lock); map->pm_binding = 0; rpc_wake_up(&map->pm_bindwait); spin_unlock(&pmap_lock); } /* * Set or unset a port registration with the local portmapper. * port == 0 means unregister, port != 0 means register. */ int rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) { struct sockaddr_in sin; struct rpc_portmap map; struct rpc_clnt *pmap_clnt; int error = 0; dprintk("RPC: registering (%d, %d, %d, %d) with portmapper.\n", prog, vers, prot, port); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP, 1); if (IS_ERR(pmap_clnt)) { error = PTR_ERR(pmap_clnt); dprintk("RPC: couldn't create pmap client. Error = %d\n", error); return error; } map.pm_prog = prog; map.pm_vers = vers; map.pm_prot = prot; map.pm_port = port; error = rpc_call(pmap_clnt, port? PMAP_SET : PMAP_UNSET, &map, okay, 0); if (error < 0) { printk(KERN_WARNING "RPC: failed to contact portmap (errno %d).\n", error); } dprintk("RPC: registration status %d/%d\n", error, *okay); /* Client deleted automatically because cl_oneshot == 1 */ return error; } static struct rpc_clnt * pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto, int privileged) { struct rpc_xprt *xprt; struct rpc_clnt *clnt; /* printk("pmap: create xprt\n"); */ xprt = xprt_create_proto(proto, srvaddr, NULL); if (IS_ERR(xprt)) return (struct rpc_clnt *)xprt; xprt->ops->set_port(xprt, RPC_PMAP_PORT); if (!privileged) xprt->resvport = 0; /* printk("pmap: create clnt\n"); */ clnt = rpc_new_client(xprt, hostname, &pmap_program, RPC_PMAP_VERSION, RPC_AUTH_UNIX); if (!IS_ERR(clnt)) { clnt->cl_softrtry = 1; clnt->cl_oneshot = 1; } return clnt; } /* * XDR encode/decode functions for PMAP */ static int xdr_encode_mapping(struct rpc_rqst *req, u32 *p, struct rpc_portmap *map) { dprintk("RPC: xdr_encode_mapping(%d, %d, %d, %d)\n", map->pm_prog, map->pm_vers, map->pm_prot, map->pm_port); *p++ = htonl(map->pm_prog); *p++ = htonl(map->pm_vers); *p++ = htonl(map->pm_prot); *p++ = htonl(map->pm_port); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; } static int xdr_decode_port(struct rpc_rqst *req, u32 *p, unsigned short *portp) { *portp = (unsigned short) ntohl(*p++); return 0; }
{ struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; unsigned int replen; u32 count = args->count; p = xdr_encode_fhandle(p, args->fh); p = xdr_encode_hyper(p, args->cookie); *p++ = args->verf[0]; *p++ = args->verf[1]; if (args->plus) { /* readdirplus: need dircount + buffer size. * We just make sure we make dircount big enough */ *p++ = htonl(count >> 3); } *p++ = htonl(count); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); /* Inline the page array */ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2; xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count); return 0; } /* * Decode the result of a readdir call. * We just check for syntactical correctness. */ static int nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res) { struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
/** * rpcb_register - set or unset a port registration with the local rpcbind svc * @prog: RPC program number to bind * @vers: RPC version number to bind * @prot: transport protocol to use to make this request * @port: port value to register * @okay: result code * * port == 0 means unregister, port != 0 means register. * * This routine supports only rpcbind version 2. */ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) { struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK), }; struct rpcbind_args map = { .r_prog = prog, .r_vers = vers, .r_prot = prot, .r_port = port, }; struct rpc_message msg = { .rpc_proc = &rpcb_procedures2[port ? RPCBPROC_SET : RPCBPROC_UNSET], .rpc_argp = &map, .rpc_resp = okay, }; struct rpc_clnt *rpcb_clnt; int error = 0; dprintk("RPC: %sregistering (%u, %u, %d, %u) with local " "rpcbind\n", (port ? "" : "un"), prog, vers, prot, port); rpcb_clnt = rpcb_create("localhost", (struct sockaddr *) &sin, sizeof(sin), XPRT_TRANSPORT_UDP, 2, 1); if (IS_ERR(rpcb_clnt)) return PTR_ERR(rpcb_clnt); error = rpc_call_sync(rpcb_clnt, &msg, 0); rpc_shutdown_client(rpcb_clnt); if (error < 0) printk(KERN_WARNING "RPC: failed to contact local rpcbind " "server (errno %d).\n", -error); dprintk("RPC: registration status %d/%d\n", error, *okay); return error; } /** * rpcb_getport_sync - obtain the port for an RPC service on a given host * @sin: address of remote peer * @prog: RPC program number to bind * @vers: RPC version number to bind * @prot: transport protocol to use to make this request * * Return value is the requested advertised port number, * or a negative errno value. * * Called from outside the RPC client in a synchronous task context. * Uses default timeout parameters specified by underlying transport. * * XXX: Needs to support IPv6 */ int rpcb_getport_sync(struct sockaddr_in *sin, u32 prog, u32 vers, int prot) { struct rpcbind_args map = { .r_prog = prog, .r_vers = vers, .r_prot = prot, .r_port = 0, }; struct rpc_message msg = { .rpc_proc = &rpcb_procedures2[RPCBPROC_GETPORT], .rpc_argp = &map, .rpc_resp = &map.r_port, }; struct rpc_clnt *rpcb_clnt; int status; dprintk("RPC: %s(" NIPQUAD_FMT ", %u, %u, %d)\n", __FUNCTION__, NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot); rpcb_clnt = rpcb_create(NULL, (struct sockaddr *)sin, sizeof(*sin), prot, 2, 0); if (IS_ERR(rpcb_clnt)) return PTR_ERR(rpcb_clnt); status = rpc_call_sync(rpcb_clnt, &msg, 0); rpc_shutdown_client(rpcb_clnt); if (status >= 0) { if (map.r_port != 0) return map.r_port; status = -EACCES; } return status; } EXPORT_SYMBOL_GPL(rpcb_getport_sync); static struct rpc_task *rpcb_call_async(struct rpc_clnt *rpcb_clnt, struct rpcbind_args *map, int version) { struct rpc_message msg = { .rpc_proc = rpcb_next_version[version].rpc_proc, .rpc_argp = map, .rpc_resp = &map->r_port, }; struct rpc_task_setup task_setup_data = { .rpc_client = rpcb_clnt, .rpc_message = &msg, .callback_ops = &rpcb_getport_ops, .callback_data = map, .flags = RPC_TASK_ASYNC, }; return rpc_run_task(&task_setup_data); } /** * rpcb_getport_async - obtain the port for a given RPC service on a given host * @task: task that is waiting for portmapper request * * This one can be called for an ongoing RPC request, and can be used in * an async (rpciod) context. */ void rpcb_getport_async(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; u32 bind_version; struct rpc_xprt *xprt = task->tk_xprt; struct rpc_clnt *rpcb_clnt; static struct rpcbind_args *map; struct rpc_task *child; struct sockaddr_storage addr; struct sockaddr *sap = (struct sockaddr *)&addr; size_t salen; int status; struct rpcb_info *info; dprintk("RPC: %5u %s(%s, %u, %u, %d)\n", task->tk_pid, __FUNCTION__, clnt->cl_server, clnt->cl_prog, clnt->cl_vers, xprt->prot); /* Autobind on cloned rpc clients is discouraged */ BUG_ON(clnt->cl_parent != clnt); if (xprt_test_and_set_binding(xprt)) { status = -EAGAIN; /* tell caller to check again */ dprintk("RPC: %5u %s: waiting for another binder\n", task->tk_pid, __FUNCTION__); goto bailout_nowake; } /* Put self on queue before sending rpcbind request, in case * rpcb_getport_done completes before we return from rpc_run_task */ rpc_sleep_on(&xprt->binding, task, NULL, NULL); /* Someone else may have bound if we slept */ if (xprt_bound(xprt)) { status = 0; dprintk("RPC: %5u %s: already bound\n", task->tk_pid, __FUNCTION__); goto bailout_nofree; } salen = rpc_peeraddr(clnt, sap, sizeof(addr)); /* Don't ever use rpcbind v2 for AF_INET6 requests */ switch (sap->sa_family) { case AF_INET: info = rpcb_next_version; break; case AF_INET6: info = rpcb_next_version6; break; default: status = -EAFNOSUPPORT; dprintk("RPC: %5u %s: bad address family\n", task->tk_pid, __FUNCTION__); goto bailout_nofree; } if (info[xprt->bind_index].rpc_proc == NULL) { xprt->bind_index = 0; status = -EPFNOSUPPORT; dprintk("RPC: %5u %s: no more getport versions available\n", task->tk_pid, __FUNCTION__); goto bailout_nofree; } bind_version = info[xprt->bind_index].rpc_vers; dprintk("RPC: %5u %s: trying rpcbind version %u\n", task->tk_pid, __FUNCTION__, bind_version); rpcb_clnt = rpcb_create(clnt->cl_server, sap, salen, xprt->prot, bind_version, 0); if (IS_ERR(rpcb_clnt)) { status = PTR_ERR(rpcb_clnt); dprintk("RPC: %5u %s: rpcb_create failed, error %ld\n", task->tk_pid, __FUNCTION__, PTR_ERR(rpcb_clnt)); goto bailout_nofree; } map = kzalloc(sizeof(struct rpcbind_args), GFP_ATOMIC); if (!map) { status = -ENOMEM; dprintk("RPC: %5u %s: no memory available\n", task->tk_pid, __FUNCTION__); goto bailout_nofree; } map->r_prog = clnt->cl_prog; map->r_vers = clnt->cl_vers; map->r_prot = xprt->prot; map->r_port = 0; map->r_xprt = xprt_get(xprt); map->r_netid = rpc_peeraddr2str(clnt, RPC_DISPLAY_NETID); map->r_addr = rpc_peeraddr2str(rpcb_clnt, RPC_DISPLAY_UNIVERSAL_ADDR); map->r_owner = RPCB_OWNER_STRING; /* ignored for GETADDR */ child = rpcb_call_async(rpcb_clnt, map, xprt->bind_index); rpc_release_client(rpcb_clnt); if (IS_ERR(child)) { status = -EIO; dprintk("RPC: %5u %s: rpc_run_task failed\n", task->tk_pid, __FUNCTION__); goto bailout; } rpc_put_task(child); task->tk_xprt->stat.bind_count++; return; bailout: kfree(map); xprt_put(xprt); bailout_nofree: rpcb_wake_rpcbind_waiters(xprt, status); bailout_nowake: task->tk_status = status; } EXPORT_SYMBOL_GPL(rpcb_getport_async); /* * Rpcbind child task calls this callback via tk_exit. */ static void rpcb_getport_done(struct rpc_task *child, void *data) { struct rpcbind_args *map = data; struct rpc_xprt *xprt = map->r_xprt; int status = child->tk_status; /* Garbage reply: retry with a lesser rpcbind version */ if (status == -EIO) status = -EPROTONOSUPPORT; /* rpcbind server doesn't support this rpcbind protocol version */ if (status == -EPROTONOSUPPORT) xprt->bind_index++; if (status < 0) { /* rpcbind server not available on remote host? */ xprt->ops->set_port(xprt, 0); } else if (map->r_port == 0) { /* Requested RPC service wasn't registered on remote host */ xprt->ops->set_port(xprt, 0); status = -EACCES; } else { /* Succeeded */ xprt->ops->set_port(xprt, map->r_port); xprt_set_bound(xprt); status = 0; } dprintk("RPC: %5u rpcb_getport_done(status %d, port %u)\n", child->tk_pid, status, map->r_port); rpcb_wake_rpcbind_waiters(xprt, status); } static int rpcb_encode_mapping(struct rpc_rqst *req, __be32 *p, struct rpcbind_args *rpcb) { dprintk("RPC: rpcb_encode_mapping(%u, %u, %d, %u)\n", rpcb->r_prog, rpcb->r_vers, rpcb->r_prot, rpcb->r_port); *p++ = htonl(rpcb->r_prog); *p++ = htonl(rpcb->r_vers); *p++ = htonl(rpcb->r_prot); *p++ = htonl(rpcb->r_port); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; } static int rpcb_decode_getport(struct rpc_rqst *req, __be32 *p, unsigned short *portp) { *portp = (unsigned short) ntohl(*p++); dprintk("RPC: rpcb_decode_getport result %u\n", *portp); return 0; } static int rpcb_decode_set(struct rpc_rqst *req, __be32 *p, unsigned int *boolp) { *boolp = (unsigned int) ntohl(*p++); dprintk("RPC: rpcb_decode_set result %u\n", *boolp); return 0; } static int rpcb_encode_getaddr(struct rpc_rqst *req, __be32 *p, struct rpcbind_args *rpcb) { dprintk("RPC: rpcb_encode_getaddr(%u, %u, %s)\n", rpcb->r_prog, rpcb->r_vers, rpcb->r_addr); *p++ = htonl(rpcb->r_prog); *p++ = htonl(rpcb->r_vers); p = xdr_encode_string(p, rpcb->r_netid); p = xdr_encode_string(p, rpcb->r_addr); p = xdr_encode_string(p, rpcb->r_owner); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); return 0; }