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
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, apr_size_t *nbytes)
36 /* Handle the zero timeout non-blocking case */
37 if (file->timeout == 0) {
38 /* Peek at the pipe. If there is no data available, return APR_EAGAIN.
39 * If data is available, go ahead and read it.
43 if (!PeekNamedPipe(file->filehand, NULL, 0, NULL, &bytes, NULL)) {
44 rv = apr_get_os_error();
45 if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
60 /* ToDo: Handle zero timeout non-blocking file i/o
61 * This is not needed until an APR application needs to
62 * timeout file i/o (which means setting file i/o non-blocking)
67 if (file->pOverlapped && !file->pipe) {
68 file->pOverlapped->Offset = (DWORD)file->filePtr;
69 file->pOverlapped->OffsetHigh = (DWORD)(file->filePtr >> 32);
72 rv = ReadFile(file->filehand, buf, len, nbytes, file->pOverlapped);
75 rv = apr_get_os_error();
76 if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
77 /* Wait for the pending i/o */
78 if (file->timeout > 0) {
79 /* timeout in milliseconds... */
80 rv = WaitForSingleObject(file->pOverlapped->hEvent,
81 (DWORD)(file->timeout/1000));
83 else if (file->timeout == -1) {
84 rv = WaitForSingleObject(file->pOverlapped->hEvent, INFINITE);
88 GetOverlappedResult(file->filehand, file->pOverlapped,
98 rv = apr_get_os_error();
105 if (rv != APR_SUCCESS) {
106 if (apr_os_level >= APR_WIN_98) {
107 CancelIo(file->filehand);
111 else if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
112 /* Assume ERROR_BROKEN_PIPE signals an EOF reading from a pipe */
114 } else if (rv == APR_FROM_OS_ERROR(ERROR_HANDLE_EOF)) {
115 /* Did we hit EOF reading from the handle? */
119 /* OK and 0 bytes read ==> end of file */
125 if (rv == APR_SUCCESS && file->pOverlapped && !file->pipe) {
126 file->filePtr += *nbytes;
131 APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *len)
134 DWORD bytes_read = 0;
141 /* If the file is open for xthread support, allocate and
142 * initialize the overlapped and io completion event (hEvent).
143 * Threads should NOT share an apr_file_t or its hEvent.
145 if ((thefile->flags & APR_XTHREAD) && !thefile->pOverlapped ) {
146 thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool,
148 thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
149 if (!thefile->pOverlapped->hEvent) {
150 rv = apr_get_os_error();
155 /* Handle the ungetchar if there is one */
156 if (thefile->ungetchar != -1) {
158 *(char *)buf = (char)thefile->ungetchar;
159 buf = (char *)buf + 1;
161 thefile->ungetchar = -1;
167 if (thefile->buffered) {
168 char *pos = (char *)buf;
169 apr_size_t blocksize;
170 apr_size_t size = *len;
172 apr_thread_mutex_lock(thefile->mutex);
174 if (thefile->direction == 1) {
175 rv = apr_file_flush(thefile);
176 if (rv != APR_SUCCESS) {
177 apr_thread_mutex_unlock(thefile->mutex);
181 thefile->direction = 0;
182 thefile->dataRead = 0;
186 while (rv == 0 && size > 0) {
187 if (thefile->bufpos >= thefile->dataRead) {
189 rv = read_with_timeout(thefile, thefile->buffer,
190 APR_FILE_BUFSIZE, &read);
193 thefile->eof_hit = TRUE;
197 thefile->dataRead = read;
198 thefile->filePtr += thefile->dataRead;
203 blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size;
204 memcpy(pos, thefile->buffer + thefile->bufpos, blocksize);
205 thefile->bufpos += blocksize;
210 *len = pos - (char *)buf;
214 apr_thread_mutex_unlock(thefile->mutex);
218 rv = read_with_timeout(thefile, buf, *len, &nbytes);
220 thefile->eof_hit = TRUE;
227 APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes)
232 /* If the file is open for xthread support, allocate and
233 * initialize the overlapped and io completion event (hEvent).
234 * Threads should NOT share an apr_file_t or its hEvent.
236 if ((thefile->flags & APR_XTHREAD) && !thefile->pOverlapped ) {
237 thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool,
239 thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
240 if (!thefile->pOverlapped->hEvent) {
241 rv = apr_get_os_error();
246 if (thefile->buffered) {
247 char *pos = (char *)buf;
248 apr_size_t blocksize;
249 apr_size_t size = *nbytes;
251 apr_thread_mutex_lock(thefile->mutex);
253 if (thefile->direction == 0) {
254 // Position file pointer for writing at the offset we are logically reading from
255 apr_off_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
256 DWORD offlo = (DWORD)offset;
257 DWORD offhi = (DWORD)(offset >> 32);
258 if (offset != thefile->filePtr)
259 SetFilePointer(thefile->filehand, offlo, &offhi, FILE_BEGIN);
260 thefile->bufpos = thefile->dataRead = 0;
261 thefile->direction = 1;
265 while (rv == 0 && size > 0) {
266 if (thefile->bufpos == APR_FILE_BUFSIZE) // write buffer is full
267 rv = apr_file_flush(thefile);
269 blocksize = size > APR_FILE_BUFSIZE - thefile->bufpos ? APR_FILE_BUFSIZE - thefile->bufpos : size;
270 memcpy(thefile->buffer + thefile->bufpos, pos, blocksize);
271 thefile->bufpos += blocksize;
276 apr_thread_mutex_unlock(thefile->mutex);
279 if (!thefile->pipe) {
280 apr_off_t offset = 0;
282 if (thefile->append) {
283 /* apr_file_lock will mutex the file across processes.
284 * The call to apr_thread_mutex_lock is added to avoid
285 * a race condition between LockFile and WriteFile
286 * that occasionally leads to deadlocked threads.
288 apr_thread_mutex_lock(thefile->mutex);
289 rc = apr_file_lock(thefile, APR_FLOCK_EXCLUSIVE);
290 if (rc != APR_SUCCESS) {
291 apr_thread_mutex_unlock(thefile->mutex);
294 rc = apr_file_seek(thefile, APR_END, &offset);
295 if (rc != APR_SUCCESS) {
296 apr_thread_mutex_unlock(thefile->mutex);
300 if (thefile->pOverlapped) {
301 thefile->pOverlapped->Offset = (DWORD)thefile->filePtr;
302 thefile->pOverlapped->OffsetHigh = (DWORD)(thefile->filePtr >> 32);
304 rv = WriteFile(thefile->filehand, buf, *nbytes, &bwrote,
305 thefile->pOverlapped);
306 if (thefile->append) {
307 apr_file_unlock(thefile);
308 apr_thread_mutex_unlock(thefile->mutex);
312 rv = WriteFile(thefile->filehand, buf, *nbytes, &bwrote,
313 thefile->pOverlapped);
321 rv = apr_get_os_error();
322 if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
326 if (thefile->timeout == 0) {
329 else if (thefile->timeout < 0) {
330 timeout_ms = INFINITE;
333 timeout_ms = (DWORD)(thefile->timeout / 1000);
336 rv = WaitForSingleObject(thefile->pOverlapped->hEvent, timeout_ms);
339 GetOverlappedResult(thefile->filehand, thefile->pOverlapped, nbytes, TRUE);
346 rv = apr_get_os_error();
351 if (rv != APR_SUCCESS) {
352 if (apr_os_level >= APR_WIN_98)
353 CancelIo(thefile->filehand);
357 if (rv == APR_SUCCESS && thefile->pOverlapped && !thefile->pipe) {
358 thefile->filePtr += *nbytes;
363 /* ToDo: Write for it anyway and test the oslevel!
364 * Too bad WriteFileGather() is not supported on 95&98 (or NT prior to SP2)
366 APR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile,
367 const struct iovec *vec,
371 apr_status_t rv = APR_SUCCESS;
377 for (i = 0; i < nvec; i++) {
378 buf = vec[i].iov_base;
379 bwrote = vec[i].iov_len;
380 rv = apr_file_write(thefile, buf, &bwrote);
382 if (rv != APR_SUCCESS) {
389 APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile)
393 return apr_file_write(thefile, &ch, &len);
396 APR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile)
398 thefile->ungetchar = (unsigned char) ch;
402 APR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile)
408 rc = apr_file_read(thefile, ch, &bread);
415 thefile->eof_hit = TRUE;
421 APR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile)
423 DWORD len = strlen(str);
425 return apr_file_write(thefile, str, &len);
428 APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
431 apr_status_t rv = APR_SUCCESS;
434 for (i = 0; i < len-1; i++) {
436 rv = apr_file_read(thefile, str+i, &readlen);
438 if (rv != APR_SUCCESS && rv != APR_EOF)
442 /* If we have bytes, defer APR_EOF to the next call */
448 if (str[i] == '\n') {
449 i++; /* don't clobber this char below */
457 APR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile)
459 if (thefile->buffered) {
463 if (thefile->direction == 1 && thefile->bufpos) {
464 if (!WriteFile(thefile->filehand, thefile->buffer, thefile->bufpos, &written, NULL))
465 rc = apr_get_os_error();
466 thefile->filePtr += written;
475 /* There isn't anything to do if we aren't buffering the output
476 * so just return success.
481 struct apr_file_printf_data {
482 apr_vformatter_buff_t vbuff;
487 static int file_printf_flush(apr_vformatter_buff_t *buff)
489 struct apr_file_printf_data *data = (struct apr_file_printf_data *)buff;
491 if (apr_file_write_full(data->fptr, data->buf,
492 data->vbuff.curpos - data->buf, NULL)) {
496 data->vbuff.curpos = data->buf;
500 APR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr,
501 const char *format, ...)
503 struct apr_file_printf_data data;
507 data.buf = malloc(HUGE_STRING_LEN);
508 if (data.buf == NULL) {
511 data.vbuff.curpos = data.buf;
512 data.vbuff.endpos = data.buf + HUGE_STRING_LEN;
514 va_start(ap, format);
515 count = apr_vformatter(file_printf_flush,
516 (apr_vformatter_buff_t *)&data, format, ap);
517 /* apr_vformatter does not call flush for the last bits */
518 if (count >= 0) file_printf_flush((apr_vformatter_buff_t *)&data);