readwrite.c revision 251886
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apr_arch_file_io.h"
18#include "apr_strings.h"
19#include "apr_thread_mutex.h"
20#include "apr_support.h"
21
22/* The only case where we don't use wait_for_io_or_timeout is on
23 * pre-BONE BeOS, so this check should be sufficient and simpler */
24#if !BEOS_R5
25#define USE_WAIT_FOR_IO
26#endif
27
28static apr_status_t file_read_buffered(apr_file_t *thefile, void *buf,
29                                       apr_size_t *nbytes)
30{
31    apr_ssize_t rv;
32    char *pos = (char *)buf;
33    apr_uint64_t blocksize;
34    apr_uint64_t size = *nbytes;
35
36    if (thefile->direction == 1) {
37        rv = apr_file_flush_locked(thefile);
38        if (rv) {
39            return rv;
40        }
41        thefile->bufpos = 0;
42        thefile->direction = 0;
43        thefile->dataRead = 0;
44    }
45
46    rv = 0;
47    if (thefile->ungetchar != -1) {
48        *pos = (char)thefile->ungetchar;
49        ++pos;
50        --size;
51        thefile->ungetchar = -1;
52    }
53    while (rv == 0 && size > 0) {
54        if (thefile->bufpos >= thefile->dataRead) {
55            int bytesread = read(thefile->filedes, thefile->buffer,
56                                 thefile->bufsize);
57            if (bytesread == 0) {
58                thefile->eof_hit = TRUE;
59                rv = APR_EOF;
60                break;
61            }
62            else if (bytesread == -1) {
63                rv = errno;
64                break;
65            }
66            thefile->dataRead = bytesread;
67            thefile->filePtr += thefile->dataRead;
68            thefile->bufpos = 0;
69        }
70
71        blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size;
72        memcpy(pos, thefile->buffer + thefile->bufpos, blocksize);
73        thefile->bufpos += blocksize;
74        pos += blocksize;
75        size -= blocksize;
76    }
77
78    *nbytes = pos - (char *)buf;
79    if (*nbytes) {
80        rv = 0;
81    }
82    return rv;
83}
84
85APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *nbytes)
86{
87    apr_ssize_t rv;
88    apr_size_t bytes_read;
89
90    if (*nbytes <= 0) {
91        *nbytes = 0;
92        return APR_SUCCESS;
93    }
94
95    if (thefile->buffered) {
96        file_lock(thefile);
97        rv = file_read_buffered(thefile, buf, nbytes);
98        file_unlock(thefile);
99        return rv;
100    }
101    else {
102        bytes_read = 0;
103        if (thefile->ungetchar != -1) {
104            bytes_read = 1;
105            *(char *)buf = (char)thefile->ungetchar;
106            buf = (char *)buf + 1;
107            (*nbytes)--;
108            thefile->ungetchar = -1;
109            if (*nbytes == 0) {
110                *nbytes = bytes_read;
111                return APR_SUCCESS;
112            }
113        }
114
115        do {
116            rv = read(thefile->filedes, buf, *nbytes);
117        } while (rv == -1 && errno == EINTR);
118#ifdef USE_WAIT_FOR_IO
119        if (rv == -1 &&
120            (errno == EAGAIN || errno == EWOULDBLOCK) &&
121            thefile->timeout != 0) {
122            apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 1);
123            if (arv != APR_SUCCESS) {
124                *nbytes = bytes_read;
125                return arv;
126            }
127            else {
128                do {
129                    rv = read(thefile->filedes, buf, *nbytes);
130                } while (rv == -1 && errno == EINTR);
131            }
132        }
133#endif
134        *nbytes = bytes_read;
135        if (rv == 0) {
136            thefile->eof_hit = TRUE;
137            return APR_EOF;
138        }
139        if (rv > 0) {
140            *nbytes += rv;
141            return APR_SUCCESS;
142        }
143        return errno;
144    }
145}
146
147APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes)
148{
149    apr_size_t rv;
150
151    if (thefile->buffered) {
152        char *pos = (char *)buf;
153        int blocksize;
154        int size = *nbytes;
155
156        file_lock(thefile);
157
158        if ( thefile->direction == 0 ) {
159            /* Position file pointer for writing at the offset we are
160             * logically reading from
161             */
162            apr_int64_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
163            if (offset != thefile->filePtr)
164                lseek(thefile->filedes, offset, SEEK_SET);
165            thefile->bufpos = thefile->dataRead = 0;
166            thefile->direction = 1;
167        }
168
169        rv = 0;
170        while (rv == 0 && size > 0) {
171            if (thefile->bufpos == thefile->bufsize)   /* write buffer is full*/
172                rv = apr_file_flush_locked(thefile);
173
174            blocksize = size > thefile->bufsize - thefile->bufpos ?
175                        thefile->bufsize - thefile->bufpos : size;
176            memcpy(thefile->buffer + thefile->bufpos, pos, blocksize);
177            thefile->bufpos += blocksize;
178            pos += blocksize;
179            size -= blocksize;
180        }
181
182        file_unlock(thefile);
183
184        return rv;
185    }
186    else {
187        do {
188            rv = write(thefile->filedes, buf, *nbytes);
189        } while (rv == (apr_size_t)-1 && errno == EINTR);
190#ifdef USE_WAIT_FOR_IO
191        if (rv == (apr_size_t)-1 &&
192            (errno == EAGAIN || errno == EWOULDBLOCK) &&
193            thefile->timeout != 0) {
194            apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 0);
195            if (arv != APR_SUCCESS) {
196                *nbytes = 0;
197                return arv;
198            }
199            else {
200                do {
201                    do {
202                        rv = write(thefile->filedes, buf, *nbytes);
203                    } while (rv == (apr_size_t)-1 && errno == EINTR);
204                    if (rv == (apr_size_t)-1 &&
205                        (errno == EAGAIN || errno == EWOULDBLOCK)) {
206                        *nbytes /= 2; /* yes, we'll loop if kernel lied
207                                       * and we can't even write 1 byte
208                                       */
209                    }
210                    else {
211                        break;
212                    }
213                } while (1);
214            }
215        }
216#endif
217        if (rv == (apr_size_t)-1) {
218            (*nbytes) = 0;
219            return errno;
220        }
221        *nbytes = rv;
222        return APR_SUCCESS;
223    }
224}
225
226APR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile, const struct iovec *vec,
227                                          apr_size_t nvec, apr_size_t *nbytes)
228{
229#ifdef HAVE_WRITEV
230    apr_status_t rv;
231    apr_ssize_t bytes;
232
233    if (thefile->buffered) {
234        file_lock(thefile);
235
236        rv = apr_file_flush_locked(thefile);
237        if (rv != APR_SUCCESS) {
238            file_unlock(thefile);
239            return rv;
240        }
241        if (thefile->direction == 0) {
242            /* Position file pointer for writing at the offset we are
243             * logically reading from
244             */
245            apr_int64_t offset = thefile->filePtr - thefile->dataRead +
246                                 thefile->bufpos;
247            if (offset != thefile->filePtr)
248                lseek(thefile->filedes, offset, SEEK_SET);
249            thefile->bufpos = thefile->dataRead = 0;
250        }
251
252        file_unlock(thefile);
253    }
254
255    if ((bytes = writev(thefile->filedes, vec, nvec)) < 0) {
256        *nbytes = 0;
257        rv = errno;
258    }
259    else {
260        *nbytes = bytes;
261        rv = APR_SUCCESS;
262    }
263    return rv;
264#else
265    /**
266     * The problem with trying to output the entire iovec is that we cannot
267     * maintain the behaviour that a real writev would have.  If we iterate
268     * over the iovec one at a time, we lose the atomic properties of
269     * writev().  The other option is to combine the entire iovec into one
270     * buffer that we could then send in one call to write().  This is not
271     * reasonable since we do not know how much data an iovec could contain.
272     *
273     * The only reasonable option, that maintains the semantics of a real
274     * writev(), is to only write the first iovec.  Callers of file_writev()
275     * must deal with partial writes as they normally would. If you want to
276     * ensure an entire iovec is written, use apr_file_writev_full().
277     */
278
279    *nbytes = vec[0].iov_len;
280    return apr_file_write(thefile, vec[0].iov_base, nbytes);
281#endif
282}
283
284APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile)
285{
286    apr_size_t nbytes = 1;
287
288    return apr_file_write(thefile, &ch, &nbytes);
289}
290
291APR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile)
292{
293    thefile->ungetchar = (unsigned char)ch;
294    return APR_SUCCESS;
295}
296
297APR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile)
298{
299    apr_size_t nbytes = 1;
300
301    return apr_file_read(thefile, ch, &nbytes);
302}
303
304APR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile)
305{
306    return apr_file_write_full(thefile, str, strlen(str), NULL);
307}
308
309apr_status_t apr_file_flush_locked(apr_file_t *thefile)
310{
311    apr_status_t rv = APR_SUCCESS;
312
313    if (thefile->direction == 1 && thefile->bufpos) {
314        apr_ssize_t written = 0, ret;
315
316        do {
317            ret = write(thefile->filedes, thefile->buffer + written,
318                        thefile->bufpos - written);
319            if (ret > 0)
320                written += ret;
321        } while (written < thefile->bufpos &&
322                 (ret > 0 || (ret == -1 && errno == EINTR)));
323        if (ret == -1) {
324            rv = errno;
325        } else {
326            thefile->filePtr += written;
327            thefile->bufpos = 0;
328        }
329    }
330
331    return rv;
332}
333
334APR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile)
335{
336    apr_status_t rv = APR_SUCCESS;
337
338    if (thefile->buffered) {
339        file_lock(thefile);
340        rv = apr_file_flush_locked(thefile);
341        file_unlock(thefile);
342    }
343    /* There isn't anything to do if we aren't buffering the output
344     * so just return success.
345     */
346    return rv;
347}
348
349APR_DECLARE(apr_status_t) apr_file_sync(apr_file_t *thefile)
350{
351    apr_status_t rv = APR_SUCCESS;
352
353    file_lock(thefile);
354
355    if (thefile->buffered) {
356        rv = apr_file_flush_locked(thefile);
357
358        if (rv != APR_SUCCESS) {
359            file_unlock(thefile);
360            return rv;
361        }
362    }
363
364    if (fsync(thefile->filedes)) {
365        rv = apr_get_os_error();
366    }
367
368    file_unlock(thefile);
369
370    return rv;
371}
372
373APR_DECLARE(apr_status_t) apr_file_datasync(apr_file_t *thefile)
374{
375    apr_status_t rv = APR_SUCCESS;
376
377    file_lock(thefile);
378
379    if (thefile->buffered) {
380        rv = apr_file_flush_locked(thefile);
381
382        if (rv != APR_SUCCESS) {
383            file_unlock(thefile);
384            return rv;
385        }
386    }
387
388#ifdef HAVE_FDATASYNC
389    if (fdatasync(thefile->filedes)) {
390#else
391    if (fsync(thefile->filedes)) {
392#endif
393        rv = apr_get_os_error();
394    }
395
396    file_unlock(thefile);
397
398    return rv;
399}
400
401APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
402{
403    apr_status_t rv = APR_SUCCESS; /* get rid of gcc warning */
404    apr_size_t nbytes;
405    const char *str_start = str;
406    char *final = str + len - 1;
407
408    if (len <= 1) {
409        /* sort of like fgets(), which returns NULL and stores no bytes
410         */
411        return APR_SUCCESS;
412    }
413
414    /* If we have an underlying buffer, we can be *much* more efficient
415     * and skip over the apr_file_read calls.
416     */
417    if (thefile->buffered) {
418        file_lock(thefile);
419
420        if (thefile->direction == 1) {
421            rv = apr_file_flush_locked(thefile);
422            if (rv) {
423                file_unlock(thefile);
424                return rv;
425            }
426
427            thefile->direction = 0;
428            thefile->bufpos = 0;
429            thefile->dataRead = 0;
430        }
431
432        while (str < final) { /* leave room for trailing '\0' */
433            /* Force ungetc leftover to call apr_file_read. */
434            if (thefile->bufpos < thefile->dataRead &&
435                thefile->ungetchar == -1) {
436                *str = thefile->buffer[thefile->bufpos++];
437            }
438            else {
439                nbytes = 1;
440                rv = file_read_buffered(thefile, str, &nbytes);
441                if (rv != APR_SUCCESS) {
442                    break;
443                }
444            }
445            if (*str == '\n') {
446                ++str;
447                break;
448            }
449            ++str;
450        }
451        file_unlock(thefile);
452    }
453    else {
454        while (str < final) { /* leave room for trailing '\0' */
455            nbytes = 1;
456            rv = apr_file_read(thefile, str, &nbytes);
457            if (rv != APR_SUCCESS) {
458                break;
459            }
460            if (*str == '\n') {
461                ++str;
462                break;
463            }
464            ++str;
465        }
466    }
467
468    /* We must store a terminating '\0' if we've stored any chars. We can
469     * get away with storing it if we hit an error first.
470     */
471    *str = '\0';
472    if (str > str_start) {
473        /* we stored chars; don't report EOF or any other errors;
474         * the app will find out about that on the next call
475         */
476        return APR_SUCCESS;
477    }
478    return rv;
479}
480
481struct apr_file_printf_data {
482    apr_vformatter_buff_t vbuff;
483    apr_file_t *fptr;
484    char *buf;
485};
486
487static int file_printf_flush(apr_vformatter_buff_t *buff)
488{
489    struct apr_file_printf_data *data = (struct apr_file_printf_data *)buff;
490
491    if (apr_file_write_full(data->fptr, data->buf,
492                            data->vbuff.curpos - data->buf, NULL)) {
493        return -1;
494    }
495
496    data->vbuff.curpos = data->buf;
497    return 0;
498}
499
500APR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr,
501                                        const char *format, ...)
502{
503    struct apr_file_printf_data data;
504    va_list ap;
505    int count;
506
507    /* don't really need a HUGE_STRING_LEN anymore */
508    data.buf = malloc(HUGE_STRING_LEN);
509    if (data.buf == NULL) {
510        return -1;
511    }
512    data.vbuff.curpos = data.buf;
513    data.vbuff.endpos = data.buf + HUGE_STRING_LEN;
514    data.fptr = fptr;
515    va_start(ap, format);
516    count = apr_vformatter(file_printf_flush,
517                           (apr_vformatter_buff_t *)&data, format, ap);
518    /* apr_vformatter does not call flush for the last bits */
519    if (count >= 0) file_printf_flush((apr_vformatter_buff_t *)&data);
520
521    va_end(ap);
522
523    free(data.buf);
524
525    return count;
526}
527