Coverage Report

Created: 2024-02-25 06:29

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