Coverage Report

Created: 2026-09-04 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/httpd/srclib/apr/file_io/unix/open.c
Line
Count
Source
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_portable.h"
20
#include "apr_thread_mutex.h"
21
#include "apr_arch_inherit.h"
22
#include "apr_time.h"
23
24
static apr_status_t file_cleanup(apr_file_t *file, int is_child)
25
1.22k
{
26
1.22k
    apr_status_t rv = APR_SUCCESS;
27
1.22k
    int fd = file->filedes;
28
29
    /* Set file descriptor to -1 before close(), so that there is no
30
     * chance of returning an already closed FD from apr_os_file_get().
31
     */
32
1.22k
    file->filedes = -1;
33
34
1.22k
    if (close(fd) == 0) {
35
        /* Only the parent process should delete the file! */
36
1.22k
        if (!is_child && (file->flags & APR_FOPEN_DELONCLOSE)) {
37
0
            unlink(file->fname);
38
0
        }
39
1.22k
#if APR_HAS_THREADS
40
1.22k
        if (file->thlock) {
41
0
            rv = apr_thread_mutex_destroy(file->thlock);
42
0
        }
43
1.22k
#endif
44
1.22k
    }
45
0
    else {
46
        /* Restore, close() was not successful. */
47
0
        file->filedes = fd;
48
49
        /* Are there any error conditions other than EINTR or EBADF? */
50
0
        rv = errno;
51
0
    }
52
#ifndef WAITIO_USES_POLL
53
    if (file->pollset != NULL) {
54
        apr_status_t pollset_rv = apr_pollset_destroy(file->pollset);
55
        /* If the file close failed, return its error value,
56
         * not apr_pollset_destroy()'s.
57
         */
58
        if (rv == APR_SUCCESS) {
59
            rv = pollset_rv;
60
        }
61
    }
62
#endif /* !WAITIO_USES_POLL */
63
1.22k
    return rv;
64
1.22k
}
65
66
apr_status_t apr_unix_file_cleanup(void *thefile)
67
1.22k
{
68
1.22k
    apr_file_t *file = thefile;
69
1.22k
    apr_status_t flush_rv = APR_SUCCESS, rv = APR_SUCCESS;
70
71
1.22k
    if (file->buffered) {
72
1.22k
        flush_rv = apr_file_flush(file);
73
1.22k
    }
74
75
1.22k
    rv = file_cleanup(file, 0);
76
77
1.22k
    return rv != APR_SUCCESS ? rv : flush_rv;
78
1.22k
}
79
80
apr_status_t apr_unix_child_file_cleanup(void *thefile)
81
0
{
82
0
    return file_cleanup(thefile, 1);
83
0
}
84
85
APR_DECLARE(apr_status_t) apr_file_open(apr_file_t **new,
86
                                        const char *fname,
87
                                        apr_int32_t flag,
88
                                        apr_fileperms_t perm,
89
                                        apr_pool_t *pool)
