Coverage Report

Created: 2025-11-16 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/lib/sendfile.c
Line
Count
Source
1
/*
2
 Unix SMB/Netbios implementation.
3
 Version 2.2.x / 3.0.x
4
 sendfile implementations.
5
 Copyright (C) Jeremy Allison 2002.
6
7
 This program is free software; you can redistribute it and/or modify
8
 it under the terms of the GNU General Public License as published by
9
 the Free Software Foundation; either version 3 of the License, or
10
 (at your option) any later version.
11
 This program is distributed in the hope that it will be useful,
12
 but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 GNU General Public License for more details.
15
16
 You should have received a copy of the GNU General Public License
17
 along with this program; if not, see <http://www.gnu.org/licenses/>.
18
*/
19
20
/*
21
 * This file handles the OS dependent sendfile implementations.
22
 * The API is such that it returns -1 on error, else returns the
23
 * number of bytes written.
24
 */
25
26
#include "includes.h"
27
#include "system/filesys.h"
28
29
#if defined(LINUX_SENDFILE_API)
30
31
#include <sys/sendfile.h>
32
33
#ifndef MSG_MORE
34
#define MSG_MORE 0x8000
35
#endif
36
37
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
38
0
{
39
0
  size_t total=0;
40
0
  ssize_t ret = -1;
41
0
  size_t hdr_len = 0;
42
0
  int old_flags = 0;
43
0
  bool socket_flags_changed = false;
44
45
  /*
46
   * Send the header first.
47
   * Use MSG_MORE to cork the TCP output until sendfile is called.
48
   */
49
50
0
  if (header) {
51
0
    hdr_len = header->length;
52
0
    while (total < hdr_len) {
53
0
      ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
54
0
      if (ret == -1) {
55
0
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
56
          /*
57
           * send() must complete before we can
58
           * send any other outgoing data on the
59
           * socket. Ensure socket is in blocking
60
           * mode. For SMB2 by default the socket
61
           * is in non-blocking mode.
62
           */
63
0
          old_flags = fcntl(tofd, F_GETFL, 0);
64
0
          ret = set_blocking(tofd, true);
65
0
          if (ret == -1) {
66
0
            goto out;
67
0
          }
68
0
          socket_flags_changed = true;
69
0
          continue;
70
0
        }
71
0
        goto out;
72
0
      }
73
0
      total += ret;
74
0
    }
75
0
  }
76
77
0
  total = count;
78
0
  while (total) {
79
0
    ssize_t nwritten;
80
0
    do {
81
0
      nwritten = sendfile(tofd, fromfd, &offset, total);
82
0
    } while (nwritten == -1 && errno == EINTR);
83
0
    if (nwritten == -1) {
84
0
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
85
0
        if (socket_flags_changed) {
86
          /*
87
           * We're already in blocking
88
           * mode. This is an error.
89
           */
90
0
          ret = -1;
91
0
          goto out;
92
0
        }
93
94
        /*
95
         * Sendfile must complete before we can
96
         * send any other outgoing data on the socket.
97
         * Ensure socket is in blocking mode.
98
         * For SMB2 by default the socket is in
99
         * non-blocking mode.
100
         */
101
0
        old_flags = fcntl(tofd, F_GETFL, 0);
102
0
        ret = set_blocking(tofd, true);
103
0
        if (ret == -1) {
104
0
          goto out;
105
0
        }
106
0
        socket_flags_changed = true;
107
0
        continue;
108
0
      }
109
110
0
      if (errno == ENOSYS || errno == EINVAL) {
111
        /* Ok - we're in a world of pain here. We just sent
112
         * the header, but the sendfile failed. We have to
113
         * emulate the sendfile at an upper layer before we
114
         * disable it's use. So we do something really ugly.
115
         * We set the errno to a strange value so we can detect
116
         * this at the upper level and take care of it without
117
         * layer violation. JRA.
118
         */
119
0
        errno = EINTR; /* Normally we can never return this. */
120
0
      }
121
0
      ret = -1;
122
0
      goto out;
123
0
    }
124
0
    if (nwritten == 0) {
125
      /*
126
       * EOF, return a short read
127
       */
128
0
      ret = hdr_len + (count - total);
129
0
      goto out;
130
0
    }
131
0
    total -= nwritten;
132
0
  }
133
134
0
  ret = count + hdr_len;
135
136
0
  out:
137
138
0
  if (socket_flags_changed) {
139
0
    int saved_errno = errno;
140
0
    int err;
141
142
    /* Restore the old state of the socket. */
143
0
    err = fcntl(tofd, F_SETFL, old_flags);
144
0
    if (err == -1) {
145
0
      return -1;
146
0
    }
147
0
    if (ret == -1) {
148
0
      errno = saved_errno;
149
0
    }
150
0
  }
