/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
** File: ptio.c
** Descritpion: Implemenation of I/O methods for pthreads
*/
#if defined(_PR_PTHREADS)
# if defined(_PR_POLL_WITH_SELECT)
# if !(
defined(HPUX) &&
defined(_USE_BIG_FDS))
/* set fd limit for select(), before including system header files */
# define FD_SETSIZE (16 * 1024)
# endif
# endif
# include <pthread.h>
# include <string.h>
/* for memset() */
# include <sys/types.h>
# include <dirent.h>
# include <fcntl.h>
# include <unistd.h>
# include <sys/socket.h>
# include <sys/stat.h>
# include <sys/uio.h>
# include <sys/file.h>
# include <sys/ioctl.h>
# if defined(DARWIN)
# include <sys/utsname.h>
/* for uname */
# endif
# if defined(SOLARIS)
# include <sys/filio.h>
/* to pick up FIONREAD */
# endif
# ifdef _PR_POLL_AVAILABLE
# include <poll.h>
# endif
# ifdef AIX
/* To pick up sysconf() */
# include <unistd.h>
# include <dlfcn.h>
/* for dlopen */
# else
/* To pick up getrlimit() etc. */
# include <sys/time.h>
# include <sys/resource.h>
# endif
# ifdef SOLARIS
/*
* Define HAVE_SENDFILEV if the system has the sendfilev() system call.
* Code built this way won't run on a system without sendfilev().
* We can define HAVE_SENDFILEV by default when the minimum release
* of Solaris that NSPR supports has sendfilev().
*/
# ifdef HAVE_SENDFILEV
# include <sys/sendfile.h>
# define SOLARIS_SENDFILEV(a, b, c, d) sendfilev((a), (b), (c), (d))
# else
# include <dlfcn.h>
/* for dlopen */
/*
* Match the definitions in <sys/sendfile.h>.
*/
typedef struct sendfilevec {
int sfv_fd;
/* input fd */
uint_t sfv_flag;
/* flags */
off_t sfv_off;
/* offset to start reading from */
size_t sfv_len;
/* amount of data */
} sendfilevec_t;
# define SFV_FD_SELF (-2)
/*
* extern ssize_t sendfilev(int, const struct sendfilevec *, int, size_t *);
*/
static ssize_t (*pt_solaris_sendfilev_fptr)() = NULL;
# define SOLARIS_SENDFILEV(a, b, c, d) \
(*pt_solaris_sendfilev_fptr)((a), (b), (c), (d))
# endif
/* HAVE_SENDFILEV */
# endif
/* SOLARIS */
/*
* The send_file() system call is available in AIX 4.3.2 or later.
* If this file is compiled on an older AIX system, it attempts to
* look up the send_file symbol at run time to determine whether
* we can use the faster PR_SendFile/PR_TransmitFile implementation based on
* send_file(). On AIX 4.3.2 or later, we can safely skip this
* runtime function dispatching and just use the send_file based
* implementation.
*/
# ifdef AIX
# ifdef SF_CLOSE
# define HAVE_SEND_FILE
# endif
# ifdef HAVE_SEND_FILE
# define AIX_SEND_FILE(a, b, c) send_file(a, b, c)
# else /* HAVE_SEND_FILE */
/*
* The following definitions match those in <sys/socket.h>
* on AIX 4.3.2.
*/
/*
* Structure for the send_file() system call
*/
struct sf_parms {
/* --------- header parms ---------- */
void* header_data;
/* Input/Output. Points to header buf */
uint_t header_length;
/* Input/Output. Length of the header */
/* --------- file parms ------------ */
int file_descriptor;
/* Input. File descriptor of the file */
unsigned long long file_size;
/* Output. Size of the file */
unsigned long long file_offset;
/* Input/Output. Starting offset */
long long file_bytes;
/* Input/Output. no. of bytes to send */
/* --------- trailer parms --------- */
void* trailer_data;
/* Input/Output. Points to trailer buf */
uint_t trailer_length;
/* Input/Output. Length of the trailer */
/* --------- return info ----------- */
unsigned long long bytes_sent;
/* Output. no. of bytes sent */
};
/*
* Flags for the send_file() system call
*/
# define SF_CLOSE 0x00000001
/* close the socket after completion */
# define SF_REUSE 0x00000002
/* reuse socket. not supported */
# define SF_DONT_CACHE 0x00000004
/* don't apply network buffer cache */
# define SF_SYNC_CACHE 0x00000008
/* sync/update network buffer cache */
/*
* prototype: size_t send_file(int *, struct sf_parms *, uint_t);
*/
static ssize_t (*pt_aix_sendfile_fptr)() = NULL;
# define AIX_SEND_FILE(a, b, c) (*pt_aix_sendfile_fptr)(a, b, c)
# endif
/* HAVE_SEND_FILE */
# endif
/* AIX */
# ifdef LINUX
# include <sys/sendfile.h>
# endif
# include
"primpl.h"
# if defined(LINUX) ||
defined(ANDROID)
# include <netinet/in.h>
# endif
# ifdef DARWIN
# include <netinet/in.h>
# include <netinet/ip.h>
# endif
# ifdef HAVE_NETINET_TCP_H
# include <netinet/tcp.h>
/* TCP_NODELAY, TCP_MAXSEG */
# endif
# ifdef LINUX
/* TCP_CORK is not defined in <netinet/tcp.h> on Red Hat Linux 6.0 */
# ifndef TCP_CORK
# define TCP_CORK 3
# endif
# ifndef MSG_FASTOPEN
# define MSG_FASTOPEN 0x20000000
# endif
# endif
# ifdef _PR_IPV6_V6ONLY_PROBE
static PRBool _pr_ipv6_v6only_on_by_default;
# endif
# if (
defined(HPUX) && !
defined(HPUX10_30) && !
defined(HPUX11))
# define _PRSelectFdSetArg_t
int*
# elif
defined(AIX4_1)
# define _PRSelectFdSetArg_t
void*
# elif (
defined(AIX) && !
defined(AIX4_1)) ||
defined(SOLARIS) || \
defined(HPUX10_30) ||
defined(HPUX11) ||
defined(LINUX) || \
defined(__GNU__) ||
defined(__GLIBC__) ||
defined(FREEBSD) || \
defined(NETBSD) ||
defined(OPENBSD) ||
defined(NTO) || \
defined(DARWIN) ||
defined(RISCOS)
# define _PRSelectFdSetArg_t fd_set*
# else
# error
"Cannot determine architecture"
# endif
# if defined(SOLARIS)
# ifndef PROTO_SDP
/* on solaris, SDP is a new type of protocol */
# define PROTO_SDP 257
# endif
# define _PR_HAVE_SDP
# elif
defined(LINUX)
# ifndef AF_INET_SDP
/* on linux, SDP is a new type of address family */
# define AF_INET_SDP 27
# endif
# define _PR_HAVE_SDP
# endif
/* LINUX */
static PRFileDesc* pt_SetMethods(PRIntn osfd, PRDescType type,
PRBool isAcceptedSocket, PRBool imported);
static PRLock* _pr_flock_lock;
/* For PR_LockFile() etc. */
static PRCondVar* _pr_flock_cv;
/* For PR_LockFile() etc. */
static PRLock* _pr_rename_lock;
/* For PR_Rename() */
/**************************************************************************/
/* These two functions are only used in assertions. */
# if defined(DEBUG)
PRBool IsValidNetAddr(
const PRNetAddr* addr) {
if ((addr != NULL) && (addr->raw.family != AF_UNIX) &&
(addr->raw.family != PR_AF_INET6) && (addr->raw.family != AF_INET)) {
return PR_FALSE;
}
return PR_TRUE;
}
static PRBool IsValidNetAddrLen(
const PRNetAddr* addr, PRInt32 addr_len) {
/*
* The definition of the length of a Unix domain socket address
* is not uniform, so we don't check it.
*/
if ((addr != NULL) && (addr->raw.family != AF_UNIX) &&
(PR_NETADDR_SIZE(addr) != addr_len)) {
# if defined(LINUX) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 1
/*
* In glibc 2.1, struct sockaddr_in6 is 24 bytes. In glibc 2.2
* and in the 2.4 kernel, struct sockaddr_in6 has the scope_id
* field and is 28 bytes. It is possible for socket functions
* to return an addr_len greater than sizeof(struct sockaddr_in6).
* We need to allow that. (Bugzilla bug #77264)
*/
if ((PR_AF_INET6 == addr->raw.family) && (
sizeof(addr->ipv6) == addr_len)) {
return PR_TRUE;
}
# endif
return PR_FALSE;
}
return PR_TRUE;
}
# endif
/* DEBUG */
/*****************************************************************************/
/************************* I/O Continuation machinery ************************/
/*****************************************************************************/
/*
* The polling interval defines the maximum amount of time that a thread
* might hang up before an interrupt is noticed.
*/
# define PT_DEFAULT_POLL_MSEC 5000
# if defined(_PR_POLL_WITH_SELECT)
# define PT_DEFAULT_SELECT_SEC (PT_DEFAULT_POLL_MSEC / PR_MSEC_PER_SEC)
# define PT_DEFAULT_SELECT_USEC \
((PT_DEFAULT_POLL_MSEC % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC)
# endif
/*
* pt_SockLen is the type for the length of a socket address
* structure, used in the address length argument to bind,
* connect, accept, getsockname, getpeername, etc. Posix.1g
* defines this type as socklen_t. It is size_t or int on
* most current systems.
*/
# if defined(HAVE_SOCKLEN_T) || (
defined(__GLIBC__) && __GLIBC__ >= 2)
typedef socklen_t pt_SockLen;
# elif (
defined(AIX) && !
defined(AIX4_1))
typedef PRSize pt_SockLen;
# else
typedef PRIntn pt_SockLen;
# endif
typedef struct pt_Continuation pt_Continuation;
typedef PRBool (*ContinuationFn)(pt_Continuation* op, PRInt16 revents);
typedef enum pr_ContuationStatus {
pt_continuation_pending,
pt_continuation_done
} pr_ContuationStatus;
struct pt_Continuation {
/* The building of the continuation operation */
ContinuationFn function;
/* what function to continue */
union {
PRIntn osfd;
} arg1;
/* #1 - the op's fd */
union {
void* buffer;
} arg2;
/* #2 - primary transfer buffer */
union {
PRSize amount;
/* #3 - size of 'buffer', or */
pt_SockLen* addr_len;
/* - length of address */
# ifdef HPUX11
/*
* For sendfile()
*/
struct file_spec {
off_t offset;
/* offset in file to send */
size_t nbytes;
/* length of file data to send */
size_t st_size;
/* file size */
} file_spec;
# endif
} arg3;
union {
PRIntn flags;
} arg4;
/* #4 - read/write flags */
union {
PRNetAddr* addr;
} arg5;
/* #5 - send/recv address */
# ifdef HPUX11
/*
* For sendfile()
*/
int filedesc;
/* descriptor of file to send */
int nbytes_to_send;
/* size of header and file */
# endif
/* HPUX11 */
# ifdef SOLARIS
/*
* For sendfilev()
*/
int nbytes_to_send;
/* size of header and file */
# endif
/* SOLARIS */
# ifdef LINUX
/*
* For sendfile()
*/
int in_fd;
/* descriptor of file to send */
off_t offset;
size_t count;
# endif
/* LINUX */
PRIntervalTime timeout;
/* client (relative) timeout */
PRInt16 event;
/* flags for poll()'s events */
/*
** The representation and notification of the results of the operation.
** These function can either return an int return code or a pointer to
** some object.
*/
union {
PRSize code;
void* object;
} result;
PRIntn syserrno;
/* in case it failed, why (errno) */
pr_ContuationStatus status;
/* the status of the operation */
};
# if defined(DEBUG)
PTDebug pt_debug;
/* this is shared between several modules */
PR_IMPLEMENT(
void) PT_FPrintStats(PRFileDesc* debug_out,
const char* msg) {
PTDebug stats;
char buffer[100];
PRExplodedTime tod;
PRInt64 elapsed, aMil;
stats = pt_debug;
/* a copy */
PR_ExplodeTime(stats.timeStarted, PR_LocalTimeParameters, &tod);
(
void)PR_FormatTime(buffer,
sizeof(buffer),
"%T", &tod);
LL_SUB(elapsed, PR_Now(), stats.timeStarted);
LL_I2L(aMil, 1000000);
LL_DIV(elapsed, elapsed, aMil);
if (NULL != msg) {
PR_fprintf(debug_out,
"%s", msg);
}
PR_fprintf(debug_out,
"\tstarted: %s[%lld]\n", buffer, elapsed);
PR_fprintf(debug_out,
"\tlocks [created: %u, destroyed: %u]\n",
stats.locks_created, stats.locks_destroyed);
PR_fprintf(debug_out,
"\tlocks [acquired: %u, released: %u]\n",
stats.locks_acquired, stats.locks_released);
PR_fprintf(debug_out,
"\tcvars [created: %u, destroyed: %u]\n",
stats.cvars_created, stats.cvars_destroyed);
PR_fprintf(debug_out,
"\tcvars [notified: %u, delayed_delete: %u]\n",
stats.cvars_notified, stats.delayed_cv_deletes);
}
/* PT_FPrintStats */
# else
PR_IMPLEMENT(
void) PT_FPrintStats(PRFileDesc* debug_out,
const char* msg) {
/* do nothing */
}
/* PT_FPrintStats */
# endif
/* DEBUG */
# if defined(_PR_POLL_WITH_SELECT)
/*
* HPUX report the POLLHUP event for a socket when the
* shutdown(SHUT_WR) operation is called for the remote end, even though
* the socket is still writeable. Use select(), instead of poll(), to
* workaround this problem.
*/
static void pt_poll_now_with_select(pt_Continuation* op) {
PRInt32 msecs;
fd_set rd, wr, *rdp, *wrp;
struct timeval tv;
PRIntervalTime epoch, now, elapsed, remaining;
PRBool wait_for_remaining;
PRThread* self = PR_GetCurrentThread();
PR_ASSERT(PR_INTERVAL_NO_WAIT != op->timeout);
PR_ASSERT(op->arg1.osfd < FD_SETSIZE);
switch (op->timeout) {
case PR_INTERVAL_NO_TIMEOUT:
tv.tv_sec = PT_DEFAULT_SELECT_SEC;
tv.tv_usec = PT_DEFAULT_SELECT_USEC;
do {
PRIntn rv;
if (op->event & POLLIN) {
FD_ZERO(&rd);
FD_SET(op->arg1.osfd, &rd);
rdp = &rd;
}
else {
rdp = NULL;
}
if (op->event & POLLOUT) {
FD_ZERO(&wr);
FD_SET(op->arg1.osfd, &wr);
wrp = ≀
}
else {
wrp = NULL;
}
rv = select(op->arg1.osfd + 1, rdp, wrp, NULL, &tv);
if (_PT_THREAD_INTERRUPTED(self)) {
self->state &= ~PT_THREAD_ABORTED;
op->result.code = -1;
op->syserrno = EINTR;
op->status = pt_continuation_done;
return;
}
if ((-1 == rv) && ((errno == EINTR) || (errno == EAGAIN))) {
continue;
/* go around the loop again */
}
if (rv > 0) {
PRInt16 revents = 0;
if ((op->event & POLLIN) && FD_ISSET(op->arg1.osfd, &rd)) {
revents |= POLLIN;
}
if ((op->event & POLLOUT) && FD_ISSET(op->arg1.osfd, &wr)) {
revents |= POLLOUT;
}
if (op->function(op, revents)) {
op->status = pt_continuation_done;
}
}
else if (rv == -1) {
op->result.code = -1;
op->syserrno = errno;
op->status = pt_continuation_done;
}
/* else, select timed out */
}
while (pt_continuation_done != op->status);
break;
default:
now = epoch = PR_IntervalNow();
remaining = op->timeout;
do {
PRIntn rv;
if (op->event & POLLIN) {
FD_ZERO(&rd);
FD_SET(op->arg1.osfd, &rd);
rdp = &rd;
}
else {
rdp = NULL;
}
if (op->event & POLLOUT) {
FD_ZERO(&wr);
FD_SET(op->arg1.osfd, &wr);
wrp = ≀
}
else {
wrp = NULL;
}
wait_for_remaining = PR_TRUE;
msecs = (PRInt32)PR_IntervalToMilliseconds(remaining);
if (msecs > PT_DEFAULT_POLL_MSEC) {
wait_for_remaining = PR_FALSE;
msecs = PT_DEFAULT_POLL_MSEC;
}
tv.tv_sec = msecs / PR_MSEC_PER_SEC;
tv.tv_usec = (msecs % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC;
rv = select(op->arg1.osfd + 1, rdp, wrp, NULL, &tv);
if (_PT_THREAD_INTERRUPTED(self)) {
self->state &= ~PT_THREAD_ABORTED;
op->result.code = -1;
op->syserrno = EINTR;
op->status = pt_continuation_done;
return;
}
if (rv > 0) {
PRInt16 revents = 0;
if ((op->event & POLLIN) && FD_ISSET(op->arg1.osfd, &rd)) {
revents |= POLLIN;
}
if ((op->event & POLLOUT) && FD_ISSET(op->arg1.osfd, &wr)) {
revents |= POLLOUT;
}
if (op->function(op, revents)) {
op->status = pt_continuation_done;
}
}
else if ((rv == 0) || ((errno == EINTR) || (errno == EAGAIN))) {
if (rv == 0) {
/* select timed out */
if (wait_for_remaining) {
now += remaining;
}
else {
now += PR_MillisecondsToInterval(msecs);
}
}
else {
now = PR_IntervalNow();
}
elapsed = (PRIntervalTime)(now - epoch);
if (elapsed >= op->timeout) {
op->result.code = -1;
op->syserrno = ETIMEDOUT;
op->status = pt_continuation_done;
}
else {
remaining = op->timeout - elapsed;
}
}
else {
op->result.code = -1;
op->syserrno = errno;
op->status = pt_continuation_done;
}
}
while (pt_continuation_done != op->status);
break;
}
}
/* pt_poll_now_with_select */
# endif
/* _PR_POLL_WITH_SELECT */
static void pt_poll_now(pt_Continuation* op) {
PRInt32 msecs;
PRIntervalTime epoch, now, elapsed, remaining;
PRBool wait_for_remaining;
PRThread* self = PR_GetCurrentThread();
PR_ASSERT(PR_INTERVAL_NO_WAIT != op->timeout);
# if defined(_PR_POLL_WITH_SELECT)
/*
* If the fd is small enough call the select-based poll operation
*/
if (op->arg1.osfd < FD_SETSIZE) {
pt_poll_now_with_select(op);
return;
}
# endif
switch (op->timeout) {
case PR_INTERVAL_NO_TIMEOUT:
msecs = PT_DEFAULT_POLL_MSEC;
do {
PRIntn rv;
struct pollfd tmp_pfd;
tmp_pfd.revents = 0;
tmp_pfd.fd = op->arg1.osfd;
tmp_pfd.events = op->event;
rv = poll(&tmp_pfd, 1, msecs);
if (_PT_THREAD_INTERRUPTED(self)) {
self->state &= ~PT_THREAD_ABORTED;
op->result.code = -1;
op->syserrno = EINTR;
op->status = pt_continuation_done;
return;
}
if ((-1 == rv) && ((errno == EINTR) || (errno == EAGAIN))) {
continue;
/* go around the loop again */
}
if (rv > 0) {
PRInt16 events = tmp_pfd.events;
PRInt16 revents = tmp_pfd.revents;
if ((revents & POLLNVAL)
/* busted in all cases */
|| ((events & POLLOUT) && (revents & POLLHUP)))
/* write op & hup */
{
op->result.code = -1;
if (POLLNVAL & revents) {
op->syserrno = EBADF;
}
else if (POLLHUP & revents) {
op->syserrno = EPIPE;
}
op->status = pt_continuation_done;
}
else {
if (op->function(op, revents)) {
op->status = pt_continuation_done;
}
}
}
else if (rv == -1) {
op->result.code = -1;
op->syserrno = errno;
op->status = pt_continuation_done;
}
/* else, poll timed out */
}
while (pt_continuation_done != op->status);
break;
default:
now = epoch = PR_IntervalNow();
remaining = op->timeout;
do {
PRIntn rv;
struct pollfd tmp_pfd;
tmp_pfd.revents = 0;
tmp_pfd.fd = op->arg1.osfd;
tmp_pfd.events = op->event;
wait_for_remaining = PR_TRUE;
msecs = (PRInt32)PR_IntervalToMilliseconds(remaining);
if (msecs > PT_DEFAULT_POLL_MSEC) {
wait_for_remaining = PR_FALSE;
msecs = PT_DEFAULT_POLL_MSEC;
}
rv = poll(&tmp_pfd, 1, msecs);
if (_PT_THREAD_INTERRUPTED(self)) {
self->state &= ~PT_THREAD_ABORTED;
op->result.code = -1;
op->syserrno = EINTR;
op->status = pt_continuation_done;
return;
}
if (rv > 0) {
PRInt16 events = tmp_pfd.events;
PRInt16 revents = tmp_pfd.revents;
if ((revents & POLLNVAL)
/* busted in all cases */
|| ((events & POLLOUT) && (revents & POLLHUP)))
/* write op & hup */
{
op->result.code = -1;
if (POLLNVAL & revents) {
op->syserrno = EBADF;
}
else if (POLLHUP & revents) {
op->syserrno = EPIPE;
}
op->status = pt_continuation_done;
}
else {
if (op->function(op, revents)) {
op->status = pt_continuation_done;
}
}
}
else if ((rv == 0) || ((errno == EINTR) || (errno == EAGAIN))) {
if (rv == 0)
/* poll timed out */
{
if (wait_for_remaining) {
now += remaining;
}
else {
now += PR_MillisecondsToInterval(msecs);
}
}
else {
now = PR_IntervalNow();
}
elapsed = (PRIntervalTime)(now - epoch);
if (elapsed >= op->timeout) {
op->result.code = -1;
op->syserrno = ETIMEDOUT;
op->status = pt_continuation_done;
}
else {
remaining = op->timeout - elapsed;
}
}
else {
op->result.code = -1;
op->syserrno = errno;
op->status = pt_continuation_done;
}
}
while (pt_continuation_done != op->status);
break;
}
}
/* pt_poll_now */
static PRIntn pt_Continue(pt_Continuation* op) {
op->status = pt_continuation_pending;
/* set default value */
/*
* let each thread call poll directly
*/
pt_poll_now(op);
PR_ASSERT(pt_continuation_done == op->status);
return op->result.code;
}
/* pt_Continue */
/*****************************************************************************/
/*********************** specific continuation functions *********************/
/*****************************************************************************/
static PRBool pt_connect_cont(pt_Continuation* op, PRInt16 revents) {
op->syserrno = _MD_unix_get_nonblocking_connect_error(op->arg1.osfd);
if (op->syserrno != 0) {
op->result.code = -1;
}
else {
op->result.code = 0;
}
return PR_TRUE;
/* this one is cooked */
}
/* pt_connect_cont */
static PRBool pt_accept_cont(pt_Continuation* op, PRInt16 revents) {
op->syserrno = 0;
op->result.code = accept(op->arg1.osfd, op->arg2.buffer, op->arg3.addr_len);
if (-1 == op->result.code) {
op->syserrno = errno;
if (EWOULDBLOCK == errno || EAGAIN == errno || ECONNABORTED == errno) {
return PR_FALSE;
/* do nothing - this one ain't finished */
}
}
return PR_TRUE;
}
/* pt_accept_cont */
static PRBool pt_read_cont(pt_Continuation* op, PRInt16 revents) {
/*
* Any number of bytes will complete the operation. It need
* not (and probably will not) satisfy the request. The only
* error we continue is EWOULDBLOCK|EAGAIN.
*/
op->result.code = read(op->arg1.osfd, op->arg2.buffer, op->arg3.amount);
op->syserrno = errno;
return ((-1 == op->result.code) &&
(EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno))
? PR_FALSE
: PR_TRUE;
}
/* pt_read_cont */
static PRBool pt_recv_cont(pt_Continuation* op, PRInt16 revents) {
/*
* Any number of bytes will complete the operation. It need
* not (and probably will not) satisfy the request. The only
* error we continue is EWOULDBLOCK|EAGAIN.
*/
# if defined(SOLARIS)
if (0 == op->arg4.flags)
op->result.code = read(op->arg1.osfd, op->arg2.buffer, op->arg3.amount);
else
op->result.code =
recv(op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags);
# else
op->result.code =
recv(op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags);
# endif
op->syserrno = errno;
return ((-1 == op->result.code) &&
(EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno))
? PR_FALSE
: PR_TRUE;
}
/* pt_recv_cont */
static PRBool pt_send_cont(pt_Continuation* op, PRInt16 revents) {
PRIntn bytes;
# if defined(SOLARIS)
PRInt32 tmp_amount = op->arg3.amount;
# endif
/*
* We want to write the entire amount out, no matter how many
* tries it takes. Keep advancing the buffer and the decrementing
* the amount until the amount goes away. Return the total bytes
* (which should be the original amount) when finished (or an
* error).
*/
# if defined(SOLARIS)
retry:
bytes = write(op->arg1.osfd, op->arg2.buffer, tmp_amount);
# else
bytes = send(op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags);
# endif
op->syserrno = errno;
# if defined(SOLARIS)
/*
* The write system call has been reported to return the ERANGE error
* on occasion. Try to write in smaller chunks to workaround this bug.
*/
if ((bytes == -1) && (op->syserrno == ERANGE)) {
if (tmp_amount > 1) {
tmp_amount = tmp_amount / 2;
/* half the bytes */
goto retry;
}
}
# endif
if (bytes >= 0)
/* this is progress */
{
char* bp = (
char*)op->arg2.buffer;
bp += bytes;
/* adjust the buffer pointer */
op->arg2.buffer = bp;
op->result.code += bytes;
/* accumulate the number sent */
op->arg3.amount -= bytes;
/* and reduce the required count */
return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
}
if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) {
op->result.code = -1;
return PR_TRUE;
}
else {
return PR_FALSE;
}
}
/* pt_send_cont */
static PRBool pt_write_cont(pt_Continuation* op, PRInt16 revents) {
PRIntn bytes;
/*
* We want to write the entire amount out, no matter how many
* tries it takes. Keep advancing the buffer and the decrementing
* the amount until the amount goes away. Return the total bytes
* (which should be the original amount) when finished (or an
* error).
*/
bytes = write(op->arg1.osfd, op->arg2.buffer, op->arg3.amount);
op->syserrno = errno;
if (bytes >= 0)
/* this is progress */
{
char* bp = (
char*)op->arg2.buffer;
bp += bytes;
/* adjust the buffer pointer */
op->arg2.buffer = bp;
op->result.code += bytes;
/* accumulate the number sent */
op->arg3.amount -= bytes;
/* and reduce the required count */
return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
}
if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) {
op->result.code = -1;
return PR_TRUE;
}
else {
return PR_FALSE;
}
}
/* pt_write_cont */
static PRBool pt_writev_cont(pt_Continuation* op, PRInt16 revents) {
PRIntn bytes;
struct iovec* iov = (
struct iovec*)op->arg2.buffer;
/*
* Same rules as write, but continuing seems to be a bit more
* complicated. As the number of bytes sent grows, we have to
* redefine the vector we're pointing at. We might have to
* modify an individual vector parms or we might have to eliminate
* a pair altogether.
*/
bytes = writev(op->arg1.osfd, iov, op->arg3.amount);
op->syserrno = errno;
if (bytes >= 0)
/* this is progress */
{
PRIntn iov_index;
op->result.code += bytes;
/* accumulate the number sent */
for (iov_index = 0; iov_index < op->arg3.amount; ++iov_index) {
/* how much progress did we make in the i/o vector? */
if (bytes < iov[iov_index].iov_len) {
/* this element's not done yet */
char** bp = (
char**)&(iov[iov_index].iov_base);
iov[iov_index].iov_len -= bytes;
/* there's that much left */
*bp += bytes;
/* starting there */
break;
/* go off and do that */
}
bytes -= iov[iov_index].iov_len;
/* that element's consumed */
}
op->arg2.buffer = &iov[iov_index];
/* new start of array */
op->arg3.amount -= iov_index;
/* and array length */
return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
}
if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) {
op->result.code = -1;
return PR_TRUE;
}
else {
return PR_FALSE;
}
}
/* pt_writev_cont */
static PRBool pt_sendto_cont(pt_Continuation* op, PRInt16 revents) {
PRIntn bytes =
sendto(op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
(
struct sockaddr*)op->arg5.addr, PR_NETADDR_SIZE(op->arg5.addr));
op->syserrno = errno;
if (bytes >= 0)
/* this is progress */
{
char* bp = (
char*)op->arg2.buffer;
bp += bytes;
/* adjust the buffer pointer */
op->arg2.buffer = bp;
op->result.code += bytes;
/* accumulate the number sent */
op->arg3.amount -= bytes;
/* and reduce the required count */
return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
}
if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) {
op->result.code = -1;
return PR_TRUE;
}
else {
return PR_FALSE;
}
}
/* pt_sendto_cont */
static PRBool pt_recvfrom_cont(pt_Continuation* op, PRInt16 revents) {
pt_SockLen addr_len =
sizeof(PRNetAddr);
op->result.code =
recvfrom(op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
(
struct sockaddr*)op->arg5.addr, &addr_len);
op->syserrno = errno;
return ((-1 == op->result.code) &&
(EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno))
? PR_FALSE
: PR_TRUE;
}
/* pt_recvfrom_cont */
# ifdef AIX
static PRBool pt_aix_sendfile_cont(pt_Continuation* op, PRInt16 revents) {
struct sf_parms* sf_struct = (
struct sf_parms*)op->arg2.buffer;
ssize_t rv;
unsigned long long saved_file_offset;
long long saved_file_bytes;
saved_file_offset = sf_struct->file_offset;
saved_file_bytes = sf_struct->file_bytes;
sf_struct->bytes_sent = 0;
if ((sf_struct->file_bytes > 0) && (sf_struct->file_size > 0))
PR_ASSERT((sf_struct->file_bytes + sf_struct->file_offset) <=
sf_struct->file_size);
rv = AIX_SEND_FILE(&op->arg1.osfd, sf_struct, op->arg4.flags);
op->syserrno = errno;
if (rv != -1) {
op->result.code += sf_struct->bytes_sent;
/*
* A bug in AIX 4.3.2 prevents the 'file_bytes' field from
* being updated. So, 'file_bytes' is maintained by NSPR to
* avoid conflict when this bug is fixed in AIX, in the future.
*/
if (saved_file_bytes != -1) {
saved_file_bytes -= (sf_struct->file_offset - saved_file_offset);
}
sf_struct->file_bytes = saved_file_bytes;
}
else if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) {
op->result.code = -1;
}
else {
return PR_FALSE;
}
if (rv == 1) {
/* more data to send */
return PR_FALSE;
}
return PR_TRUE;
}
# endif
/* AIX */
# ifdef HPUX11
static PRBool pt_hpux_sendfile_cont(pt_Continuation* op, PRInt16 revents) {
struct iovec* hdtrl = (
struct iovec*)op->arg2.buffer;
int count;
count = sendfile(op->arg1.osfd, op->filedesc, op->arg3.file_spec.offset,
op->arg3.file_spec.nbytes, hdtrl, op->arg4.flags);
PR_ASSERT(count <= op->nbytes_to_send);
op->syserrno = errno;
if (count != -1) {
op->result.code += count;
}
else if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) {
op->result.code = -1;
}
else {
return PR_FALSE;
}
if (count != -1 && count < op->nbytes_to_send) {
if (count < hdtrl[0].iov_len) {
/* header not sent */
hdtrl[0].iov_base = ((
char*)hdtrl[0].iov_base) + count;
hdtrl[0].iov_len -= count;
}
else if (count < (hdtrl[0].iov_len + op->arg3.file_spec.nbytes)) {
/* header sent, file not sent */
PRUint32 file_nbytes_sent = count - hdtrl[0].iov_len;
hdtrl[0].iov_base = NULL;
hdtrl[0].iov_len = 0;
op->arg3.file_spec.offset += file_nbytes_sent;
op->arg3.file_spec.nbytes -= file_nbytes_sent;
}
else if (count < (hdtrl[0].iov_len + op->arg3.file_spec.nbytes +
hdtrl[1].iov_len)) {
PRUint32 trailer_nbytes_sent =
count - (hdtrl[0].iov_len + op->arg3.file_spec.nbytes);
/* header sent, file sent, trailer not sent */
hdtrl[0].iov_base = NULL;
hdtrl[0].iov_len = 0;
/*
* set file offset and len so that no more file data is
* sent
*/
op->arg3.file_spec.offset = op->arg3.file_spec.st_size;
op->arg3.file_spec.nbytes = 0;
hdtrl[1].iov_base = ((
char*)hdtrl[1].iov_base) + trailer_nbytes_sent;
hdtrl[1].iov_len -= trailer_nbytes_sent;
}
op->nbytes_to_send -= count;
return PR_FALSE;
}
return PR_TRUE;
}
# endif
/* HPUX11 */
# ifdef SOLARIS
static PRBool pt_solaris_sendfile_cont(pt_Continuation* op, PRInt16 revents) {
struct sendfilevec* vec = (
struct sendfilevec*)op->arg2.buffer;
size_t xferred;
ssize_t count;
count = SOLARIS_SENDFILEV(op->arg1.osfd, vec, op->arg3.amount, &xferred);
op->syserrno = errno;
PR_ASSERT((count == -1) || (count == xferred));
if (count == -1) {
if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN &&
op->syserrno != EINTR) {
op->result.code = -1;
return PR_TRUE;
}
count = xferred;
}
else if (count == 0) {
/*
* We are now at EOF. The file was truncated. Solaris sendfile is
* supposed to return 0 and no error in this case, though some versions
* may return -1 and EINVAL .
*/
op->result.code = -1;
op->syserrno = 0;
/* will be treated as EOF */
return PR_TRUE;
}
PR_ASSERT(count <= op->nbytes_to_send);
op->result.code += count;
if (count < op->nbytes_to_send) {
op->nbytes_to_send -= count;
while (count >= vec->sfv_len) {
count -= vec->sfv_len;
vec++;
op->arg3.amount--;
}
PR_ASSERT(op->arg3.amount > 0);
vec->sfv_off += count;
vec->sfv_len -= count;
PR_ASSERT(vec->sfv_len > 0);
op->arg2.buffer = vec;
return PR_FALSE;
}
return PR_TRUE;
}
# endif
/* SOLARIS */
# ifdef LINUX
static PRBool pt_linux_sendfile_cont(pt_Continuation* op, PRInt16 revents) {
ssize_t rv;
off_t oldoffset;
oldoffset = op->offset;
rv = sendfile(op->arg1.osfd, op->in_fd, &op->offset, op->count);
op->syserrno = errno;
if (rv == -1) {
if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) {
op->result.code = -1;
return PR_TRUE;
}
rv = 0;
}
PR_ASSERT(rv == op->offset - oldoffset);
op->result.code += rv;
if (rv < op->count) {
op->count -= rv;
return PR_FALSE;
}
return PR_TRUE;
}
# endif
/* LINUX */
void _PR_InitIO(
void) {
# if defined(DEBUG)
memset(&pt_debug, 0,
sizeof(PTDebug));
pt_debug.timeStarted = PR_Now();
# endif
_pr_flock_lock = PR_NewLock();
PR_ASSERT(NULL != _pr_flock_lock);
_pr_flock_cv = PR_NewCondVar(_pr_flock_lock);
PR_ASSERT(NULL != _pr_flock_cv);
_pr_rename_lock = PR_NewLock();
PR_ASSERT(NULL != _pr_rename_lock);
_PR_InitFdCache();
/* do that */
_pr_stdin = pt_SetMethods(0, PR_DESC_FILE, PR_FALSE, PR_TRUE);
_pr_stdout = pt_SetMethods(1, PR_DESC_FILE, PR_FALSE, PR_TRUE);
_pr_stderr = pt_SetMethods(2, PR_DESC_FILE, PR_FALSE, PR_TRUE);
PR_ASSERT(_pr_stdin && _pr_stdout && _pr_stderr);
# ifdef _PR_IPV6_V6ONLY_PROBE
/* In Mac OS X v10.3 Panther Beta the IPV6_V6ONLY socket option
* is turned on by default, contrary to what RFC 3493, Section
* 5.3 says. So we have to turn it off. Find out whether we
* are running on such a system.
*/
{
int osfd;
osfd = socket(AF_INET6, SOCK_STREAM, 0);
if (osfd != -1) {
int on;
socklen_t optlen =
sizeof(on);
if (getsockopt(osfd, IPPROTO_IPV6, IPV6_V6ONLY, &on, &optlen) == 0) {
_pr_ipv6_v6only_on_by_default = on;
}
close(osfd);
}
}
# endif
}
/* _PR_InitIO */
void _PR_CleanupIO(
void) {
_PR_Putfd(_pr_stdin);
_pr_stdin = NULL;
_PR_Putfd(_pr_stdout);
_pr_stdout = NULL;
_PR_Putfd(_pr_stderr);
_pr_stderr = NULL;
_PR_CleanupFdCache();
if (_pr_flock_cv) {
PR_DestroyCondVar(_pr_flock_cv);
_pr_flock_cv = NULL;
}
if (_pr_flock_lock) {
PR_DestroyLock(_pr_flock_lock);
_pr_flock_lock = NULL;
}
if (_pr_rename_lock) {
PR_DestroyLock(_pr_rename_lock);
_pr_rename_lock = NULL;
}
}
/* _PR_CleanupIO */
PR_IMPLEMENT(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD osfd) {
PRFileDesc* result = NULL;
PR_ASSERT(osfd >= PR_StandardInput && osfd <= PR_StandardError);
if (!_pr_initialized) {
_PR_ImplicitInitialization();
}
switch (osfd) {
case PR_StandardInput:
result = _pr_stdin;
break;
case PR_StandardOutput:
result = _pr_stdout;
break;
case PR_StandardError:
result = _pr_stderr;
break;
default:
(
void)PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
}
return result;
}
/* PR_GetSpecialFD */
/*****************************************************************************/
/***************************** I/O private methods ***************************/
/*****************************************************************************/
static PRBool pt_TestAbort(
void) {
PRThread* me = PR_GetCurrentThread();
if (_PT_THREAD_INTERRUPTED(me)) {
PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
me->state &= ~PT_THREAD_ABORTED;
return PR_TRUE;
}
return PR_FALSE;
}
/* pt_TestAbort */
static void pt_MapError(
void (*mapper)(PRIntn), PRIntn syserrno) {
switch (syserrno) {
case EINTR:
PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
break;
case ETIMEDOUT:
PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
break;
default:
mapper(syserrno);
}
}
/* pt_MapError */
static PRStatus pt_Close(PRFileDesc* fd) {
if ((NULL == fd) || (NULL == fd->secret) ||
((_PR_FILEDESC_OPEN != fd->secret->state) &&
(_PR_FILEDESC_CLOSED != fd->secret->state))) {
PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
return PR_FAILURE;
}
if (pt_TestAbort()) {
return PR_FAILURE;
}
if (_PR_FILEDESC_OPEN == fd->secret->state) {
if (-1 == close(fd->secret->md.osfd)) {
pt_MapError(_PR_MD_MAP_CLOSE_ERROR, errno);
return PR_FAILURE;
}
fd->secret->state = _PR_FILEDESC_CLOSED;
}
_PR_Putfd(fd);
return PR_SUCCESS;
}
/* pt_Close */
static PRInt32 pt_Read(PRFileDesc* fd,
void* buf, PRInt32 amount) {
PRInt32 syserrno, bytes = -1;
if (pt_TestAbort()) {
return bytes;
}
bytes = read(fd->secret->md.osfd, buf, amount);
syserrno = errno;
if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) &&
(!fd->secret->nonblocking)) {
pt_Continuation op;
op.arg1.osfd = fd->secret->md.osfd;
op.arg2.buffer = buf;
op.arg3.amount = amount;
op.timeout = PR_INTERVAL_NO_TIMEOUT;
op.function = pt_read_cont;
op.event = POLLIN | POLLPRI;
bytes = pt_Continue(&op);
syserrno = op.syserrno;
}
if (bytes < 0) {
pt_MapError(_PR_MD_MAP_READ_ERROR, syserrno);
}
return bytes;
}
/* pt_Read */
static PRInt32 pt_Write(PRFileDesc* fd,
const void* buf, PRInt32 amount) {
PRInt32 syserrno, bytes = -1;
PRBool fNeedContinue = PR_FALSE;
if (pt_TestAbort()) {
return bytes;
}
bytes = write(fd->secret->md.osfd, buf, amount);
syserrno = errno;
if ((bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking)) {
buf = (
char*)buf + bytes;
amount -= bytes;
fNeedContinue = PR_TRUE;
}
if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) &&
(!fd->secret->nonblocking)) {
bytes = 0;
fNeedContinue = PR_TRUE;
}
if (fNeedContinue == PR_TRUE) {
pt_Continuation op;
op.arg1.osfd = fd->secret->md.osfd;
op.arg2.buffer = (
void*)buf;
op.arg3.amount = amount;
op.timeout = PR_INTERVAL_NO_TIMEOUT;
op.result.code = bytes;
/* initialize the number sent */
op.function = pt_write_cont;
op.event = POLLOUT | POLLPRI;
bytes = pt_Continue(&op);
syserrno = op.syserrno;
}
if (bytes == -1) {
pt_MapError(_PR_MD_MAP_WRITE_ERROR, syserrno);
}
return bytes;
}
/* pt_Write */
static PRInt32 pt_Writev(PRFileDesc* fd,
const PRIOVec* iov, PRInt32 iov_len,
PRIntervalTime timeout) {
PRIntn iov_index;
PRBool fNeedContinue = PR_FALSE;
PRInt32 syserrno, bytes, rv = -1;
struct iovec osiov_local[PR_MAX_IOVECTOR_SIZE], *osiov;
int osiov_len;
if (pt_TestAbort()) {
return rv;
}
/* Ensured by PR_Writev */
PR_ASSERT(iov_len <= PR_MAX_IOVECTOR_SIZE);
/*
* We can't pass iov to writev because PRIOVec and struct iovec
* may not be binary compatible. Make osiov a copy of iov and
* pass osiov to writev. We can modify osiov if we need to
* continue the operation.
*/
osiov = osiov_local;
osiov_len = iov_len;
for (iov_index = 0; iov_index < osiov_len; iov_index++) {
osiov[iov_index].iov_base = iov[iov_index].iov_base;
osiov[iov_index].iov_len = iov[iov_index].iov_len;
}
rv = bytes = writev(fd->secret->md.osfd, osiov, osiov_len);
syserrno = errno;
if (!fd->secret->nonblocking) {
if (bytes >= 0) {
/*
* If we moved some bytes, how does that implicate the
* i/o vector list? In other words, exactly where are
* we within that array? What are the parameters for
* resumption? Maybe we're done!
*/
for (; osiov_len > 0; osiov++, osiov_len--) {
if (bytes < osiov->iov_len) {
/* this one's not done yet */
osiov->iov_base = (
char*)osiov->iov_base + bytes;
osiov->iov_len -= bytes;
break;
/* go off and do that */
}
bytes -= osiov->iov_len;
/* this one's done cooked */
}
PR_ASSERT(osiov_len > 0 || bytes == 0);
if (osiov_len > 0) {
if (PR_INTERVAL_NO_WAIT == timeout) {
rv = -1;
syserrno = ETIMEDOUT;
}
else {
fNeedContinue = PR_TRUE;
}
}
}
else if (syserrno == EWOULDBLOCK || syserrno == EAGAIN) {
if (PR_INTERVAL_NO_WAIT == timeout) {
syserrno = ETIMEDOUT;
}
else {
rv = 0;
fNeedContinue = PR_TRUE;
}
}
}
if (fNeedContinue == PR_TRUE) {
pt_Continuation op;
op.arg1.osfd = fd->secret->md.osfd;
op.arg2.buffer = (
void*)osiov;
op.arg3.amount = osiov_len;
op.timeout = timeout;
op.result.code = rv;
op.function = pt_writev_cont;
op.event = POLLOUT | POLLPRI;
rv = pt_Continue(&op);
syserrno = op.syserrno;
}
if (rv == -1) {
pt_MapError(_PR_MD_MAP_WRITEV_ERROR, syserrno);
}
return rv;
}
/* pt_Writev */
static PRInt32 pt_Seek(PRFileDesc* fd, PRInt32 offset, PRSeekWhence whence) {
return _PR_MD_LSEEK(fd, offset, whence);
}
/* pt_Seek */
static PRInt64 pt_Seek64(PRFileDesc* fd, PRInt64 offset, PRSeekWhence whence) {
return _PR_MD_LSEEK64(fd, offset, whence);
}
/* pt_Seek64 */
static PRInt32 pt_Available_f(PRFileDesc* fd) {
PRInt32 result, cur, end;
cur = _PR_MD_LSEEK(fd, 0, PR_SEEK_CUR);
if (cur >= 0) {
end = _PR_MD_LSEEK(fd, 0, PR_SEEK_END);
}
if ((cur < 0) || (end < 0)) {
return -1;
}
result = end - cur;
_PR_MD_LSEEK(fd, cur, PR_SEEK_SET);
return result;
}
/* pt_Available_f */
static PRInt64 pt_Available64_f(PRFileDesc* fd) {
PRInt64 result, cur, end;
PRInt64 minus_one;
LL_I2L(minus_one, -1);
cur = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_CUR);
if (LL_GE_ZERO(cur)) {
end = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_END);
}
if (!LL_GE_ZERO(cur) || !LL_GE_ZERO(end)) {
return minus_one;
}
LL_SUB(result, end, cur);
(
void)_PR_MD_LSEEK64(fd, cur, PR_SEEK_SET);
return result;
}
/* pt_Available64_f */
static PRInt32 pt_Available_s(PRFileDesc* fd) {
PRInt32 rv, bytes = -1;
if (pt_TestAbort()) {
return bytes;
}
rv = ioctl(fd->secret->md.osfd, FIONREAD, &bytes);
if (rv == -1) {
pt_MapError(_PR_MD_MAP_SOCKETAVAILABLE_ERROR, errno);
}
return bytes;
}
/* pt_Available_s */
static PRInt64 pt_Available64_s(PRFileDesc* fd) {
PRInt64 rv;
LL_I2L(rv, pt_Available_s(fd));
return rv;
}
/* pt_Available64_s */
static PRStatus pt_FileInfo(PRFileDesc* fd, PRFileInfo* info) {
PRInt32 rv = _PR_MD_GETOPENFILEINFO(fd, info);
return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
}
/* pt_FileInfo */
static PRStatus pt_FileInfo64(PRFileDesc* fd, PRFileInfo64* info) {
PRInt32 rv = _PR_MD_GETOPENFILEINFO64(fd, info);
return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
}
/* pt_FileInfo64 */
static PRStatus pt_Synch(PRFileDesc* fd) {
return (NULL == fd) ? PR_FAILURE : PR_SUCCESS;
}
/* pt_Synch */
static PRStatus pt_Fsync(PRFileDesc* fd) {
PRIntn rv = -1;
if (pt_TestAbort()) {
return PR_FAILURE;
}
rv = fsync(fd->secret->md.osfd);
if (rv < 0) {
pt_MapError(_PR_MD_MAP_FSYNC_ERROR, errno);
return PR_FAILURE;
}
return PR_SUCCESS;
}
/* pt_Fsync */
static PRStatus pt_Connect(PRFileDesc* fd,
const PRNetAddr* addr,
PRIntervalTime timeout) {
PRIntn rv = -1, syserrno;
pt_SockLen addr_len;
const PRNetAddr* addrp = addr;
# if defined(_PR_HAVE_SOCKADDR_LEN) ||
defined(_PR_INET6)
PRNetAddr addrCopy;
# endif
# ifdef _PR_HAVE_SOCKADDR_LEN
PRUint16 md_af = addr->raw.family;
# endif
if (pt_TestAbort()) {
return PR_FAILURE;
}
PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
addr_len = PR_NETADDR_SIZE(addr);
# ifdef _PR_INET6
if (addr->raw.family == PR_AF_INET6) {
# ifdef _PR_HAVE_SOCKADDR_LEN
md_af = AF_INET6;
# else
addrCopy = *addr;
addrCopy.raw.family = AF_INET6;
addrp = &addrCopy;
# endif
}
# endif
# ifdef _PR_HAVE_SOCKADDR_LEN
addrCopy = *addr;
((
struct sockaddr*)&addrCopy)->sa_len = addr_len;
((
struct sockaddr*)&addrCopy)->sa_family = md_af;
addrp = &addrCopy;
# endif
rv = connect(fd->secret->md.osfd, (
struct sockaddr*)addrp, addr_len);
syserrno = errno;
if ((-1 == rv) && (EINPROGRESS == syserrno) && (!fd->secret->nonblocking)) {
if (PR_INTERVAL_NO_WAIT == timeout) {
syserrno = ETIMEDOUT;
}
else {
pt_Continuation op;
op.arg1.osfd = fd->secret->md.osfd;
op.arg2.buffer = (
void*)addrp;
op.arg3.amount = addr_len;
op.timeout = timeout;
op.function = pt_connect_cont;
op.event = POLLOUT | POLLPRI;
rv = pt_Continue(&op);
syserrno = op.syserrno;
}
}
if (-1 == rv) {
pt_MapError(_PR_MD_MAP_CONNECT_ERROR, syserrno);
return PR_FAILURE;
}
return PR_SUCCESS;
}
/* pt_Connect */
static PRStatus pt_ConnectContinue(PRFileDesc* fd, PRInt16 out_flags) {
int err;
PRInt32 osfd;
if (out_flags & PR_POLL_NVAL) {
PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
return PR_FAILURE;
}
if ((out_flags &
(PR_POLL_WRITE | PR_POLL_EXCEPT | PR_POLL_ERR | PR_POLL_HUP)) == 0) {
PR_ASSERT(out_flags == 0);
PR_SetError(PR_IN_PROGRESS_ERROR, 0);
return PR_FAILURE;
}
osfd = fd->secret->md.osfd;
err = _MD_unix_get_nonblocking_connect_error(osfd);
if (err != 0) {
_PR_MD_MAP_CONNECT_ERROR(err);
return PR_FAILURE;
}
return PR_SUCCESS;
}
/* pt_ConnectContinue */
PR_IMPLEMENT(PRStatus) PR_GetConnectStatus(
const PRPollDesc* pd) {
/* Find the NSPR layer and invoke its connectcontinue method */
PRFileDesc* bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
if (NULL == bottom) {
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
return PR_FAILURE;
}
return pt_ConnectContinue(bottom, pd->out_flags);
}
/* PR_GetConnectStatus */
static PRFileDesc* pt_Accept(PRFileDesc* fd, PRNetAddr* addr,
PRIntervalTime timeout) {
PRFileDesc* newfd = NULL;
PRIntn syserrno, osfd = -1;
pt_SockLen addr_len =
sizeof(PRNetAddr);
if (pt_TestAbort()) {
return newfd;
}
# ifdef _PR_STRICT_ADDR_LEN
if (addr) {
/*
* Set addr->raw.family just so that we can use the
* PR_NETADDR_SIZE macro.
*/
addr->raw.family = fd->secret->af;
addr_len = PR_NETADDR_SIZE(addr);
}
# endif
osfd = accept(fd->secret->md.osfd, (
struct sockaddr*)addr, &addr_len);
syserrno = errno;
if (osfd == -1) {
if (fd->secret->nonblocking) {
goto failed;
}
if (EWOULDBLOCK != syserrno && EAGAIN != syserrno &&
ECONNABORTED != syserrno) {
goto failed;
}
else {
if (PR_INTERVAL_NO_WAIT == timeout) {
syserrno = ETIMEDOUT;
}
else {
pt_Continuation op;
op.arg1.osfd = fd->secret->md.osfd;
op.arg2.buffer = addr;
op.arg3.addr_len = &addr_len;
op.timeout = timeout;
op.function = pt_accept_cont;
op.event = POLLIN | POLLPRI;
osfd = pt_Continue(&op);
syserrno = op.syserrno;
}
if (osfd < 0) {
goto failed;
}
}
}
# ifdef _PR_HAVE_SOCKADDR_LEN
/* ignore the sa_len field of struct sockaddr */
if (addr) {
addr->raw.family = ((
struct sockaddr*)addr)->sa_family;
}
# endif
/* _PR_HAVE_SOCKADDR_LEN */
# ifdef _PR_INET6
if (addr && (AF_INET6 == addr->raw.family)) {
addr->raw.family = PR_AF_INET6;
}
# endif
newfd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP, PR_TRUE, PR_FALSE);
if (newfd == NULL) {
close(osfd);
/* $$$ whoops! this doesn't work $$$ */
}
else {
PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE);
# ifdef LINUX
/*
* On Linux, experiments showed that the accepted sockets
* inherit the TCP_NODELAY socket option of the listening
* socket.
*/
newfd->secret->md.tcp_nodelay = fd->secret->md.tcp_nodelay;
# endif
}
return newfd;
failed:
pt_MapError(_PR_MD_MAP_ACCEPT_ERROR, syserrno);
return NULL;
}
/* pt_Accept */
static PRStatus pt_Bind(PRFileDesc* fd,
const PRNetAddr* addr) {
PRIntn rv;
pt_SockLen addr_len;
const PRNetAddr* addrp = addr;
# if defined(_PR_HAVE_SOCKADDR_LEN) ||
defined(_PR_INET6)
PRNetAddr addrCopy;
# endif
# ifdef _PR_HAVE_SOCKADDR_LEN
PRUint16 md_af = addr->raw.family;
# endif
if (pt_TestAbort()) {
return PR_FAILURE;
}
PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
if (addr->raw.family == AF_UNIX) {
/* Disallow relative pathnames */
if (addr->local.path[0] !=
'/'
# if defined(LINUX)
/* Linux has abstract socket address support */
&& addr->local.path[0] != 0
# endif
) {
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
return PR_FAILURE;
}
}
# ifdef _PR_INET6
if (addr->raw.family == PR_AF_INET6) {
# ifdef _PR_HAVE_SOCKADDR_LEN
md_af = AF_INET6;
# else
addrCopy = *addr;
addrCopy.raw.family = AF_INET6;
addrp = &addrCopy;
# endif
}
# endif
addr_len = PR_NETADDR_SIZE(addr);
# ifdef _PR_HAVE_SOCKADDR_LEN
addrCopy = *addr;
((
struct sockaddr*)&addrCopy)->sa_len = addr_len;
((
struct sockaddr*)&addrCopy)->sa_family = md_af;
addrp = &addrCopy;
# endif
rv = bind(fd->secret->md.osfd, (
struct sockaddr*)addrp, addr_len);
if (rv == -1) {
pt_MapError(_PR_MD_MAP_BIND_ERROR, errno);
return PR_FAILURE;
}
return PR_SUCCESS;
}
/* pt_Bind */
static PRStatus pt_Listen(PRFileDesc* fd, PRIntn backlog) {
PRIntn rv;
if (pt_TestAbort()) {
return PR_FAILURE;
}
rv = listen(fd->secret->md.osfd, backlog);
if (rv == -1) {
pt_MapError(_PR_MD_MAP_LISTEN_ERROR, errno);
return PR_FAILURE;
}
return PR_SUCCESS;
}
/* pt_Listen */
static PRStatus pt_Shutdown(PRFileDesc* fd, PRIntn how) {
PRIntn rv = -1;
if (pt_TestAbort()) {
return PR_FAILURE;
}
rv = shutdown(fd->secret->md.osfd, how);
if (rv == -1) {
pt_MapError(_PR_MD_MAP_SHUTDOWN_ERROR, errno);
return PR_FAILURE;
}
return PR_SUCCESS;
}
/* pt_Shutdown */
static PRInt16 pt_Poll(PRFileDesc* fd, PRInt16 in_flags, PRInt16* out_flags) {
*out_flags = 0;
return in_flags;
}
/* pt_Poll */
static PRInt32 pt_Recv(PRFileDesc* fd,
void* buf, PRInt32 amount, PRIntn flags,
PRIntervalTime timeout) {
PRInt32 syserrno, bytes = -1;
PRIntn osflags;
if (0 == flags) {
osflags = 0;
}
else if (PR_MSG_PEEK == flags) {
osflags = MSG_PEEK;
}
else {
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
return bytes;
}
if (pt_TestAbort()) {
return bytes;
}
/* recv() is a much slower call on pre-2.6 Solaris than read(). */
# if defined(SOLARIS)
if (0 == osflags) {
bytes = read(fd->secret->md.osfd, buf, amount);
}
else {
bytes = recv(fd->secret->md.osfd, buf, amount, osflags);
}
# else
bytes = recv(fd->secret->md.osfd, buf, amount, osflags);
# endif
syserrno = errno;
if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) &&
(!fd->secret->nonblocking)) {
if (PR_INTERVAL_NO_WAIT == timeout) {
syserrno = ETIMEDOUT;
}
else {
pt_Continuation op;
op.arg1.osfd = fd->secret->md.osfd;
op.arg2.buffer = buf;
op.arg3.amount = amount;
op.arg4.flags = osflags;
op.timeout = timeout;
op.function = pt_recv_cont;
op.event = POLLIN | POLLPRI;
bytes = pt_Continue(&op);
syserrno = op.syserrno;
}
}
if (bytes < 0) {
pt_MapError(_PR_MD_MAP_RECV_ERROR, syserrno);
}
return bytes;
}
/* pt_Recv */
static PRInt32 pt_SocketRead(PRFileDesc* fd,
void* buf, PRInt32 amount) {
return pt_Recv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
}
/* pt_SocketRead */
static PRInt32 pt_Send(PRFileDesc* fd,
const void* buf, PRInt32 amount,
PRIntn flags, PRIntervalTime timeout) {
PRInt32 syserrno, bytes = -1;
PRBool fNeedContinue = PR_FALSE;
# if defined(SOLARIS)
PRInt32 tmp_amount = amount;
# endif
if (pt_TestAbort()) {
return bytes;
}
/*
* On pre-2.6 Solaris, send() is much slower than write().
* On 2.6 and beyond, with in-kernel sockets, send() and
* write() are fairly equivalent in performance.
*/
# if defined(SOLARIS)
PR_ASSERT(0 == flags);
retry:
bytes = write(fd->secret->md.osfd, buf, tmp_amount);
# else
bytes = send(fd->secret->md.osfd, buf, amount, flags);
# endif
syserrno = errno;
# if defined(SOLARIS)
/*
* The write system call has been reported to return the ERANGE error
* on occasion. Try to write in smaller chunks to workaround this bug.
*/
if ((bytes == -1) && (syserrno == ERANGE)) {
if (tmp_amount > 1) {
tmp_amount = tmp_amount / 2;
/* half the bytes */
goto retry;
}
}
# endif
if ((bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking)) {
if (PR_INTERVAL_NO_WAIT == timeout) {
bytes = -1;
syserrno = ETIMEDOUT;
}
else {
buf = (
char*)buf + bytes;
amount -= bytes;
fNeedContinue = PR_TRUE;
}
}
if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) &&
(!fd->secret->nonblocking)) {
if (PR_INTERVAL_NO_WAIT == timeout) {
syserrno = ETIMEDOUT;
}
else {
bytes = 0;
fNeedContinue = PR_TRUE;
}
}
if (fNeedContinue == PR_TRUE) {
pt_Continuation op;
op.arg1.osfd = fd->secret->md.osfd;
op.arg2.buffer = (
void*)buf;
op.arg3.amount = amount;
op.arg4.flags = flags;
op.timeout = timeout;
op.result.code = bytes;
/* initialize the number sent */
op.function = pt_send_cont;
op.event = POLLOUT | POLLPRI;
bytes = pt_Continue(&op);
syserrno = op.syserrno;
}
if (bytes == -1) {
pt_MapError(_PR_MD_MAP_SEND_ERROR, syserrno);
}
return bytes;
}
/* pt_Send */
static PRInt32 pt_SocketWrite(PRFileDesc* fd,
const void* buf, PRInt32 amount) {
return pt_Send(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
}
/* pt_SocketWrite */
static PRInt32 pt_SendTo(PRFileDesc* fd,
const void* buf, PRInt32 amount,
PRIntn flags,
const PRNetAddr* addr,
PRIntervalTime timeout) {
PRInt32 syserrno, bytes = -1;
PRBool fNeedContinue = PR_FALSE;
pt_SockLen addr_len;
const PRNetAddr* addrp = addr;
# if defined(_PR_HAVE_SOCKADDR_LEN) ||
defined(_PR_INET6)
PRNetAddr addrCopy;
# endif
# ifdef _PR_HAVE_SOCKADDR_LEN
PRUint16 md_af = addr->raw.family;
# endif
if (pt_TestAbort()) {
return bytes;
}
PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
# ifdef _PR_INET6
if (addr->raw.family == PR_AF_INET6) {
# ifdef _PR_HAVE_SOCKADDR_LEN
md_af = AF_INET6;
# else
addrCopy = *addr;
addrCopy.raw.family = AF_INET6;
addrp = &addrCopy;
# endif
}
# endif
addr_len = PR_NETADDR_SIZE(addr);
# ifdef _PR_HAVE_SOCKADDR_LEN
addrCopy = *addr;
((
struct sockaddr*)&addrCopy)->sa_len = addr_len;
((
struct sockaddr*)&addrCopy)->sa_family = md_af;
addrp = &addrCopy;
# endif
bytes = sendto(fd->secret->md.osfd, buf, amount, flags,
(
struct sockaddr*)addrp, addr_len);
syserrno = errno;
if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) &&
(!fd->secret->nonblocking)) {
if (PR_INTERVAL_NO_WAIT == timeout) {
syserrno = ETIMEDOUT;
}
else {
fNeedContinue = PR_TRUE;
}
}
if (fNeedContinue == PR_TRUE) {
pt_Continuation op;
op.arg1.osfd = fd->secret->md.osfd;
op.arg2.buffer = (
void*)buf;
op.arg3.amount = amount;
op.arg4.flags = flags;
op.arg5.addr = (PRNetAddr*)addrp;
op.timeout = timeout;
op.result.code = 0;
/* initialize the number sent */
op.function = pt_sendto_cont;
op.event = POLLOUT | POLLPRI;
bytes = pt_Continue(&op);
syserrno = op.syserrno;
}
if (bytes < 0) {
pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno);
}
return bytes;
}
/* pt_SendTo */
# if defined(LINUX) ||
defined(DARWIN)
/* Linux uses SendTo to send data during TCP Fast Open. OSX uses connectx, but
* we will make it imitate the Linux's interface. */
static PRInt32 pt_TCP_SendTo(PRFileDesc* fd,
const void* buf, PRInt32 amount,
PRIntn flags,
const PRNetAddr* addr,
PRIntervalTime timeout) {
# if defined(LINUX) || HAS_CONNECTX
PRInt32 syserrno, bytes = -1;
PRBool fNeedContinue = PR_FALSE;
pt_SockLen addr_len;
const PRNetAddr* addrp = addr;
# if defined(_PR_HAVE_SOCKADDR_LEN) ||
defined(_PR_INET6)
PRNetAddr addrCopy;
# endif
# ifdef _PR_HAVE_SOCKADDR_LEN
PRUint16 md_af = addr->raw.family;
# endif
if (pt_TestAbort()) {
return bytes;
}
PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
addr_len = PR_NETADDR_SIZE(addr);
# if defined(_PR_INET6)
if (addr->raw.family == PR_AF_INET6) {
# ifdef _PR_HAVE_SOCKADDR_LEN
md_af = AF_INET6;
# else
/* If _PR_INET6 is defined and it is PR_AF_INET6 we set family
* to AF_INET6. */
addrCopy = *addr;
addrCopy.raw.family = AF_INET6;
addrp = &addrCopy;
# endif
}
# endif
# ifdef _PR_HAVE_SOCKADDR_LEN
/* if _PR_HAVE_SOCKADDR_LEN is defined and it is PR_AF_INET6 we set family
* to AF_INET6 and we set address length. */
addrCopy = *addr;
((
struct sockaddr*)&addrCopy)->sa_len = addr_len;
((
struct sockaddr*)&addrCopy)->sa_family = md_af;
addrp = &addrCopy;
# endif
# ifndef HAS_CONNECTX
bytes = sendto(fd->secret->md.osfd, buf, amount, MSG_FASTOPEN,
(
struct sockaddr*)addrp, addr_len);
# else
sa_endpoints_t endpoints;
endpoints.sae_srcif = 0;
endpoints.sae_srcaddr = NULL;
endpoints.sae_srcaddrlen = 0;
endpoints.sae_dstaddr = (
struct sockaddr*)addrp;
endpoints.sae_dstaddrlen = addr_len;
struct iovec iov[1];
iov[0].iov_base = buf;
iov[0].iov_len = amount;
PRInt32 rv = connectx(fd->secret->md.osfd, &endpoints, SAE_ASSOCID_ANY,
CONNECT_DATA_IDEMPOTENT, iov, 1, &bytes, NULL);
# endif
syserrno = errno;
if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) &&
(!fd->secret->nonblocking)) {
if (PR_INTERVAL_NO_WAIT == timeout) {
syserrno = ETIMEDOUT;
}
else {
fNeedContinue = PR_TRUE;
}
}
if (fNeedContinue == PR_TRUE) {
pt_Continuation op;
op.arg1.osfd = fd->secret->md.osfd;
op.arg2.buffer = (
void*)buf;
op.arg3.amount = amount;
op.arg4.flags = flags;
op.arg5.addr = (PRNetAddr*)addrp;
op.timeout = timeout;
op.result.code = 0;
/* initialize the number sent */
op.function = pt_sendto_cont;
op.event = POLLOUT | POLLPRI;
bytes = pt_Continue(&op);
syserrno = op.syserrno;
}
if (bytes < 0) {
pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno);
}
return bytes;
# else /* !HAS_CONNECTX */
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
return -1;
# endif
}
/* pt_TCP_SendTo */
# endif
/* LINUX || DARWIN */
static PRInt32 pt_RecvFrom(PRFileDesc* fd,
void* buf, PRInt32 amount,
PRIntn flags, PRNetAddr* addr,
PRIntervalTime timeout) {
PRBool fNeedContinue = PR_FALSE;
PRInt32 syserrno, bytes = -1;
pt_SockLen addr_len =
sizeof(PRNetAddr);
if (pt_TestAbort()) {
return bytes;
}
bytes = recvfrom(fd->secret->md.osfd, buf, amount, flags,
(
struct sockaddr*)addr, &addr_len);
syserrno = errno;
if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) &&
(!fd->secret->nonblocking)) {
if (PR_INTERVAL_NO_WAIT == timeout) {
syserrno = ETIMEDOUT;
}
else {
fNeedContinue = PR_TRUE;
}
}
if (fNeedContinue == PR_TRUE) {
pt_Continuation op;
op.arg1.osfd = fd->secret->md.osfd;
op.arg2.buffer = buf;
op.arg3.amount = amount;
op.arg4.flags = flags;
op.arg5.addr = addr;
op.timeout = timeout;
op.function = pt_recvfrom_cont;
op.event = POLLIN | POLLPRI;
bytes = pt_Continue(&op);
syserrno = op.syserrno;
}
if (bytes >= 0) {
# ifdef _PR_HAVE_SOCKADDR_LEN
/* ignore the sa_len field of struct sockaddr */
if (addr) {
addr->raw.family = ((
struct sockaddr*)addr)->sa_family;
}
# endif
/* _PR_HAVE_SOCKADDR_LEN */
# ifdef _PR_INET6
if (addr && (AF_INET6 == addr->raw.family)) {
addr->raw.family = PR_AF_INET6;
}
# endif
}
else {
pt_MapError(_PR_MD_MAP_RECVFROM_ERROR, syserrno);
}
return bytes;
}
/* pt_RecvFrom */
# ifdef AIX
# ifndef HAVE_SEND_FILE
static pthread_once_t pt_aix_sendfile_once_block = PTHREAD_ONCE_INIT;
static void pt_aix_sendfile_init_routine(
void) {
void* handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
pt_aix_sendfile_fptr = (ssize_t(*)())dlsym(handle,
"send_file");
dlclose(handle);
}
/*
* pt_AIXDispatchSendFile
*/
static PRInt32 pt_AIXDispatchSendFile(PRFileDesc* sd, PRSendFileData* sfd,
PRTransmitFileFlags flags,
PRIntervalTime timeout) {
int rv;
rv = pthread_once(&pt_aix_sendfile_once_block, pt_aix_sendfile_init_routine);
PR_ASSERT(0 == rv);
if (pt_aix_sendfile_fptr) {
return pt_AIXSendFile(sd, sfd, flags, timeout);
}
else {
return PR_EmulateSendFile(sd, sfd, flags, timeout);
}
}
# endif
/* !HAVE_SEND_FILE */
/*
* pt_AIXSendFile
*
* Send file sfd->fd across socket sd. If specified, header and trailer
* buffers are sent before and after the file, respectively.
*
* PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
*
* return number of bytes sent or -1 on error
*
* This implementation takes advantage of the send_file() system
* call available in AIX 4.3.2.
*/
static PRInt32 pt_AIXSendFile(PRFileDesc* sd, PRSendFileData* sfd,
PRTransmitFileFlags flags,
PRIntervalTime timeout) {
struct sf_parms sf_struct;
uint_t send_flags;
ssize_t rv;
int syserrno;
PRInt32 count;
unsigned long long saved_file_offset;
long long saved_file_bytes;
sf_struct.header_data = (
void*)sfd->header;
/* cast away the 'const' */
sf_struct.header_length = sfd->hlen;
sf_struct.file_descriptor = sfd->fd->secret->md.osfd;
sf_struct.file_size = 0;
sf_struct.file_offset = sfd->file_offset;
if (sfd->file_nbytes == 0) {
sf_struct.file_bytes = -1;
}
else {
sf_struct.file_bytes = sfd->file_nbytes;
}
sf_struct.trailer_data = (
void*)sfd->trailer;
sf_struct.trailer_length = sfd->tlen;
sf_struct.bytes_sent = 0;
saved_file_offset = sf_struct.file_offset;
saved_file_bytes = sf_struct.file_bytes;
send_flags = 0;
/* flags processed at the end */
/* The first argument to send_file() is int*. */
PR_ASSERT(
sizeof(
int) ==
sizeof(sd->secret->md.osfd));
do {
rv = AIX_SEND_FILE(&sd->secret->md.osfd, &sf_struct, send_flags);
}
while (rv == -1 && (syserrno = errno) == EINTR);
if (rv == -1) {
if (syserrno == EAGAIN || syserrno == EWOULDBLOCK) {
count = 0;
/* Not a real error. Need to continue. */
}
else {
count = -1;
}
}
else {
count = sf_struct.bytes_sent;
/*
* A bug in AIX 4.3.2 prevents the 'file_bytes' field from
* being updated. So, 'file_bytes' is maintained by NSPR to
* avoid conflict when this bug is fixed in AIX, in the future.
*/
if (saved_file_bytes != -1) {
saved_file_bytes -= (sf_struct.file_offset - saved_file_offset);
}
sf_struct.file_bytes = saved_file_bytes;
}
if ((rv == 1) || ((rv == -1) && (count == 0))) {
pt_Continuation op;
op.arg1.osfd = sd->secret->md.osfd;
op.arg2.buffer = &sf_struct;
op.arg4.flags = send_flags;
op.result.code = count;
op.timeout = timeout;
op.function = pt_aix_sendfile_cont;
op.event = POLLOUT | POLLPRI;
count = pt_Continue(&op);
syserrno = op.syserrno;
}
if (count == -1) {
pt_MapError(_MD_aix_map_sendfile_error, syserrno);
return -1;
}
if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
PR_Close(sd);
}
PR_ASSERT(count ==
(sfd->hlen + sfd->tlen +
((sfd->file_nbytes == 0) ? sf_struct.file_size - sfd->file_offset
: sfd->file_nbytes)));
return count;
}
# endif
/* AIX */
# ifdef HPUX11
/*
* pt_HPUXSendFile
*
* Send file sfd->fd across socket sd. If specified, header and trailer
* buffers are sent before and after the file, respectively.
*
* PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
*
--> --------------------
--> maximum size reached
--> --------------------