| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| |
| /* ***** BEGIN LICENSE BLOCK ***** |
| * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
| * |
| * The contents of this file are subject to the Mozilla Public License Version |
| * 1.1 (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * |
| * Software distributed under the License is distributed on an "AS IS" basis, |
| * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
| * for the specific language governing rights and limitations under the |
| * License. |
| * |
| * The Original Code is the Netscape Portable Runtime (NSPR). |
| * |
| * The Initial Developer of the Original Code is |
| * Netscape Communications Corporation. |
| * Portions created by the Initial Developer are Copyright (C) 1998-2000 |
| * the Initial Developer. All Rights Reserved. |
| * |
| * Contributor(s): |
| * |
| * Alternatively, the contents of this file may be used under the terms of |
| * either the GNU General Public License Version 2 or later (the "GPL"), or |
| * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
| * in which case the provisions of the GPL or the LGPL are applicable instead |
| * of those above. If you wish to allow use of your version of this file only |
| * under the terms of either the GPL or the LGPL, and not to allow others to |
| * use your version of this file under the terms of the MPL, indicate your |
| * decision by deleting the provisions above and replace them with the notice |
| * and other provisions required by the GPL or the LGPL. If you do not delete |
| * the provisions above, a recipient may use your version of this file under |
| * the terms of any one of the MPL, the GPL or the LGPL. |
| * |
| * ***** END LICENSE BLOCK ***** */ |
| #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_ASSERT(!"I/O method is invalid"); |
| PR_SetError(PR_INVALID_METHOD_ERROR, 0); |
| return -1; |
| } /* _PR_InvalidInt */ |
| |
| PRInt16 _PR_InvalidInt16(void) |
| { |
| PR_ASSERT(!"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_ASSERT(!"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_ASSERT(!"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_ASSERT(!"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 */ |