151
152
0
  return ret;
153
0
}
154
155
#elif defined(SOLARIS_SENDFILE_API)
156
157
/*
158
 * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
159
 */
160
161
#include <sys/sendfile.h>
162
163
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
164
{
165
  int sfvcnt;
166
  size_t total, xferred;
167
  struct sendfilevec vec[2];
168
  ssize_t hdr_len = 0;
169
  int old_flags = 0;
170
  ssize_t ret = -1;
171
  bool socket_flags_changed = false;
172
173
  if (header) {
174
    sfvcnt = 2;
175
176
    vec[0].sfv_fd = SFV_FD_SELF;
177
    vec[0].sfv_flag = 0;
178
    vec[0].sfv_off = (off_t)header->data;
179
    vec[0].sfv_len = hdr_len = header->length;
180
181
    vec[1].sfv_fd = fromfd;
182
    vec[1].sfv_flag = 0;
183
    vec[1].sfv_off = offset;
184
    vec[1].sfv_len = count;
185
186
  } else {
187
    sfvcnt = 1;
188
189
    vec[0].sfv_fd = fromfd;
190
    vec[0].sfv_flag = 0;
191
    vec[0].sfv_off = offset;
192
    vec[0].sfv_len = count;
193
  }
194
195
  total = count + hdr_len;
196
197
  while (total) {
198
    ssize_t nwritten;
199
200
    /*
201
     * Although not listed in the API error returns, this is almost certainly
202
     * a slow system call and will be interrupted by a signal with EINTR. JRA.
203
     */
204
205
    xferred = 0;
206
207
      nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
208
    if  (nwritten == -1 && errno == EINTR) {
209
      if (xferred == 0)
210
        continue; /* Nothing written yet. */
211
      else
212
        nwritten = xferred;
213
    }
214
215
    if (nwritten == -1) {
216
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
217
        /*
218
         * Sendfile must complete before we can
219
         * send any other outgoing data on the socket.
220
         * Ensure socket is in blocking mode.
221
         * For SMB2 by default the socket is in
222
         * non-blocking mode.
223
         */
224
        old_flags = fcntl(tofd, F_GETFL, 0);
225
        ret = set_blocking(tofd, true);
226
        if (ret == -1) {
227
          goto out;
228
        }
229
        socket_flags_changed = true;
230
        continue;
231
      }
232
      ret = -1;
233
      goto out;
234
    }
235
    if (nwritten == 0) {
236
      ret = -1;
237
      goto out; /* I think we're at EOF here... */
238
    }
239
240
    /*
241
     * If this was a short (signal interrupted) write we may need
242
     * to subtract it from the header data, or null out the header
243
     * data altogether if we wrote more than vec[0].sfv_len bytes.
244
     * We move vec[1].* to vec[0].* and set sfvcnt to 1
245
     */
246
247
    if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
248
      vec[1].sfv_off += nwritten - vec[0].sfv_len;
249
      vec[1].sfv_len -= nwritten - vec[0].sfv_len;
250
251
      /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
252
      vec[0] = vec[1];
253
      sfvcnt = 1;
254
    } else {
255
      vec[0].sfv_off += nwritten;
256
      vec[0].sfv_len -= nwritten;
257
    }
258
    total -= nwritten;
259
  }
260
  ret = count + hdr_len;
261
262
  out:
263
264
  if (socket_flags_changed) {
265
    int saved_errno;
266
    int err;
267
268
    if (ret == -1) {
269
      saved_errno = errno;
270
    }
271
    /* Restore the old state of the socket. */
272
    err = fcntl(tofd, F_SETFL, old_flags);
273
    if (err == -1) {
274
      return -1;
275
    }
276
    if (ret == -1) {
277
      errno = saved_errno;
278
    }
279
  }
280
281
  return ret;
