~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source3/lib/sendfile.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
28
#if defined(LINUX_SENDFILE_API)
 
29
 
 
30
#include <sys/sendfile.h>
 
31
 
 
32
#ifndef MSG_MORE
 
33
#define MSG_MORE 0x8000
 
34
#endif
 
35
 
 
36
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
 
37
{
 
38
        size_t total=0;
 
39
        ssize_t ret;
 
40
        size_t hdr_len = 0;
 
41
 
 
42
        /*
 
43
         * Send the header first.
 
44
         * Use MSG_MORE to cork the TCP output until sendfile is called.
 
45
         */
 
46
 
 
47
        if (header) {
 
48
                hdr_len = header->length;
 
49
                while (total < hdr_len) {
 
50
                        ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
 
51
                        if (ret == -1)
 
52
                                return -1;
 
53
                        total += ret;
 
54
                }
 
55
        }
 
56
 
 
57
        total = count;
 
58
        while (total) {
 
59
                ssize_t nwritten;
 
60
                do {
 
61
#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
 
62
                        nwritten = sendfile64(tofd, fromfd, &offset, total);
 
63
#else
 
64
                        nwritten = sendfile(tofd, fromfd, &offset, total);
 
65
#endif
 
66
                } while (nwritten == -1 && errno == EINTR);
 
67
                if (nwritten == -1) {
 
68
                        if (errno == ENOSYS || errno == EINVAL) {
 
69
                                /* Ok - we're in a world of pain here. We just sent
 
70
                                 * the header, but the sendfile failed. We have to
 
71
                                 * emulate the sendfile at an upper layer before we
 
72
                                 * disable it's use. So we do something really ugly.
 
73
                                 * We set the errno to a strange value so we can detect
 
74
                                 * this at the upper level and take care of it without
 
75
                                 * layer violation. JRA.
 
76
                                 */
 
77
                                errno = EINTR; /* Normally we can never return this. */
 
78
                        }
 
79
                        return -1;
 
80
                }
 
81
                if (nwritten == 0) {
 
82
                        /*
 
83
                         * EOF, return a short read
 
84
                         */
 
85
                        return hdr_len + (count - total);
 
86
                }
 
87
                total -= nwritten;
 
88
        }
 
89
        return count + hdr_len;
 
90
}
 
91
 
 
92
#elif defined(LINUX_BROKEN_SENDFILE_API)
 
93
 
 
94
/*
 
95
 * We must use explicit 32 bit types here. This code path means Linux
 
96
 * won't do proper 64-bit sendfile. JRA.
 
97
 */
 
98
 
 
99
extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
 
100
 
 
101
 
 
102
#ifndef MSG_MORE
 
103
#define MSG_MORE 0x8000
 
104
#endif
 
105
 
 
106
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
 
107
{
 
108
        size_t total=0;
 
109
        ssize_t ret;
 
110
        ssize_t hdr_len = 0;
 
111
        uint32 small_total = 0;
 
112
        int32 small_offset;
 
113
 
 
114
        /* 
 
115
         * Fix for broken Linux 2.4 systems with no working sendfile64().
 
116
         * If the offset+count > 2 GB then pretend we don't have the
 
117
         * system call sendfile at all. The upper layer catches this
 
118
         * and uses a normal read. JRA.
 
119
         */
 
120
 
 
121
        if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
 
122
                errno = ENOSYS;
 
123
                return -1;
 
124
        }
 
125
 
 
126
        /*
 
127
         * Send the header first.
 
128
         * Use MSG_MORE to cork the TCP output until sendfile is called.
 
129
         */
 
130
 
 
131
        if (header) {
 
132
                hdr_len = header->length;
 
133
                while (total < hdr_len) {
 
134
                        ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
 
135
                        if (ret == -1)
 
136
                                return -1;
 
137
                        total += ret;
 
138
                }
 
139
        }
 
140
 
 
141
        small_total = (uint32)count;
 
142
        small_offset = (int32)offset;
 