90
1.22k
{
91
1.22k
    apr_os_file_t fd;
92
1.22k
    int oflags = 0;
93
1.22k
#if APR_HAS_THREADS
94
1.22k
    apr_thread_mutex_t *thlock = NULL;
95
1.22k
#endif
96
1.22k
    apr_status_t rv;
97
98
1.22k
    if ((flag & APR_FOPEN_READ) && (flag & APR_FOPEN_WRITE)) {
99
0
        oflags = O_RDWR;
100
0
    }
101
1.22k
    else if (flag & APR_FOPEN_READ) {
102
1.22k
        oflags = O_RDONLY;
103
1.22k
    }
104
0
    else if (flag & APR_FOPEN_WRITE) {
105
0
        oflags = O_WRONLY;
106
0
    }
107
0
    else {
108
0
        return APR_EACCES;
109
0
    }
110
111
1.22k
    if (flag & APR_FOPEN_CREATE) {
112
0
        oflags |= O_CREAT;
113
0
        if (flag & APR_FOPEN_EXCL) {
114
0
            oflags |= O_EXCL;
115
0
        }
116
0
    }
117
1.22k
    if ((flag & APR_FOPEN_EXCL) && !(flag & APR_FOPEN_CREATE)) {
118
0
        return APR_EACCES;
119
0
    }
120
121
1.22k
    if (flag & APR_FOPEN_APPEND) {
122
0
        oflags |= O_APPEND;
123
0
    }
124
1.22k
    if (flag & APR_FOPEN_TRUNCATE) {
125
0
        oflags |= O_TRUNC;
126
0
    }
127
#ifdef O_BINARY
128
    if (flag & APR_FOPEN_BINARY) {
129
        oflags |= O_BINARY;
130
    }
131
#endif
132
133
1.22k
    if (flag & APR_FOPEN_NONBLOCK) {
134
0
#ifdef O_NONBLOCK
135
0
        oflags |= O_NONBLOCK;
136
#else
137
        return APR_ENOTIMPL;
138
#endif
139
0
    }
140
141
1.22k
#ifdef O_CLOEXEC
142
    /* Introduced in Linux 2.6.23. Silently ignored on earlier Linux kernels.
143
     */
144
1.22k
    if (!(flag & APR_FOPEN_NOCLEANUP)) {
145
1.22k
        oflags |= O_CLOEXEC;
146
1.22k
}
147
1.22k
#endif
148
149
#if APR_HAS_LARGE_FILES && defined(_LARGEFILE64_SOURCE)
150
    oflags |= O_LARGEFILE;
151
#elif defined(O_LARGEFILE)
152
1.22k
    if (flag & APR_FOPEN_LARGEFILE) {
153
0
        oflags |= O_LARGEFILE;
154
0
    }
155
1.22k
#endif
156
157
1.22k
#if APR_HAS_THREADS
158
1.22k
    if ((flag & APR_FOPEN_BUFFERED) && (flag & APR_FOPEN_XTHREAD)) {
159
0
        rv = apr_thread_mutex_create(&thlock,
160
0
                                     APR_THREAD_MUTEX_DEFAULT, pool);
161
0
        if (rv) {
162
0
            return rv;
163
0
        }
164
0
    }
165
1.22k
#endif
166
167
1.22k
    if (perm == APR_FPROT_OS_DEFAULT) {
168
1.22k
        fd = open(fname, oflags, 0666);
169
1.22k
    }
170
0
    else {
171
0
        fd = open(fname, oflags, apr_unix_perms2mode(perm));
172
0
    }
173
1.22k
    if (fd < 0) {
174
0
       return errno;
175
0
    }
176
1.22k
    if (!(flag & APR_FOPEN_NOCLEANUP)) {
177
1.22k
#ifdef O_CLOEXEC
178
1.22k
        static int has_o_cloexec = 0;
179
1.22k
        if (!has_o_cloexec)
180
1
#endif
181
1
        {
182
1
            int flags;
183
184
1
            if ((flags = fcntl(fd, F_GETFD)) == -1) {
185
0
                close(fd);
186
0
                return errno;
187
0
            }
188
1
            if ((flags & FD_CLOEXEC) == 0) {
189
0
                flags |= FD_CLOEXEC;
190
0
                if (fcntl(fd, F_SETFD, flags) == -1) {
191
0
                    close(fd);
192
0
                    return errno;
193
0
                }
194
0
            }
195
1
#ifdef O_CLOEXEC
196
1
            else {
197
1
                has_o_cloexec = 1;
198
1
            }
199
1
#endif
200
1
        }
201
1.22k
    }
202
203
1.22k
    (*new) = (apr_file_t *)apr_pcalloc(pool, sizeof(apr_file_t));
204
1.22k
    (*new)->pool = pool;
205
1.22k
    (*new)->flags = flag;
206
1.22k
    (*new)->filedes = fd;
207
208
1.22k
    (*new)->fname = apr_pstrdup(pool, fname);
209
210
1.22k
    (*new)->blocking = BLK_ON;
211
1.22k
    (*new)->buffered = (flag & APR_FOPEN_BUFFERED) > 0;
212
213
1.22k
    if ((*new)->buffered) {
214
1.22k
        (*new)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
215
1.22k
        (*new)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
216
1.22k
    }
217
0
    else {
218
0
        (*new)->buffer = NULL;
219
0
    }
220
221
1.22k
#if APR_HAS_THREADS
222
1.22k
    (*new)->thlock = thlock;
223
1.22k
#endif
224
225
1.22k
    (*new)->is_pipe = 0;
226
1.22k
    (*new)->timeout = -1;
227
1.22k
    (*new)->ungetchar = -1;
228
1.22k
    (*new)->eof_hit = 0;
229
1.22k
    (*new)->filePtr = 0;
230
1.22k
    (*new)->bufpos = 0;
231
1.22k
    (*new)->dataRead = 0;
232
1.22k
    (*new)->direction = 0;
233
#ifndef WAITIO_USES_POLL
234
    /* Start out with no pollset.  apr_wait_for_io_or_timeout() will
235
     * initialize the pollset if needed.
236
     */
237
    (*new)->pollset = NULL;
238
#endif
239
1.22k
    if (!(flag & APR_FOPEN_NOCLEANUP)) {
240
1.22k
        apr_pool_cleanup_register((*new)->pool, (void *)(*new),
241
1.22k
                                  apr_unix_file_cleanup,
242
1.22k
                                  apr_unix_child_file_cleanup);
243
1.22k
    }
244
245
1.22k
    if ((flag & APR_FOPEN_ROTATING) || (flag & APR_FOPEN_MANUAL_ROTATE)) {
246
0
        (*new)->rotating = (apr_rotating_info_t *)apr_pcalloc(pool,
247
0
                                                              sizeof(apr_rotating_info_t));
248
249
0
        rv =  apr_file_info_get(&(*new)->rotating->finfo,
250
0
                                APR_FINFO_DEV|APR_FINFO_INODE, *new);
251
0
        if (rv != APR_SUCCESS) {
252
0
            return rv;
253
0
        }
254
255
0
        if (flag & APR_FOPEN_MANUAL_ROTATE) {
256
0
            (*new)->rotating->manual = 1;
257
0
        }
258
0
        else {
259
0
            (*new)->rotating->manual = 0;
260
0
        }
261
0
        (*new)->rotating->timeout = 60;
262
0
        (*new)->rotating->lastcheck = apr_time_sec(apr_time_now());
263
0
        (*new)->rotating->oflags = oflags;
264
0
        (*new)->rotating->perm = perm;
265
0
    }
266
1.22k
    else {
267
1.22k
        (*new)->rotating = NULL;
268
1.22k
    }
269
270
1.22k
    return APR_SUCCESS;
271
1.22k
}
272
273
APR_DECLARE(apr_status_t) apr_file_close(apr_file_t *file)
274
0
{
275
0
    return apr_pool_cleanup_run(file->pool, file, apr_unix_file_cleanup);
276
0
}
277
278
APR_DECLARE(apr_status_t) apr_file_remove(const char *path, apr_pool_t *pool)
279
0
{
280
0
    if (unlink(path) == 0) {
281
0
        return APR_SUCCESS;
282
0
    }
283
0
    else {
284
0
        return errno;
285
0
    }
286
0
}
287
288
APR_DECLARE(apr_status_t) apr_file_rename(const char *from_path,
289
                                          const char *to_path,
290
                                          apr_pool_t *p)