282
}
283
284
#elif defined(HPUX_SENDFILE_API)
285
286
#include <sys/socket.h>
287
#include <sys/uio.h>
288
289
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
290
{
291
  size_t total=0;
292
  struct iovec hdtrl[2];
293
  size_t hdr_len = 0;
294
  int old_flags = 0;
295
  ssize_t ret = -1;
296
  bool socket_flags_changed = false;
297
298
  if (header) {
299
    /* Set up the header/trailer iovec. */
300
    hdtrl[0].iov_base = (void *)header->data;
301
    hdtrl[0].iov_len = hdr_len = header->length;
302
  } else {
303
    hdtrl[0].iov_base = NULL;
304
    hdtrl[0].iov_len = hdr_len = 0;
305
  }
306
  hdtrl[1].iov_base = NULL;
307
  hdtrl[1].iov_len = 0;
308
309
  total = count;
310
  while (total + hdtrl[0].iov_len) {
311
    ssize_t nwritten;
312
313
    /*
314
     * HPUX guarantees that if any data was written before
315
     * a signal interrupt then sendfile returns the number of
316
     * bytes written (which may be less than requested) not -1.
317
     * nwritten includes the header data sent.
318
     */
319
320
    do {
321
      nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
322
    } while (nwritten == -1 && errno == EINTR);
323
    if (nwritten == -1) {
324
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
325
        /*
326
         * Sendfile must complete before we can
327
         * send any other outgoing data on the socket.
328
         * Ensure socket is in blocking mode.
329
         * For SMB2 by default the socket is in
330
         * non-blocking mode.
331
         */
332
        old_flags = fcntl(tofd, F_GETFL, 0);
333
        ret = set_blocking(tofd, true);
334
        if (ret == -1) {
335
          goto out;
336
        }
337
        socket_flags_changed = true;
338
        continue;
339
      }
340
      ret = -1;
341
      goto out;
342
    }
343
    if (nwritten == 0) {
344
      ret = -1; /* I think we're at EOF here... */
345
      goto out;
346
    }
347
348
    /*
349
     * If this was a short (signal interrupted) write we may need
350
     * to subtract it from the header data, or null out the header
351
     * data altogether if we wrote more than hdtrl[0].iov_len bytes.
352
     * We change nwritten to be the number of file bytes written.
353
     */
354
355
    if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
356
      if (nwritten >= hdtrl[0].iov_len) {
357
        nwritten -= hdtrl[0].iov_len;
358
        hdtrl[0].iov_base = NULL;
359
        hdtrl[0].iov_len = 0;
360
      } else {
361
        /* iov_base is defined as a void *... */
362
        hdtrl[0].iov_base = (void *)(((char *)hdtrl[0].iov_base) + nwritten);
363
        hdtrl[0].iov_len -= nwritten;
364
        nwritten = 0;
365
      }
366
    }
367
    total -= nwritten;
368
    offset += nwritten;
369
  }
370
  ret = count + hdr_len;
371
372
  out:
373
374
  if (socket_flags_changed) {
375
    int saved_errno;
376
    int err;
377
378
    if (ret == -1) {
379
      saved_errno = errno;
380
    }
381
    /* Restore the old state of the socket. */
382
    err = fcntl(tofd, F_SETFL, old_flags);
383
    if (err == -1) {
384
      return -1;
385
    }
386
    if (ret == -1) {
387
      errno = saved_errno;
388
    }
389
  }
390
391
  return ret;
392
}
393
394
#elif defined(FREEBSD_SENDFILE_API) || defined(DARWIN_SENDFILE_API)
395
396
#include <sys/types.h>
397
#include <sys/socket.h>
398
#include <sys/uio.h>
399
400
ssize_t sys_sendfile(int tofd, int fromfd,
401
      const DATA_BLOB *header, off_t offset, size_t count)
402
{
403
  struct sf_hdtr  sf_header = {0};
404
  struct iovec  io_header = {0};
405
  int old_flags = 0;
406
407
  off_t nwritten;
408
  ssize_t ret = -1;
409
  bool socket_flags_changed = false;
410
411
  if (header) {
412
    sf_header.headers = &io_header;
413
    sf_header.hdr_cnt = 1;
414
    io_header.iov_base = header->data;
415
    io_header.iov_len = header->length;
416
    sf_header.trailers = NULL;
417
    sf_header.trl_cnt = 0;
418
  }
419
420
  while (count != 0) {
421
422
    nwritten = count;
423
#if defined(DARWIN_SENDFILE_API)
424
    /* Darwin recycles nwritten as a value-result parameter, apart from that this
425
       sendfile implementation is quite the same as the FreeBSD one */
426
    ret = sendfile(fromfd, tofd, offset, &nwritten, &sf_header, 0);
427
#else
428
    ret = sendfile(fromfd, tofd, offset, count, &sf_header, &nwritten, 0);
429
#endif
430
    if (ret == -1 && errno != EINTR) {
431
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
432
        /*
433
         * Sendfile must complete before we can
434
         * send any other outgoing data on the socket.
435
         * Ensure socket is in blocking mode.
436
         * For SMB2 by default the socket is in
437
         * non-blocking mode.
438
         */
439
        old_flags = fcntl(tofd, F_GETFL, 0);
440
        ret = set_blocking(tofd, true);
441
        if (ret == -1) {
442
          goto out;
443
        }
444
        socket_flags_changed = true;
445
        continue;
446
      }
447
      /* Send failed, we are toast. */
448
      ret = -1;
449
      goto out;
450
    }
451
452
    if (nwritten == 0) {
453
      /* EOF of offset is after EOF. */
454
      break;
455
    }
456
457
    if (sf_header.hdr_cnt) {
458
      if (io_header.iov_len <= nwritten) {
459
        /* Entire header was sent. */
460
        sf_header.headers = NULL;
461
        sf_header.hdr_cnt = 0;
462
        nwritten -= io_header.iov_len;
463
      } else {
464
        /* Partial header was sent. */
465
        io_header.iov_len -= nwritten;
466
        io_header.iov_base =
467
            ((uint8_t *)io_header.iov_base) + nwritten;
468
        nwritten = 0;
469
      }
470
    }
471
472
    offset += nwritten;
473
    count -= nwritten;
474
  }
