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 
file_read_buffered(apr_file_t * thefile,void * buf,apr_size_t * nbytes)28 static 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 
apr_file_read(apr_file_t * thefile,void * buf,apr_size_t * nbytes)85 APR_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 
apr_file_write(apr_file_t * thefile,const void * buf,apr_size_t * nbytes)147 APR_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 
apr_file_writev(apr_file_t * thefile,const struct iovec * vec,apr_size_t nvec,apr_size_t * nbytes)226 APR_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 
apr_file_putc(char ch,apr_file_t * thefile)284 APR_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 
apr_file_ungetc(char ch,apr_file_t * thefile)291 APR_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 
apr_file_getc(char * ch,apr_file_t * thefile)297 APR_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 
apr_file_puts(const char * str,apr_file_t * thefile)304 APR_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 
apr_file_flush_locked(apr_file_t * thefile)309 apr_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 
apr_file_flush(apr_file_t * thefile)334 APR_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 
apr_file_sync(apr_file_t * thefile)349 APR_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 
apr_file_datasync(apr_file_t * thefile)373 APR_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 
403 APR_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 
483 struct apr_file_printf_data {
484     apr_vformatter_buff_t vbuff;
485     apr_file_t *fptr;
486     char *buf;
487 };
488 
489 static 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 
502 APR_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