1251875Speter/* Licensed to the Apache Software Foundation (ASF) under one or more
2251875Speter * contributor license agreements.  See the NOTICE file distributed with
3251875Speter * this work for additional information regarding copyright ownership.
4251875Speter * The ASF licenses this file to You under the Apache License, Version 2.0
5251875Speter * (the "License"); you may not use this file except in compliance with
6251875Speter * the License.  You may obtain a copy of the License at
7251875Speter *
8251875Speter *     http://www.apache.org/licenses/LICENSE-2.0
9251875Speter *
10251875Speter * Unless required by applicable law or agreed to in writing, software
11251875Speter * distributed under the License is distributed on an "AS IS" BASIS,
12251875Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13251875Speter * See the License for the specific language governing permissions and
14251875Speter * limitations under the License.
15251875Speter */
16251875Speter
17251875Speter#include "apr_arch_file_io.h"
18251875Speter#include "apr_strings.h"
19251875Speter#include "apr_thread_mutex.h"
20251875Speter#include "apr_support.h"
21251875Speter
22251875Speter/* The only case where we don't use wait_for_io_or_timeout is on
23251875Speter * pre-BONE BeOS, so this check should be sufficient and simpler */
24266735Speter#if !defined(BEOS_R5)
25251875Speter#define USE_WAIT_FOR_IO
26251875Speter#endif
27251875Speter
28251875Speterstatic apr_status_t file_read_buffered(apr_file_t *thefile, void *buf,
29251875Speter                                       apr_size_t *nbytes)
30251875Speter{
31251875Speter    apr_ssize_t rv;
32251875Speter    char *pos = (char *)buf;
33251875Speter    apr_uint64_t blocksize;
34251875Speter    apr_uint64_t size = *nbytes;
35251875Speter
36251875Speter    if (thefile->direction == 1) {
37251875Speter        rv = apr_file_flush_locked(thefile);
38251875Speter        if (rv) {
39251875Speter            return rv;
40251875Speter        }
41251875Speter        thefile->bufpos = 0;
42251875Speter        thefile->direction = 0;
43251875Speter        thefile->dataRead = 0;
44251875Speter    }
45251875Speter
46251875Speter    rv = 0;
47251875Speter    if (thefile->ungetchar != -1) {
48251875Speter        *pos = (char)thefile->ungetchar;
49251875Speter        ++pos;
50251875Speter        --size;
51251875Speter        thefile->ungetchar = -1;
52251875Speter    }
53251875Speter    while (rv == 0 && size > 0) {
54251875Speter        if (thefile->bufpos >= thefile->dataRead) {
55251875Speter            int bytesread = read(thefile->filedes, thefile->buffer,
56251875Speter                                 thefile->bufsize);
57251875Speter            if (bytesread == 0) {
58251875Speter                thefile->eof_hit = TRUE;
59251875Speter                rv = APR_EOF;
60251875Speter                break;
61251875Speter            }
62251875Speter            else if (bytesread == -1) {
63251875Speter                rv = errno;
64251875Speter                break;
65251875Speter            }
66251875Speter            thefile->dataRead = bytesread;
67251875Speter            thefile->filePtr += thefile->dataRead;
68251875Speter            thefile->bufpos = 0;
69251875Speter        }
70251875Speter
71251875Speter        blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size;
72251875Speter        memcpy(pos, thefile->buffer + thefile->bufpos, blocksize);
73251875Speter        thefile->bufpos += blocksize;
74251875Speter        pos += blocksize;
75251875Speter        size -= blocksize;
76251875Speter    }
77251875Speter
78251875Speter    *nbytes = pos - (char *)buf;
79251875Speter    if (*nbytes) {
80251875Speter        rv = 0;
81251875Speter    }
82251875Speter    return rv;
83251875Speter}
84251875Speter
85251875SpeterAPR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *nbytes)
86251875Speter{
87251875Speter    apr_ssize_t rv;
88251875Speter    apr_size_t bytes_read;
89251875Speter
90251875Speter    if (*nbytes <= 0) {
91251875Speter        *nbytes = 0;
92251875Speter        return APR_SUCCESS;
93251875Speter    }
94251875Speter
95251875Speter    if (thefile->buffered) {
96251875Speter        file_lock(thefile);
97251875Speter        rv = file_read_buffered(thefile, buf, nbytes);
98251875Speter        file_unlock(thefile);
99251875Speter        return rv;
100251875Speter    }
101251875Speter    else {
102251875Speter        bytes_read = 0;
103251875Speter        if (thefile->ungetchar != -1) {
104251875Speter            bytes_read = 1;
105251875Speter            *(char *)buf = (char)thefile->ungetchar;
106251875Speter            buf = (char *)buf + 1;
107251875Speter            (*nbytes)--;
108251875Speter            thefile->ungetchar = -1;
109251875Speter            if (*nbytes == 0) {
110251875Speter                *nbytes = bytes_read;
111251875Speter                return APR_SUCCESS;
112251875Speter            }
113251875Speter        }
114251875Speter
115251875Speter        do {
116251875Speter            rv = read(thefile->filedes, buf, *nbytes);
117251875Speter        } while (rv == -1 && errno == EINTR);
118251875Speter#ifdef USE_WAIT_FOR_IO
119251875Speter        if (rv == -1 &&
120251875Speter            (errno == EAGAIN || errno == EWOULDBLOCK) &&
121251875Speter            thefile->timeout != 0) {
122251875Speter            apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 1);
123251875Speter            if (arv != APR_SUCCESS) {
124251875Speter                *nbytes = bytes_read;
125251875Speter                return arv;
126251875Speter            }
127251875Speter            else {
128251875Speter                do {
129251875Speter                    rv = read(thefile->filedes, buf, *nbytes);
130251875Speter                } while (rv == -1 && errno == EINTR);
131251875Speter            }
132251875Speter        }
133251875Speter#endif
134251875Speter        *nbytes = bytes_read;
135251875Speter        if (rv == 0) {
136251875Speter            thefile->eof_hit = TRUE;
137251875Speter            return APR_EOF;
138251875Speter        }
139251875Speter        if (rv > 0) {
140251875Speter            *nbytes += rv;
141251875Speter            return APR_SUCCESS;
142251875Speter        }
143251875Speter        return errno;
144251875Speter    }
145251875Speter}
146251875Speter
147251875SpeterAPR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes)
148251875Speter{
149251875Speter    apr_size_t rv;
150251875Speter
151251875Speter    if (thefile->buffered) {
152251875Speter        char *pos = (char *)buf;
153251875Speter        int blocksize;
154251875Speter        int size = *nbytes;
155251875Speter
156251875Speter        file_lock(thefile);
157251875Speter
158251875Speter        if ( thefile->direction == 0 ) {
159251875Speter            /* Position file pointer for writing at the offset we are
160251875Speter             * logically reading from
161251875Speter             */
162251875Speter            apr_int64_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
163251875Speter            if (offset != thefile->filePtr)
164251875Speter                lseek(thefile->filedes, offset, SEEK_SET);
165251875Speter            thefile->bufpos = thefile->dataRead = 0;
166251875Speter            thefile->direction = 1;
167251875Speter        }
168251875Speter
169251875Speter        rv = 0;
170251875Speter        while (rv == 0 && size > 0) {
171251875Speter            if (thefile->bufpos == thefile->bufsize)   /* write buffer is full*/
172251875Speter                rv = apr_file_flush_locked(thefile);
173251875Speter
174251875Speter            blocksize = size > thefile->bufsize - thefile->bufpos ?
175251875Speter                        thefile->bufsize - thefile->bufpos : size;
176251875Speter            memcpy(thefile->buffer + thefile->bufpos, pos, blocksize);
177251875Speter            thefile->bufpos += blocksize;
178251875Speter            pos += blocksize;
179251875Speter            size -= blocksize;
180251875Speter        }
181251875Speter
182251875Speter        file_unlock(thefile);
183251875Speter
184251875Speter        return rv;
185251875Speter    }
186251875Speter    else {
187251875Speter        do {
188251875Speter            rv = write(thefile->filedes, buf, *nbytes);
189251875Speter        } while (rv == (apr_size_t)-1 && errno == EINTR);
190251875Speter#ifdef USE_WAIT_FOR_IO
191251875Speter        if (rv == (apr_size_t)-1 &&
192251875Speter            (errno == EAGAIN || errno == EWOULDBLOCK) &&
193251875Speter            thefile->timeout != 0) {
194251875Speter            apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 0);
195251875Speter            if (arv != APR_SUCCESS) {
196251875Speter                *nbytes = 0;
197251875Speter                return arv;
198251875Speter            }
199251875Speter            else {
200251875Speter                do {
201251875Speter                    do {
202251875Speter                        rv = write(thefile->filedes, buf, *nbytes);
203251875Speter                    } while (rv == (apr_size_t)-1 && errno == EINTR);
204251875Speter                    if (rv == (apr_size_t)-1 &&
205251875Speter                        (errno == EAGAIN || errno == EWOULDBLOCK)) {
206251875Speter                        *nbytes /= 2; /* yes, we'll loop if kernel lied
207251875Speter                                       * and we can't even write 1 byte
208251875Speter                                       */
209251875Speter                    }
210251875Speter                    else {
211251875Speter                        break;
212251875Speter                    }
213251875Speter                } while (1);
214251875Speter            }
215251875Speter        }
216251875Speter#endif
217251875Speter        if (rv == (apr_size_t)-1) {
218251875Speter            (*nbytes) = 0;
219251875Speter            return errno;
220251875Speter        }
221251875Speter        *nbytes = rv;
222251875Speter        return APR_SUCCESS;
223251875Speter    }
224251875Speter}
225251875Speter
226251875SpeterAPR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile, const struct iovec *vec,
227251875Speter                                          apr_size_t nvec, apr_size_t *nbytes)
228251875Speter{
229251875Speter#ifdef HAVE_WRITEV
230251875Speter    apr_status_t rv;
231251875Speter    apr_ssize_t bytes;
232251875Speter
233251875Speter    if (thefile->buffered) {
234251875Speter        file_lock(thefile);
235251875Speter
236251875Speter        rv = apr_file_flush_locked(thefile);
237251875Speter        if (rv != APR_SUCCESS) {
238251875Speter            file_unlock(thefile);
239251875Speter            return rv;
240251875Speter        }
241251875Speter        if (thefile->direction == 0) {
242251875Speter            /* Position file pointer for writing at the offset we are
243251875Speter             * logically reading from
244251875Speter             */
245251875Speter            apr_int64_t offset = thefile->filePtr - thefile->dataRead +
246251875Speter                                 thefile->bufpos;
247251875Speter            if (offset != thefile->filePtr)
248251875Speter                lseek(thefile->filedes, offset, SEEK_SET);
249251875Speter            thefile->bufpos = thefile->dataRead = 0;
250251875Speter        }
251251875Speter
252251875Speter        file_unlock(thefile);
253251875Speter    }
254251875Speter
255251875Speter    if ((bytes = writev(thefile->filedes, vec, nvec)) < 0) {
256251875Speter        *nbytes = 0;
257251875Speter        rv = errno;
258251875Speter    }
259251875Speter    else {
260251875Speter        *nbytes = bytes;
261251875Speter        rv = APR_SUCCESS;
262251875Speter    }
263251875Speter    return rv;
264251875Speter#else
265251875Speter    /**
266251875Speter     * The problem with trying to output the entire iovec is that we cannot
267251875Speter     * maintain the behaviour that a real writev would have.  If we iterate
268251875Speter     * over the iovec one at a time, we lose the atomic properties of
269251875Speter     * writev().  The other option is to combine the entire iovec into one
270251875Speter     * buffer that we could then send in one call to write().  This is not
271251875Speter     * reasonable since we do not know how much data an iovec could contain.
272251875Speter     *
273251875Speter     * The only reasonable option, that maintains the semantics of a real
274251875Speter     * writev(), is to only write the first iovec.  Callers of file_writev()
275251875Speter     * must deal with partial writes as they normally would. If you want to
276251875Speter     * ensure an entire iovec is written, use apr_file_writev_full().
277251875Speter     */
278251875Speter
279251875Speter    *nbytes = vec[0].iov_len;
280251875Speter    return apr_file_write(thefile, vec[0].iov_base, nbytes);
281251875Speter#endif
282251875Speter}
283251875Speter
284251875SpeterAPR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile)
285251875Speter{
286251875Speter    apr_size_t nbytes = 1;
287251875Speter
288251875Speter    return apr_file_write(thefile, &ch, &nbytes);
289251875Speter}
290251875Speter
291251875SpeterAPR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile)
292251875Speter{
293251875Speter    thefile->ungetchar = (unsigned char)ch;
294251875Speter    return APR_SUCCESS;
295251875Speter}
296251875Speter
297251875SpeterAPR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile)
298251875Speter{
299251875Speter    apr_size_t nbytes = 1;
300251875Speter
301251875Speter    return apr_file_read(thefile, ch, &nbytes);
302251875Speter}
303251875Speter
304251875SpeterAPR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile)
305251875Speter{
306251875Speter    return apr_file_write_full(thefile, str, strlen(str), NULL);
307251875Speter}
308251875Speter
309251875Speterapr_status_t apr_file_flush_locked(apr_file_t *thefile)
310251875Speter{
311251875Speter    apr_status_t rv = APR_SUCCESS;
312251875Speter
313251875Speter    if (thefile->direction == 1 && thefile->bufpos) {
314251875Speter        apr_ssize_t written = 0, ret;
315251875Speter
316251875Speter        do {
317251875Speter            ret = write(thefile->filedes, thefile->buffer + written,
318251875Speter                        thefile->bufpos - written);
319251875Speter            if (ret > 0)
320251875Speter                written += ret;
321251875Speter        } while (written < thefile->bufpos &&
322251875Speter                 (ret > 0 || (ret == -1 && errno == EINTR)));
323251875Speter        if (ret == -1) {
324251875Speter            rv = errno;
325251875Speter        } else {
326251875Speter            thefile->filePtr += written;
327251875Speter            thefile->bufpos = 0;
328251875Speter        }
329251875Speter    }
330251875Speter
331251875Speter    return rv;
332251875Speter}
333251875Speter
334251875SpeterAPR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile)
335251875Speter{
336251875Speter    apr_status_t rv = APR_SUCCESS;
337251875Speter
338251875Speter    if (thefile->buffered) {
339251875Speter        file_lock(thefile);
340251875Speter        rv = apr_file_flush_locked(thefile);
341251875Speter        file_unlock(thefile);
342251875Speter    }
343251875Speter    /* There isn't anything to do if we aren't buffering the output
344251875Speter     * so just return success.
345251875Speter     */
346251875Speter    return rv;
347251875Speter}
348251875Speter
349251875SpeterAPR_DECLARE(apr_status_t) apr_file_sync(apr_file_t *thefile)
350251875Speter{
351251875Speter    apr_status_t rv = APR_SUCCESS;
352251875Speter
353251875Speter    file_lock(thefile);
354251875Speter
355251875Speter    if (thefile->buffered) {
356251875Speter        rv = apr_file_flush_locked(thefile);
357251875Speter
358251875Speter        if (rv != APR_SUCCESS) {
359251875Speter            file_unlock(thefile);
360251875Speter            return rv;
361251875Speter        }
362251875Speter    }
363251875Speter
364251875Speter    if (fsync(thefile->filedes)) {
365251875Speter        rv = apr_get_os_error();
366251875Speter    }
367251875Speter
368251875Speter    file_unlock(thefile);
369251875Speter
370251875Speter    return rv;
371251875Speter}
372251875Speter
373251875SpeterAPR_DECLARE(apr_status_t) apr_file_datasync(apr_file_t *thefile)
374251875Speter{
375251875Speter    apr_status_t rv = APR_SUCCESS;
376251875Speter
377251875Speter    file_lock(thefile);
378251875Speter
379251875Speter    if (thefile->buffered) {
380251875Speter        rv = apr_file_flush_locked(thefile);
381251875Speter
382251875Speter        if (rv != APR_SUCCESS) {
383251875Speter            file_unlock(thefile);
384251875Speter            return rv;
385251875Speter        }
386251875Speter    }
387251875Speter
388251875Speter#ifdef HAVE_FDATASYNC
389251875Speter    if (fdatasync(thefile->filedes)) {
390362181Sdim#elif defined(F_FULLFSYNC)
391362181Sdim    if (fcntl(thefile->filedes, F_FULLFSYNC)) {
392251875Speter#else
393251875Speter    if (fsync(thefile->filedes)) {
394251875Speter#endif
395251875Speter        rv = apr_get_os_error();
396251875Speter    }
397251875Speter
398251875Speter    file_unlock(thefile);
399251875Speter
400251875Speter    return rv;
401251875Speter}
402251875Speter
403251875SpeterAPR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
404251875Speter{
405251875Speter    apr_status_t rv = APR_SUCCESS; /* get rid of gcc warning */
406251875Speter    apr_size_t nbytes;
407251875Speter    const char *str_start = str;
408251875Speter    char *final = str + len - 1;
409251875Speter
410251875Speter    if (len <= 1) {
411251875Speter        /* sort of like fgets(), which returns NULL and stores no bytes
412251875Speter         */
413251875Speter        return APR_SUCCESS;
414251875Speter    }
415251875Speter
416251875Speter    /* If we have an underlying buffer, we can be *much* more efficient
417251875Speter     * and skip over the apr_file_read calls.
418251875Speter     */
419251875Speter    if (thefile->buffered) {
420251875Speter        file_lock(thefile);
421251875Speter
422251875Speter        if (thefile->direction == 1) {
423251875Speter            rv = apr_file_flush_locked(thefile);
424251875Speter            if (rv) {
425251875Speter                file_unlock(thefile);
426251875Speter                return rv;
427251875Speter            }
428251875Speter
429251875Speter            thefile->direction = 0;
430251875Speter            thefile->bufpos = 0;
431251875Speter            thefile->dataRead = 0;
432251875Speter        }
433251875Speter
434251875Speter        while (str < final) { /* leave room for trailing '\0' */
435251875Speter            /* Force ungetc leftover to call apr_file_read. */
436251875Speter            if (thefile->bufpos < thefile->dataRead &&
437251875Speter                thefile->ungetchar == -1) {
438251875Speter                *str = thefile->buffer[thefile->bufpos++];
439251875Speter            }
440251875Speter            else {
441251875Speter                nbytes = 1;
442251875Speter                rv = file_read_buffered(thefile, str, &nbytes);
443251875Speter                if (rv != APR_SUCCESS) {
444251875Speter                    break;
445251875Speter                }
446251875Speter            }
447251875Speter            if (*str == '\n') {
448251875Speter                ++str;
449251875Speter                break;
450251875Speter            }
451251875Speter            ++str;
452251875Speter        }
453251875Speter        file_unlock(thefile);
454251875Speter    }
455251875Speter    else {
456251875Speter        while (str < final) { /* leave room for trailing '\0' */
457251875Speter            nbytes = 1;
458251875Speter            rv = apr_file_read(thefile, str, &nbytes);
459251875Speter            if (rv != APR_SUCCESS) {
460251875Speter                break;
461251875Speter            }
462251875Speter            if (*str == '\n') {
463251875Speter                ++str;
464251875Speter                break;
465251875Speter            }
466251875Speter            ++str;
467251875Speter        }
468251875Speter    }
469251875Speter
470251875Speter    /* We must store a terminating '\0' if we've stored any chars. We can
471251875Speter     * get away with storing it if we hit an error first.
472251875Speter     */
473251875Speter    *str = '\0';
474251875Speter    if (str > str_start) {
475251875Speter        /* we stored chars; don't report EOF or any other errors;
476251875Speter         * the app will find out about that on the next call
477251875Speter         */
478251875Speter        return APR_SUCCESS;
479251875Speter    }
480251875Speter    return rv;
481251875Speter}
482251875Speter
483251875Speterstruct apr_file_printf_data {
484251875Speter    apr_vformatter_buff_t vbuff;
485251875Speter    apr_file_t *fptr;
486251875Speter    char *buf;
487251875Speter};
488251875Speter
489251875Speterstatic int file_printf_flush(apr_vformatter_buff_t *buff)
490251875Speter{
491251875Speter    struct apr_file_printf_data *data = (struct apr_file_printf_data *)buff;
492251875Speter
493251875Speter    if (apr_file_write_full(data->fptr, data->buf,
494251875Speter                            data->vbuff.curpos - data->buf, NULL)) {
495251875Speter        return -1;
496251875Speter    }
497251875Speter
498251875Speter    data->vbuff.curpos = data->buf;
499251875Speter    return 0;
500251875Speter}
501251875Speter
502251875SpeterAPR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr,
503251875Speter                                        const char *format, ...)
504251875Speter{
505251875Speter    struct apr_file_printf_data data;
506251875Speter    va_list ap;
507251875Speter    int count;
508251875Speter
509251875Speter    /* don't really need a HUGE_STRING_LEN anymore */
510251875Speter    data.buf = malloc(HUGE_STRING_LEN);
511251875Speter    if (data.buf == NULL) {
512251875Speter        return -1;
513251875Speter    }
514251875Speter    data.vbuff.curpos = data.buf;
515251875Speter    data.vbuff.endpos = data.buf + HUGE_STRING_LEN;
516251875Speter    data.fptr = fptr;
517251875Speter    va_start(ap, format);
518251875Speter    count = apr_vformatter(file_printf_flush,
519251875Speter                           (apr_vformatter_buff_t *)&data, format, ap);
520251875Speter    /* apr_vformatter does not call flush for the last bits */
521251875Speter    if (count >= 0) file_printf_flush((apr_vformatter_buff_t *)&data);
522251875Speter
523251875Speter    va_end(ap);
524251875Speter
525251875Speter    free(data.buf);
526251875Speter
527251875Speter    return count;
528251875Speter}
529