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_strings.h" 19#include "apr_thread_mutex.h" 20#include "apr_support.h" 21 22/* The only case where we don't use wait_for_io_or_timeout is on 23 * pre-BONE BeOS, so this check should be sufficient and simpler */ 24#if !defined(BEOS_R5) 25#define USE_WAIT_FOR_IO 26#endif 27 28static apr_status_t file_read_buffered(apr_file_t *thefile, void *buf, 29 apr_size_t *nbytes) 30{ 31 apr_ssize_t rv; 32 char *pos = (char *)buf; 33 apr_uint64_t blocksize; 34 apr_uint64_t size = *nbytes; 35 36 if (thefile->direction == 1) { 37 rv = apr_file_flush_locked(thefile); 38 if (rv) { 39 return rv; 40 } 41 thefile->bufpos = 0; 42 thefile->direction = 0; 43 thefile->dataRead = 0; 44 } 45 46 rv = 0; 47 if (thefile->ungetchar != -1) { 48 *pos = (char)thefile->ungetchar; 49 ++pos; 50 --size; 51 thefile->ungetchar = -1; 52 } 53 while (rv == 0 && size > 0) { 54 if (thefile->bufpos >= thefile->dataRead) { 55 int bytesread = read(thefile->filedes, thefile->buffer, 56 thefile->bufsize); 57 if (bytesread == 0) { 58 thefile->eof_hit = TRUE; 59 rv = APR_EOF; 60 break; 61 } 62 else if (bytesread == -1) { 63 rv = errno; 64 break; 65 } 66 thefile->dataRead = bytesread; 67 thefile->filePtr += thefile->dataRead; 68 thefile->bufpos = 0; 69 } 70 71 blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size; 72 memcpy(pos, thefile->buffer + thefile->bufpos, blocksize); 73 thefile->bufpos += blocksize; 74 pos += blocksize; 75 size -= blocksize; 76 } 77 78 *nbytes = pos - (char *)buf; 79 if (*nbytes) { 80 rv = 0; 81 } 82 return rv; 83} 84 85APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *nbytes) 86{ 87 apr_ssize_t rv; 88 apr_size_t bytes_read; 89 90 if (*nbytes <= 0) { 91 *nbytes = 0; 92 return APR_SUCCESS; 93 } 94 95 if (thefile->buffered) { 96 file_lock(thefile); 97 rv = file_read_buffered(thefile, buf, nbytes); 98 file_unlock(thefile); 99 return rv; 100 } 101 else { 102 bytes_read = 0; 103 if (thefile->ungetchar != -1) { 104 bytes_read = 1; 105 *(char *)buf = (char)thefile->ungetchar; 106 buf = (char *)buf + 1; 107 (*nbytes)--; 108 thefile->ungetchar = -1; 109 if (*nbytes == 0) { 110 *nbytes = bytes_read; 111 return APR_SUCCESS; 112 } 113 } 114 115 do { 116 rv = read(thefile->filedes, buf, *nbytes); 117 } while (rv == -1 && errno == EINTR); 118#ifdef USE_WAIT_FOR_IO 119 if (rv == -1 && 120 (errno == EAGAIN || errno == EWOULDBLOCK) && 121 thefile->timeout != 0) { 122 apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 1); 123 if (arv != APR_SUCCESS) { 124 *nbytes = bytes_read; 125 return arv; 126 } 127 else { 128 do { 129 rv = read(thefile->filedes, buf, *nbytes); 130 } while (rv == -1 && errno == EINTR); 131 } 132 } 133#endif 134 *nbytes = bytes_read; 135 if (rv == 0) { 136 thefile->eof_hit = TRUE; 137 return APR_EOF; 138 } 139 if (rv > 0) { 140 *nbytes += rv; 141 return APR_SUCCESS; 142 } 143 return errno; 144 } 145} 146 147APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes) 148{ 149 apr_size_t rv; 150 151 if (thefile->buffered) { 152 char *pos = (char *)buf; 153 int blocksize; 154 int size = *nbytes; 155 156 file_lock(thefile); 157 158 if ( thefile->direction == 0 ) { 159 /* Position file pointer for writing at the offset we are 160 * logically reading from 161 */ 162 apr_int64_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos; 163 if (offset != thefile->filePtr) 164 lseek(thefile->filedes, offset, SEEK_SET); 165 thefile->bufpos = thefile->dataRead = 0; 166 thefile->direction = 1; 167 } 168 169 rv = 0; 170 while (rv == 0 && size > 0) { 171 if (thefile->bufpos == thefile->bufsize) /* write buffer is full*/ 172 rv = apr_file_flush_locked(thefile); 173 174 blocksize = size > thefile->bufsize - thefile->bufpos ? 175 thefile->bufsize - thefile->bufpos : size; 176 memcpy(thefile->buffer + thefile->bufpos, pos, blocksize); 177 thefile->bufpos += blocksize; 178 pos += blocksize; 179 size -= blocksize; 180 } 181 182 file_unlock(thefile); 183 184 return rv; 185 } 186 else { 187 do { 188 rv = write(thefile->filedes, buf, *nbytes); 189 } while (rv == (apr_size_t)-1 && errno == EINTR); 190#ifdef USE_WAIT_FOR_IO 191 if (rv == (apr_size_t)-1 && 192 (errno == EAGAIN || errno == EWOULDBLOCK) && 193 thefile->timeout != 0) { 194 apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 0); 195 if (arv != APR_SUCCESS) { 196 *nbytes = 0; 197 return arv; 198 } 199 else { 200 do { 201 do { 202 rv = write(thefile->filedes, buf, *nbytes); 203 } while (rv == (apr_size_t)-1 && errno == EINTR); 204 if (rv == (apr_size_t)-1 && 205 (errno == EAGAIN || errno == EWOULDBLOCK)) { 206 *nbytes /= 2; /* yes, we'll loop if kernel lied 207 * and we can't even write 1 byte 208 */ 209 } 210 else { 211 break; 212 } 213 } while (1); 214 } 215 } 216#endif 217 if (rv == (apr_size_t)-1) { 218 (*nbytes) = 0; 219 return errno; 220 } 221 *nbytes = rv; 222 return APR_SUCCESS; 223 } 224} 225 226APR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile, const struct iovec *vec, 227 apr_size_t nvec, apr_size_t *nbytes) 228{ 229#ifdef HAVE_WRITEV 230 apr_status_t rv; 231 apr_ssize_t bytes; 232 233 if (thefile->buffered) { 234 file_lock(thefile); 235 236 rv = apr_file_flush_locked(thefile); 237 if (rv != APR_SUCCESS) { 238 file_unlock(thefile); 239 return rv; 240 } 241 if (thefile->direction == 0) { 242 /* Position file pointer for writing at the offset we are 243 * logically reading from 244 */ 245 apr_int64_t offset = thefile->filePtr - thefile->dataRead + 246 thefile->bufpos; 247 if (offset != thefile->filePtr) 248 lseek(thefile->filedes, offset, SEEK_SET); 249 thefile->bufpos = thefile->dataRead = 0; 250 } 251 252 file_unlock(thefile); 253 } 254 255 if ((bytes = writev(thefile->filedes, vec, nvec)) < 0) { 256 *nbytes = 0; 257 rv = errno; 258 } 259 else { 260 *nbytes = bytes; 261 rv = APR_SUCCESS; 262 } 263 return rv; 264#else 265 /** 266 * The problem with trying to output the entire iovec is that we cannot 267 * maintain the behaviour that a real writev would have. If we iterate 268 * over the iovec one at a time, we lose the atomic properties of 269 * writev(). The other option is to combine the entire iovec into one 270 * buffer that we could then send in one call to write(). This is not 271 * reasonable since we do not know how much data an iovec could contain. 272 * 273 * The only reasonable option, that maintains the semantics of a real 274 * writev(), is to only write the first iovec. Callers of file_writev() 275 * must deal with partial writes as they normally would. If you want to 276 * ensure an entire iovec is written, use apr_file_writev_full(). 277 */ 278 279 *nbytes = vec[0].iov_len; 280 return apr_file_write(thefile, vec[0].iov_base, nbytes); 281#endif 282} 283 284APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile) 285{ 286 apr_size_t nbytes = 1; 287 288 return apr_file_write(thefile, &ch, &nbytes); 289} 290 291APR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile) 292{ 293 thefile->ungetchar = (unsigned char)ch; 294 return APR_SUCCESS; 295} 296 297APR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile) 298{ 299 apr_size_t nbytes = 1; 300 301 return apr_file_read(thefile, ch, &nbytes); 302} 303 304APR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile) 305{ 306 return apr_file_write_full(thefile, str, strlen(str), NULL); 307} 308 309apr_status_t apr_file_flush_locked(apr_file_t *thefile) 310{ 311 apr_status_t rv = APR_SUCCESS; 312 313 if (thefile->direction == 1 && thefile->bufpos) { 314 apr_ssize_t written = 0, ret; 315 316 do { 317 ret = write(thefile->filedes, thefile->buffer + written, 318 thefile->bufpos - written); 319 if (ret > 0) 320 written += ret; 321 } while (written < thefile->bufpos && 322 (ret > 0 || (ret == -1 && errno == EINTR))); 323 if (ret == -1) { 324 rv = errno; 325 } else { 326 thefile->filePtr += written; 327 thefile->bufpos = 0; 328 } 329 } 330 331 return rv; 332} 333 334APR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile) 335{ 336 apr_status_t rv = APR_SUCCESS; 337 338 if (thefile->buffered) { 339 file_lock(thefile); 340 rv = apr_file_flush_locked(thefile); 341 file_unlock(thefile); 342 } 343 /* There isn't anything to do if we aren't buffering the output 344 * so just return success. 345 */ 346 return rv; 347} 348 349APR_DECLARE(apr_status_t) apr_file_sync(apr_file_t *thefile) 350{ 351 apr_status_t rv = APR_SUCCESS; 352 353 file_lock(thefile); 354 355 if (thefile->buffered) { 356 rv = apr_file_flush_locked(thefile); 357 358 if (rv != APR_SUCCESS) { 359 file_unlock(thefile); 360 return rv; 361 } 362 } 363 364 if (fsync(thefile->filedes)) { 365 rv = apr_get_os_error(); 366 } 367 368 file_unlock(thefile); 369 370 return rv; 371} 372 373APR_DECLARE(apr_status_t) apr_file_datasync(apr_file_t *thefile) 374{ 375 apr_status_t rv = APR_SUCCESS; 376 377 file_lock(thefile); 378 379 if (thefile->buffered) { 380 rv = apr_file_flush_locked(thefile); 381 382 if (rv != APR_SUCCESS) { 383 file_unlock(thefile); 384 return rv; 385 } 386 } 387 388#ifdef HAVE_FDATASYNC 389 if (fdatasync(thefile->filedes)) { 390#elif defined(F_FULLFSYNC) 391 if (fcntl(thefile->filedes, F_FULLFSYNC)) { 392#else 393 if (fsync(thefile->filedes)) { 394#endif 395 rv = apr_get_os_error(); 396 } 397 398 file_unlock(thefile); 399 400 return rv; 401} 402 403APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile) 404{ 405 apr_status_t rv = APR_SUCCESS; /* get rid of gcc warning */ 406 apr_size_t nbytes; 407 const char *str_start = str; 408 char *final = str + len - 1; 409 410 if (len <= 1) { 411 /* sort of like fgets(), which returns NULL and stores no bytes 412 */ 413 return APR_SUCCESS; 414 } 415 416 /* If we have an underlying buffer, we can be *much* more efficient 417 * and skip over the apr_file_read calls. 418 */ 419 if (thefile->buffered) { 420 file_lock(thefile); 421 422 if (thefile->direction == 1) { 423 rv = apr_file_flush_locked(thefile); 424 if (rv) { 425 file_unlock(thefile); 426 return rv; 427 } 428 429 thefile->direction = 0; 430 thefile->bufpos = 0; 431 thefile->dataRead = 0; 432 } 433 434 while (str < final) { /* leave room for trailing '\0' */ 435 /* Force ungetc leftover to call apr_file_read. */ 436 if (thefile->bufpos < thefile->dataRead && 437 thefile->ungetchar == -1) { 438 *str = thefile->buffer[thefile->bufpos++]; 439 } 440 else { 441 nbytes = 1; 442 rv = file_read_buffered(thefile, str, &nbytes); 443 if (rv != APR_SUCCESS) { 444 break; 445 } 446 } 447 if (*str == '\n') { 448 ++str; 449 break; 450 } 451 ++str; 452 } 453 file_unlock(thefile); 454 } 455 else { 456 while (str < final) { /* leave room for trailing '\0' */ 457 nbytes = 1; 458 rv = apr_file_read(thefile, str, &nbytes); 459 if (rv != APR_SUCCESS) { 460 break; 461 } 462 if (*str == '\n') { 463 ++str; 464 break; 465 } 466 ++str; 467 } 468 } 469 470 /* We must store a terminating '\0' if we've stored any chars. We can 471 * get away with storing it if we hit an error first. 472 */ 473 *str = '\0'; 474 if (str > str_start) { 475 /* we stored chars; don't report EOF or any other errors; 476 * the app will find out about that on the next call 477 */ 478 return APR_SUCCESS; 479 } 480 return rv; 481} 482 483struct apr_file_printf_data { 484 apr_vformatter_buff_t vbuff; 485 apr_file_t *fptr; 486 char *buf; 487}; 488 489static int file_printf_flush(apr_vformatter_buff_t *buff) 490{ 491 struct apr_file_printf_data *data = (struct apr_file_printf_data *)buff; 492 493 if (apr_file_write_full(data->fptr, data->buf, 494 data->vbuff.curpos - data->buf, NULL)) { 495 return -1; 496 } 497 498 data->vbuff.curpos = data->buf; 499 return 0; 500} 501 502APR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr, 503 const char *format, ...) 504{ 505 struct apr_file_printf_data data; 506 va_list ap; 507 int count; 508 509 /* don't really need a HUGE_STRING_LEN anymore */ 510 data.buf = malloc(HUGE_STRING_LEN); 511 if (data.buf == NULL) { 512 return -1; 513 } 514 data.vbuff.curpos = data.buf; 515 data.vbuff.endpos = data.buf + HUGE_STRING_LEN; 516 data.fptr = fptr; 517 va_start(ap, format); 518 count = apr_vformatter(file_printf_flush, 519 (apr_vformatter_buff_t *)&data, format, ap); 520 /* apr_vformatter does not call flush for the last bits */ 521 if (count >= 0) file_printf_flush((apr_vformatter_buff_t *)&data); 522 523 va_end(ap); 524 525 free(data.buf); 526 527 return count; 528} 529