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
file_read_buffered(apr_file_t * thefile,void * buf,apr_size_t * nbytes)28 static 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
apr_file_read(apr_file_t * thefile,void * buf,apr_size_t * nbytes)85 APR_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
apr_file_write(apr_file_t * thefile,const void * buf,apr_size_t * nbytes)147 APR_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
apr_file_writev(apr_file_t * thefile,const struct iovec * vec,apr_size_t nvec,apr_size_t * nbytes)226 APR_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
apr_file_putc(char ch,apr_file_t * thefile)284 APR_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
apr_file_ungetc(char ch,apr_file_t * thefile)291 APR_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
apr_file_getc(char * ch,apr_file_t * thefile)297 APR_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
apr_file_puts(const char * str,apr_file_t * thefile)304 APR_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
apr_file_flush_locked(apr_file_t * thefile)309 apr_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
apr_file_flush(apr_file_t * thefile)334 APR_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
apr_file_sync(apr_file_t * thefile)349 APR_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
apr_file_datasync(apr_file_t * thefile)373 APR_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 #else
391 if (fsync(thefile->filedes)) {
392 #endif
393 rv = apr_get_os_error();
394 }
395
396 file_unlock(thefile);
397
398 return rv;
399 }
400
401 APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
402 {
403 apr_status_t rv = APR_SUCCESS; /* get rid of gcc warning */
404 apr_size_t nbytes;
405 const char *str_start = str;
406 char *final = str + len - 1;
407
408 if (len <= 1) {
409 /* sort of like fgets(), which returns NULL and stores no bytes
410 */
411 return APR_SUCCESS;
412 }
413
414 /* If we have an underlying buffer, we can be *much* more efficient
415 * and skip over the apr_file_read calls.
416 */
417 if (thefile->buffered) {
418 file_lock(thefile);
419
420 if (thefile->direction == 1) {
421 rv = apr_file_flush_locked(thefile);
422 if (rv) {
423 file_unlock(thefile);
424 return rv;
425 }
426
427 thefile->direction = 0;
428 thefile->bufpos = 0;
429 thefile->dataRead = 0;
430 }
431
432 while (str < final) { /* leave room for trailing '\0' */
433 /* Force ungetc leftover to call apr_file_read. */
434 if (thefile->bufpos < thefile->dataRead &&
435 thefile->ungetchar == -1) {
436 *str = thefile->buffer[thefile->bufpos++];
437 }
438 else {
439 nbytes = 1;
440 rv = file_read_buffered(thefile, str, &nbytes);
441 if (rv != APR_SUCCESS) {
442 break;
443 }
444 }
445 if (*str == '\n') {
446 ++str;
447 break;
448 }
449 ++str;
450 }
451 file_unlock(thefile);
452 }
453 else {
454 while (str < final) { /* leave room for trailing '\0' */
455 nbytes = 1;
456 rv = apr_file_read(thefile, str, &nbytes);
457 if (rv != APR_SUCCESS) {
458 break;
459 }
460 if (*str == '\n') {
461 ++str;
462 break;
463 }
464 ++str;
465 }
466 }
467
468 /* We must store a terminating '\0' if we've stored any chars. We can
469 * get away with storing it if we hit an error first.
470 */
471 *str = '\0';
472 if (str > str_start) {
473 /* we stored chars; don't report EOF or any other errors;
474 * the app will find out about that on the next call
475 */
476 return APR_SUCCESS;
477 }
478 return rv;
479 }
480
481 struct apr_file_printf_data {
482 apr_vformatter_buff_t vbuff;
483 apr_file_t *fptr;
484 char *buf;
485 };
486
487 static int file_printf_flush(apr_vformatter_buff_t *buff)
488 {
489 struct apr_file_printf_data *data = (struct apr_file_printf_data *)buff;
490
491 if (apr_file_write_full(data->fptr, data->buf,
492 data->vbuff.curpos - data->buf, NULL)) {
493 return -1;
494 }
495
496 data->vbuff.curpos = data->buf;
497 return 0;
498 }
499
500 APR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr,
501 const char *format, ...)
502 {
503 struct apr_file_printf_data data;
504 va_list ap;
505 int count;
506
507 /* don't really need a HUGE_STRING_LEN anymore */
508 data.buf = malloc(HUGE_STRING_LEN);
509 if (data.buf == NULL) {
510 return -1;
511 }
512 data.vbuff.curpos = data.buf;
513 data.vbuff.endpos = data.buf + HUGE_STRING_LEN;
514 data.fptr = fptr;
515 va_start(ap, format);
516 count = apr_vformatter(file_printf_flush,
517 (apr_vformatter_buff_t *)&data, format, ap);
518 /* apr_vformatter does not call flush for the last bits */
519 if (count >= 0) file_printf_flush((apr_vformatter_buff_t *)&data);
520
521 va_end(ap);
522
523 free(data.buf);
524
525 return count;
526 }
527