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 */
31static 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
143APR_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        apr_thread_mutex_lock(thefile->mutex);
185
186        if (thefile->direction == 1) {
187            rv = apr_file_flush(thefile);
188            if (rv != APR_SUCCESS) {
189                apr_thread_mutex_unlock(thefile->mutex);
190                return rv;
191            }
192            thefile->bufpos = 0;
193            thefile->direction = 0;
194            thefile->dataRead = 0;
195        }
196
197        rv = 0;
198        while (rv == 0 && size > 0) {
199            if (thefile->bufpos >= thefile->dataRead) {
200                apr_size_t read;
201                rv = read_with_timeout(thefile, thefile->buffer,
202                                       thefile->bufsize, &read);
203                if (read == 0) {
204                    if (rv == APR_EOF)
205                        thefile->eof_hit = TRUE;
206                    break;
207                }
208                else {
209                    thefile->dataRead = read;
210                    thefile->filePtr += thefile->dataRead;
211                    thefile->bufpos = 0;
212                }
213            }
214
215            blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size;
216            memcpy(pos, thefile->buffer + thefile->bufpos, blocksize);
217            thefile->bufpos += blocksize;
218            pos += blocksize;
219            size -= blocksize;
220        }
221
222        *len = pos - (char *)buf;
223        if (*len) {
224            rv = APR_SUCCESS;
225        }
226        apr_thread_mutex_unlock(thefile->mutex);
227    } else {
228        /* Unbuffered i/o */
229        apr_size_t nbytes;
230        rv = read_with_timeout(thefile, buf, *len, &nbytes);
231        if (rv == APR_EOF)
232            thefile->eof_hit = TRUE;
233        *len = nbytes;
234    }
235
236    return rv;
237}
238
239APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes)
240{
241    apr_status_t rv;
242    DWORD bwrote;
243
244    /* If the file is open for xthread support, allocate and
245     * initialize the overlapped and io completion event (hEvent).
246     * Threads should NOT share an apr_file_t or its hEvent.
247     */
248    if ((thefile->flags & APR_FOPEN_XTHREAD) && !thefile->pOverlapped ) {
249        thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool,
250                                                         sizeof(OVERLAPPED));
251        thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
252        if (!thefile->pOverlapped->hEvent) {
253            rv = apr_get_os_error();
254            return rv;
255        }
256    }
257
258    if (thefile->buffered) {
259        char *pos = (char *)buf;
260        apr_size_t blocksize;
261        apr_size_t size = *nbytes;
262
263        apr_thread_mutex_lock(thefile->mutex);
264
265        if (thefile->direction == 0) {
266            /* Position file pointer for writing at the offset we are logically reading from */
267            apr_off_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
268            DWORD offlo = (DWORD)offset;
269            LONG  offhi = (LONG)(offset >> 32);
270            if (offset != thefile->filePtr)
271                SetFilePointer(thefile->filehand, offlo, &offhi, FILE_BEGIN);
272            thefile->bufpos = thefile->dataRead = 0;
273            thefile->direction = 1;
274        }
275
276        rv = 0;
277        while (rv == 0 && size > 0) {
278            if (thefile->bufpos == thefile->bufsize)   // write buffer is full
279                rv = apr_file_flush(thefile);
280
281            blocksize = size > thefile->bufsize - thefile->bufpos ?
282                                     thefile->bufsize - thefile->bufpos : size;
283            memcpy(thefile->buffer + thefile->bufpos, pos, blocksize);
284            thefile->bufpos += blocksize;
285            pos += blocksize;
286            size -= blocksize;
287        }
288
289        apr_thread_mutex_unlock(thefile->mutex);
290        return rv;
291    } else {
292        if (!thefile->pipe) {
293            apr_off_t offset = 0;
294            apr_status_t rc;
295            if (thefile->append) {
296                /* apr_file_lock will mutex the file across processes.
297                 * The call to apr_thread_mutex_lock is added to avoid
298                 * a race condition between LockFile and WriteFile
299                 * that occasionally leads to deadlocked threads.
300                 */
301                apr_thread_mutex_lock(thefile->mutex);
302                rc = apr_file_lock(thefile, APR_FLOCK_EXCLUSIVE);
303                if (rc != APR_SUCCESS) {
304                    apr_thread_mutex_unlock(thefile->mutex);
305                    return rc;
306                }
307                rc = apr_file_seek(thefile, APR_END, &offset);
308                if (rc != APR_SUCCESS) {
309                    apr_thread_mutex_unlock(thefile->mutex);
310                    return rc;
311                }
312            }
313            if (thefile->pOverlapped) {
314                thefile->pOverlapped->Offset     = (DWORD)thefile->filePtr;
315                thefile->pOverlapped->OffsetHigh = (DWORD)(thefile->filePtr >> 32);
316            }
317            rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote,
318                           thefile->pOverlapped);
319            if (thefile->append) {
320                apr_file_unlock(thefile);
321                apr_thread_mutex_unlock(thefile->mutex);
322            }
323        }
324        else {
325            rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote,
326                           thefile->pOverlapped);
327        }
328        if (rv) {
329            *nbytes = bwrote;
330            rv = APR_SUCCESS;
331        }
332        else {
333            (*nbytes) = 0;
334            rv = apr_get_os_error();
335
336            /* XXX: This must be corrected, per the apr_file_read logic!!! */
337            if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
338
339                DWORD timeout_ms;
340
341                if (thefile->timeout == 0) {
342                    timeout_ms = 0;
343                }
344                else if (thefile->timeout < 0) {
345                    timeout_ms = INFINITE;
346                }
347                else {
348                    timeout_ms = (DWORD)(thefile->timeout / 1000);
349                }
350
351                rv = WaitForSingleObject(thefile->pOverlapped->hEvent, timeout_ms);
352                switch (rv) {
353                    case WAIT_OBJECT_0:
354                        GetOverlappedResult(thefile->filehand, thefile->pOverlapped,
355                                            &bwrote, TRUE);
356                        *nbytes = bwrote;
357                        rv = APR_SUCCESS;
358                        break;
359                    case WAIT_TIMEOUT:
360                        rv = (timeout_ms == 0) ? APR_EAGAIN : APR_TIMEUP;
361                        break;
362                    case WAIT_FAILED:
363                        rv = apr_get_os_error();
364                        break;
365                    default:
366                        break;
367                }
368                if (rv != APR_SUCCESS) {
369                    if (apr_os_level >= APR_WIN_98)
370                        CancelIo(thefile->filehand);
371                }
372            }
373        }
374        if (rv == APR_SUCCESS && thefile->pOverlapped && !thefile->pipe) {
375            thefile->filePtr += *nbytes;
376        }
377    }
378    return rv;
379}
380/* ToDo: Write for it anyway and test the oslevel!
381 * Too bad WriteFileGather() is not supported on 95&98 (or NT prior to SP2)
382 */
383APR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile,
384                                     const struct iovec *vec,
385                                     apr_size_t nvec,
386                                     apr_size_t *nbytes)
387{
388    apr_status_t rv = APR_SUCCESS;
389    apr_size_t i;
390    apr_size_t bwrote = 0;
391    char *buf;
392
393    *nbytes = 0;
394    for (i = 0; i < nvec; i++) {
395        buf = vec[i].iov_base;
396        bwrote = vec[i].iov_len;
397        rv = apr_file_write(thefile, buf, &bwrote);
398        *nbytes += bwrote;
399        if (rv != APR_SUCCESS) {
400            break;
401        }
402    }
403    return rv;
404}
405
406APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile)
407{
408    apr_size_t len = 1;
409
410    return apr_file_write(thefile, &ch, &len);
411}
412
413APR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile)
414{
415    thefile->ungetchar = (unsigned char) ch;
416    return APR_SUCCESS;
417}
418
419APR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile)
420{
421    apr_status_t rc;
422    apr_size_t bread;
423
424    bread = 1;
425    rc = apr_file_read(thefile, ch, &bread);
426
427    if (rc) {
428        return rc;
429    }
430
431    if (bread == 0) {
432        thefile->eof_hit = TRUE;
433        return APR_EOF;
434    }
435    return APR_SUCCESS;
436}
437
438APR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile)
439{
440    apr_size_t len = strlen(str);
441
442    return apr_file_write(thefile, str, &len);
443}
444
445APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
446{
447    apr_size_t readlen;
448    apr_status_t rv = APR_SUCCESS;
449    int i;
450
451    for (i = 0; i < len-1; i++) {
452        readlen = 1;
453        rv = apr_file_read(thefile, str+i, &readlen);
454
455        if (rv != APR_SUCCESS && rv != APR_EOF)
456            return rv;
457
458        if (readlen == 0) {
459            /* If we have bytes, defer APR_EOF to the next call */
460            if (i > 0)
461                rv = APR_SUCCESS;
462            break;
463        }
464
465        if (str[i] == '\n') {
466            i++; /* don't clobber this char below */
467            break;
468        }
469    }
470    str[i] = 0;
471    return rv;
472}
473
474APR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile)
475{
476    if (thefile->buffered) {
477        DWORD numbytes, written = 0;
478        apr_status_t rc = 0;
479        char *buffer;
480        apr_size_t bytesleft;
481
482        if (thefile->direction == 1 && thefile->bufpos) {
483            buffer = thefile->buffer;
484            bytesleft = thefile->bufpos;
485
486            do {
487                if (bytesleft > APR_DWORD_MAX) {
488                    numbytes = APR_DWORD_MAX;
489                }
490                else {
491                    numbytes = (DWORD)bytesleft;
492                }
493
494                if (!WriteFile(thefile->filehand, buffer, numbytes, &written, NULL)) {
495                    rc = apr_get_os_error();
496                    thefile->filePtr += written;
497                    break;
498                }
499
500                thefile->filePtr += written;
501                bytesleft -= written;
502                buffer += written;
503
504            } while (bytesleft > 0);
505
506            if (rc == 0)
507                thefile->bufpos = 0;
508        }
509
510        return rc;
511    }
512
513    /* There isn't anything to do if we aren't buffering the output
514     * so just return success.
515     */
516    return APR_SUCCESS;
517}
518
519APR_DECLARE(apr_status_t) apr_file_sync(apr_file_t *thefile){
520    apr_status_t rv;
521
522    rv = apr_file_flush(thefile);
523    if (rv != APR_SUCCESS) {
524        return rv;
525    }
526
527    if (!FlushFileBuffers(thefile->filehand)) {
528        rv = apr_get_os_error();
529    }
530
531    return rv;
532}
533
534APR_DECLARE(apr_status_t) apr_file_datasync(apr_file_t *thefile){
535    return apr_file_sync(thefile);
536}
537
538struct apr_file_printf_data {
539    apr_vformatter_buff_t vbuff;
540    apr_file_t *fptr;
541    char *buf;
542};
543
544static int file_printf_flush(apr_vformatter_buff_t *buff)
545{
546    struct apr_file_printf_data *data = (struct apr_file_printf_data *)buff;
547
548    if (apr_file_write_full(data->fptr, data->buf,
549                            data->vbuff.curpos - data->buf, NULL)) {
550        return -1;
551    }
552
553    data->vbuff.curpos = data->buf;
554    return 0;
555}
556
557APR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr,
558                                        const char *format, ...)
559{
560    struct apr_file_printf_data data;
561    va_list ap;
562    int count;
563
564    data.buf = malloc(HUGE_STRING_LEN);
565    if (data.buf == NULL) {
566        return 0;
567    }
568    data.vbuff.curpos = data.buf;
569    data.vbuff.endpos = data.buf + HUGE_STRING_LEN;
570    data.fptr = fptr;
571    va_start(ap, format);
572    count = apr_vformatter(file_printf_flush,
573                           (apr_vformatter_buff_t *)&data, format, ap);
574    /* apr_vformatter does not call flush for the last bits */
575    if (count >= 0) file_printf_flush((apr_vformatter_buff_t *)&data);
576
577    va_end(ap);
578
579    free(data.buf);
580    return count;
581}
582