Coverage Report

Created: 2025-07-01 06:25

/src/nspr/pr/src/io/priometh.c
Line
Count
Source (jump to first uncovered line)
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
0
PRIntn _PR_InvalidInt(void) {
51
0
  PR_NOT_REACHED("I/O method is invalid");
52
0
  PR_SetError(PR_INVALID_METHOD_ERROR, 0);
53
0
  return -1;
54
0
} /* _PR_InvalidInt */
55
56
0
PRInt16 _PR_InvalidInt16(void) {
57
0
  PR_NOT_REACHED("I/O method is invalid");
58
0
  PR_SetError(PR_INVALID_METHOD_ERROR, 0);
59
0
  return -1;
60
0
} /* _PR_InvalidInt */
61
62
0
PRInt64 _PR_InvalidInt64(void) {
63
0
  PRInt64 rv;
64
0
  LL_I2L(rv, -1);
65
0
  PR_NOT_REACHED("I/O method is invalid");
66
0
  PR_SetError(PR_INVALID_METHOD_ERROR, 0);
67
0
  return rv;
68
0
} /* _PR_InvalidInt */
69
70
/*
71
 * An invalid method that returns PRStatus
72
 */
73
74
0
PRStatus _PR_InvalidStatus(void) {
75
0
  PR_NOT_REACHED("I/O method is invalid");
76
0
  PR_SetError(PR_INVALID_METHOD_ERROR, 0);
77
0
  return PR_FAILURE;
78
0
} /* _PR_InvalidDesc */
79
80
/*
81
 * An invalid method that returns a pointer
82
 */