143
 
 
144
        while (small_total) {
 
145
                int32 nwritten;
 
146
                do {
 
147
                        nwritten = sendfile(tofd, fromfd, &small_offset, small_total);
 
148
                } while (nwritten == -1 && errno == EINTR);
 
149
                if (nwritten == -1) {
 
150
                        if (errno == ENOSYS || errno == EINVAL) {
 
151
                                /* Ok - we're in a world of pain here. We just sent
 
152
                                 * the header, but the sendfile failed. We have to
 
153
                                 * emulate the sendfile at an upper layer before we
 
154
                                 * disable it's use. So we do something really ugly.
 
155
                                 * We set the errno to a strange value so we can detect
 
156
                                 * this at the upper level and take care of it without
 
157
                                 * layer violation. JRA.
 
158
                                 */
 
159
                                errno = EINTR; /* Normally we can never return this. */
 
160
                        }
 
161
                        return -1;
 
162
                }
 
163
                if (nwritten == 0) {
 
164
                        /*
 
165
                         * EOF, return a short read
 
166
                         */
 
167
                        return hdr_len + (((uint32)count) - small_total);
 
168
                }
 
169
                small_total -= nwritten;
 
170
        }
 
171
        return count + hdr_len;
 
172
}
 
173
 
 
174
 
 
175
#elif defined(SOLARIS_SENDFILE_API)
 
176
 
 
177
/*
 
178
 * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
 
179
 */
 
180
 
 
181
#include <sys/sendfile.h>
 
182
 
 
183
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
 
184
{
 
185
        int sfvcnt;
 
186
        size_t total, xferred;
 
187
        struct sendfilevec vec[2];
 
188
        ssize_t hdr_len = 0;
 
189
 
 
190
        if (header) {
 
191
                sfvcnt = 2;
 
192
 
 
193
                vec[0].sfv_fd = SFV_FD_SELF;
 
194
                vec[0].sfv_flag = 0;
 
195
                vec[0].sfv_off = (off_t)header->data;
 
196
                vec[0].sfv_len = hdr_len = header->length;
 
197
 
 
198
                vec[1].sfv_fd = fromfd;
 
199
                vec[1].sfv_flag = 0;
 
200
                vec[1].sfv_off = offset;
 
201
                vec[1].sfv_len = count;
 
202
 
 
203
        } else {
 
204
                sfvcnt = 1;
 
205
 
 
206
                vec[0].sfv_fd = fromfd;
 
207
                vec[0].sfv_flag = 0;
 
208
                vec[0].sfv_off = offset;
 
209
                vec[0].sfv_len = count;
 
210
        }
 
211
 
 
212
        total = count + hdr_len;
 
213
 
 
214
        while (total) {
 
215
                ssize_t nwritten;
 
216
 
 
217
                /*
 
218
                 * Although not listed in the API error returns, this is almost certainly
 
219
                 * a slow system call and will be interrupted by a signal with EINTR. JRA.
 
220
                 */
 
221
 
 
222
                xferred = 0;
 
223
 
 
224
#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILEV64)
 
225
                        nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
 
226
#else
 
227
                        nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
 
228
#endif
 
229
                if (nwritten == -1 && errno == EINTR) {
 
230
                        if (xferred == 0)
 
231
                                continue; /* Nothing written yet. */
 
232
                        else
 
233
                                nwritten = xferred;
 
234
                }
 
235
 
 
236
                if (nwritten == -1)
 
237
                        return -1;
 
238
                if (nwritten == 0)
 
239
                        return -1; /* I think we're at EOF here... */
 
240
 
 
241
                /*
 
242
                 * If this was a short (signal interrupted) write we may need
 
243
                 * to subtract it from the header data, or null out the header
 
244
                 * data altogether if we wrote more than vec[0].sfv_len bytes.
 
245
                 * We move vec[1].* to vec[0].* and set sfvcnt to 1
 
246
                 */
 
247
 
 
248
                if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
 
249
                        vec[1].sfv_off += nwritten - vec[0].sfv_len;
 
250
                        vec[1].sfv_len -= nwritten - vec[0].sfv_len;
 
251
 
 
252
                        /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
 
253
                        vec[0] = vec[1];
 
254
                        sfvcnt = 1;
 
255
                } else {
 
256
                        vec[0].sfv_off += nwritten;
 
257
                        vec[0].sfv_len -= nwritten;
 
258
                }
 
259
                total -= nwritten;
 
260
        }
 
