/* -*- 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 /*****************************************************************************/ /************************** 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 */