83
84
0
PRFileDesc* _PR_InvalidDesc(void) {
85
0
  PR_NOT_REACHED("I/O method is invalid");
86
0
  PR_SetError(PR_INVALID_METHOD_ERROR, 0);
87
0
  return NULL;
88
0
} /* _PR_InvalidDesc */
89
90
0
PR_IMPLEMENT(PRDescType) PR_GetDescType(PRFileDesc* file) {
91
0
  return file->methods->file_type;
92
0
}
93
94
0
PR_IMPLEMENT(PRStatus) PR_Close(PRFileDesc* fd) {
95
0
  return (fd->methods->close)(fd);
96
0
}
97
98
0
PR_IMPLEMENT(PRInt32) PR_Read(PRFileDesc* fd, void* buf, PRInt32 amount) {
99
0
  return ((fd->methods->read)(fd, buf, amount));
100
0
}
101
102
PR_IMPLEMENT(PRInt32)
103
0
PR_Write(PRFileDesc* fd, const void* buf, PRInt32 amount) {
104
0
  return ((fd->methods->write)(fd, buf, amount));
105
0
}
106
107
PR_IMPLEMENT(PRInt32)
108
0
PR_Seek(PRFileDesc* fd, PRInt32 offset, PRSeekWhence whence) {
109
0
  return ((fd->methods->seek)(fd, offset, whence));
110
0
}
111
112
PR_IMPLEMENT(PRInt64)
113
0
PR_Seek64(PRFileDesc* fd, PRInt64 offset, PRSeekWhence whence) {
114
0
  return ((fd->methods->seek64)(fd, offset, whence));
115
0
}
116
117
0
PR_IMPLEMENT(PRInt32) PR_Available(PRFileDesc* fd) {
118
0
  return ((fd->methods->available)(fd));
119
0
}
120
121
0
PR_IMPLEMENT(PRInt64) PR_Available64(PRFileDesc* fd) {
122
0
  return ((fd->methods->available64)(fd));
123
0
}
124
125
0
PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo(PRFileDesc* fd, PRFileInfo* info) {
126
0
  return ((fd->methods->fileInfo)(fd, info));
127
0
}
128
129
PR_IMPLEMENT(PRStatus)
130
0
PR_GetOpenFileInfo64(PRFileDesc* fd, PRFileInfo64* info) {
131
0
  return ((fd->methods->fileInfo64)(fd, info));
132
0
}
133
134
0
PR_IMPLEMENT(PRStatus) PR_Sync(PRFileDesc* fd) {
135
0
  return ((fd->methods->fsync)(fd));
136
0
}
137
138
PR_IMPLEMENT(PRStatus)
139
0
PR_Connect(PRFileDesc* fd, const PRNetAddr* addr, PRIntervalTime timeout) {
140
0
  return ((fd->methods->connect)(fd, addr, timeout));
141
0
}
142
143
0
PR_IMPLEMENT(PRStatus) PR_ConnectContinue(PRFileDesc* fd, PRInt16 out_flags) {
144
0
  return ((fd->methods->connectcontinue)(fd, out_flags));
145
0
}
146
147
PR_IMPLEMENT(PRFileDesc*)
148
0
PR_Accept(PRFileDesc* fd, PRNetAddr* addr, PRIntervalTime timeout) {
149
0
  return ((fd->methods->accept)(fd, addr, timeout));
150
0
}
151
152
0
PR_IMPLEMENT(PRStatus) PR_Bind(PRFileDesc* fd, const PRNetAddr* addr) {
153
0
  return ((fd->methods->bind)(fd, addr));
154
0
}
155
156
0
PR_IMPLEMENT(PRStatus) PR_Shutdown(PRFileDesc* fd, PRShutdownHow how) {
157
0
  return ((fd->methods->shutdown)(fd, how));
158
0
}
159
160
0
PR_IMPLEMENT(PRStatus) PR_Listen(PRFileDesc* fd, PRIntn backlog) {
161
0
  return ((fd->methods->listen)(fd, backlog));
162
0
}
163
164
PR_IMPLEMENT(PRInt32)
165
PR_Recv(PRFileDesc* fd, void* buf, PRInt32 amount, PRIntn flags,
166
0
        PRIntervalTime timeout) {
167
0
  return ((fd->methods->recv)(fd, buf, amount, flags, timeout));
168
0
}
169
170
PR_IMPLEMENT(PRInt32)
171
PR_Send(PRFileDesc* fd, const void* buf, PRInt32 amount, PRIntn flags,
172
0
        PRIntervalTime timeout) {
173
0
  return ((fd->methods->send)(fd, buf, amount, flags, timeout));
174
0
}
175
176
PR_IMPLEMENT(PRInt32)
177
PR_Writev(PRFileDesc* fd, const PRIOVec* iov, PRInt32 iov_size,
178
0
          PRIntervalTime timeout) {
179
0
  if (iov_size > PR_MAX_IOVECTOR_SIZE) {
180
0
    PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
181
0
    return -1;
182
0
  }
183
0
  return ((fd->methods->writev)(fd, iov, iov_size, timeout));
184
0
}
185
186
PR_IMPLEMENT(PRInt32)
187
PR_RecvFrom(PRFileDesc* fd, void* buf, PRInt32 amount, PRIntn flags,
188
0
            PRNetAddr* addr, PRIntervalTime timeout) {
189
0
  return ((fd->methods->recvfrom)(fd, buf, amount, flags, addr, timeout));
190
0
}
191
192
PR_IMPLEMENT(PRInt32)
193
PR_SendTo(PRFileDesc* fd, const void* buf, PRInt32 amount, PRIntn flags,
194
0
          const PRNetAddr* addr, PRIntervalTime timeout) {
195
0
  return ((fd->methods->sendto)(fd, buf, amount, flags, addr, timeout));
196
0
}
197
198
PR_IMPLEMENT(PRInt32)
199
PR_TransmitFile(PRFileDesc* sd, PRFileDesc* fd, const void* hdr, PRInt32 hlen,
200
0
                PRTransmitFileFlags flags, PRIntervalTime timeout) {
201
0
  return ((sd->methods->transmitfile)(sd, fd, hdr, hlen, flags, timeout));
202
0
}
203
204
PR_IMPLEMENT(PRInt32)
205
PR_AcceptRead(PRFileDesc* sd, PRFileDesc** nd, PRNetAddr** raddr, void* buf,
206
0
              PRInt32 amount, PRIntervalTime timeout) {
207
0
  return ((sd->methods->acceptread)(sd, nd, raddr, buf, amount, timeout));
208
0
}
209
210
0
PR_IMPLEMENT(PRStatus) PR_GetSockName(PRFileDesc* fd, PRNetAddr* addr) {
211
0
  return ((fd->methods->getsockname)(fd, addr));
212
0
}
213
214
0
PR_IMPLEMENT(PRStatus) PR_GetPeerName(PRFileDesc* fd, PRNetAddr* addr) {
215
0
  return ((fd->methods->getpeername)(fd, addr));
216
0
}
217
218
PR_IMPLEMENT(PRStatus)
219
0
PR_GetSocketOption(PRFileDesc* fd, PRSocketOptionData* data) {
220
0
  return ((fd->methods->getsocketoption)(fd, data));
221
0
}
222
223
PR_IMPLEMENT(PRStatus)
224
0
PR_SetSocketOption(PRFileDesc* fd, const PRSocketOptionData* data) {
225
0
  return ((fd->methods->setsocketoption)(fd, data));
226
0
}
227
228
PR_IMPLEMENT(PRInt32)
229
PR_SendFile(PRFileDesc* sd, PRSendFileData* sfd, PRTransmitFileFlags flags,
230
0
            PRIntervalTime timeout) {
231
0
  return ((sd->methods->sendfile)(sd, sfd, flags, timeout));
232
0
}
233
234
PR_IMPLEMENT(PRInt32)
235
PR_EmulateAcceptRead(PRFileDesc* sd, PRFileDesc** nd, PRNetAddr** raddr,
236
0
                     void* buf, PRInt32 amount, PRIntervalTime timeout) {
237
0
  PRInt32 rv = -1;
238
0
  PRNetAddr remote;
239
0
  PRFileDesc* accepted = NULL;
240
241
  /*
242
  ** The timeout does not apply to the accept portion of the
243
  ** operation - it waits indefinitely.
244
  */
245
0
  accepted = PR_Accept(sd, &remote, PR_INTERVAL_NO_TIMEOUT);
246
0
  if (NULL == accepted) {
247
0
    return rv;
248
0
  }
249
250
0
  rv = PR_Recv(accepted, buf, amount, 0, timeout);
251
0
  if (rv >= 0) {
252
    /* copy the new info out where caller can see it */
253
0
#define AMASK ((PRPtrdiff)7) /* mask for alignment of PRNetAddr */
254
0
    PRPtrdiff aligned = (PRPtrdiff)buf + amount + AMASK;
255
0
    *raddr = (PRNetAddr*)(aligned & ~AMASK);
256
0
    memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote));