291
0
{
292
0
    if (rename(from_path, to_path) != 0) {
293
0
        return errno;
294
0
    }
295
0
    return APR_SUCCESS;
296
0
}
297
298
APR_DECLARE(apr_status_t) apr_os_file_get(apr_os_file_t *thefile,
299
                                          apr_file_t *file)
300
0
{
301
0
    *thefile = file->filedes;
302
0
    return APR_SUCCESS;
303
0
}
304
305
APR_DECLARE(apr_status_t) apr_os_file_put(apr_file_t **file,
306
                                          apr_os_file_t *thefile,
307
                                          apr_int32_t flags, apr_pool_t *pool)
308
2
{
309
2
    int *dafile = thefile;
310
311
2
    (*file) = apr_pcalloc(pool, sizeof(apr_file_t));
312
2
    (*file)->pool = pool;
313
2
    (*file)->eof_hit = 0;
314
2
    (*file)->blocking = BLK_UNKNOWN; /* in case it is a pipe */
315
2
    (*file)->timeout = -1;
316
2
    (*file)->ungetchar = -1; /* no char avail */
317
2
    (*file)->filedes = *dafile;
318
2
    (*file)->flags = flags | APR_FOPEN_NOCLEANUP;
319
2
    (*file)->buffered = (flags & APR_FOPEN_BUFFERED) > 0;
320
321
#ifndef WAITIO_USES_POLL
322
    /* Start out with no pollset.  apr_wait_for_io_or_timeout() will
323
     * initialize the pollset if needed.
324
     */
325
    (*file)->pollset = NULL;
326
#endif
327
328
2
    if ((*file)->buffered) {
329
0
        (*file)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
330
0
        (*file)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
331
0
#if APR_HAS_THREADS
332
0
        if ((*file)->flags & APR_FOPEN_XTHREAD) {
333
0
            apr_status_t rv;
334
0
            rv = apr_thread_mutex_create(&((*file)->thlock),
335
0
                                         APR_THREAD_MUTEX_DEFAULT, pool);
336
0
            if (rv) {
337
0
                return rv;
338
0
            }
339
0
        }
340
0
#endif
341
0
    }
342
2
    return APR_SUCCESS;
343
2
}
344
345
APR_DECLARE(apr_status_t) apr_file_eof(apr_file_t *fptr)
346
0
{
347
0
    if (fptr->eof_hit == 1) {
348
0
        return APR_EOF;
349
0
    }
350
0
    return APR_SUCCESS;
351
0
}
352
353
APR_DECLARE(apr_status_t) apr_file_open_flags_stderr(apr_file_t **thefile,
354
                                                     apr_int32_t flags,
355
                                                     apr_pool_t *pool)
