source: web/old/remctl-2.14/util/xwrite.c @ f6f3e91

web
Last change on this file since f6f3e91 was f6f3e91, checked in by Jessica B. Hamrick <jhamrick@…>, 15 years ago

Preserve directory hierarchy (not sure what happened to it)

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