257
0
    *nd = accepted;
258
0
    return rv;
259
0
  }
260
261
0
  PR_Close(accepted);
262
0
  return rv;
263
0
}
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
0
                   PRTransmitFileFlags flags, PRIntervalTime timeout) {
288
0
  PRInt32 rv, count = 0;
289
0
  PRInt32 len, file_bytes, index = 0;
290
0
  PRFileInfo info;
291
0
  PRIOVec iov[3];
292
0
  PRFileMap* mapHandle = NULL;
293
0
  void* addr = (void*)0; /* initialized to some arbitrary value. Keeps compiler
294
                            warnings down. */
295
0
  PRUint32 file_mmap_offset, alignment;
296
0
  PRInt64 zero64;
297
0
  PROffset64 file_mmap_offset64;
298
0
  PRUint32 addr_offset, mmap_len;
299
300
  /* Get file size */
301
0
  if (PR_SUCCESS != PR_GetOpenFileInfo(sfd->fd, &info)) {
302
0
    count = -1;
303
0
    goto done;
304
0
  }
305
0
  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
0
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
310
0
    count = -1;
311
0
    goto done;
312
0
  }
313
0
  if (sfd->file_nbytes) {
314
0
    file_bytes = sfd->file_nbytes;
315
0
  } else {
316
0
    file_bytes = info.size - sfd->file_offset;
317
0
  }
318
319
0
  alignment = PR_GetMemMapAlignment();
320
321
  /* number of initial bytes to skip in mmap'd segment */
