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