filedup.c revision 269847
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 23static apr_status_t file_dup(apr_file_t **new_file, 24 apr_file_t *old_file, apr_pool_t *p, 25 int which_dup) 26{ 27 int rv; 28#ifdef HAVE_DUP3 29 int flags = 0; 30#endif 31 32 if (which_dup == 2) { 33 if ((*new_file) == NULL) { 34 /* We can't dup2 unless we have a valid new_file */ 35 return APR_EINVAL; 36 } 37#ifdef HAVE_DUP3 38 if (!((*new_file)->flags & (APR_FOPEN_NOCLEANUP|APR_INHERIT))) 39 flags |= O_CLOEXEC; 40 rv = dup3(old_file->filedes, (*new_file)->filedes, flags); 41#else 42 rv = dup2(old_file->filedes, (*new_file)->filedes); 43 if (!((*new_file)->flags & (APR_FOPEN_NOCLEANUP|APR_INHERIT))) { 44 int flags; 45 46 if (rv == -1) 47 return errno; 48 49 if ((flags = fcntl((*new_file)->filedes, F_GETFD)) == -1) 50 return errno; 51 52 flags |= FD_CLOEXEC; 53 if (fcntl((*new_file)->filedes, F_SETFD, flags) == -1) 54 return errno; 55 56 } 57#endif 58 } else { 59 rv = dup(old_file->filedes); 60 } 61 62 if (rv == -1) 63 return errno; 64 65 if (which_dup == 1) { 66 (*new_file) = (apr_file_t *)apr_pcalloc(p, sizeof(apr_file_t)); 67 (*new_file)->pool = p; 68 (*new_file)->filedes = rv; 69 } 70 71 (*new_file)->fname = apr_pstrdup(p, old_file->fname); 72 (*new_file)->buffered = old_file->buffered; 73 74 /* If the existing socket in a dup2 is already buffered, we 75 * have an existing and valid (hopefully) mutex, so we don't 76 * want to create it again as we could leak! 77 */ 78#if APR_HAS_THREADS 79 if ((*new_file)->buffered && !(*new_file)->thlock && old_file->thlock) { 80 apr_thread_mutex_create(&((*new_file)->thlock), 81 APR_THREAD_MUTEX_DEFAULT, p); 82 } 83#endif 84 /* As above, only create the buffer if we haven't already 85 * got one. 86 */ 87 if ((*new_file)->buffered && !(*new_file)->buffer) { 88 (*new_file)->buffer = apr_palloc(p, old_file->bufsize); 89 (*new_file)->bufsize = old_file->bufsize; 90 } 91 92 /* this is the way dup() works */ 93 (*new_file)->blocking = old_file->blocking; 94 95 /* make sure unget behavior is consistent */ 96 (*new_file)->ungetchar = old_file->ungetchar; 97 98 /* apr_file_dup2() retains the original cleanup, reflecting 99 * the existing inherit and nocleanup flags. This means, 100 * that apr_file_dup2() cannot be called against an apr_file_t 101 * already closed with apr_file_close, because the expected 102 * cleanup was already killed. 103 */ 104 if (which_dup == 2) { 105 return APR_SUCCESS; 106 } 107 108 /* apr_file_dup() retains all old_file flags with the exceptions 109 * of APR_INHERIT and APR_FOPEN_NOCLEANUP. 110 * The user must call apr_file_inherit_set() on the dupped 111 * apr_file_t when desired. 112 */ 113 (*new_file)->flags = old_file->flags 114 & ~(APR_INHERIT | APR_FOPEN_NOCLEANUP); 115 116 apr_pool_cleanup_register((*new_file)->pool, (void *)(*new_file), 117 apr_unix_file_cleanup, 118 apr_unix_child_file_cleanup); 119#ifndef WAITIO_USES_POLL 120 /* Start out with no pollset. apr_wait_for_io_or_timeout() will 121 * initialize the pollset if needed. 122 */ 123 (*new_file)->pollset = NULL; 124#endif 125 return APR_SUCCESS; 126} 127 128APR_DECLARE(apr_status_t) apr_file_dup(apr_file_t **new_file, 129 apr_file_t *old_file, apr_pool_t *p) 130{ 131 return file_dup(new_file, old_file, p, 1); 132} 133 134APR_DECLARE(apr_status_t) apr_file_dup2(apr_file_t *new_file, 135 apr_file_t *old_file, apr_pool_t *p) 136{ 137 return file_dup(&new_file, old_file, p, 2); 138} 139 140APR_DECLARE(apr_status_t) apr_file_setaside(apr_file_t **new_file, 141 apr_file_t *old_file, 142 apr_pool_t *p) 143{ 144 *new_file = (apr_file_t *)apr_pmemdup(p, old_file, sizeof(apr_file_t)); 145 (*new_file)->pool = p; 146 if (old_file->buffered) { 147 (*new_file)->buffer = apr_palloc(p, old_file->bufsize); 148 (*new_file)->bufsize = old_file->bufsize; 149 if (old_file->direction == 1) { 150 memcpy((*new_file)->buffer, old_file->buffer, old_file->bufpos); 151 } 152 else { 153 memcpy((*new_file)->buffer, old_file->buffer, old_file->dataRead); 154 } 155#if APR_HAS_THREADS 156 if (old_file->thlock) { 157 apr_thread_mutex_create(&((*new_file)->thlock), 158 APR_THREAD_MUTEX_DEFAULT, p); 159 apr_thread_mutex_destroy(old_file->thlock); 160 } 161#endif /* APR_HAS_THREADS */ 162 } 163 if (old_file->fname) { 164 (*new_file)->fname = apr_pstrdup(p, old_file->fname); 165 } 166 if (!(old_file->flags & APR_FOPEN_NOCLEANUP)) { 167 apr_pool_cleanup_register(p, (void *)(*new_file), 168 apr_unix_file_cleanup, 169 ((*new_file)->flags & APR_INHERIT) 170 ? apr_pool_cleanup_null 171 : apr_unix_child_file_cleanup); 172 } 173 174 old_file->filedes = -1; 175 apr_pool_cleanup_kill(old_file->pool, (void *)old_file, 176 apr_unix_file_cleanup); 177#ifndef WAITIO_USES_POLL 178 (*new_file)->pollset = NULL; 179#endif 180 return APR_SUCCESS; 181} 182