356
2
{
357
2
    int fd = STDERR_FILENO;
358
359
2
    return apr_os_file_put(thefile, &fd, flags | APR_FOPEN_WRITE, pool);
360
2
}
361
362
APR_DECLARE(apr_status_t) apr_file_open_flags_stdout(apr_file_t **thefile,
363
                                                     apr_int32_t flags,
364
                                                     apr_pool_t *pool)
365
0
{
366
0
    int fd = STDOUT_FILENO;
367
368
0
    return apr_os_file_put(thefile, &fd, flags | APR_FOPEN_WRITE, pool);
369
0
}
370
371
APR_DECLARE(apr_status_t) apr_file_open_flags_stdin(apr_file_t **thefile,
372
                                                    apr_int32_t flags,
373
                                                    apr_pool_t *pool)
374
0
{
375
0
    int fd = STDIN_FILENO;
376
377
0
    return apr_os_file_put(thefile, &fd, flags | APR_FOPEN_READ, pool);
378
0
}
379
380
APR_DECLARE(apr_status_t) apr_file_open_stderr(apr_file_t **thefile,
381
                                               apr_pool_t *pool)
382
2
{
383
2
    return apr_file_open_flags_stderr(thefile, 0, pool);
384
2
}
385
386
APR_DECLARE(apr_status_t) apr_file_open_stdout(apr_file_t **thefile,
387
                                               apr_pool_t *pool)
388
0
{
389
0
    return apr_file_open_flags_stdout(thefile, 0, pool);
390
0
}
391
392
APR_DECLARE(apr_status_t) apr_file_open_stdin(apr_file_t **thefile,
393
                                              apr_pool_t *pool)
394
0
{
395
0
    return apr_file_open_flags_stdin(thefile, 0, pool);
396
0
}
397
398
0
APR_IMPLEMENT_INHERIT_SET(file, flags, pool, apr_unix_file_cleanup)
399
400
/* We need to do this by hand instead of using APR_IMPLEMENT_INHERIT_UNSET
401
 * because the macro sets both cleanups to the same function, which is not
402
 * suitable on Unix (see PR 41119). */
403
APR_DECLARE(apr_status_t) apr_file_inherit_unset(apr_file_t *thefile)
404
0
{
405
0
    if (thefile->flags & APR_FOPEN_NOCLEANUP) {
406
0
        return APR_EINVAL;
407
0
    }
408
0
    if (thefile->flags & APR_INHERIT) {
409
0
        int flags;
410
411
0
        if ((flags = fcntl(thefile->filedes, F_GETFD)) == -1)
412
0
            return errno;
413
414
0
        flags |= FD_CLOEXEC;
415
0
        if (fcntl(thefile->filedes, F_SETFD, flags) == -1)
416
0
            return errno;
417
418
0
        thefile->flags &= ~APR_INHERIT;
419
0
        apr_pool_child_cleanup_set(thefile->pool,
420
0
                                   (void *)thefile,
421
0
                                   apr_unix_file_cleanup,
422
0
                                   apr_unix_child_file_cleanup);
423
0
    }
424
0
    return APR_SUCCESS;
425
0
}
426
427
APR_POOL_IMPLEMENT_ACCESSOR(file)
428
429
APR_DECLARE(apr_status_t) apr_file_link(const char *from_path,
430
                                          const char *to_path)
431
0
{
432
0
    if (link(from_path, to_path) == -1) {
433
0
        return errno;
434
0
    }
435
436
0
    return APR_SUCCESS;
437
0
}