322
0
  addr_offset = sfd->file_offset % alignment;
323
324
  /* find previous mmap alignment boundary */
325
0
  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
0
  mmap_len = PR_MIN(file_bytes + addr_offset, SENDFILE_MMAP_CHUNK);
332
0
  len = mmap_len - addr_offset;
333
334
  /*
335
   * Map in (part of) file. Take care of zero-length files.
336
   */
337
0
  if (len) {
338
0
    LL_I2L(zero64, 0);
339
0
    mapHandle = PR_CreateFileMap(sfd->fd, zero64, PR_PROT_READONLY);
340
0
    if (!mapHandle) {
341
0
      count = -1;
342
0
      goto done;
343
0
    }
344
0
    LL_I2L(file_mmap_offset64, file_mmap_offset);
345
0
    addr = PR_MemMap(mapHandle, file_mmap_offset64, mmap_len);
346
0
    if (!addr) {
347
0
      count = -1;
348
0
      goto done;
349
0
    }
350
0
  }
351
  /*
352
   * send headers first, followed by the file
353
   */
354
0
  if (sfd->hlen) {
355
0
    iov[index].iov_base = (char*)sfd->header;
356
0
    iov[index].iov_len = sfd->hlen;
357
0
    index++;
358
0
  }
359
0
  if (len) {
360
0
    iov[index].iov_base = (char*)addr + addr_offset;
361
0
    iov[index].iov_len = len;
362
0
    index++;
363
0
  }
364
0
  if ((file_bytes == len) && (sfd->tlen)) {
365
    /*
366
     * all file data is mapped in; send the trailer too
367
     */
368
0
    iov[index].iov_base = (char*)sfd->trailer;
369
0
    iov[index].iov_len = sfd->tlen;
370
0
    index++;
371
0
  }
372
0
  rv = PR_Writev(sd, iov, index, timeout);
373
0
  if (len) {
374
0
    PR_MemUnmap(addr, mmap_len);
375
0
  }
376
0
  if (rv < 0) {
377
0
    count = -1;
378
0
    goto done;
379
0
  }
380
381
0
  PR_ASSERT(rv == sfd->hlen + len + ((len == file_bytes) ? sfd->tlen : 0));
382
383
0
  file_bytes -= len;
384
0
  count += rv;
385
0
  if (!file_bytes) { /* header, file and trailer are sent */
386
0
    goto done;
387
0
  }
388
389
  /*
390
   * send remaining bytes of the file, if any
391
   */
392
0
  len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK);
393
0
  while (len > 0) {
394
    /*
395
     * Map in (part of) file
396
     */
397
0
    file_mmap_offset = sfd->file_offset + count - sfd->hlen;
398
0
    PR_ASSERT((file_mmap_offset % alignment) == 0);
399
400
0
    LL_I2L(file_mmap_offset64, file_mmap_offset);
401
0
    addr = PR_MemMap(mapHandle, file_mmap_offset64, len);
402
0
    if (!addr) {
403
0
      count = -1;
404
0
      goto done;
405
0
    }
406
0
    rv = PR_Send(sd, addr, len, 0, timeout);
407
0
    PR_MemUnmap(addr, len);
408
0
    if (rv < 0) {
409
0
      count = -1;
410
0
      goto done;
411
0
    }
412
413
0
    PR_ASSERT(rv == len);
414
0
    file_bytes -= rv;
415
0
    count += rv;
416
0
    len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK);
417
0
  }
418
0
  PR_ASSERT(0 == file_bytes);
419
0
  if (sfd->tlen) {
420
0
    rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout);
421
0
    if (rv >= 0) {
422
0
      PR_ASSERT(rv == sfd->tlen);
423
0
      count += rv;
424
0
    } else {
425
0
      count = -1;
426
0
    }
427
0
  }
428
0
done:
429
0
  if (mapHandle) {
430
0
    PR_CloseFileMap(mapHandle);
431
0
  }
432
0
  if ((count >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) {
433
0
    PR_Close(sd);
434
0
  }
435
0
  return count;
436
0
}
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 */