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_portable.h"
20251875Speter#include "apr_thread_mutex.h"
21251875Speter#include "apr_arch_inherit.h"
22251875Speter
23251875Speterstatic apr_status_t file_dup(apr_file_t **new_file,
24251875Speter                             apr_file_t *old_file, apr_pool_t *p,
25251875Speter                             int which_dup)
26251875Speter{
27251875Speter    int rv;
28251875Speter#ifdef HAVE_DUP3
29251875Speter    int flags = 0;
30251875Speter#endif
31251875Speter
32251875Speter    if (which_dup == 2) {
33251875Speter        if ((*new_file) == NULL) {
34251875Speter            /* We can't dup2 unless we have a valid new_file */
35251875Speter            return APR_EINVAL;
36251875Speter        }
37251875Speter#ifdef HAVE_DUP3
38251875Speter        if (!((*new_file)->flags & (APR_FOPEN_NOCLEANUP|APR_INHERIT)))
39251875Speter            flags |= O_CLOEXEC;
40251875Speter        rv = dup3(old_file->filedes, (*new_file)->filedes, flags);
41251875Speter#else
42251875Speter        rv = dup2(old_file->filedes, (*new_file)->filedes);
43251875Speter        if (!((*new_file)->flags & (APR_FOPEN_NOCLEANUP|APR_INHERIT))) {
44251875Speter            int flags;
45251875Speter
46251875Speter            if (rv == -1)
47251875Speter                return errno;
48251875Speter
49251875Speter            if ((flags = fcntl((*new_file)->filedes, F_GETFD)) == -1)
50251875Speter                return errno;
51251875Speter
52251875Speter            flags |= FD_CLOEXEC;
53251875Speter            if (fcntl((*new_file)->filedes, F_SETFD, flags) == -1)
54251875Speter                return errno;
55251875Speter
56251875Speter        }
57251875Speter#endif
58251875Speter    } else {
59251875Speter        rv = dup(old_file->filedes);
60251875Speter    }
61251875Speter
62251875Speter    if (rv == -1)
63251875Speter        return errno;
64251875Speter
65251875Speter    if (which_dup == 1) {
66251875Speter        (*new_file) = (apr_file_t *)apr_pcalloc(p, sizeof(apr_file_t));
67251875Speter        (*new_file)->pool = p;
68251875Speter        (*new_file)->filedes = rv;
69251875Speter    }
70251875Speter
71251875Speter    (*new_file)->fname = apr_pstrdup(p, old_file->fname);
72251875Speter    (*new_file)->buffered = old_file->buffered;
73251875Speter
74251875Speter    /* If the existing socket in a dup2 is already buffered, we
75251875Speter     * have an existing and valid (hopefully) mutex, so we don't
76251875Speter     * want to create it again as we could leak!
77251875Speter     */
78251875Speter#if APR_HAS_THREADS
79251875Speter    if ((*new_file)->buffered && !(*new_file)->thlock && old_file->thlock) {
80251875Speter        apr_thread_mutex_create(&((*new_file)->thlock),
81251875Speter                                APR_THREAD_MUTEX_DEFAULT, p);
82251875Speter    }
83251875Speter#endif
84251875Speter    /* As above, only create the buffer if we haven't already
85251875Speter     * got one.
86251875Speter     */
87251875Speter    if ((*new_file)->buffered && !(*new_file)->buffer) {
88251875Speter        (*new_file)->buffer = apr_palloc(p, old_file->bufsize);
89251875Speter        (*new_file)->bufsize = old_file->bufsize;
90251875Speter    }
91251875Speter
92251875Speter    /* this is the way dup() works */
93251875Speter    (*new_file)->blocking = old_file->blocking;
94251875Speter
95251875Speter    /* make sure unget behavior is consistent */
96251875Speter    (*new_file)->ungetchar = old_file->ungetchar;
97251875Speter
98251875Speter    /* apr_file_dup2() retains the original cleanup, reflecting
99251875Speter     * the existing inherit and nocleanup flags.  This means,
100251875Speter     * that apr_file_dup2() cannot be called against an apr_file_t
101251875Speter     * already closed with apr_file_close, because the expected
102251875Speter     * cleanup was already killed.
103251875Speter     */
104251875Speter    if (which_dup == 2) {
105251875Speter        return APR_SUCCESS;
106251875Speter    }
107251875Speter
108251875Speter    /* apr_file_dup() retains all old_file flags with the exceptions
109251875Speter     * of APR_INHERIT and APR_FOPEN_NOCLEANUP.
110251875Speter     * The user must call apr_file_inherit_set() on the dupped
111251875Speter     * apr_file_t when desired.
112251875Speter     */
113251875Speter    (*new_file)->flags = old_file->flags
114251875Speter                       & ~(APR_INHERIT | APR_FOPEN_NOCLEANUP);
115251875Speter
116251875Speter    apr_pool_cleanup_register((*new_file)->pool, (void *)(*new_file),
117251875Speter                              apr_unix_file_cleanup,
118251875Speter                              apr_unix_child_file_cleanup);
119251875Speter#ifndef WAITIO_USES_POLL
120251875Speter    /* Start out with no pollset.  apr_wait_for_io_or_timeout() will
121251875Speter     * initialize the pollset if needed.
122251875Speter     */
123251875Speter    (*new_file)->pollset = NULL;
124251875Speter#endif
125251875Speter    return APR_SUCCESS;
126251875Speter}
127251875Speter
128251875SpeterAPR_DECLARE(apr_status_t) apr_file_dup(apr_file_t **new_file,
129251875Speter                                       apr_file_t *old_file, apr_pool_t *p)
130251875Speter{
131251875Speter    return file_dup(new_file, old_file, p, 1);
132251875Speter}
133251875Speter
134251875SpeterAPR_DECLARE(apr_status_t) apr_file_dup2(apr_file_t *new_file,
135251875Speter                                        apr_file_t *old_file, apr_pool_t *p)
136251875Speter{
137251875Speter    return file_dup(&new_file, old_file, p, 2);
138251875Speter}
139251875Speter
140251875SpeterAPR_DECLARE(apr_status_t) apr_file_setaside(apr_file_t **new_file,
141251875Speter                                            apr_file_t *old_file,
142251875Speter                                            apr_pool_t *p)
143251875Speter{
144251875Speter    *new_file = (apr_file_t *)apr_palloc(p, sizeof(apr_file_t));
145251875Speter    memcpy(*new_file, old_file, sizeof(apr_file_t));
146251875Speter    (*new_file)->pool = p;
147251875Speter    if (old_file->buffered) {
148251875Speter        (*new_file)->buffer = apr_palloc(p, old_file->bufsize);
149251875Speter        (*new_file)->bufsize = old_file->bufsize;
150251875Speter        if (old_file->direction == 1) {
151251875Speter            memcpy((*new_file)->buffer, old_file->buffer, old_file->bufpos);
152251875Speter        }
153251875Speter        else {
154251875Speter            memcpy((*new_file)->buffer, old_file->buffer, old_file->dataRead);
155251875Speter        }
156251875Speter#if APR_HAS_THREADS
157251875Speter        if (old_file->thlock) {
158251875Speter            apr_thread_mutex_create(&((*new_file)->thlock),
159251875Speter                                    APR_THREAD_MUTEX_DEFAULT, p);
160251875Speter            apr_thread_mutex_destroy(old_file->thlock);
161251875Speter        }
162251875Speter#endif /* APR_HAS_THREADS */
163251875Speter    }
164251875Speter    if (old_file->fname) {
165251875Speter        (*new_file)->fname = apr_pstrdup(p, old_file->fname);
166251875Speter    }
167251875Speter    if (!(old_file->flags & APR_FOPEN_NOCLEANUP)) {
168251875Speter        apr_pool_cleanup_register(p, (void *)(*new_file),
169251875Speter                                  apr_unix_file_cleanup,
170251875Speter                                  ((*new_file)->flags & APR_INHERIT)
171251875Speter                                     ? apr_pool_cleanup_null
172251875Speter                                     : apr_unix_child_file_cleanup);
173251875Speter    }
174251875Speter
175251875Speter    old_file->filedes = -1;
176251875Speter    apr_pool_cleanup_kill(old_file->pool, (void *)old_file,
177251875Speter                          apr_unix_file_cleanup);
178251875Speter#ifndef WAITIO_USES_POLL
179251875Speter    (*new_file)->pollset = NULL;
180251875Speter#endif
181251875Speter    return APR_SUCCESS;
182251875Speter}
183