1251875Speter/* Licensed to the Apache Software Foundation (ASF) under one or more 2251875Speter * contributor license agreements. See the NOTICE file distributed with 3251875Speter * this work for additional information regarding copyright ownership. 4251875Speter * The ASF licenses this file to You under the Apache License, Version 2.0 5251875Speter * (the "License"); you may not use this file except in compliance with 6251875Speter * the License. You may obtain a copy of the License at 7251875Speter * 8251875Speter * http://www.apache.org/licenses/LICENSE-2.0 9251875Speter * 10251875Speter * Unless required by applicable law or agreed to in writing, software 11251875Speter * distributed under the License is distributed on an "AS IS" BASIS, 12251875Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13251875Speter * See the License for the specific language governing permissions and 14251875Speter * limitations under the License. 15251875Speter */ 16251875Speter 17251875Speter#include "apr_arch_file_io.h" 18251875Speter#include "apr_strings.h" 19251875Speter#include "apr_thread_mutex.h" 20251875Speter#include "apr_support.h" 21251875Speter 22251875Speter/* The only case where we don't use wait_for_io_or_timeout is on 23251875Speter * pre-BONE BeOS, so this check should be sufficient and simpler */ 24266735Speter#if !defined(BEOS_R5) 25251875Speter#define USE_WAIT_FOR_IO 26251875Speter#endif 27251875Speter 28251875Speterstatic apr_status_t file_read_buffered(apr_file_t *thefile, void *buf, 29251875Speter apr_size_t *nbytes) 30251875Speter{ 31251875Speter apr_ssize_t rv; 32251875Speter char *pos = (char *)buf; 33251875Speter apr_uint64_t blocksize; 34251875Speter apr_uint64_t size = *nbytes; 35251875Speter 36251875Speter if (thefile->direction == 1) { 37251875Speter rv = apr_file_flush_locked(thefile); 38251875Speter if (rv) { 39251875Speter return rv; 40251875Speter } 41251875Speter thefile->bufpos = 0; 42251875Speter thefile->direction = 0; 43251875Speter thefile->dataRead = 0; 44251875Speter } 45251875Speter 46251875Speter rv = 0; 47251875Speter if (thefile->ungetchar != -1) { 48251875Speter *pos = (char)thefile->ungetchar; 49251875Speter ++pos; 50251875Speter --size; 51251875Speter thefile->ungetchar = -1; 52251875Speter } 53251875Speter while (rv == 0 && size > 0) { 54251875Speter if (thefile->bufpos >= thefile->dataRead) { 55251875Speter int bytesread = read(thefile->filedes, thefile->buffer, 56251875Speter thefile->bufsize); 57251875Speter if (bytesread == 0) { 58251875Speter thefile->eof_hit = TRUE; 59251875Speter rv = APR_EOF; 60251875Speter break; 61251875Speter } 62251875Speter else if (bytesread == -1) { 63251875Speter rv = errno; 64251875Speter break; 65251875Speter } 66251875Speter thefile->dataRead = bytesread; 67251875Speter thefile->filePtr += thefile->dataRead; 68251875Speter thefile->bufpos = 0; 69251875Speter } 70251875Speter 71251875Speter blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size; 72251875Speter memcpy(pos, thefile->buffer + thefile->bufpos, blocksize); 73251875Speter thefile->bufpos += blocksize; 74251875Speter pos += blocksize; 75251875Speter size -= blocksize; 76251875Speter } 77251875Speter 78251875Speter *nbytes = pos - (char *)buf; 79251875Speter if (*nbytes) { 80251875Speter rv = 0; 81251875Speter } 82251875Speter return rv; 83251875Speter} 84251875Speter 85251875SpeterAPR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *nbytes) 86251875Speter{ 87251875Speter apr_ssize_t rv; 88251875Speter apr_size_t bytes_read; 89251875Speter 90251875Speter if (*nbytes <= 0) { 91251875Speter *nbytes = 0; 92251875Speter return APR_SUCCESS; 93251875Speter } 94251875Speter 95251875Speter if (thefile->buffered) { 96251875Speter file_lock(thefile); 97251875Speter rv = file_read_buffered(thefile, buf, nbytes); 98251875Speter file_unlock(thefile); 99251875Speter return rv; 100251875Speter } 101251875Speter else { 102251875Speter bytes_read = 0; 103251875Speter if (thefile->ungetchar != -1) { 104251875Speter bytes_read = 1; 105251875Speter *(char *)buf = (char)thefile->ungetchar; 106251875Speter buf = (char *)buf + 1; 107251875Speter (*nbytes)--; 108251875Speter thefile->ungetchar = -1; 109251875Speter if (*nbytes == 0) { 110251875Speter *nbytes = bytes_read; 111251875Speter return APR_SUCCESS; 112251875Speter } 113251875Speter } 114251875Speter 115251875Speter do { 116251875Speter rv = read(thefile->filedes, buf, *nbytes); 117251875Speter } while (rv == -1 && errno == EINTR); 118251875Speter#ifdef USE_WAIT_FOR_IO 119251875Speter if (rv == -1 && 120251875Speter (errno == EAGAIN || errno == EWOULDBLOCK) && 121251875Speter thefile->timeout != 0) { 122251875Speter apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 1); 123251875Speter if (arv != APR_SUCCESS) { 124251875Speter *nbytes = bytes_read; 125251875Speter return arv; 126251875Speter } 127251875Speter else { 128251875Speter do { 129251875Speter rv = read(thefile->filedes, buf, *nbytes); 130251875Speter } while (rv == -1 && errno == EINTR); 131251875Speter } 132251875Speter } 133251875Speter#endif 134251875Speter *nbytes = bytes_read; 135251875Speter if (rv == 0) { 136251875Speter thefile->eof_hit = TRUE; 137251875Speter return APR_EOF; 138251875Speter } 139251875Speter if (rv > 0) { 140251875Speter *nbytes += rv; 141251875Speter return APR_SUCCESS; 142251875Speter } 143251875Speter return errno; 144251875Speter } 145251875Speter} 146251875Speter 147251875SpeterAPR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes) 148251875Speter{ 149251875Speter apr_size_t rv; 150251875Speter 151251875Speter if (thefile->buffered) { 152251875Speter char *pos = (char *)buf; 153251875Speter int blocksize; 154251875Speter int size = *nbytes; 155251875Speter 156251875Speter file_lock(thefile); 157251875Speter 158251875Speter if ( thefile->direction == 0 ) { 159251875Speter /* Position file pointer for writing at the offset we are 160251875Speter * logically reading from 161251875Speter */ 162251875Speter apr_int64_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos; 163251875Speter if (offset != thefile->filePtr) 164251875Speter lseek(thefile->filedes, offset, SEEK_SET); 165251875Speter thefile->bufpos = thefile->dataRead = 0; 166251875Speter thefile->direction = 1; 167251875Speter } 168251875Speter 169251875Speter rv = 0; 170251875Speter while (rv == 0 && size > 0) { 171251875Speter if (thefile->bufpos == thefile->bufsize) /* write buffer is full*/ 172251875Speter rv = apr_file_flush_locked(thefile); 173251875Speter 174251875Speter blocksize = size > thefile->bufsize - thefile->bufpos ? 175251875Speter thefile->bufsize - thefile->bufpos : size; 176251875Speter memcpy(thefile->buffer + thefile->bufpos, pos, blocksize); 177251875Speter thefile->bufpos += blocksize; 178251875Speter pos += blocksize; 179251875Speter size -= blocksize; 180251875Speter } 181251875Speter 182251875Speter file_unlock(thefile); 183251875Speter 184251875Speter return rv; 185251875Speter } 186251875Speter else { 187251875Speter do { 188251875Speter rv = write(thefile->filedes, buf, *nbytes); 189251875Speter } while (rv == (apr_size_t)-1 && errno == EINTR); 190251875Speter#ifdef USE_WAIT_FOR_IO 191251875Speter if (rv == (apr_size_t)-1 && 192251875Speter (errno == EAGAIN || errno == EWOULDBLOCK) && 193251875Speter thefile->timeout != 0) { 194251875Speter apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 0); 195251875Speter if (arv != APR_SUCCESS) { 196251875Speter *nbytes = 0; 197251875Speter return arv; 198251875Speter } 199251875Speter else { 200251875Speter do { 201251875Speter do { 202251875Speter rv = write(thefile->filedes, buf, *nbytes); 203251875Speter } while (rv == (apr_size_t)-1 && errno == EINTR); 204251875Speter if (rv == (apr_size_t)-1 && 205251875Speter (errno == EAGAIN || errno == EWOULDBLOCK)) { 206251875Speter *nbytes /= 2; /* yes, we'll loop if kernel lied 207251875Speter * and we can't even write 1 byte 208251875Speter */ 209251875Speter } 210251875Speter else { 211251875Speter break; 212251875Speter } 213251875Speter } while (1); 214251875Speter } 215251875Speter } 216251875Speter#endif 217251875Speter if (rv == (apr_size_t)-1) { 218251875Speter (*nbytes) = 0; 219251875Speter return errno; 220251875Speter } 221251875Speter *nbytes = rv; 222251875Speter return APR_SUCCESS; 223251875Speter } 224251875Speter} 225251875Speter 226251875SpeterAPR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile, const struct iovec *vec, 227251875Speter apr_size_t nvec, apr_size_t *nbytes) 228251875Speter{ 229251875Speter#ifdef HAVE_WRITEV 230251875Speter apr_status_t rv; 231251875Speter apr_ssize_t bytes; 232251875Speter 233251875Speter if (thefile->buffered) { 234251875Speter file_lock(thefile); 235251875Speter 236251875Speter rv = apr_file_flush_locked(thefile); 237251875Speter if (rv != APR_SUCCESS) { 238251875Speter file_unlock(thefile); 239251875Speter return rv; 240251875Speter } 241251875Speter if (thefile->direction == 0) { 242251875Speter /* Position file pointer for writing at the offset we are 243251875Speter * logically reading from 244251875Speter */ 245251875Speter apr_int64_t offset = thefile->filePtr - thefile->dataRead + 246251875Speter thefile->bufpos; 247251875Speter if (offset != thefile->filePtr) 248251875Speter lseek(thefile->filedes, offset, SEEK_SET); 249251875Speter thefile->bufpos = thefile->dataRead = 0; 250251875Speter } 251251875Speter 252251875Speter file_unlock(thefile); 253251875Speter } 254251875Speter 255251875Speter if ((bytes = writev(thefile->filedes, vec, nvec)) < 0) { 256251875Speter *nbytes = 0; 257251875Speter rv = errno; 258251875Speter } 259251875Speter else { 260251875Speter *nbytes = bytes; 261251875Speter rv = APR_SUCCESS; 262251875Speter } 263251875Speter return rv; 264251875Speter#else 265251875Speter /** 266251875Speter * The problem with trying to output the entire iovec is that we cannot 267251875Speter * maintain the behaviour that a real writev would have. If we iterate 268251875Speter * over the iovec one at a time, we lose the atomic properties of 269251875Speter * writev(). The other option is to combine the entire iovec into one 270251875Speter * buffer that we could then send in one call to write(). This is not 271251875Speter * reasonable since we do not know how much data an iovec could contain. 272251875Speter * 273251875Speter * The only reasonable option, that maintains the semantics of a real 274251875Speter * writev(), is to only write the first iovec. Callers of file_writev() 275251875Speter * must deal with partial writes as they normally would. If you want to 276251875Speter * ensure an entire iovec is written, use apr_file_writev_full(). 277251875Speter */ 278251875Speter 279251875Speter *nbytes = vec[0].iov_len; 280251875Speter return apr_file_write(thefile, vec[0].iov_base, nbytes); 281251875Speter#endif 282251875Speter} 283251875Speter 284251875SpeterAPR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile) 285251875Speter{ 286251875Speter apr_size_t nbytes = 1; 287251875Speter 288251875Speter return apr_file_write(thefile, &ch, &nbytes); 289251875Speter} 290251875Speter 291251875SpeterAPR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile) 292251875Speter{ 293251875Speter thefile->ungetchar = (unsigned char)ch; 294251875Speter return APR_SUCCESS; 295251875Speter} 296251875Speter 297251875SpeterAPR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile) 298251875Speter{ 299251875Speter apr_size_t nbytes = 1; 300251875Speter 301251875Speter return apr_file_read(thefile, ch, &nbytes); 302251875Speter} 303251875Speter 304251875SpeterAPR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile) 305251875Speter{ 306251875Speter return apr_file_write_full(thefile, str, strlen(str), NULL); 307251875Speter} 308251875Speter 309251875Speterapr_status_t apr_file_flush_locked(apr_file_t *thefile) 310251875Speter{ 311251875Speter apr_status_t rv = APR_SUCCESS; 312251875Speter 313251875Speter if (thefile->direction == 1 && thefile->bufpos) { 314251875Speter apr_ssize_t written = 0, ret; 315251875Speter 316251875Speter do { 317251875Speter ret = write(thefile->filedes, thefile->buffer + written, 318251875Speter thefile->bufpos - written); 319251875Speter if (ret > 0) 320251875Speter written += ret; 321251875Speter } while (written < thefile->bufpos && 322251875Speter (ret > 0 || (ret == -1 && errno == EINTR))); 323251875Speter if (ret == -1) { 324251875Speter rv = errno; 325251875Speter } else { 326251875Speter thefile->filePtr += written; 327251875Speter thefile->bufpos = 0; 328251875Speter } 329251875Speter } 330251875Speter 331251875Speter return rv; 332251875Speter} 333251875Speter 334251875SpeterAPR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile) 335251875Speter{ 336251875Speter apr_status_t rv = APR_SUCCESS; 337251875Speter 338251875Speter if (thefile->buffered) { 339251875Speter file_lock(thefile); 340251875Speter rv = apr_file_flush_locked(thefile); 341251875Speter file_unlock(thefile); 342251875Speter } 343251875Speter /* There isn't anything to do if we aren't buffering the output 344251875Speter * so just return success. 345251875Speter */ 346251875Speter return rv; 347251875Speter} 348251875Speter 349251875SpeterAPR_DECLARE(apr_status_t) apr_file_sync(apr_file_t *thefile) 350251875Speter{ 351251875Speter apr_status_t rv = APR_SUCCESS; 352251875Speter 353251875Speter file_lock(thefile); 354251875Speter 355251875Speter if (thefile->buffered) { 356251875Speter rv = apr_file_flush_locked(thefile); 357251875Speter 358251875Speter if (rv != APR_SUCCESS) { 359251875Speter file_unlock(thefile); 360251875Speter return rv; 361251875Speter } 362251875Speter } 363251875Speter 364251875Speter if (fsync(thefile->filedes)) { 365251875Speter rv = apr_get_os_error(); 366251875Speter } 367251875Speter 368251875Speter file_unlock(thefile); 369251875Speter 370251875Speter return rv; 371251875Speter} 372251875Speter 373251875SpeterAPR_DECLARE(apr_status_t) apr_file_datasync(apr_file_t *thefile) 374251875Speter{ 375251875Speter apr_status_t rv = APR_SUCCESS; 376251875Speter 377251875Speter file_lock(thefile); 378251875Speter 379251875Speter if (thefile->buffered) { 380251875Speter rv = apr_file_flush_locked(thefile); 381251875Speter 382251875Speter if (rv != APR_SUCCESS) { 383251875Speter file_unlock(thefile); 384251875Speter return rv; 385251875Speter } 386251875Speter } 387251875Speter 388251875Speter#ifdef HAVE_FDATASYNC 389251875Speter if (fdatasync(thefile->filedes)) { 390362181Sdim#elif defined(F_FULLFSYNC) 391362181Sdim if (fcntl(thefile->filedes, F_FULLFSYNC)) { 392251875Speter#else 393251875Speter if (fsync(thefile->filedes)) { 394251875Speter#endif 395251875Speter rv = apr_get_os_error(); 396251875Speter } 397251875Speter 398251875Speter file_unlock(thefile); 399251875Speter 400251875Speter return rv; 401251875Speter} 402251875Speter 403251875SpeterAPR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile) 404251875Speter{ 405251875Speter apr_status_t rv = APR_SUCCESS; /* get rid of gcc warning */ 406251875Speter apr_size_t nbytes; 407251875Speter const char *str_start = str; 408251875Speter char *final = str + len - 1; 409251875Speter 410251875Speter if (len <= 1) { 411251875Speter /* sort of like fgets(), which returns NULL and stores no bytes 412251875Speter */ 413251875Speter return APR_SUCCESS; 414251875Speter } 415251875Speter 416251875Speter /* If we have an underlying buffer, we can be *much* more efficient 417251875Speter * and skip over the apr_file_read calls. 418251875Speter */ 419251875Speter if (thefile->buffered) { 420251875Speter file_lock(thefile); 421251875Speter 422251875Speter if (thefile->direction == 1) { 423251875Speter rv = apr_file_flush_locked(thefile); 424251875Speter if (rv) { 425251875Speter file_unlock(thefile); 426251875Speter return rv; 427251875Speter } 428251875Speter 429251875Speter thefile->direction = 0; 430251875Speter thefile->bufpos = 0; 431251875Speter thefile->dataRead = 0; 432251875Speter } 433251875Speter 434251875Speter while (str < final) { /* leave room for trailing '\0' */ 435251875Speter /* Force ungetc leftover to call apr_file_read. */ 436251875Speter if (thefile->bufpos < thefile->dataRead && 437251875Speter thefile->ungetchar == -1) { 438251875Speter *str = thefile->buffer[thefile->bufpos++]; 439251875Speter } 440251875Speter else { 441251875Speter nbytes = 1; 442251875Speter rv = file_read_buffered(thefile, str, &nbytes); 443251875Speter if (rv != APR_SUCCESS) { 444251875Speter break; 445251875Speter } 446251875Speter } 447251875Speter if (*str == '\n') { 448251875Speter ++str; 449251875Speter break; 450251875Speter } 451251875Speter ++str; 452251875Speter } 453251875Speter file_unlock(thefile); 454251875Speter } 455251875Speter else { 456251875Speter while (str < final) { /* leave room for trailing '\0' */ 457251875Speter nbytes = 1; 458251875Speter rv = apr_file_read(thefile, str, &nbytes); 459251875Speter if (rv != APR_SUCCESS) { 460251875Speter break; 461251875Speter } 462251875Speter if (*str == '\n') { 463251875Speter ++str; 464251875Speter break; 465251875Speter } 466251875Speter ++str; 467251875Speter } 468251875Speter } 469251875Speter 470251875Speter /* We must store a terminating '\0' if we've stored any chars. We can 471251875Speter * get away with storing it if we hit an error first. 472251875Speter */ 473251875Speter *str = '\0'; 474251875Speter if (str > str_start) { 475251875Speter /* we stored chars; don't report EOF or any other errors; 476251875Speter * the app will find out about that on the next call 477251875Speter */ 478251875Speter return APR_SUCCESS; 479251875Speter } 480251875Speter return rv; 481251875Speter} 482251875Speter 483251875Speterstruct apr_file_printf_data { 484251875Speter apr_vformatter_buff_t vbuff; 485251875Speter apr_file_t *fptr; 486251875Speter char *buf; 487251875Speter}; 488251875Speter 489251875Speterstatic int file_printf_flush(apr_vformatter_buff_t *buff) 490251875Speter{ 491251875Speter struct apr_file_printf_data *data = (struct apr_file_printf_data *)buff; 492251875Speter 493251875Speter if (apr_file_write_full(data->fptr, data->buf, 494251875Speter data->vbuff.curpos - data->buf, NULL)) { 495251875Speter return -1; 496251875Speter } 497251875Speter 498251875Speter data->vbuff.curpos = data->buf; 499251875Speter return 0; 500251875Speter} 501251875Speter 502251875SpeterAPR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr, 503251875Speter const char *format, ...) 504251875Speter{ 505251875Speter struct apr_file_printf_data data; 506251875Speter va_list ap; 507251875Speter int count; 508251875Speter 509251875Speter /* don't really need a HUGE_STRING_LEN anymore */ 510251875Speter data.buf = malloc(HUGE_STRING_LEN); 511251875Speter if (data.buf == NULL) { 512251875Speter return -1; 513251875Speter } 514251875Speter data.vbuff.curpos = data.buf; 515251875Speter data.vbuff.endpos = data.buf + HUGE_STRING_LEN; 516251875Speter data.fptr = fptr; 517251875Speter va_start(ap, format); 518251875Speter count = apr_vformatter(file_printf_flush, 519251875Speter (apr_vformatter_buff_t *)&data, format, ap); 520251875Speter /* apr_vformatter does not call flush for the last bits */ 521251875Speter if (count >= 0) file_printf_flush((apr_vformatter_buff_t *)&data); 522251875Speter 523251875Speter va_end(ap); 524251875Speter 525251875Speter free(data.buf); 526251875Speter 527251875Speter return count; 528251875Speter} 529