summaryrefslogtreecommitdiffstats
path: root/nsprpub/pr/src/io/priometh.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--nsprpub/pr/src/io/priometh.c607
1 files changed, 607 insertions, 0 deletions
diff --git a/nsprpub/pr/src/io/priometh.c b/nsprpub/pr/src/io/priometh.c
new file mode 100644
index 0000000000..508719a809
--- /dev/null
+++ b/nsprpub/pr/src/io/priometh.c
@@ -0,0 +1,607 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* 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/. */
+#include "primpl.h"
+
+#include <string.h>
+
+/*****************************************************************************/
+/************************** Invalid I/O method object ************************/
+/*****************************************************************************/
+PRIOMethods _pr_faulty_methods = {
+ (PRDescType)0,
+ (PRCloseFN)_PR_InvalidStatus,
+ (PRReadFN)_PR_InvalidInt,
+ (PRWriteFN)_PR_InvalidInt,
+ (PRAvailableFN)_PR_InvalidInt,
+ (PRAvailable64FN)_PR_InvalidInt64,
+ (PRFsyncFN)_PR_InvalidStatus,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ (PRWritevFN)_PR_InvalidInt,
+ (PRConnectFN)_PR_InvalidStatus,
+ (PRAcceptFN)_PR_InvalidDesc,
+ (PRBindFN)_PR_InvalidStatus,
+ (PRListenFN)_PR_InvalidStatus,
+ (PRShutdownFN)_PR_InvalidStatus,
+ (PRRecvFN)_PR_InvalidInt,
+ (PRSendFN)_PR_InvalidInt,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ (PRPollFN)_PR_InvalidInt16,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ (PRGetsocknameFN)_PR_InvalidStatus,
+ (PRGetpeernameFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRGetsocketoptionFN)_PR_InvalidStatus,
+ (PRSetsocketoptionFN)_PR_InvalidStatus,
+ (PRSendfileFN)_PR_InvalidInt,
+ (PRConnectcontinueFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+PRIntn _PR_InvalidInt(void)
+{
+ PR_NOT_REACHED("I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+} /* _PR_InvalidInt */
+
+PRInt16 _PR_InvalidInt16(void)
+{
+ PR_NOT_REACHED("I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+} /* _PR_InvalidInt */
+
+PRInt64 _PR_InvalidInt64(void)
+{
+ PRInt64 rv;
+ LL_I2L(rv, -1);
+ PR_NOT_REACHED("I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return rv;
+} /* _PR_InvalidInt */
+
+/*
+ * An invalid method that returns PRStatus
+ */
+
+PRStatus _PR_InvalidStatus(void)
+{
+ PR_NOT_REACHED("I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+} /* _PR_InvalidDesc */
+
+/*
+ * An invalid method that returns a pointer
+ */
+
+PRFileDesc *_PR_InvalidDesc(void)
+{
+ PR_NOT_REACHED("I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return NULL;
+} /* _PR_InvalidDesc */
+
+PR_IMPLEMENT(PRDescType) PR_GetDescType(PRFileDesc *file)
+{
+ return file->methods->file_type;
+}
+
+PR_IMPLEMENT(PRStatus) PR_Close(PRFileDesc *fd)
+{
+ return (fd->methods->close)(fd);
+}
+
+PR_IMPLEMENT(PRInt32) PR_Read(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ return((fd->methods->read)(fd,buf,amount));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Write(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ return((fd->methods->write)(fd,buf,amount));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence)
+{
+ return((fd->methods->seek)(fd, offset, whence));
+}
+
+PR_IMPLEMENT(PRInt64) PR_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
+{
+ return((fd->methods->seek64)(fd, offset, whence));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Available(PRFileDesc *fd)
+{
+ return((fd->methods->available)(fd));
+}
+
+PR_IMPLEMENT(PRInt64) PR_Available64(PRFileDesc *fd)
+{
+ return((fd->methods->available64)(fd));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo(PRFileDesc *fd, PRFileInfo *info)
+{
+ return((fd->methods->fileInfo)(fd, info));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo64(PRFileDesc *fd, PRFileInfo64 *info)
+{
+ return((fd->methods->fileInfo64)(fd, info));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Sync(PRFileDesc *fd)
+{
+ return((fd->methods->fsync)(fd));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Connect(
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ return((fd->methods->connect)(fd,addr,timeout));
+}
+
+PR_IMPLEMENT(PRStatus) PR_ConnectContinue(
+ PRFileDesc *fd, PRInt16 out_flags)
+{
+ return((fd->methods->connectcontinue)(fd,out_flags));
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_Accept(PRFileDesc *fd, PRNetAddr *addr,
+ PRIntervalTime timeout)
+{
+ return((fd->methods->accept)(fd,addr,timeout));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Bind(PRFileDesc *fd, const PRNetAddr *addr)
+{
+ return((fd->methods->bind)(fd,addr));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Shutdown(PRFileDesc *fd, PRShutdownHow how)
+{
+ return((fd->methods->shutdown)(fd,how));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Listen(PRFileDesc *fd, PRIntn backlog)
+{
+ return((fd->methods->listen)(fd,backlog));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ return((fd->methods->recv)(fd,buf,amount,flags,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Send(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ return((fd->methods->send)(fd,buf,amount,flags,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Writev(PRFileDesc *fd, const PRIOVec *iov,
+ PRInt32 iov_size, PRIntervalTime timeout)
+{
+ if (iov_size > PR_MAX_IOVECTOR_SIZE)
+ {
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ return -1;
+ }
+ return((fd->methods->writev)(fd,iov,iov_size,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ return((fd->methods->recvfrom)(fd,buf,amount,flags,addr,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_SendTo(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ return((fd->methods->sendto)(fd,buf,amount,flags,addr,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_TransmitFile(
+ PRFileDesc *sd, PRFileDesc *fd, const void *hdr, PRInt32 hlen,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ return((sd->methods->transmitfile)(sd,fd,hdr,hlen,flags,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_AcceptRead(
+ PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
+ void *buf, PRInt32 amount, PRIntervalTime timeout)
+{
+ return((sd->methods->acceptread)(sd, nd, raddr, buf, amount,timeout));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetSockName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ return((fd->methods->getsockname)(fd,addr));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ return((fd->methods->getpeername)(fd,addr));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetSocketOption(
+ PRFileDesc *fd, PRSocketOptionData *data)
+{
+ return((fd->methods->getsocketoption)(fd, data));
+}
+
+PR_IMPLEMENT(PRStatus) PR_SetSocketOption(
+ PRFileDesc *fd, const PRSocketOptionData *data)
+{
+ return((fd->methods->setsocketoption)(fd, data));
+}
+
+PR_IMPLEMENT(PRInt32) PR_SendFile(
+ PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ return((sd->methods->sendfile)(sd,sfd,flags,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_EmulateAcceptRead(
+ PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
+ void *buf, PRInt32 amount, PRIntervalTime timeout)
+{
+ PRInt32 rv = -1;
+ PRNetAddr remote;
+ PRFileDesc *accepted = NULL;
+
+ /*
+ ** The timeout does not apply to the accept portion of the
+ ** operation - it waits indefinitely.
+ */
+ accepted = PR_Accept(sd, &remote, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == accepted) {
+ return rv;
+ }
+
+ rv = PR_Recv(accepted, buf, amount, 0, timeout);
+ if (rv >= 0)
+ {
+ /* copy the new info out where caller can see it */
+#define AMASK ((PRPtrdiff)7) /* mask for alignment of PRNetAddr */
+ PRPtrdiff aligned = (PRPtrdiff)buf + amount + AMASK;
+ *raddr = (PRNetAddr*)(aligned & ~AMASK);
+ memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote));
+ *nd = accepted;
+ return rv;
+ }
+
+ PR_Close(accepted);
+ return rv;
+}
+
+/*
+ * PR_EmulateSendFile
+ *
+ * Send file sfd->fd across socket sd. If header/trailer are specified
+ * they 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
+ *
+ */
+
+#if defined(XP_UNIX) || defined(WIN32)
+
+/*
+ * An implementation based on memory-mapped files
+ */
+
+#define SENDFILE_MMAP_CHUNK (256 * 1024)
+
+PR_IMPLEMENT(PRInt32) PR_EmulateSendFile(
+ PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ PRInt32 rv, count = 0;
+ PRInt32 len, file_bytes, index = 0;
+ PRFileInfo info;
+ PRIOVec iov[3];
+ PRFileMap *mapHandle = NULL;
+ void *addr = (void*)0; /* initialized to some arbitrary value. Keeps compiler warnings down. */
+ PRUint32 file_mmap_offset, alignment;
+ PRInt64 zero64;
+ PROffset64 file_mmap_offset64;
+ PRUint32 addr_offset, mmap_len;
+
+ /* Get file size */
+ if (PR_SUCCESS != PR_GetOpenFileInfo(sfd->fd, &info)) {
+ count = -1;
+ goto done;
+ }
+ if (sfd->file_nbytes &&
+ (info.size < (sfd->file_offset + sfd->file_nbytes))) {
+ /*
+ * there are fewer bytes in file to send than specified
+ */
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ count = -1;
+ goto done;
+ }
+ if (sfd->file_nbytes) {
+ file_bytes = sfd->file_nbytes;
+ }
+ else {
+ file_bytes = info.size - sfd->file_offset;
+ }
+
+ alignment = PR_GetMemMapAlignment();
+
+ /* number of initial bytes to skip in mmap'd segment */
+ addr_offset = sfd->file_offset % alignment;
+
+ /* find previous mmap alignment boundary */
+ file_mmap_offset = sfd->file_offset - addr_offset;
+
+ /*
+ * If the file is large, mmap and send the file in chunks so as
+ * to not consume too much virtual address space
+ */
+ mmap_len = PR_MIN(file_bytes + addr_offset, SENDFILE_MMAP_CHUNK);
+ len = mmap_len - addr_offset;
+
+ /*
+ * Map in (part of) file. Take care of zero-length files.
+ */
+ if (len) {
+ LL_I2L(zero64, 0);
+ mapHandle = PR_CreateFileMap(sfd->fd, zero64, PR_PROT_READONLY);
+ if (!mapHandle) {
+ count = -1;
+ goto done;
+ }
+ LL_I2L(file_mmap_offset64, file_mmap_offset);
+ addr = PR_MemMap(mapHandle, file_mmap_offset64, mmap_len);
+ if (!addr) {
+ count = -1;
+ goto done;
+ }
+ }
+ /*
+ * send headers first, followed by the file
+ */
+ if (sfd->hlen) {
+ iov[index].iov_base = (char *) sfd->header;
+ iov[index].iov_len = sfd->hlen;
+ index++;
+ }
+ if (len) {
+ iov[index].iov_base = (char*)addr + addr_offset;
+ iov[index].iov_len = len;
+ index++;
+ }
+ if ((file_bytes == len) && (sfd->tlen)) {
+ /*
+ * all file data is mapped in; send the trailer too
+ */
+ iov[index].iov_base = (char *) sfd->trailer;
+ iov[index].iov_len = sfd->tlen;
+ index++;
+ }
+ rv = PR_Writev(sd, iov, index, timeout);
+ if (len) {
+ PR_MemUnmap(addr, mmap_len);
+ }
+ if (rv < 0) {
+ count = -1;
+ goto done;
+ }
+
+ PR_ASSERT(rv == sfd->hlen + len + ((len == file_bytes) ? sfd->tlen : 0));
+
+ file_bytes -= len;
+ count += rv;
+ if (!file_bytes) { /* header, file and trailer are sent */
+ goto done;
+ }
+
+ /*
+ * send remaining bytes of the file, if any
+ */
+ len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK);
+ while (len > 0) {
+ /*
+ * Map in (part of) file
+ */
+ file_mmap_offset = sfd->file_offset + count - sfd->hlen;
+ PR_ASSERT((file_mmap_offset % alignment) == 0);
+
+ LL_I2L(file_mmap_offset64, file_mmap_offset);
+ addr = PR_MemMap(mapHandle, file_mmap_offset64, len);
+ if (!addr) {
+ count = -1;
+ goto done;
+ }
+ rv = PR_Send(sd, addr, len, 0, timeout);
+ PR_MemUnmap(addr, len);
+ if (rv < 0) {
+ count = -1;
+ goto done;
+ }
+
+ PR_ASSERT(rv == len);
+ file_bytes -= rv;
+ count += rv;
+ len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK);
+ }
+ PR_ASSERT(0 == file_bytes);
+ if (sfd->tlen) {
+ rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout);
+ if (rv >= 0) {
+ PR_ASSERT(rv == sfd->tlen);
+ count += rv;
+ } else {
+ count = -1;
+ }
+ }
+done:
+ if (mapHandle) {
+ PR_CloseFileMap(mapHandle);
+ }
+ if ((count >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) {
+ PR_Close(sd);
+ }
+ return count;
+}
+
+#else
+
+PR_IMPLEMENT(PRInt32) PR_EmulateSendFile(
+ PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ PRInt32 rv, count = 0;
+ PRInt32 rlen;
+ const void * buffer;
+ PRInt32 buflen;
+ PRInt32 sendbytes, readbytes;
+ char *buf;
+
+#define _SENDFILE_BUFSIZE (16 * 1024)
+
+ buf = (char*)PR_MALLOC(_SENDFILE_BUFSIZE);
+ if (buf == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+
+ /*
+ * send header first
+ */
+ buflen = sfd->hlen;
+ buffer = sfd->header;
+ while (buflen) {
+ rv = PR_Send(sd, buffer, buflen, 0, timeout);
+ if (rv < 0) {
+ /* PR_Send() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ } else {
+ count += rv;
+ buffer = (const void*) ((const char*)buffer + rv);
+ buflen -= rv;
+ }
+ }
+
+ /*
+ * send file next
+ */
+ if (PR_Seek(sfd->fd, sfd->file_offset, PR_SEEK_SET) < 0) {
+ rv = -1;
+ goto done;
+ }
+ sendbytes = sfd->file_nbytes;
+ if (sendbytes == 0) {
+ /* send entire file */
+ while ((rlen = PR_Read(sfd->fd, buf, _SENDFILE_BUFSIZE)) > 0) {
+ while (rlen) {
+ char *bufptr = buf;
+
+ rv = PR_Send(sd, bufptr, rlen, 0, timeout);
+ if (rv < 0) {
+ /* PR_Send() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ } else {
+ count += rv;
+ bufptr = ((char*)bufptr + rv);
+ rlen -= rv;
+ }
+ }
+ }
+ if (rlen < 0) {
+ /* PR_Read() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ }
+ } else {
+ readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE);
+ while (readbytes && ((rlen = PR_Read(sfd->fd, buf, readbytes)) > 0)) {
+ while (rlen) {
+ char *bufptr = buf;
+
+ rv = PR_Send(sd, bufptr, rlen, 0, timeout);
+ if (rv < 0) {
+ /* PR_Send() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ } else {
+ count += rv;
+ sendbytes -= rv;
+ bufptr = ((char*)bufptr + rv);
+ rlen -= rv;
+ }
+ }
+ readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE);
+ }
+ if (rlen < 0) {
+ /* PR_Read() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ } else if (sendbytes != 0) {
+ /*
+ * there are fewer bytes in file to send than specified
+ */
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ }
+
+ /*
+ * send trailer last
+ */
+ buflen = sfd->tlen;
+ buffer = sfd->trailer;
+ while (buflen) {
+ rv = PR_Send(sd, buffer, buflen, 0, timeout);
+ if (rv < 0) {
+ /* PR_Send() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ } else {
+ count += rv;
+ buffer = (const void*) ((const char*)buffer + rv);
+ buflen -= rv;
+ }
+ }
+ rv = count;
+
+done:
+ if (buf) {
+ PR_DELETE(buf);
+ }
+ if ((rv >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) {
+ PR_Close(sd);
+ }
+ return rv;
+}
+
+#endif
+
+/* priometh.c */