mirror of
https://github.com/apache/nuttx.git
synced 2025-01-13 10:58:49 +08:00
NFS update
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4820 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
parent
44d6f19868
commit
03b5bdf3db
2 changed files with 261 additions and 206 deletions
|
@ -99,6 +99,8 @@ struct nfs_dirent
|
|||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
static int nfs_create(FAR struct nfsmount *nmp, struct nfsnode *np,
|
||||
FAR const char *relpath, mode_t mode);
|
||||
static int nfs_open(FAR struct file *filep, const char *relpath,
|
||||
int oflags, mode_t mode);
|
||||
static ssize_t nfs_read(FAR struct file *filep, char *buffer, size_t buflen);
|
||||
|
@ -172,58 +174,28 @@ const struct mountpt_operations nfs_operations =
|
|||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nfs_open
|
||||
* Name: nfs_create
|
||||
*
|
||||
* Description:
|
||||
* If oflags == O_CREAT it creates a file, if not it check to see if the
|
||||
* type is ok and that deletion is not in progress.
|
||||
* Create a file. This is part of the file open logic that is executed if
|
||||
* the user asks to create a file.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success; a negated errno value on failure.
|
||||
* 0 on success; a positive errno value on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int nfs_open(FAR struct file *filep, FAR const char *relpath,
|
||||
int oflags, mode_t mode)
|
||||
static int nfs_create(FAR struct nfsmount *nmp, struct nfsnode *np,
|
||||
FAR const char *relpath, mode_t mode)
|
||||
{
|
||||
struct inode *in;
|
||||
//struct nfs_fattr vap;
|
||||
struct nfsv3_sattr sp;
|
||||
struct nfsmount *nmp;
|
||||
struct nfsnode *np;
|
||||
struct CREATE3args create;
|
||||
struct rpc_reply_create resok;
|
||||
int error = 0;
|
||||
|
||||
/* Sanity checks */
|
||||
|
||||
DEBUGASSERT(filep->f_inode != NULL);
|
||||
|
||||
/* Get the mountpoint inode reference from the file structure and the
|
||||
* mountpoint private data from the inode structure
|
||||
*/
|
||||
|
||||
in = filep->f_inode;
|
||||
nmp = (struct nfsmount*)in->i_private;
|
||||
np = (struct nfsnode*)filep->f_priv;
|
||||
|
||||
DEBUGASSERT(nmp != NULL);
|
||||
|
||||
/* Check if the mount is still healthy */
|
||||
|
||||
nfs_semtake(nmp);
|
||||
error = nfs_checkmount(nmp);
|
||||
if (error != 0)
|
||||
do
|
||||
{
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
if (oflags == O_CREAT)
|
||||
{
|
||||
/* Sanity checks */
|
||||
|
||||
DEBUGASSERT(filep->f_priv == NULL);
|
||||
again:
|
||||
nfsstats.rpccnt[NFSPROC_CREATE]++;
|
||||
memset(&sp, 0, sizeof(struct nfsv3_sattr));
|
||||
//memset(&vap, 0, sizeof(struct nfs_fattr));
|
||||
|
@ -241,6 +213,7 @@ again:
|
|||
memset(&create, 0, sizeof(struct CREATE3args));
|
||||
memset(&resok, 0, sizeof(struct rpc_reply_create));
|
||||
memcpy(&create.how, &sp, sizeof(struct nfsv3_sattr));
|
||||
#warning "BUG HERE: np has not yet been initialized"
|
||||
create.where.dir.length = txdr_unsigned(np->n_fhsize);
|
||||
memcpy(&create.where.dir.handle, &np->n_fhp, sizeof(nfsfh_t));
|
||||
create.where.length = txdr_unsigned(64);
|
||||
|
@ -248,91 +221,205 @@ again:
|
|||
|
||||
error = nfs_request(nmp, NFSPROC_CREATE, (FAR const void *)&create,
|
||||
(void *)&resok, sizeof(struct rpc_reply_create));
|
||||
if (!error)
|
||||
{
|
||||
/* Create an instance of the file private data to describe the opened
|
||||
* file.
|
||||
*/
|
||||
|
||||
np = (struct nfsnode *)kzalloc(sizeof(struct nfsnode));
|
||||
if (!np)
|
||||
{
|
||||
fdbg("ERROR: Failed to allocate private data\n");
|
||||
error = -ENOMEM;
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
/* Initialize the file private data (only need to initialize
|
||||
* non-zero elements)
|
||||
*/
|
||||
|
||||
// np->nfsv3_type = fxdr_unsigned(uint32_t, resok.attributes.fa_type);
|
||||
|
||||
/* The full path exists -- but is the final component a file
|
||||
* or a directory?
|
||||
*/
|
||||
|
||||
if (np->nfsv3_type == NFDIR)
|
||||
{
|
||||
/* It is a directory */
|
||||
|
||||
error = EISDIR;
|
||||
fdbg("ERROR: '%s' is a directory\n", relpath);
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
np->n_open = true;
|
||||
memcpy(&np->n_fhp, &resok.create.fshandle.handle, sizeof(nfsfh_t));
|
||||
np->n_size = fxdr_hyper(&resok.create.attributes.fa3_size);
|
||||
memcpy(&resok.create.attributes, &np->n_fattr, sizeof(struct nfs_fattr));
|
||||
fxdr_nfsv3time(&resok.create.attributes.fa3_mtime, &np->n_mtime)
|
||||
np->n_ctime = fxdr_hyper(&resok.create.attributes.fa3_ctime);
|
||||
|
||||
/* Attach the private date to the struct file instance */
|
||||
|
||||
filep->f_priv = np;
|
||||
|
||||
/* Then insert the new instance into the mountpoint structure.
|
||||
* It needs to be there (1) to handle error conditions that effect
|
||||
* all files, and (2) to inform the umount logic that we are busy
|
||||
* (but a simple reference count could have done that).
|
||||
*/
|
||||
|
||||
np->n_next = nmp->nm_head;
|
||||
nmp->nm_head = np->n_next;
|
||||
error = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (error == EOPNOTSUPP)
|
||||
{
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
np->n_flag |= NMODIFIED;
|
||||
}
|
||||
else
|
||||
while (error == EOPNOTSUPP);
|
||||
|
||||
if (error == 0)
|
||||
{
|
||||
if (np->nfsv3_type != NFREG)
|
||||
//np->nfsv3_type = fxdr_unsigned(uint32_t, resok.attributes.fa_type);
|
||||
|
||||
np->n_open = true;
|
||||
memcpy(&np->n_fhp, &resok.create.fshandle.handle, sizeof(nfsfh_t));
|
||||
np->n_size = fxdr_hyper(&resok.create.attributes.fa3_size);
|
||||
memcpy(&resok.create.attributes, &np->n_fattr, sizeof(struct nfs_fattr));
|
||||
fxdr_nfsv3time(&resok.create.attributes.fa3_mtime, &np->n_mtime)
|
||||
np->n_ctime = fxdr_hyper(&resok.create.attributes.fa3_ctime);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nfs_open
|
||||
*
|
||||
* Description:
|
||||
* If oflags == O_CREAT it creates a file, if not it check to see if the
|
||||
* type is ok and that deletion is not in progress.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success; a negated errno value on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int nfs_open(FAR struct file *filep, FAR const char *relpath,
|
||||
int oflags, mode_t mode)
|
||||
{
|
||||
struct stat buf;
|
||||
struct inode *in;
|
||||
//struct nfs_fattr vap;
|
||||
struct nfsmount *nmp;
|
||||
struct nfsnode *np;
|
||||
bool readonly;
|
||||
int error = 0;
|
||||
|
||||
/* Sanity checks */
|
||||
|
||||
DEBUGASSERT(filep->f_inode != NULL);
|
||||
|
||||
/* Get the mountpoint inode reference from the file structure and the
|
||||
* mountpoint private data from the inode structure
|
||||
*/
|
||||
|
||||
in = filep->f_inode;
|
||||
nmp = (struct nfsmount*)in->i_private;
|
||||
|
||||
DEBUGASSERT(nmp != NULL);
|
||||
|
||||
/* Pre-allocate the file private data to describe the opened file. */
|
||||
|
||||
np = (struct nfsnode *)kzalloc(sizeof(struct nfsnode));
|
||||
if (!np)
|
||||
{
|
||||
fdbg("ERROR: Failed to allocate private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Check if the mount is still healthy */
|
||||
|
||||
nfs_semtake(nmp);
|
||||
error = nfs_checkmount(nmp);
|
||||
if (error != 0)
|
||||
{
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
/* First check if the file already exists */
|
||||
|
||||
error = nfs_getfsinfo(nmp, relpath, &buf);
|
||||
if (error == 0)
|
||||
{
|
||||
/* Check if the file is a directory */
|
||||
|
||||
if (S_ISDIR(buf.st_mode))
|
||||
{
|
||||
fdbg("ERROR: open eacces typ=%d\n", np->nfsv3_type);
|
||||
/* Exit with EISDIR if we attempt to open an directory */
|
||||
|
||||
fdbg("ERROR: Path is a directory\n");
|
||||
error = EISDIR;
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
/* Check if the caller has sufficient privileges to open the file */
|
||||
|
||||
readonly = ((buf.st_mode & (S_IWOTH|S_IWGRP|S_IXUSR)) == 0);
|
||||
if (((oflags & O_WRONLY) != 0) && readonly)
|
||||
{
|
||||
fdbg("ERROR: File is read-only\n");
|
||||
error = EACCES;
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
if (np->n_flag & NMODIFIED)
|
||||
/* It would be an error if we are asked to create it exclusively */
|
||||
|
||||
if ((oflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
|
||||
{
|
||||
if (np->nfsv3_type == NFDIR)
|
||||
{
|
||||
np->n_direofoffset = 0;
|
||||
}
|
||||
/* Already exists -- can't create it exclusively */
|
||||
|
||||
fdbg("ERROR: File exists\n");
|
||||
error = EEXIST;
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
/* If O_TRUNC is specified and the file is opened for writing,
|
||||
* then truncate the file. This operation requires that the file is
|
||||
* writable, but we have already checked that. O_TRUNC without write
|
||||
* access is ignored.
|
||||
*/
|
||||
|
||||
if ((oflags & (O_TRUNC|O_WRONLY)) == (O_TRUNC|O_WRONLY))
|
||||
{
|
||||
/* Truncate the file to zero length */
|
||||
|
||||
fvdbg("Truncating file\n");
|
||||
#warning "Missing logic"
|
||||
}
|
||||
|
||||
/* Initialize the file private data from the FSINFO return data */
|
||||
/* NOTE: This will require some re-structuring */
|
||||
#if 0
|
||||
np->n_open = true;
|
||||
memcpy(&np->n_fhp, &resok.fsinfo.fshandle.handle, sizeof(nfsfh_t));
|
||||
np->n_size = fxdr_hyper(&resok.fsinfo.attributes.fa3_size);
|
||||
memcpy(&resok.fsinfo.attributes, &np->n_fattr, sizeof(struct nfs_fattr));
|
||||
fxdr_nfsv3time(&resok.fsinfo.attributes.fa3_mtime, &np->n_mtime)
|
||||
np->n_ctime = fxdr_hyper(&resok.fsinfo.attributes.fa3_ctime);
|
||||
#else
|
||||
#warning "Missing logic"
|
||||
fdbg("ERROR: Logic to open an existing file is not fully implemented");
|
||||
errno = ENOSYS; /* Just fail for now */
|
||||
goto errout_with_semaphore;
|
||||
#endif
|
||||
/* Fall through to finish the file open operation */
|
||||
}
|
||||
|
||||
/* An error occurred while getting the file status */
|
||||
|
||||
else
|
||||
{
|
||||
/* Check if the stat failed because the file does not exist. */
|
||||
#warning "Missing logic"
|
||||
|
||||
/* If the file does not exist then check if we were asked to create
|
||||
* the file. If the O_CREAT bit is set in the oflags then we should
|
||||
* create the file if it does not exist.
|
||||
*/
|
||||
|
||||
if ((oflags & O_CREAT) == 0)
|
||||
{
|
||||
/* Return ENOENT if the file does not exist and we were not asked
|
||||
* to create it.
|
||||
*/
|
||||
|
||||
fdbg("ERROR: File does not exist\n");
|
||||
error = ENOENT;
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
/* Create the file */
|
||||
|
||||
error = nfs_create(nmp, np, relpath, mode);
|
||||
if (error != 0)
|
||||
{
|
||||
fdbg("ERROR: nfs_create failed: %d\n", error);
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
/* Fall through to finish the file open operation */
|
||||
}
|
||||
|
||||
/* Initialize the file private data (only need to initialize
|
||||
* non-zero elements)
|
||||
*/
|
||||
|
||||
/* Attach the private data to the struct file instance */
|
||||
|
||||
filep->f_priv = np;
|
||||
|
||||
/* Then insert the new instance into the mountpoint structure.
|
||||
* It needs to be there (1) to handle error conditions that effect
|
||||
* all files, and (2) to inform the umount logic that we are busy
|
||||
* (but a simple reference count could have done that).
|
||||
*/
|
||||
|
||||
np->n_next = nmp->nm_head;
|
||||
np->n_flag |= NMODIFIED;
|
||||
nmp->nm_head = np->n_next;
|
||||
|
||||
/* For open/close consistency. */
|
||||
|
||||
NFS_INVALIDATE_ATTRCACHE(np);
|
||||
nfs_semgive(nmp);
|
||||
return OK;
|
||||
|
||||
errout_with_semaphore:
|
||||
kfree(np);
|
||||
|
|
|
@ -241,10 +241,12 @@ rpcclnt_send(struct socket *so, struct sockaddr *nam, int procid, int prog,
|
|||
void *call, struct rpctask *rep)
|
||||
{
|
||||
struct sockaddr *sendnam;
|
||||
ssize_t nbytes;
|
||||
int error = ESRCH;
|
||||
#ifdef CONFIG_NFS_TCPIP
|
||||
int soflags;
|
||||
#endif
|
||||
int length;
|
||||
int flags;
|
||||
|
||||
if (rep != NULL)
|
||||
|
@ -290,34 +292,29 @@ rpcclnt_send(struct socket *so, struct sockaddr *nam, int procid, int prog,
|
|||
flags = 0;
|
||||
}
|
||||
|
||||
/* Get the length of the call messsage */
|
||||
|
||||
error = 0;
|
||||
if (prog == PMAPPROG)
|
||||
{
|
||||
if (procid == PMAPPROC_GETPORT)
|
||||
if (procid == PMAPPROC_GETPORT || procid == PMAPPROC_UNSET)
|
||||
{
|
||||
struct rpc_call_pmap *callmsg = (struct rpc_call_pmap *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
length = sizeof(struct rpc_call_pmap);
|
||||
}
|
||||
else if (procid == PMAPPROC_UNSET)
|
||||
else
|
||||
{
|
||||
struct rpc_call_pmap *callmsg = (struct rpc_call_pmap *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
error = EINVAL;
|
||||
}
|
||||
}
|
||||
else if (prog == RPCPROG_MNT)
|
||||
{
|
||||
if (procid == RPCMNT_UMOUNT)
|
||||
if (procid == RPCMNT_UMOUNT || procid == RPCMNT_MOUNT)
|
||||
{
|
||||
struct rpc_call_mount *callmsg = (struct rpc_call_mount *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
length = sizeof(struct rpc_call_mount);
|
||||
}
|
||||
else if (procid == RPCMNT_MOUNT)
|
||||
else
|
||||
{
|
||||
struct rpc_call_mount *callmsg = (struct rpc_call_mount *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
error = EINVAL;
|
||||
}
|
||||
}
|
||||
else if (prog == NFS_PROG)
|
||||
|
@ -325,127 +322,98 @@ rpcclnt_send(struct socket *so, struct sockaddr *nam, int procid, int prog,
|
|||
switch (procid)
|
||||
{
|
||||
case NFSPROC_CREATE:
|
||||
{
|
||||
struct rpc_call_create *callmsg = (struct rpc_call_create *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
}
|
||||
length = sizeof(struct rpc_call_create);
|
||||
break;
|
||||
|
||||
case NFSPROC_READ:
|
||||
{
|
||||
struct rpc_call_read *callmsg = (struct rpc_call_read *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
}
|
||||
length = sizeof(struct rpc_call_read);
|
||||
break;
|
||||
|
||||
case NFSPROC_WRITE:
|
||||
{
|
||||
struct rpc_call_write *callmsg = (struct rpc_call_write *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
}
|
||||
length = sizeof(struct rpc_call_write);
|
||||
break;
|
||||
|
||||
case NFSPROC_READDIR:
|
||||
{
|
||||
struct rpc_call_readdir *callmsg = (struct rpc_call_readdir *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
}
|
||||
length = sizeof(struct rpc_call_readdir);
|
||||
break;
|
||||
|
||||
case NFSPROC_FSSTAT:
|
||||
{
|
||||
struct rpc_call_fs *callmsg = (struct rpc_call_fs *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
}
|
||||
length = sizeof(struct rpc_call_fs);
|
||||
break;
|
||||
|
||||
case NFSPROC_GETATTR:
|
||||
{
|
||||
struct rpc_call_fs *callmsg = (struct rpc_call_fs *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
}
|
||||
length = sizeof(struct rpc_call_fs);
|
||||
break;
|
||||
|
||||
case NFSPROC_REMOVE:
|
||||
{
|
||||
struct rpc_call_remove *callmsg = (struct rpc_call_remove *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
}
|
||||
length = sizeof(struct rpc_call_remove);
|
||||
break;
|
||||
|
||||
case NFSPROC_MKDIR:
|
||||
{
|
||||
struct rpc_call_mkdir *callmsg = (struct rpc_call_mkdir *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
}
|
||||
length = sizeof(struct rpc_call_mkdir);
|
||||
break;
|
||||
|
||||
case NFSPROC_RMDIR:
|
||||
{
|
||||
struct rpc_call_rmdir *callmsg = (struct rpc_call_rmdir *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
}
|
||||
length = sizeof(struct rpc_call_rmdir);
|
||||
break;
|
||||
|
||||
case NFSPROC_RENAME:
|
||||
{
|
||||
struct rpc_call_rename *callmsg = (struct rpc_call_rename *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
}
|
||||
length = sizeof(struct rpc_call_rename);
|
||||
break;
|
||||
|
||||
case NFSPROC_FSINFO:
|
||||
{
|
||||
struct rpc_call_fs *callmsg = (struct rpc_call_fs *)call;
|
||||
error = psock_sendto(so, callmsg, sizeof(*callmsg), flags,
|
||||
sendnam, sizeof(*sendnam));
|
||||
}
|
||||
length = sizeof(struct rpc_call_fs);
|
||||
break;
|
||||
|
||||
default:
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (error < 0)
|
||||
{
|
||||
if (rep != NULL)
|
||||
{
|
||||
fdbg("rpc send error %d for service %s\n", error,
|
||||
rep->r_rpcclnt->rc_prog->prog_name);
|
||||
|
||||
/* Deal with errors for the client side. */
|
||||
|
||||
if (rep->r_flags & TASK_SOFTTERM)
|
||||
{
|
||||
error = EINTR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rep->r_flags |= TASK_MUSTRESEND;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fdbg("rpc service send error %d\n", error);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
error = EINVAL;
|
||||
}
|
||||
|
||||
/* Send the call message */
|
||||
|
||||
if (error == 0)
|
||||
{
|
||||
/* On success, psock_sendto returns the number of bytes sent;
|
||||
* On failure, it returns -1 with the specific error in errno.
|
||||
*/
|
||||
|
||||
nbytes = psock_sendto(so, call, length, flags,
|
||||
sendnam, sizeof(struct sockaddr));
|
||||
if (nbytes < 0)
|
||||
{
|
||||
/* psock_sendto failed, Sample the error value (subsequent
|
||||
* calls can change the errno value!
|
||||
*/
|
||||
|
||||
error = errno;
|
||||
fdbg("ERROR: psock_sendto failed: %d\n", error);
|
||||
|
||||
if (rep != NULL)
|
||||
{
|
||||
fdbg("rpc send error %d for service %s\n", error,
|
||||
rep->r_rpcclnt->rc_prog->prog_name);
|
||||
|
||||
/* Deal with errors for the client side. */
|
||||
|
||||
if (rep->r_flags & TASK_SOFTTERM)
|
||||
{
|
||||
error = EINTR;
|
||||
}
|
||||
else
|
||||
{
|
||||
rep->r_flags |= TASK_MUSTRESEND;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Receive a Sun RPC Request/Reply. For SOCK_DGRAM, the work is all
|
||||
|
|
Loading…
Reference in a new issue