1
/* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
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.
17
#include "win32/apr_arch_file_io.h"
18
#include "apr_file_io.h"
19
#include "apr_general.h"
20
#include "apr_strings.h"
22
#include "apr_errno.h"
24
#include "apr_arch_atime.h"
25
#include "apr_arch_misc.h"
29
* Uses async i/o to emulate unix non-blocking i/o with timeouts.
31
static apr_status_t read_with_timeout(apr_file_t *file, void *buf, apr_size_t len_in, apr_size_t *nbytes)
34
DWORD len = (DWORD)len_in;
37
/* Handle the zero timeout non-blocking case */
38
if (file->timeout == 0) {
39
/* Peek at the pipe. If there is no data available, return APR_EAGAIN.
40
* If data is available, go ahead and read it.
44
if (!PeekNamedPipe(file->filehand, NULL, 0, NULL, &bytes, NULL)) {
45
rv = apr_get_os_error();
46
if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
63
/* ToDo: Handle zero timeout non-blocking file i/o
64
* This is not needed until an APR application needs to
65
* timeout file i/o (which means setting file i/o non-blocking)
70
if (file->pOverlapped && !file->pipe) {
71
file->pOverlapped->Offset = (DWORD)file->filePtr;
72
file->pOverlapped->OffsetHigh = (DWORD)(file->filePtr >> 32);
75
rv = ReadFile(file->filehand, buf, len,
76
&bytesread, file->pOverlapped);
80
rv = apr_get_os_error();
81
if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
82
/* Wait for the pending i/o */
83
if (file->timeout > 0) {
84
/* timeout in milliseconds... */
85
rv = WaitForSingleObject(file->pOverlapped->hEvent,
86
(DWORD)(file->timeout/1000));
88
else if (file->timeout == -1) {
89
rv = WaitForSingleObject(file->pOverlapped->hEvent, INFINITE);
93
GetOverlappedResult(file->filehand, file->pOverlapped,
104
rv = apr_get_os_error();
111
if (rv != APR_SUCCESS) {
112
if (apr_os_level >= APR_WIN_98) {
113
CancelIo(file->filehand);
117
else if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
118
/* Assume ERROR_BROKEN_PIPE signals an EOF reading from a pipe */
122
/* OK and 0 bytes read ==> end of file */
128
if (rv == APR_SUCCESS && file->pOverlapped && !file->pipe) {
129
file->filePtr += *nbytes;
134
APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *len)
137
DWORD bytes_read = 0;
144
/* If the file is open for xthread support, allocate and
145
* initialize the overlapped and io completion event (hEvent).
146
* Threads should NOT share an apr_file_t or its hEvent.
148
if ((thefile->flags & APR_XTHREAD) && !thefile->pOverlapped ) {
149
thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool,
151
thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
152
if (!thefile->pOverlapped->hEvent) {
153
rv = apr_get_os_error();
158
/* Handle the ungetchar if there is one */
159
if (thefile->ungetchar != -1) {
161
*(char *)buf = (char)thefile->ungetchar;
162
buf = (char *)buf + 1;
164
thefile->ungetchar = -1;
170
if (thefile->buffered) {
171
char *pos = (char *)buf;
172
apr_size_t blocksize;
173
apr_size_t size = *len;
175
apr_thread_mutex_lock(thefile->mutex);
177
if (thefile->direction == 1) {
178
rv = apr_file_flush(thefile);
179
if (rv != APR_SUCCESS) {
180
apr_thread_mutex_unlock(thefile->mutex);
184
thefile->direction = 0;
185
thefile->dataRead = 0;
189
while (rv == 0 && size > 0) {
190
if (thefile->bufpos >= thefile->dataRead) {
192
rv = read_with_timeout(thefile, thefile->buffer,
193
APR_FILE_BUFSIZE, &read);
196
thefile->eof_hit = TRUE;
200
thefile->dataRead = read;
201
thefile->filePtr += thefile->dataRead;
206
blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size;
207
memcpy(pos, thefile->buffer + thefile->bufpos, blocksize);
208
thefile->bufpos += blocksize;
213
*len = pos - (char *)buf;
217
apr_thread_mutex_unlock(thefile->mutex);
221
rv = read_with_timeout(thefile, buf, *len, &nbytes);
223
thefile->eof_hit = TRUE;
230
APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes)
235
/* If the file is open for xthread support, allocate and
236
* initialize the overlapped and io completion event (hEvent).
237
* Threads should NOT share an apr_file_t or its hEvent.
239
if ((thefile->flags & APR_XTHREAD) && !thefile->pOverlapped ) {
240
thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool,
242
thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
243
if (!thefile->pOverlapped->hEvent) {
244
rv = apr_get_os_error();
249
if (thefile->buffered) {
250
char *pos = (char *)buf;
251
apr_size_t blocksize;
252
apr_size_t size = *nbytes;
254
apr_thread_mutex_lock(thefile->mutex);
256
if (thefile->direction == 0) {
257
// Position file pointer for writing at the offset we are logically reading from
258
apr_off_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
259
DWORD offlo = (DWORD)offset;
260
DWORD offhi = (DWORD)(offset >> 32);
261
if (offset != thefile->filePtr)
262
SetFilePointer(thefile->filehand, offlo, &offhi, FILE_BEGIN);
263
thefile->bufpos = thefile->dataRead = 0;
264
thefile->direction = 1;
268
while (rv == 0 && size > 0) {
269
if (thefile->bufpos == APR_FILE_BUFSIZE) // write buffer is full
270
rv = apr_file_flush(thefile);
272
blocksize = size > APR_FILE_BUFSIZE - thefile->bufpos ? APR_FILE_BUFSIZE - thefile->bufpos : size;
273
memcpy(thefile->buffer + thefile->bufpos, pos, blocksize);
274
thefile->bufpos += blocksize;
279
apr_thread_mutex_unlock(thefile->mutex);
282
if (!thefile->pipe) {
283
apr_off_t offset = 0;
285
if (thefile->append) {
286
/* apr_file_lock will mutex the file across processes.
287
* The call to apr_thread_mutex_lock is added to avoid
288
* a race condition between LockFile and WriteFile
289
* that occasionally leads to deadlocked threads.
291
apr_thread_mutex_lock(thefile->mutex);
292
rc = apr_file_lock(thefile, APR_FLOCK_EXCLUSIVE);
293
if (rc != APR_SUCCESS) {
294
apr_thread_mutex_unlock(thefile->mutex);
297
rc = apr_file_seek(thefile, APR_END, &offset);
298
if (rc != APR_SUCCESS) {
299
apr_thread_mutex_unlock(thefile->mutex);
303
if (thefile->pOverlapped) {
304
thefile->pOverlapped->Offset = (DWORD)thefile->filePtr;
305
thefile->pOverlapped->OffsetHigh = (DWORD)(thefile->filePtr >> 32);
307
rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote,
308
thefile->pOverlapped);
309
if (thefile->append) {
310
apr_file_unlock(thefile);
311
apr_thread_mutex_unlock(thefile->mutex);
315
rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote,
316
thefile->pOverlapped);
324
rv = apr_get_os_error();
325
if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
329
if (thefile->timeout == 0) {
332
else if (thefile->timeout < 0) {
333
timeout_ms = INFINITE;
336
timeout_ms = (DWORD)(thefile->timeout / 1000);
339
rv = WaitForSingleObject(thefile->pOverlapped->hEvent, timeout_ms);
342
GetOverlappedResult(thefile->filehand, thefile->pOverlapped,
351
rv = apr_get_os_error();
356
if (rv != APR_SUCCESS) {
357
if (apr_os_level >= APR_WIN_98)
358
CancelIo(thefile->filehand);
362
if (rv == APR_SUCCESS && thefile->pOverlapped && !thefile->pipe) {
363
thefile->filePtr += *nbytes;
368
/* ToDo: Write for it anyway and test the oslevel!
369
* Too bad WriteFileGather() is not supported on 95&98 (or NT prior to SP2)
371
APR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile,
372
const struct iovec *vec,
376
apr_status_t rv = APR_SUCCESS;
378
apr_size_t bwrote = 0;
382
for (i = 0; i < nvec; i++) {
383
buf = vec[i].iov_base;
384
bwrote = vec[i].iov_len;
385
rv = apr_file_write(thefile, buf, &bwrote);
387
if (rv != APR_SUCCESS) {
394
APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile)
398
return apr_file_write(thefile, &ch, &len);
401
APR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile)
403
thefile->ungetchar = (unsigned char) ch;
407
APR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile)
413
rc = apr_file_read(thefile, ch, &bread);
420
thefile->eof_hit = TRUE;
426
APR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile)
428
apr_size_t len = strlen(str);
430
return apr_file_write(thefile, str, &len);
433
APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
436
apr_status_t rv = APR_SUCCESS;
439
for (i = 0; i < len-1; i++) {
441
rv = apr_file_read(thefile, str+i, &readlen);
443
if (rv != APR_SUCCESS && rv != APR_EOF)
447
/* If we have bytes, defer APR_EOF to the next call */
453
if (str[i] == '\n') {
454
i++; /* don't clobber this char below */
462
APR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile)
464
if (thefile->buffered) {
465
DWORD numbytes, written = 0;
468
apr_size_t bytesleft;
470
if (thefile->direction == 1 && thefile->bufpos) {
471
buffer = thefile->buffer;
472
bytesleft = thefile->bufpos;
475
if (bytesleft > APR_DWORD_MAX) {
476
numbytes = APR_DWORD_MAX;
479
numbytes = (DWORD)bytesleft;
482
if (!WriteFile(thefile->filehand, buffer, numbytes, &written, NULL)) {
483
rc = apr_get_os_error();
484
thefile->filePtr += written;
488
thefile->filePtr += written;
489
bytesleft -= written;
492
} while (bytesleft > 0);
501
/* There isn't anything to do if we aren't buffering the output
502
* so just return success.
507
struct apr_file_printf_data {
508
apr_vformatter_buff_t vbuff;
513
static int file_printf_flush(apr_vformatter_buff_t *buff)
515
struct apr_file_printf_data *data = (struct apr_file_printf_data *)buff;
517
if (apr_file_write_full(data->fptr, data->buf,
518
data->vbuff.curpos - data->buf, NULL)) {
522
data->vbuff.curpos = data->buf;
526
APR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr,
527
const char *format, ...)
529
struct apr_file_printf_data data;
533
data.buf = malloc(HUGE_STRING_LEN);
534
if (data.buf == NULL) {
537
data.vbuff.curpos = data.buf;
538
data.vbuff.endpos = data.buf + HUGE_STRING_LEN;
540
va_start(ap, format);
541
count = apr_vformatter(file_printf_flush,
542
(apr_vformatter_buff_t *)&data, format, ap);
543
/* apr_vformatter does not call flush for the last bits */
544
if (count >= 0) file_printf_flush((apr_vformatter_buff_t *)&data);