priometh.c (16388B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 #include "primpl.h" 7 8 #include <string.h> 9 10 /*****************************************************************************/ 11 /************************** Invalid I/O method object ************************/ 12 /*****************************************************************************/ 13 PRIOMethods _pr_faulty_methods = {(PRDescType)0, 14 (PRCloseFN)_PR_InvalidStatus, 15 (PRReadFN)_PR_InvalidInt, 16 (PRWriteFN)_PR_InvalidInt, 17 (PRAvailableFN)_PR_InvalidInt, 18 (PRAvailable64FN)_PR_InvalidInt64, 19 (PRFsyncFN)_PR_InvalidStatus, 20 (PRSeekFN)_PR_InvalidInt, 21 (PRSeek64FN)_PR_InvalidInt64, 22 (PRFileInfoFN)_PR_InvalidStatus, 23 (PRFileInfo64FN)_PR_InvalidStatus, 24 (PRWritevFN)_PR_InvalidInt, 25 (PRConnectFN)_PR_InvalidStatus, 26 (PRAcceptFN)_PR_InvalidDesc, 27 (PRBindFN)_PR_InvalidStatus, 28 (PRListenFN)_PR_InvalidStatus, 29 (PRShutdownFN)_PR_InvalidStatus, 30 (PRRecvFN)_PR_InvalidInt, 31 (PRSendFN)_PR_InvalidInt, 32 (PRRecvfromFN)_PR_InvalidInt, 33 (PRSendtoFN)_PR_InvalidInt, 34 (PRPollFN)_PR_InvalidInt16, 35 (PRAcceptreadFN)_PR_InvalidInt, 36 (PRTransmitfileFN)_PR_InvalidInt, 37 (PRGetsocknameFN)_PR_InvalidStatus, 38 (PRGetpeernameFN)_PR_InvalidStatus, 39 (PRReservedFN)_PR_InvalidInt, 40 (PRReservedFN)_PR_InvalidInt, 41 (PRGetsocketoptionFN)_PR_InvalidStatus, 42 (PRSetsocketoptionFN)_PR_InvalidStatus, 43 (PRSendfileFN)_PR_InvalidInt, 44 (PRConnectcontinueFN)_PR_InvalidStatus, 45 (PRReservedFN)_PR_InvalidInt, 46 (PRReservedFN)_PR_InvalidInt, 47 (PRReservedFN)_PR_InvalidInt, 48 (PRReservedFN)_PR_InvalidInt}; 49 50 PRIntn _PR_InvalidInt(void) { 51 PR_NOT_REACHED("I/O method is invalid"); 52 PR_SetError(PR_INVALID_METHOD_ERROR, 0); 53 return -1; 54 } /* _PR_InvalidInt */ 55 56 PRInt16 _PR_InvalidInt16(void) { 57 PR_NOT_REACHED("I/O method is invalid"); 58 PR_SetError(PR_INVALID_METHOD_ERROR, 0); 59 return -1; 60 } /* _PR_InvalidInt */ 61 62 PRInt64 _PR_InvalidInt64(void) { 63 PRInt64 rv; 64 LL_I2L(rv, -1); 65 PR_NOT_REACHED("I/O method is invalid"); 66 PR_SetError(PR_INVALID_METHOD_ERROR, 0); 67 return rv; 68 } /* _PR_InvalidInt */ 69 70 /* 71 * An invalid method that returns PRStatus 72 */ 73 74 PRStatus _PR_InvalidStatus(void) { 75 PR_NOT_REACHED("I/O method is invalid"); 76 PR_SetError(PR_INVALID_METHOD_ERROR, 0); 77 return PR_FAILURE; 78 } /* _PR_InvalidDesc */ 79 80 /* 81 * An invalid method that returns a pointer 82 */ 83 84 PRFileDesc* _PR_InvalidDesc(void) { 85 PR_NOT_REACHED("I/O method is invalid"); 86 PR_SetError(PR_INVALID_METHOD_ERROR, 0); 87 return NULL; 88 } /* _PR_InvalidDesc */ 89 90 PR_IMPLEMENT(PRDescType) PR_GetDescType(PRFileDesc* file) { 91 return file->methods->file_type; 92 } 93 94 PR_IMPLEMENT(PRStatus) PR_Close(PRFileDesc* fd) { 95 return (fd->methods->close)(fd); 96 } 97 98 PR_IMPLEMENT(PRInt32) PR_Read(PRFileDesc* fd, void* buf, PRInt32 amount) { 99 return ((fd->methods->read)(fd, buf, amount)); 100 } 101 102 PR_IMPLEMENT(PRInt32) 103 PR_Write(PRFileDesc* fd, const void* buf, PRInt32 amount) { 104 return ((fd->methods->write)(fd, buf, amount)); 105 } 106 107 PR_IMPLEMENT(PRInt32) 108 PR_Seek(PRFileDesc* fd, PRInt32 offset, PRSeekWhence whence) { 109 return ((fd->methods->seek)(fd, offset, whence)); 110 } 111 112 PR_IMPLEMENT(PRInt64) 113 PR_Seek64(PRFileDesc* fd, PRInt64 offset, PRSeekWhence whence) { 114 return ((fd->methods->seek64)(fd, offset, whence)); 115 } 116 117 PR_IMPLEMENT(PRInt32) PR_Available(PRFileDesc* fd) { 118 return ((fd->methods->available)(fd)); 119 } 120 121 PR_IMPLEMENT(PRInt64) PR_Available64(PRFileDesc* fd) { 122 return ((fd->methods->available64)(fd)); 123 } 124 125 PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo(PRFileDesc* fd, PRFileInfo* info) { 126 return ((fd->methods->fileInfo)(fd, info)); 127 } 128 129 PR_IMPLEMENT(PRStatus) 130 PR_GetOpenFileInfo64(PRFileDesc* fd, PRFileInfo64* info) { 131 return ((fd->methods->fileInfo64)(fd, info)); 132 } 133 134 PR_IMPLEMENT(PRStatus) PR_Sync(PRFileDesc* fd) { 135 return ((fd->methods->fsync)(fd)); 136 } 137 138 PR_IMPLEMENT(PRStatus) 139 PR_Connect(PRFileDesc* fd, const PRNetAddr* addr, PRIntervalTime timeout) { 140 return ((fd->methods->connect)(fd, addr, timeout)); 141 } 142 143 PR_IMPLEMENT(PRStatus) PR_ConnectContinue(PRFileDesc* fd, PRInt16 out_flags) { 144 return ((fd->methods->connectcontinue)(fd, out_flags)); 145 } 146 147 PR_IMPLEMENT(PRFileDesc*) 148 PR_Accept(PRFileDesc* fd, PRNetAddr* addr, PRIntervalTime timeout) { 149 return ((fd->methods->accept)(fd, addr, timeout)); 150 } 151 152 PR_IMPLEMENT(PRStatus) PR_Bind(PRFileDesc* fd, const PRNetAddr* addr) { 153 return ((fd->methods->bind)(fd, addr)); 154 } 155 156 PR_IMPLEMENT(PRStatus) PR_Shutdown(PRFileDesc* fd, PRShutdownHow how) { 157 return ((fd->methods->shutdown)(fd, how)); 158 } 159 160 PR_IMPLEMENT(PRStatus) PR_Listen(PRFileDesc* fd, PRIntn backlog) { 161 return ((fd->methods->listen)(fd, backlog)); 162 } 163 164 PR_IMPLEMENT(PRInt32) 165 PR_Recv(PRFileDesc* fd, void* buf, PRInt32 amount, PRIntn flags, 166 PRIntervalTime timeout) { 167 return ((fd->methods->recv)(fd, buf, amount, flags, timeout)); 168 } 169 170 PR_IMPLEMENT(PRInt32) 171 PR_Send(PRFileDesc* fd, const void* buf, PRInt32 amount, PRIntn flags, 172 PRIntervalTime timeout) { 173 return ((fd->methods->send)(fd, buf, amount, flags, timeout)); 174 } 175 176 PR_IMPLEMENT(PRInt32) 177 PR_Writev(PRFileDesc* fd, const PRIOVec* iov, PRInt32 iov_size, 178 PRIntervalTime timeout) { 179 if (iov_size > PR_MAX_IOVECTOR_SIZE) { 180 PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0); 181 return -1; 182 } 183 return ((fd->methods->writev)(fd, iov, iov_size, timeout)); 184 } 185 186 PR_IMPLEMENT(PRInt32) 187 PR_RecvFrom(PRFileDesc* fd, void* buf, PRInt32 amount, PRIntn flags, 188 PRNetAddr* addr, PRIntervalTime timeout) { 189 return ((fd->methods->recvfrom)(fd, buf, amount, flags, addr, timeout)); 190 } 191 192 PR_IMPLEMENT(PRInt32) 193 PR_SendTo(PRFileDesc* fd, const void* buf, PRInt32 amount, PRIntn flags, 194 const PRNetAddr* addr, PRIntervalTime timeout) { 195 return ((fd->methods->sendto)(fd, buf, amount, flags, addr, timeout)); 196 } 197 198 PR_IMPLEMENT(PRInt32) 199 PR_TransmitFile(PRFileDesc* sd, PRFileDesc* fd, const void* hdr, PRInt32 hlen, 200 PRTransmitFileFlags flags, PRIntervalTime timeout) { 201 return ((sd->methods->transmitfile)(sd, fd, hdr, hlen, flags, timeout)); 202 } 203 204 PR_IMPLEMENT(PRInt32) 205 PR_AcceptRead(PRFileDesc* sd, PRFileDesc** nd, PRNetAddr** raddr, void* buf, 206 PRInt32 amount, PRIntervalTime timeout) { 207 return ((sd->methods->acceptread)(sd, nd, raddr, buf, amount, timeout)); 208 } 209 210 PR_IMPLEMENT(PRStatus) PR_GetSockName(PRFileDesc* fd, PRNetAddr* addr) { 211 return ((fd->methods->getsockname)(fd, addr)); 212 } 213 214 PR_IMPLEMENT(PRStatus) PR_GetPeerName(PRFileDesc* fd, PRNetAddr* addr) { 215 return ((fd->methods->getpeername)(fd, addr)); 216 } 217 218 PR_IMPLEMENT(PRStatus) 219 PR_GetSocketOption(PRFileDesc* fd, PRSocketOptionData* data) { 220 return ((fd->methods->getsocketoption)(fd, data)); 221 } 222 223 PR_IMPLEMENT(PRStatus) 224 PR_SetSocketOption(PRFileDesc* fd, const PRSocketOptionData* data) { 225 return ((fd->methods->setsocketoption)(fd, data)); 226 } 227 228 PR_IMPLEMENT(PRInt32) 229 PR_SendFile(PRFileDesc* sd, PRSendFileData* sfd, PRTransmitFileFlags flags, 230 PRIntervalTime timeout) { 231 return ((sd->methods->sendfile)(sd, sfd, flags, timeout)); 232 } 233 234 PR_IMPLEMENT(PRInt32) 235 PR_EmulateAcceptRead(PRFileDesc* sd, PRFileDesc** nd, PRNetAddr** raddr, 236 void* buf, PRInt32 amount, PRIntervalTime timeout) { 237 PRInt32 rv = -1; 238 PRNetAddr remote; 239 PRFileDesc* accepted = NULL; 240 241 /* 242 ** The timeout does not apply to the accept portion of the 243 ** operation - it waits indefinitely. 244 */ 245 accepted = PR_Accept(sd, &remote, PR_INTERVAL_NO_TIMEOUT); 246 if (NULL == accepted) { 247 return rv; 248 } 249 250 rv = PR_Recv(accepted, buf, amount, 0, timeout); 251 if (rv >= 0) { 252 /* copy the new info out where caller can see it */ 253 #define AMASK ((PRPtrdiff)7) /* mask for alignment of PRNetAddr */ 254 PRPtrdiff aligned = (PRPtrdiff)buf + amount + AMASK; 255 *raddr = (PRNetAddr*)(aligned & ~AMASK); 256 memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote)); 257 *nd = accepted; 258 return rv; 259 } 260 261 PR_Close(accepted); 262 return rv; 263 } 264 265 /* 266 * PR_EmulateSendFile 267 * 268 * Send file sfd->fd across socket sd. If header/trailer are specified 269 * they are sent before and after the file, respectively. 270 * 271 * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file 272 * 273 * return number of bytes sent or -1 on error 274 * 275 */ 276 277 #if defined(XP_UNIX) || defined(WIN32) 278 279 /* 280 * An implementation based on memory-mapped files 281 */ 282 283 # define SENDFILE_MMAP_CHUNK (256 * 1024) 284 285 PR_IMPLEMENT(PRInt32) 286 PR_EmulateSendFile(PRFileDesc* sd, PRSendFileData* sfd, 287 PRTransmitFileFlags flags, PRIntervalTime timeout) { 288 PRInt32 rv, count = 0; 289 PRInt32 len, file_bytes, index = 0; 290 PRFileInfo info; 291 PRIOVec iov[3]; 292 PRFileMap* mapHandle = NULL; 293 void* addr = (void*)0; /* initialized to some arbitrary value. Keeps compiler 294 warnings down. */ 295 PRUint32 file_mmap_offset, alignment; 296 PRInt64 zero64; 297 PROffset64 file_mmap_offset64; 298 PRUint32 addr_offset, mmap_len; 299 300 /* Get file size */ 301 if (PR_SUCCESS != PR_GetOpenFileInfo(sfd->fd, &info)) { 302 count = -1; 303 goto done; 304 } 305 if (sfd->file_nbytes && (info.size < (sfd->file_offset + sfd->file_nbytes))) { 306 /* 307 * there are fewer bytes in file to send than specified 308 */ 309 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 310 count = -1; 311 goto done; 312 } 313 if (sfd->file_nbytes) { 314 file_bytes = sfd->file_nbytes; 315 } else { 316 file_bytes = info.size - sfd->file_offset; 317 } 318 319 alignment = PR_GetMemMapAlignment(); 320 321 /* number of initial bytes to skip in mmap'd segment */ 322 addr_offset = sfd->file_offset % alignment; 323 324 /* find previous mmap alignment boundary */ 325 file_mmap_offset = sfd->file_offset - addr_offset; 326 327 /* 328 * If the file is large, mmap and send the file in chunks so as 329 * to not consume too much virtual address space 330 */ 331 mmap_len = PR_MIN(file_bytes + addr_offset, SENDFILE_MMAP_CHUNK); 332 len = mmap_len - addr_offset; 333 334 /* 335 * Map in (part of) file. Take care of zero-length files. 336 */ 337 if (len) { 338 LL_I2L(zero64, 0); 339 mapHandle = PR_CreateFileMap(sfd->fd, zero64, PR_PROT_READONLY); 340 if (!mapHandle) { 341 count = -1; 342 goto done; 343 } 344 LL_I2L(file_mmap_offset64, file_mmap_offset); 345 addr = PR_MemMap(mapHandle, file_mmap_offset64, mmap_len); 346 if (!addr) { 347 count = -1; 348 goto done; 349 } 350 } 351 /* 352 * send headers first, followed by the file 353 */ 354 if (sfd->hlen) { 355 iov[index].iov_base = (char*)sfd->header; 356 iov[index].iov_len = sfd->hlen; 357 index++; 358 } 359 if (len) { 360 iov[index].iov_base = (char*)addr + addr_offset; 361 iov[index].iov_len = len; 362 index++; 363 } 364 if ((file_bytes == len) && (sfd->tlen)) { 365 /* 366 * all file data is mapped in; send the trailer too 367 */ 368 iov[index].iov_base = (char*)sfd->trailer; 369 iov[index].iov_len = sfd->tlen; 370 index++; 371 } 372 rv = PR_Writev(sd, iov, index, timeout); 373 if (len) { 374 PR_MemUnmap(addr, mmap_len); 375 } 376 if (rv < 0) { 377 count = -1; 378 goto done; 379 } 380 381 PR_ASSERT(rv == sfd->hlen + len + ((len == file_bytes) ? sfd->tlen : 0)); 382 383 file_bytes -= len; 384 count += rv; 385 if (!file_bytes) { /* header, file and trailer are sent */ 386 goto done; 387 } 388 389 /* 390 * send remaining bytes of the file, if any 391 */ 392 len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK); 393 while (len > 0) { 394 /* 395 * Map in (part of) file 396 */ 397 file_mmap_offset = sfd->file_offset + count - sfd->hlen; 398 PR_ASSERT((file_mmap_offset % alignment) == 0); 399 400 LL_I2L(file_mmap_offset64, file_mmap_offset); 401 addr = PR_MemMap(mapHandle, file_mmap_offset64, len); 402 if (!addr) { 403 count = -1; 404 goto done; 405 } 406 rv = PR_Send(sd, addr, len, 0, timeout); 407 PR_MemUnmap(addr, len); 408 if (rv < 0) { 409 count = -1; 410 goto done; 411 } 412 413 PR_ASSERT(rv == len); 414 file_bytes -= rv; 415 count += rv; 416 len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK); 417 } 418 PR_ASSERT(0 == file_bytes); 419 if (sfd->tlen) { 420 rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout); 421 if (rv >= 0) { 422 PR_ASSERT(rv == sfd->tlen); 423 count += rv; 424 } else { 425 count = -1; 426 } 427 } 428 done: 429 if (mapHandle) { 430 PR_CloseFileMap(mapHandle); 431 } 432 if ((count >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) { 433 PR_Close(sd); 434 } 435 return count; 436 } 437 438 #else 439 440 PR_IMPLEMENT(PRInt32) 441 PR_EmulateSendFile(PRFileDesc* sd, PRSendFileData* sfd, 442 PRTransmitFileFlags flags, PRIntervalTime timeout) { 443 PRInt32 rv, count = 0; 444 PRInt32 rlen; 445 const void* buffer; 446 PRInt32 buflen; 447 PRInt32 sendbytes, readbytes; 448 char* buf; 449 450 # define _SENDFILE_BUFSIZE (16 * 1024) 451 452 buf = (char*)PR_MALLOC(_SENDFILE_BUFSIZE); 453 if (buf == NULL) { 454 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 455 return -1; 456 } 457 458 /* 459 * send header first 460 */ 461 buflen = sfd->hlen; 462 buffer = sfd->header; 463 while (buflen) { 464 rv = PR_Send(sd, buffer, buflen, 0, timeout); 465 if (rv < 0) { 466 /* PR_Send() has invoked PR_SetError(). */ 467 rv = -1; 468 goto done; 469 } else { 470 count += rv; 471 buffer = (const void*)((const char*)buffer + rv); 472 buflen -= rv; 473 } 474 } 475 476 /* 477 * send file next 478 */ 479 if (PR_Seek(sfd->fd, sfd->file_offset, PR_SEEK_SET) < 0) { 480 rv = -1; 481 goto done; 482 } 483 sendbytes = sfd->file_nbytes; 484 if (sendbytes == 0) { 485 /* send entire file */ 486 while ((rlen = PR_Read(sfd->fd, buf, _SENDFILE_BUFSIZE)) > 0) { 487 while (rlen) { 488 char* bufptr = buf; 489 490 rv = PR_Send(sd, bufptr, rlen, 0, timeout); 491 if (rv < 0) { 492 /* PR_Send() has invoked PR_SetError(). */ 493 rv = -1; 494 goto done; 495 } else { 496 count += rv; 497 bufptr = ((char*)bufptr + rv); 498 rlen -= rv; 499 } 500 } 501 } 502 if (rlen < 0) { 503 /* PR_Read() has invoked PR_SetError(). */ 504 rv = -1; 505 goto done; 506 } 507 } else { 508 readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE); 509 while (readbytes && ((rlen = PR_Read(sfd->fd, buf, readbytes)) > 0)) { 510 while (rlen) { 511 char* bufptr = buf; 512 513 rv = PR_Send(sd, bufptr, rlen, 0, timeout); 514 if (rv < 0) { 515 /* PR_Send() has invoked PR_SetError(). */ 516 rv = -1; 517 goto done; 518 } else { 519 count += rv; 520 sendbytes -= rv; 521 bufptr = ((char*)bufptr + rv); 522 rlen -= rv; 523 } 524 } 525 readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE); 526 } 527 if (rlen < 0) { 528 /* PR_Read() has invoked PR_SetError(). */ 529 rv = -1; 530 goto done; 531 } else if (sendbytes != 0) { 532 /* 533 * there are fewer bytes in file to send than specified 534 */ 535 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 536 rv = -1; 537 goto done; 538 } 539 } 540 541 /* 542 * send trailer last 543 */ 544 buflen = sfd->tlen; 545 buffer = sfd->trailer; 546 while (buflen) { 547 rv = PR_Send(sd, buffer, buflen, 0, timeout); 548 if (rv < 0) { 549 /* PR_Send() has invoked PR_SetError(). */ 550 rv = -1; 551 goto done; 552 } else { 553 count += rv; 554 buffer = (const void*)((const char*)buffer + rv); 555 buflen -= rv; 556 } 557 } 558 rv = count; 559 560 done: 561 if (buf) { 562 PR_DELETE(buf); 563 } 564 if ((rv >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) { 565 PR_Close(sd); 566 } 567 return rv; 568 } 569 570 #endif 571 572 /* priometh.c */