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