261
        return count + hdr_len;
 
262
}
 
263
 
 
264
#elif defined(HPUX_SENDFILE_API)
 
265
 
 
266
#include <sys/socket.h>
 
267
#include <sys/uio.h>
 
268
 
 
269
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
 
270
{
 
271
        size_t total=0;
 
272
        struct iovec hdtrl[2];
 
273
        size_t hdr_len = 0;
 
274
 
 
275
        if (header) {
 
276
                /* Set up the header/trailer iovec. */
 
277
                hdtrl[0].iov_base = header->data;
 
278
                hdtrl[0].iov_len = hdr_len = header->length;
 
279
        } else {
 
280
                hdtrl[0].iov_base = NULL;
 
281
                hdtrl[0].iov_len = hdr_len = 0;
 
282
        }
 
283
        hdtrl[1].iov_base = NULL;
 
284
        hdtrl[1].iov_len = 0;
 
285
 
 
286
        total = count;
 
287
        while (total + hdtrl[0].iov_len) {
 
288
                ssize_t nwritten;
 
289
 
 
290
                /*
 
291
                 * HPUX guarantees that if any data was written before
 
292
                 * a signal interrupt then sendfile returns the number of
 
293
                 * bytes written (which may be less than requested) not -1.
 
294
                 * nwritten includes the header data sent.
 
295
                 */
 
296
 
 
297
                do {
 
298
#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
 
299
                        nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
 
300
#else
 
301
                        nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
 
302
#endif
 
303
                } while (nwritten == -1 && errno == EINTR);
 
304
                if (nwritten == -1)
 
305
                        return -1;
 
306
                if (nwritten == 0)
 
307
                        return -1; /* I think we're at EOF here... */
 
308
 
 
309
                /*
 
310
                 * If this was a short (signal interrupted) write we may need
 
311
                 * to subtract it from the header data, or null out the header
 
312
                 * data altogether if we wrote more than hdtrl[0].iov_len bytes.
 
313
                 * We change nwritten to be the number of file bytes written.
 
314
                 */
 
315
 
 
316
                if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
 
317
                        if (nwritten >= hdtrl[0].iov_len) {
 
318
                                nwritten -= hdtrl[0].iov_len;
 
319
                                hdtrl[0].iov_base = NULL;
 
320
                                hdtrl[0].iov_len = 0;
 
321
                        } else {
 
322
                                /* iov_base is defined as a void *... */
 
323
                                hdtrl[0].iov_base = ((char *)hdtrl[0].iov_base) + nwritten;
 
324
                                hdtrl[0].iov_len -= nwritten;
 
325
                                nwritten = 0;
 
326
                        }
 
327
                }
 
328
                total -= nwritten;
 
329
                offset += nwritten;
 
330
        }
 
331
        return count + hdr_len;
 
332
}
 
333
 
 
334
#elif defined(FREEBSD_SENDFILE_API)
 
335
 
 
336
#include <sys/types.h>
 
337
#include <sys/socket.h>
 
338
#include <sys/uio.h>
 
339
 
 
340
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
 
