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 230
|
/*
* 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 <https://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2008, 2013-2014
* The Board of Trustees of the Leland Stanford Junior University
* Copyright 2004-2006 Internet Systems Consortium, Inc. ("ISC")
* Copyright 1991, 1994-2003 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.
*
* SPDX-License-Identifier: ISC
*/
#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;
unsigned 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;
unsigned 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;
unsigned 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 < (unsigned int) 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;
}
|