chiark / gitweb /
use libinn logging where applicable
[inn-innduct.git] / lib / xwrite.c
1 /*  $Id: xwrite.c 5771 2002-09-17 17:00:05Z alexk $
2 **
3 **  write and writev replacements to handle partial writes.
4 **
5 **  Usage:
6 **
7 **      ssize_t xwrite(int fildes, const void *buf, size_t nbyte);
8 **      ssize_t xpwrite(int fildes, const void *buf, size_t nbyte,
9 **                      off_t offset);
10 **      ssize_t xwritev(int fildes, const struct iovec *iov, int iovcnt);
11 **
12 **  xwrite, xpwrite, and xwritev behave exactly like their C library
13 **  counterparts except that, if write or writev succeeds but returns a number
14 **  of bytes written less than the total bytes, the write is repeated picking
15 **  up where it left off until the full amount of the data is written.  The
16 **  write is also repeated if it failed with EINTR.  The write will be aborted
17 **  after 10 successive writes with no forward progress.
18 **
19 **  Both functions return the number of bytes written on success or -1 on an
20 **  error, and will leave errno set to whatever the underlying system call
21 **  set it to.  Note that it is possible for a write to fail after some data
22 **  was written, on the subsequent additional write; in that case, these
23 **  functions will return -1 and the number of bytes actually written will
24 **  be lost.
25 */
26
27 #include "config.h"
28 #include "clibrary.h"
29 #include <errno.h>
30 #include <sys/uio.h>
31
32 #include "libinn.h"
33
34 /* If we're running the test suite, call testing versions of the write
35    functions.  #undef pwrite first because large file support may define a
36    macro pwrite (pointing to pwrite64) on some platforms (e.g. Solaris). */
37 #if TESTING
38 # undef pwrite
39 # define pwrite fake_pwrite
40 # define write  fake_write
41 # define writev fake_writev
42 ssize_t fake_pwrite(int, const void *, size_t, off_t);
43 ssize_t fake_write(int, const void *, size_t);
44 ssize_t fake_writev(int, const struct iovec *, int);
45 #endif
46
47 ssize_t
48 xwrite(int fd, const void *buffer, size_t size)
49 {
50     size_t total;
51     ssize_t status;
52     int count = 0;
53
54     if (size == 0)
55         return 0;
56
57     /* Abort the write if we try ten times with no forward progress. */
58     for (total = 0; total < size; total += status) {
59         if (++count > 10)
60             break;
61         status = write(fd, (const char *) buffer + total, size - total);
62         if (status > 0)
63             count = 0;
64         if (status < 0) {
65             if (errno != EINTR)
66                 break;
67             status = 0;
68         }
69     }
70     return (total < size) ? -1 : (ssize_t) total;
71 }
72
73 ssize_t
74 xpwrite(int fd, const void *buffer, size_t size, off_t offset)
75 {
76     size_t total;
77     ssize_t status;
78     int count = 0;
79
80     if (size == 0)
81         return 0;
82
83     /* Abort the write if we try ten times with no forward progress. */
84     for (total = 0; total < size; total += status) {
85         if (++count > 10)
86             break;
87         status = pwrite(fd, (const char *) buffer + total, size - total,
88                         offset + total);
89         if (status > 0)
90             count = 0;
91         if (status < 0) {
92             if (errno != EINTR)
93                 break;
94             status = 0;
95         }
96     }
97     return (total < size) ? -1 : (ssize_t) total;
98 }
99
100 ssize_t
101 xwritev(int fd, const struct iovec iov[], int iovcnt)
102 {
103     ssize_t total, status = 0;
104     size_t left, offset;
105     int iovleft, i, count;
106     struct iovec *tmpiov;
107
108     if (iovcnt == 0)
109         return 0;
110
111     /* Get a count of the total number of bytes in the iov array. */
112     for (total = 0, i = 0; i < iovcnt; i++)
113         total += iov[i].iov_len;
114
115     if (total == 0)
116         return 0;
117
118     /* First, try just writing it all out.  Most of the time this will
119        succeed and save us lots of work.  Abort the write if we try ten times 
120        with no forward progress. */
121     count = 0;
122     do {
123         if (++count > 10)
124             break;
125         status = writev(fd, iov, iovcnt);
126         if (status > 0)
127             count = 0;
128     } while (status < 0 && errno == EINTR);
129     if (status < 0)
130         return -1;
131     if (status == total)
132         return total;
133
134     /* If we fell through to here, the first write partially succeeded.
135        Figure out how far through the iov array we got, and then duplicate
136        the rest of it so that we can modify it to reflect how much we manage
137        to write on successive tries. */
138     offset = status;
139     left = total - offset;
140     for (i = 0; offset >= (size_t) iov[i].iov_len; i++)
141         offset -= iov[i].iov_len;
142     iovleft = iovcnt - i;
143     tmpiov = xmalloc(iovleft * sizeof(struct iovec));
144     memcpy(tmpiov, iov + i, iovleft * sizeof(struct iovec));
145
146     /* status now contains the offset into the first iovec struct in tmpiov.
147        Go into the write loop, trying to write out everything remaining at
148        each point.  At the top of the loop, status will contain a count of
149        bytes written out at the beginning of the set of iovec structs. */
150     i = 0;
151     do {
152         if (++count > 10)
153             break;
154
155         /* Skip any leading data that has been written out. */
156         for (; offset >= (size_t) tmpiov[i].iov_len && iovleft > 0; i++) {
157             offset -= tmpiov[i].iov_len;
158             iovleft--;
159         }
160         tmpiov[i].iov_base = (char *) tmpiov[i].iov_base + offset;
161         tmpiov[i].iov_len -= offset;
162
163         /* Write out what's left and return success if it's all written. */
164         status = writev(fd, tmpiov + i, iovleft);
165         if (status <= 0)
166             offset = 0;
167         else {
168             offset = status;
169             left -= offset;
170             count = 0;
171         }
172     } while (left > 0 && (status >= 0 || errno == EINTR));
173
174     /* We're either done or got an error; if we're done, left is now 0. */
175     free(tmpiov);
176     return (left == 0) ? total : -1;
177 }