475
476
  ret = nwritten;
477
478
  out:
479
480
  if (socket_flags_changed) {
481
    int saved_errno;
482
    int err;
483
484
    if (ret == -1) {
485
      saved_errno = errno;
486
    }
487
    /* Restore the old state of the socket. */
488
    err = fcntl(tofd, F_SETFL, old_flags);
489
    if (err == -1) {
490
      return -1;
491
    }
492
    if (ret == -1) {
493
      errno = saved_errno;
494
    }
495
  }
496
497
  return ret;
498
}
499
500
#elif defined(AIX_SENDFILE_API)
501
502
/* BEGIN AIX SEND_FILE */
503
504
/* Contributed by William Jojo <jojowil@hvcc.edu> */
505
#include <sys/socket.h>
506
507
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
508
{
509
  struct sf_parms hdtrl;
510
  int old_flags = 0;
511
  ssize_t ret = -1;
512
  bool socket_flags_changed = false;
513
514
  /* Set up the header/trailer struct params. */
515
  if (header) {
516
    hdtrl.header_data = header->data;
517
    hdtrl.header_length = header->length;
518
  } else {
519
    hdtrl.header_data = NULL;
520
    hdtrl.header_length = 0;
521
  }
522
  hdtrl.trailer_data = NULL;
523
  hdtrl.trailer_length = 0;
524
525
  hdtrl.file_descriptor = fromfd;
526
  hdtrl.file_offset = offset;
527
  hdtrl.file_bytes = count;
528
529
  while ( hdtrl.file_bytes + hdtrl.header_length ) {
530
    /*
531
     Return Value
532
533
     There are three possible return values from send_file:
534
535
     Value Description
536
537
     -1 an error has occurred, errno contains the error code.
538
539
     0 the command has completed successfully.
540
541
     1 the command was completed partially, some data has been
542
     transmitted but the command has to return for some reason,
543
     for example, the command was interrupted by signals.
544
    */
545
    do {
546
      ret = send_file(&tofd, &hdtrl, 0);
547
    } while ((ret == 1) || (ret == -1 && errno == EINTR));
548
    if ( ret == -1 ) {
549
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
550
        /*
551
         * Sendfile must complete before we can
552
         * send any other outgoing data on the socket.
553
         * Ensure socket is in blocking mode.
554
         * For SMB2 by default the socket is in
555
         * non-blocking mode.
556
         */
557
        old_flags = fcntl(tofd, F_GETFL, 0);
558
        ret = set_blocking(tofd, true);
559
        if (ret == -1) {
560
          goto out;
561
        }
562
        socket_flags_changed = true;
563
        continue;
564
      }
565
      goto out;
566
    }
567
  }
568
569
  ret = count + header->length;
570
571
  out:
572
573
  if (socket_flags_changed) {
574
    int saved_errno;
575
    int err;
576
577
    if (ret == -1) {
578
      saved_errno = errno;
579
    }
580
    /* Restore the old state of the socket. */
581
    err = fcntl(tofd, F_SETFL, old_flags);
582
    if (err == -1) {
583
      return -1;
584
    }
585
    if (ret == -1) {
586
      errno = saved_errno;
587
    }
588
  }
589
590
  return ret;
591
}
592
/* END AIX SEND_FILE */
593
594
#else /* No sendfile implementation. Return error. */
595
596
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
597
{
598
  /* No sendfile syscall. */
599
  errno = ENOSYS;
600
  return -1;
601
}
602
#endif