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 !defined(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#elif defined(F_FULLFSYNC)
391    if (fcntl(thefile->filedes, F_FULLFSYNC)) {
392#else
393    if (fsync(thefile->filedes)) {
394#endif
395        rv = apr_get_os_error();
396    }
397
398    file_unlock(thefile);
399
400    return rv;
401}
402
403APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
404{
405    apr_status_t rv = APR_SUCCESS; /* get rid of gcc warning */
406    apr_size_t nbytes;
407    const char *str_start = str;
408    char *final = str + len - 1;
409
410    if (len <= 1) {
411        /* sort of like fgets(), which returns NULL and stores no bytes
412         */
413        return APR_SUCCESS;
414    }
415
416    /* If we have an underlying buffer, we can be *much* more efficient
417     * and skip over the apr_file_read calls.
418     */
419    if (thefile->buffered) {
420        file_lock(thefile);
421
422        if (thefile->direction == 1) {
423            rv = apr_file_flush_locked(thefile);
424            if (rv) {
425                file_unlock(thefile);
426                return rv;
427            }
428
429            thefile->direction = 0;
430            thefile->bufpos = 0;
431            thefile->dataRead = 0;
432        }
433
434        while (str < final) { /* leave room for trailing '\0' */
435            /* Force ungetc leftover to call apr_file_read. */
436            if (thefile->bufpos < thefile->dataRead &&
437                thefile->ungetchar == -1) {
438                *str = thefile->buffer[thefile->bufpos++];
439            }
440            else {
441                nbytes = 1;
442                rv = file_read_buffered(thefile, str, &nbytes);
443                if (rv != APR_SUCCESS) {
444                    break;
445                }
446            }
447            if (*str == '\n') {
448                ++str;
449                break;
450            }
451            ++str;
452        }
453        file_unlock(thefile);
454    }
455    else {
456        while (str < final) { /* leave room for trailing '\0' */
457            nbytes = 1;
458            rv = apr_file_read(thefile, str, &nbytes);
459            if (rv != APR_SUCCESS) {
460                break;
461            }
462            if (*str == '\n') {
463                ++str;
464                break;
465            }
466            ++str;
467        }
468    }
469
470    /* We must store a terminating '\0' if we've stored any chars. We can
471     * get away with storing it if we hit an error first.
472     */
473    *str = '\0';
474    if (str > str_start) {
475        /* we stored chars; don't report EOF or any other errors;
476         * the app will find out about that on the next call
477         */
478        return APR_SUCCESS;
479    }
480    return rv;
481}
482
483struct apr_file_printf_data {
484    apr_vformatter_buff_t vbuff;
485    apr_file_t *fptr;
486    char *buf;
487};
488
489static int file_printf_flush(apr_vformatter_buff_t *buff)
490{
491    struct apr_file_printf_data *data = (struct apr_file_printf_data *)buff;
492
493    if (apr_file_write_full(data->fptr, data->buf,
494                            data->vbuff.curpos - data->buf, NULL)) {
495        return -1;
496    }
497
498    data->vbuff.curpos = data->buf;
499    return 0;
500}
501
502APR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr,
503                                        const char *format, ...)
504{
505    struct apr_file_printf_data data;
506    va_list ap;
507    int count;
508
509    /* don't really need a HUGE_STRING_LEN anymore */
510    data.buf = malloc(HUGE_STRING_LEN);
511    if (data.buf == NULL) {
512        return -1;
513    }
514    data.vbuff.curpos = data.buf;
515    data.vbuff.endpos = data.buf + HUGE_STRING_LEN;
516    data.fptr = fptr;
517    va_start(ap, format);
518    count = apr_vformatter(file_printf_flush,
519                           (apr_vformatter_buff_t *)&data, format, ap);
520    /* apr_vformatter does not call flush for the last bits */
521    if (count >= 0) file_printf_flush((apr_vformatter_buff_t *)&data);
522
523    va_end(ap);
524
525    free(data.buf);
526
527    return count;
528}
529