341
{
 
342
        size_t total=0;
 
343
        struct sf_hdtr hdr;
 
344
        struct iovec hdtrl;
 
345
        size_t hdr_len = 0;
 
346
 
 
347
        hdr.headers = &hdtrl;
 
348
        hdr.hdr_cnt = 1;
 
349
        hdr.trailers = NULL;
 
350
        hdr.trl_cnt = 0;
 
351
 
 
352
        /* Set up the header iovec. */
 
353
        if (header) {
 
354
                hdtrl.iov_base = header->data;
 
355
                hdtrl.iov_len = hdr_len = header->length;
 
356
        } else {
 
357
                hdtrl.iov_base = NULL;
 
358
                hdtrl.iov_len = 0;
 
359
        }
 
360
 
 
361
        total = count;
 
362
        while (total + hdtrl.iov_len) {
 
363
                SMB_OFF_T nwritten;
 
364
                int ret;
 
365
 
 
366
                /*
 
367
                 * FreeBSD sendfile returns 0 on success, -1 on error.
 
368
                 * Remember, the tofd and fromfd are reversed..... :-).
 
369
                 * nwritten includes the header data sent.
 
370
                 */
 
371
 
 
372
                do {
 
373
                        ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
 
374
                } while (ret == -1 && errno == EINTR);
 
375
                if (ret == -1)
 
376
                        return -1;
 
377
 
 
378
                if (nwritten == 0)
 
379
                        return -1; /* I think we're at EOF here... */
 
380
 
 
381
                /*
 
382
                 * If this was a short (signal interrupted) write we may need
 
383
                 * to subtract it from the header data, or null out the header
 
384
                 * data altogether if we wrote more than hdtrl.iov_len bytes.
 
385
                 * We change nwritten to be the number of file bytes written.
 
386
                 */
 
387
 
 
388
                if (hdtrl.iov_base && hdtrl.iov_len) {
 
389
                        if (nwritten >= hdtrl.iov_len) {
 
390
                                nwritten -= hdtrl.iov_len;
 
391
                                hdtrl.iov_base = NULL;
 
392
                                hdtrl.iov_len = 0;
 
393
                        } else {
 
394
                                hdtrl.iov_base =
 
395
                                    (caddr_t)hdtrl.iov_base + nwritten;
 
396
                                hdtrl.iov_len -= nwritten;
 
397
                                nwritten = 0;
 
398
                        }
 
399
                }
 
400
                total -= nwritten;
 
401
                offset += nwritten;
 
402
        }
 
403
        return count + hdr_len;
 
404
}
 
405
 
 
406
#elif defined(AIX_SENDFILE_API)
 
407
 
 
408
/* BEGIN AIX SEND_FILE */
 
409
 
 
410
/* Contributed by William Jojo <jojowil@hvcc.edu> */
 
411
#include <sys/socket.h>
 
412
 
 
413
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
 
414
{
 
415
        struct sf_parms hdtrl;
 
416
 
 
417
        /* Set up the header/trailer struct params. */
 
418
        if (header) {
 
419
                hdtrl.header_data = header->data;
 
420
                hdtrl.header_length = header->length;
 
421
        } else {
 
422
                hdtrl.header_data = NULL;
 
423
                hdtrl.header_length = 0;
 
424
        }
 
425
        hdtrl.trailer_data = NULL;
 
426
        hdtrl.trailer_length = 0;
 
427
 
 
428
        hdtrl.file_descriptor = fromfd;
 
429
        hdtrl.file_offset = offset;
 
430
        hdtrl.file_bytes = count;
 
431
 
 
432
        while ( hdtrl.file_bytes + hdtrl.header_length ) {
 
433
                ssize_t ret;
 
434
 
 
435
                /*
 
436
                 Return Value
 
437
 
 
438
                 There are three possible return values from send_file:
 
439
 
 
440
                 Value Description
 
441
 
 
442
                 -1 an error has occurred, errno contains the error code.
 
443
 
 
444
                 0 the command has completed successfully.
 
445
 
 
446
                 1 the command was completed partially, some data has been
 
447
                 transmitted but the command has to return for some reason,
 
448
                 for example, the command was interrupted by signals.
 
449
                */
 
450
                do {
 
451
                        ret = send_file(&tofd, &hdtrl, 0);
 
452
                } while ( (ret == 1) || (ret == -1 && errno == EINTR) );
 
453
                if ( ret == -1 )
 
454
                        return -1;
 
455
        }
 
456
 
 
457
        return count + header->length;
 
458
}
 
459
/* END AIX SEND_FILE */
 
460
 
 
461
#else /* No sendfile implementation. Return error. */
 
462
 
 
463
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
 
464
{
 
465
        /* No sendfile syscall. */
 
466
        errno = ENOSYS;
 
467
        return -1;
 
468
}
 
469
#endif