File: xwrite.c

package info (click to toggle)
lbcd 3.5.2-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 1,704 kB
  • ctags: 1,065
  • sloc: ansic: 11,073; sh: 1,823; perl: 503; makefile: 164
file content (229 lines) | stat: -rw-r--r-- 7,504 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/*
 * write and writev replacements to handle partial writes.
 *
 * Usage:
 *
 *     ssize_t xwrite(int fildes, const void *buf, size_t nbyte);
 *     ssize_t xpwrite(int fildes, const void *buf, size_t nbyte,
 *                     off_t offset);
 *     ssize_t xwritev(int fildes, const struct iovec *iov, int iovcnt);
 *
 * xwrite, xpwrite, and xwritev behave exactly like their C library
 * counterparts except that, if write or writev succeeds but returns a number
 * of bytes written less than the total bytes, the write is repeated picking
 * up where it left off until the full amount of the data is written.  The
 * write is also repeated if it failed with EINTR.  The write will be aborted
 * after 10 successive writes with no forward progress.
 *
 * Both functions return the number of bytes written on success or -1 on an
 * error, and will leave errno set to whatever the underlying system call set
 * it to.  Note that it is possible for a write to fail after some data was
 * written, on the subsequent additional write; in that case, these functions
 * will return -1 and the number of bytes actually written will be lost.
 *
 * The canonical version of this file is maintained in the rra-c-util package,
 * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
 *
 * Copyright 2008, 2013, 2014
 *     The Board of Trustees of the Leland Stanford Junior University
 * Copyright (c) 2004, 2005, 2006
 *     by Internet Systems Consortium, Inc. ("ISC")
 * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
 *     2002, 2003 by The Internet Software Consortium and Rich Salz
 *
 * This code is derived from software contributed to the Internet Software
 * Consortium by Rich Salz.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include <config.h>
#include <portable/system.h>
#include <portable/uio.h>

#include <assert.h>
#include <errno.h>

#include <util/xwrite.h>

/*
 * If we're running the test suite, call testing versions of the write
 * functions.  #undef the functions first since large file support may define
 * a macro pwrite (pointing to pwrite64) on some platforms (e.g. Solaris),
 * and it's possible the other functions may be similarly affected.
 */
#if TESTING
# undef pwrite
# undef write
# undef writev
# define pwrite fake_pwrite
# define write  fake_write
# define writev fake_writev
ssize_t fake_pwrite(int, const void *, size_t, off_t);
ssize_t fake_write(int, const void *, size_t);
ssize_t fake_writev(int, const struct iovec *, int);
#endif


ssize_t
xwrite(int fd, const void *buffer, size_t size)
{
    size_t total;
    ssize_t status;
    int count = 0;

    if (size == 0)
	return 0;

    /* Abort the write if we try ten times with no forward progress. */
    for (total = 0; total < size; total += status) {
        if (++count > 10)
            break;
        status = write(fd, (const char *) buffer + total, size - total);
        if (status > 0)
            count = 0;
        if (status < 0) {
            if (errno != EINTR)
                break;
            status = 0;
        }
    }
    return (total < size) ? -1 : (ssize_t) total;
}


#ifndef _WIN32
ssize_t
xpwrite(int fd, const void *buffer, size_t size, off_t offset)
{
    size_t total;
    ssize_t status;
    int count = 0;

    if (size == 0)
	return 0;

    /* Abort the write if we try ten times with no forward progress. */
    for (total = 0; total < size; total += status) {
        if (++count > 10)
            break;
        status = pwrite(fd, (const char *) buffer + total, size - total,
                        offset + total);
        if (status > 0)
            count = 0;
        if (status < 0) {
            if (errno != EINTR)
                break;
            status = 0;
        }
    }
    return (total < size) ? -1 : (ssize_t) total;
}
#endif


ssize_t
xwritev(int fd, const struct iovec iov[], int iovcnt)
{
    ssize_t total, status = 0;
    size_t left, offset;
    int iovleft, i, count;
    struct iovec *tmpiov;

    /*
     * Bounds-check the iovcnt argument.  This is just for our safety.  The
     * system will probably impose a lower limit on iovcnt, causing the later
     * writev to fail with an error we'll return.
     */
    if (iovcnt == 0)
	return 0;
    if (iovcnt < 0 || (size_t) iovcnt > SIZE_MAX / sizeof(struct iovec)) {
        errno = EINVAL;
        return -1;
    }

    /* Get a count of the total number of bytes in the iov array. */
    for (total = 0, i = 0; i < iovcnt; i++)
        total += iov[i].iov_len;
    if (total == 0)
	return 0;

    /*
     * First, try just writing it all out.  Most of the time this will succeed
     * and save us lots of work.  Abort the write if we try ten times with no
     * forward progress.
     */
    count = 0;
    do {
        if (++count > 10)
            break;
        status = writev(fd, iov, iovcnt);
        if (status > 0)
            count = 0;
    } while (status < 0 && errno == EINTR);
    if (status < 0)
        return -1;
    if (status == total)
        return total;

    /*
     * If we fell through to here, the first write partially succeeded.
     * Figure out how far through the iov array we got, and then duplicate the
     * rest of it so that we can modify it to reflect how much we manage to
     * write on successive tries.
     */
    offset = status;
    left = total - offset;
    for (i = 0; offset >= (size_t) iov[i].iov_len; i++)
        offset -= iov[i].iov_len;
    iovleft = iovcnt - i;
    assert(iovleft > 0);
    tmpiov = calloc(iovleft, sizeof(struct iovec));
    if (tmpiov == NULL)
        return -1;
    memcpy(tmpiov, iov + i, iovleft * sizeof(struct iovec));

    /*
     * status now contains the offset into the first iovec struct in tmpiov.
     * Go into the write loop, trying to write out everything remaining at
     * each point.  At the top of the loop, status will contain a count of
     * bytes written out at the beginning of the set of iovec structs.
     */
    i = 0;
    do {
        if (++count > 10)
            break;

        /* Skip any leading data that has been written out. */
        for (; offset >= (size_t) tmpiov[i].iov_len && iovleft > 0; i++) {
            offset -= tmpiov[i].iov_len;
            iovleft--;
        }
        tmpiov[i].iov_base = (char *) tmpiov[i].iov_base + offset;
        tmpiov[i].iov_len -= offset;

        /* Write out what's left and return success if it's all written. */
        status = writev(fd, tmpiov + i, iovleft);
        if (status <= 0)
            offset = 0;
        else {
            offset = status;
            left -= offset;
            count = 0;
        }
    } while (left > 0 && (status >= 0 || errno == EINTR));

    /* We're either done or got an error; if we're done, left is now 0. */
    free(tmpiov);
    return (left == 0) ? total : -1;
}