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_file_io.h"
19 #include "apr_general.h"
20 #include "apr_strings.h"
21 #include "apr_lib.h"
22 #include "apr_errno.h"
23 #include <malloc.h>
24 #include "apr_arch_atime.h"
25 #include "apr_arch_misc.h"
26 
27 /*
28  * read_with_timeout()
29  * Uses async i/o to emulate unix non-blocking i/o with timeouts.
30  */
read_with_timeout(apr_file_t * file,void * buf,apr_size_t len_in,apr_size_t * nbytes)31 static apr_status_t read_with_timeout(apr_file_t *file, void *buf, apr_size_t len_in, apr_size_t *nbytes)
32 {
33     apr_status_t rv;
34     DWORD res;
35     DWORD len = (DWORD)len_in;
36     DWORD bytesread = 0;
37 
38     /* Handle the zero timeout non-blocking case */
39     if (file->timeout == 0) {
40         /* Peek at the pipe. If there is no data available, return APR_EAGAIN.
41          * If data is available, go ahead and read it.
42          */
43         if (file->pipe) {
44             DWORD bytes;
45             if (!PeekNamedPipe(file->filehand, NULL, 0, NULL, &bytes, NULL)) {
46                 rv = apr_get_os_error();
47                 if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
48                     rv = APR_EOF;
49                 }
50                 *nbytes = 0;
51                 return rv;
52             }
53             else {
54                 if (bytes == 0) {
55                     *nbytes = 0;
56                     return APR_EAGAIN;
57                 }
58                 if (len > bytes) {
59                     len = bytes;
60                 }
61             }
62         }
63         else {
64             /* ToDo: Handle zero timeout non-blocking file i/o
65              * This is not needed until an APR application needs to
66              * timeout file i/o (which means setting file i/o non-blocking)
67              */
68         }
69     }
70 
71     if (file->pOverlapped && !file->pipe) {
72         file->pOverlapped->Offset     = (DWORD)file->filePtr;
73         file->pOverlapped->OffsetHigh = (DWORD)(file->filePtr >> 32);
74     }
75 
76     if (ReadFile(file->filehand, buf, len,
77                  &bytesread, file->pOverlapped)) {
78         rv = APR_SUCCESS;
79     }
80     else {
81         rv = apr_get_os_error();
82         if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
83             /* Wait for the pending i/o, timeout converted from us to ms
84              * Note that we loop if someone gives up the event, since
85              * folks suggest that WAIT_ABANDONED isn't actually a result
86              * but an alert that ownership of the event has passed from
87              * one owner to a new proc/thread.
88              */
89             do {
90                 res = WaitForSingleObject(file->pOverlapped->hEvent,
91                                           (file->timeout > 0)
92                                             ? (DWORD)(file->timeout/1000)
93                                             : ((file->timeout == -1)
94                                                  ? INFINITE : 0));
95             } while (res == WAIT_ABANDONED);
96 
97             /* There is one case that represents entirely
98              * successful operations, otherwise we will cancel
99              * the operation in progress.
100              */
101             if (res != WAIT_OBJECT_0) {
102                 CancelIo(file->filehand);
103             }
104 
105             /* Ignore any failures above.  Attempt to complete
106              * the overlapped operation and use only _its_ result.
107              * For example, CancelIo or WaitForSingleObject can
108              * fail if the handle is closed, yet the read may have
109              * completed before we attempted to CancelIo...
110              */
111             if (GetOverlappedResult(file->filehand, file->pOverlapped,
112                                     &bytesread, TRUE)) {
113                 rv = APR_SUCCESS;
114             }
115             else {
116                 rv = apr_get_os_error();
117                 if (((rv == APR_FROM_OS_ERROR(ERROR_IO_INCOMPLETE))
118                         || (rv == APR_FROM_OS_ERROR(ERROR_OPERATION_ABORTED)))
119                     && (res == WAIT_TIMEOUT))
120                     rv = APR_TIMEUP;
121             }
122         }
123         if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
124             /* Assume ERROR_BROKEN_PIPE signals an EOF reading from a pipe */
125             rv = APR_EOF;
126         } else if (rv == APR_FROM_OS_ERROR(ERROR_HANDLE_EOF)) {
127             /* Did we hit EOF reading from the handle? */
128             rv = APR_EOF;
129         }
130     }
131 
132     /* OK and 0 bytes read ==> end of file */
133     if (rv == APR_SUCCESS && bytesread == 0)
134         rv = APR_EOF;
135 
136     if (rv == APR_SUCCESS && file->pOverlapped && !file->pipe) {
137         file->filePtr += bytesread;
138     }
139     *nbytes = bytesread;
140     return rv;
141 }
142 
apr_file_read(apr_file_t * thefile,void * buf,apr_size_t * len)143 APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *len)
144 {
145     apr_status_t rv;
146     DWORD bytes_read = 0;
147 
148     if (*len <= 0) {
149         *len = 0;
150         return APR_SUCCESS;
151     }
152 
153     /* If the file is open for xthread support, allocate and
154      * initialize the overlapped and io completion event (hEvent).
155      * Threads should NOT share an apr_file_t or its hEvent.
156      */
157     if ((thefile->flags & APR_FOPEN_XTHREAD) && !thefile->pOverlapped ) {
158         thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool,
159                                                          sizeof(OVERLAPPED));
160         thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
161         if (!thefile->pOverlapped->hEvent) {
162             rv = apr_get_os_error();
163             return rv;
164         }
165     }
166 
167     /* Handle the ungetchar if there is one */
168     if (thefile->ungetchar != -1) {
169         bytes_read = 1;
170         *(char *)buf = (char)thefile->ungetchar;
171         buf = (char *)buf + 1;
172         (*len)--;
173         thefile->ungetchar = -1;
174         if (*len == 0) {
175             *len = bytes_read;
176             return APR_SUCCESS;
177         }
178     }
179     if (thefile->buffered) {
180         char *pos = (char *)buf;
181         apr_size_t blocksize;
182         apr_size_t size = *len;
183 
184         if (thefile->flags & APR_FOPEN_XTHREAD) {
185             apr_thread_mutex_lock(thefile->mutex);
186         }
187 
188         if (thefile->direction == 1) {
189             rv = apr_file_flush(thefile);
190             if (rv != APR_SUCCESS) {
191                 if (thefile->flags & APR_FOPEN_XTHREAD) {
192                     apr_thread_mutex_unlock(thefile->mutex);
193                 }
194                 return rv;
195             }
196             thefile->bufpos = 0;
197             thefile->direction = 0;
198             thefile->dataRead = 0;
199         }
200 
201         rv = 0;
202         while (rv == 0 && size > 0) {
203             if (thefile->bufpos >= thefile->dataRead) {
204                 apr_size_t read;
205                 rv = read_with_timeout(thefile, thefile->buffer,
206                                        thefile->bufsize, &read);
207                 if (read == 0) {
208                     if (rv == APR_EOF)
209                         thefile->eof_hit = TRUE;
210                     break;
211                 }
212                 else {
213                     thefile->dataRead = read;
214                     thefile->filePtr += thefile->dataRead;
215                     thefile->bufpos = 0;
216                 }
217             }
218 
219             blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size;
220             memcpy(pos, thefile->buffer + thefile->bufpos, blocksize);
221             thefile->bufpos += blocksize;
222             pos += blocksize;
223             size -= blocksize;
224         }
225 
226         *len = pos - (char *)buf;
227         if (*len) {
228             rv = APR_SUCCESS;
229         }
230 
231         if (thefile->flags & APR_FOPEN_XTHREAD) {
232             apr_thread_mutex_unlock(thefile->mutex);
233         }
234     } else {
235         /* Unbuffered i/o */
236         apr_size_t nbytes;
237         rv = read_with_timeout(thefile, buf, *len, &nbytes);
238         if (rv == APR_EOF)
239             thefile->eof_hit = TRUE;
240         *len = nbytes;
241     }
242 
243     return rv;
244 }
245 
apr_file_write(apr_file_t * thefile,const void * buf,apr_size_t * nbytes)246 APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes)
247 {
248     apr_status_t rv;
249     DWORD bwrote;
250 
251     /* If the file is open for xthread support, allocate and
252      * initialize the overlapped and io completion event (hEvent).
253      * Threads should NOT share an apr_file_t or its hEvent.
254      */
255     if ((thefile->flags & APR_FOPEN_XTHREAD) && !thefile->pOverlapped ) {
256         thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool,
257                                                          sizeof(OVERLAPPED));
258         thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
259         if (!thefile->pOverlapped->hEvent) {
260             rv = apr_get_os_error();
261             return rv;
262         }
263     }
264 
265     if (thefile->buffered) {
266         char *pos = (char *)buf;
267         apr_size_t blocksize;
268         apr_size_t size = *nbytes;
269 
270         if (thefile->flags & APR_FOPEN_XTHREAD) {
271             apr_thread_mutex_lock(thefile->mutex);
272         }
273 
274         if (thefile->direction == 0) {
275             /* Position file pointer for writing at the offset we are logically reading from */
276             apr_off_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
277             DWORD offlo = (DWORD)offset;
278             LONG  offhi = (LONG)(offset >> 32);
279             if (offset != thefile->filePtr)
280                 SetFilePointer(thefile->filehand, offlo, &offhi, FILE_BEGIN);
281             thefile->bufpos = thefile->dataRead = 0;
282             thefile->direction = 1;
283         }
284 
285         rv = 0;
286         while (rv == 0 && size > 0) {
287             if (thefile->bufpos == thefile->bufsize)   /* write buffer is full */
288                 rv = apr_file_flush(thefile);
289 
290             blocksize = size > thefile->bufsize - thefile->bufpos ?
291                                      thefile->bufsize - thefile->bufpos : size;
292             memcpy(thefile->buffer + thefile->bufpos, pos, blocksize);
293             thefile->bufpos += blocksize;
294             pos += blocksize;
295             size -= blocksize;
296         }
297 
298         if (thefile->flags & APR_FOPEN_XTHREAD) {
299             apr_thread_mutex_unlock(thefile->mutex);
300         }
301         return rv;
302     } else {
303         if (!thefile->pipe) {
304             apr_off_t offset = 0;
305             apr_status_t rc;
306             if (thefile->append) {
307                 /* apr_file_lock will mutex the file across processes.
308                  * The call to apr_thread_mutex_lock is added to avoid
309                  * a race condition between LockFile and WriteFile
310                  * that occasionally leads to deadlocked threads.
311                  */
312                 apr_thread_mutex_lock(thefile->mutex);
313                 rc = apr_file_lock(thefile, APR_FLOCK_EXCLUSIVE);
314                 if (rc != APR_SUCCESS) {
315                     apr_thread_mutex_unlock(thefile->mutex);
316                     return rc;
317                 }
318                 rc = apr_file_seek(thefile, APR_END, &offset);
319                 if (rc != APR_SUCCESS) {
320                     apr_thread_mutex_unlock(thefile->mutex);
321                     return rc;
322                 }
323             }
324             if (thefile->pOverlapped) {
325                 thefile->pOverlapped->Offset     = (DWORD)thefile->filePtr;
326                 thefile->pOverlapped->OffsetHigh = (DWORD)(thefile->filePtr >> 32);
327             }
328             rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote,
329                            thefile->pOverlapped);
330             if (thefile->append) {
331                 apr_file_unlock(thefile);
332                 apr_thread_mutex_unlock(thefile->mutex);
333             }
334         }
335         else {
336             rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote,
337                            thefile->pOverlapped);
338         }
339         if (rv) {
340             *nbytes = bwrote;
341             rv = APR_SUCCESS;
342         }
343         else {
344             (*nbytes) = 0;
345             rv = apr_get_os_error();
346 
347             /* XXX: This must be corrected, per the apr_file_read logic!!! */
348             if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
349 
350                 DWORD timeout_ms;
351 
352                 if (thefile->timeout == 0) {
353                     timeout_ms = 0;
354                 }
355                 else if (thefile->timeout < 0) {
356                     timeout_ms = INFINITE;
357                 }
358                 else {
359                     timeout_ms = (DWORD)(thefile->timeout / 1000);
360                 }
361 
362                 rv = WaitForSingleObject(thefile->pOverlapped->hEvent, timeout_ms);
363                 switch (rv) {
364                     case WAIT_OBJECT_0:
365                         GetOverlappedResult(thefile->filehand, thefile->pOverlapped,
366                                             &bwrote, TRUE);
367                         *nbytes = bwrote;
368                         rv = APR_SUCCESS;
369                         break;
370                     case WAIT_TIMEOUT:
371                         rv = (timeout_ms == 0) ? APR_EAGAIN : APR_TIMEUP;
372                         break;
373                     case WAIT_FAILED:
374                         rv = apr_get_os_error();
375                         break;
376                     default:
377                         break;
378                 }
379                 if (rv != APR_SUCCESS) {
380                     if (apr_os_level >= APR_WIN_98)
381                         CancelIo(thefile->filehand);
382                 }
383             }
384         }
385         if (rv == APR_SUCCESS && thefile->pOverlapped && !thefile->pipe) {
386             thefile->filePtr += *nbytes;
387         }
388     }
389     return rv;
390 }
391 /* ToDo: Write for it anyway and test the oslevel!
392  * Too bad WriteFileGather() is not supported on 95&98 (or NT prior to SP2)
393  */
apr_file_writev(apr_file_t * thefile,const struct iovec * vec,apr_size_t nvec,apr_size_t * nbytes)394 APR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile,
395                                      const struct iovec *vec,
396                                      apr_size_t nvec,
397                                      apr_size_t *nbytes)
398 {
399     apr_status_t rv = APR_SUCCESS;
400     apr_size_t i;
401     apr_size_t bwrote = 0;
402     char *buf;
403 
404     *nbytes = 0;
405     for (i = 0; i < nvec; i++) {
406         buf = vec[i].iov_base;
407         bwrote = vec[i].iov_len;
408         rv = apr_file_write(thefile, buf, &bwrote);
409         *nbytes += bwrote;
410         if (rv != APR_SUCCESS) {
411             break;
412         }
413     }
414     return rv;
415 }
416 
apr_file_putc(char ch,apr_file_t * thefile)417 APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile)
418 {
419     apr_size_t len = 1;
420 
421     return apr_file_write(thefile, &ch, &len);
422 }
423 
apr_file_ungetc(char ch,apr_file_t * thefile)424 APR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile)
425 {
426     thefile->ungetchar = (unsigned char) ch;
427     return APR_SUCCESS;
428 }
429 
apr_file_getc(char * ch,apr_file_t * thefile)430 APR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile)
431 {
432     apr_status_t rc;
433     apr_size_t bread;
434 
435     bread = 1;
436     rc = apr_file_read(thefile, ch, &bread);
437 
438     if (rc) {
439         return rc;
440     }
441 
442     if (bread == 0) {
443         thefile->eof_hit = TRUE;
444         return APR_EOF;
445     }
446     return APR_SUCCESS;
447 }
448 
apr_file_puts(const char * str,apr_file_t * thefile)449 APR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile)
450 {
451     apr_size_t len = strlen(str);
452 
453     return apr_file_write(thefile, str, &len);
454 }
455 
apr_file_gets(char * str,int len,apr_file_t * thefile)456 APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
457 {
458     apr_size_t readlen;
459     apr_status_t rv = APR_SUCCESS;
460     int i;
461 
462     for (i = 0; i < len-1; i++) {
463         readlen = 1;
464         rv = apr_file_read(thefile, str+i, &readlen);
465 
466         if (rv != APR_SUCCESS && rv != APR_EOF)
467             return rv;
468 
469         if (readlen == 0) {
470             /* If we have bytes, defer APR_EOF to the next call */
471             if (i > 0)
472                 rv = APR_SUCCESS;
473             break;
474         }
475 
476         if (str[i] == '\n') {
477             i++; /* don't clobber this char below */
478             break;
479         }
480     }
481     str[i] = 0;
482     return rv;
483 }
484 
apr_file_flush(apr_file_t * thefile)485 APR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile)
486 {
487     if (thefile->buffered) {
488         DWORD numbytes, written = 0;
489         apr_status_t rc = 0;
490         char *buffer;
491         apr_size_t bytesleft;
492 
493         if (thefile->direction == 1 && thefile->bufpos) {
494             buffer = thefile->buffer;
495             bytesleft = thefile->bufpos;
496 
497             do {
498                 if (bytesleft > APR_DWORD_MAX) {
499                     numbytes = APR_DWORD_MAX;
500                 }
501                 else {
502                     numbytes = (DWORD)bytesleft;
503                 }
504 
505                 if (!WriteFile(thefile->filehand, buffer, numbytes, &written, NULL)) {
506                     rc = apr_get_os_error();
507                     thefile->filePtr += written;
508                     break;
509                 }
510 
511                 thefile->filePtr += written;
512                 bytesleft -= written;
513                 buffer += written;
514 
515             } while (bytesleft > 0);
516 
517             if (rc == 0)
518                 thefile->bufpos = 0;
519         }
520 
521         return rc;
522     }
523 
524     /* There isn't anything to do if we aren't buffering the output
525      * so just return success.
526      */
527     return APR_SUCCESS;
528 }
529 
apr_file_sync(apr_file_t * thefile)530 APR_DECLARE(apr_status_t) apr_file_sync(apr_file_t *thefile){
531     apr_status_t rv;
532 
533     rv = apr_file_flush(thefile);
534     if (rv != APR_SUCCESS) {
535         return rv;
536     }
537 
538     if (!FlushFileBuffers(thefile->filehand)) {
539         rv = apr_get_os_error();
540     }
541 
542     return rv;
543 }
544 
apr_file_datasync(apr_file_t * thefile)545 APR_DECLARE(apr_status_t) apr_file_datasync(apr_file_t *thefile){
546     return apr_file_sync(thefile);
547 }
548 
549 struct apr_file_printf_data {
550     apr_vformatter_buff_t vbuff;
551     apr_file_t *fptr;
552     char *buf;
553 };
554 
file_printf_flush(apr_vformatter_buff_t * buff)555 static int file_printf_flush(apr_vformatter_buff_t *buff)
556 {
557     struct apr_file_printf_data *data = (struct apr_file_printf_data *)buff;
558 
559     if (apr_file_write_full(data->fptr, data->buf,
560                             data->vbuff.curpos - data->buf, NULL)) {
561         return -1;
562     }
563 
564     data->vbuff.curpos = data->buf;
565     return 0;
566 }
567 
apr_file_printf(apr_file_t * fptr,const char * format,...)568 APR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr,
569                                         const char *format, ...)
570 {
571     struct apr_file_printf_data data;
572     va_list ap;
573     int count;
574 
575     data.buf = malloc(HUGE_STRING_LEN);
576     if (data.buf == NULL) {
577         return 0;
578     }
579     data.vbuff.curpos = data.buf;
580     data.vbuff.endpos = data.buf + HUGE_STRING_LEN;
581     data.fptr = fptr;
582     va_start(ap, format);
583     count = apr_vformatter(file_printf_flush,
584                            (apr_vformatter_buff_t *)&data, format, ap);
585     /* apr_vformatter does not call flush for the last bits */
586     if (count >= 0) file_printf_flush((apr_vformatter_buff_t *)&data);
587 
588     va_end(ap);
589 
590     free(data.buf);
591     return count;
592 }
593