1251876Speter/* Licensed to the Apache Software Foundation (ASF) under one or more 2251876Speter * contributor license agreements. See the NOTICE file distributed with 3251876Speter * this work for additional information regarding copyright ownership. 4251876Speter * The ASF licenses this file to You under the Apache License, Version 2.0 5251876Speter * (the "License"); you may not use this file except in compliance with 6251876Speter * the License. You may obtain a copy of the License at 7251876Speter * 8251876Speter * http://www.apache.org/licenses/LICENSE-2.0 9251876Speter * 10251876Speter * Unless required by applicable law or agreed to in writing, software 11251876Speter * distributed under the License is distributed on an "AS IS" BASIS, 12251876Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13251876Speter * See the License for the specific language governing permissions and 14251876Speter * limitations under the License. 15251876Speter */ 16251876Speter 17251876Speter#include "apr.h" 18251876Speter#include "apr_general.h" 19251876Speter#include "apr_file_io.h" 20251876Speter#include "apr_buckets.h" 21251876Speter 22251876Speter#if APR_HAS_MMAP 23251876Speter#include "apr_mmap.h" 24251876Speter 25251876Speter/* mmap support for static files based on ideas from John Heidemann's 26251876Speter * patch against 1.0.5. See 27251876Speter * <http://www.isi.edu/~johnh/SOFTWARE/APACHE/index.html>. 28251876Speter */ 29251876Speter 30251876Speter#endif /* APR_HAS_MMAP */ 31251876Speter 32251876Speterstatic void file_bucket_destroy(void *data) 33251876Speter{ 34251876Speter apr_bucket_file *f = data; 35251876Speter 36251876Speter if (apr_bucket_shared_destroy(f)) { 37251876Speter /* no need to close the file here; it will get 38251876Speter * done automatically when the pool gets cleaned up */ 39251876Speter apr_bucket_free(f); 40251876Speter } 41251876Speter} 42251876Speter 43251876Speter#if APR_HAS_MMAP 44251876Speterstatic int file_make_mmap(apr_bucket *e, apr_size_t filelength, 45251876Speter apr_off_t fileoffset, apr_pool_t *p) 46251876Speter{ 47251876Speter apr_bucket_file *a = e->data; 48251876Speter apr_mmap_t *mm; 49251876Speter 50251876Speter if (!a->can_mmap) { 51251876Speter return 0; 52251876Speter } 53251876Speter 54251876Speter if (filelength > APR_MMAP_LIMIT) { 55251876Speter if (apr_mmap_create(&mm, a->fd, fileoffset, APR_MMAP_LIMIT, 56251876Speter APR_MMAP_READ, p) != APR_SUCCESS) 57251876Speter { 58251876Speter return 0; 59251876Speter } 60251876Speter apr_bucket_split(e, APR_MMAP_LIMIT); 61251876Speter filelength = APR_MMAP_LIMIT; 62251876Speter } 63251876Speter else if ((filelength < APR_MMAP_THRESHOLD) || 64251876Speter (apr_mmap_create(&mm, a->fd, fileoffset, filelength, 65251876Speter APR_MMAP_READ, p) != APR_SUCCESS)) 66251876Speter { 67251876Speter return 0; 68251876Speter } 69251876Speter apr_bucket_mmap_make(e, mm, 0, filelength); 70251876Speter file_bucket_destroy(a); 71251876Speter return 1; 72251876Speter} 73251876Speter#endif 74251876Speter 75251876Speterstatic apr_status_t file_bucket_read(apr_bucket *e, const char **str, 76251876Speter apr_size_t *len, apr_read_type_e block) 77251876Speter{ 78251876Speter apr_bucket_file *a = e->data; 79251876Speter apr_file_t *f = a->fd; 80251876Speter apr_bucket *b = NULL; 81251876Speter char *buf; 82251876Speter apr_status_t rv; 83251876Speter apr_size_t filelength = e->length; /* bytes remaining in file past offset */ 84251876Speter apr_off_t fileoffset = e->start; 85251876Speter#if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES 86251876Speter apr_int32_t flags; 87251876Speter#endif 88251876Speter 89251876Speter#if APR_HAS_MMAP 90251876Speter if (file_make_mmap(e, filelength, fileoffset, a->readpool)) { 91251876Speter return apr_bucket_read(e, str, len, block); 92251876Speter } 93251876Speter#endif 94251876Speter 95251876Speter#if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES 96251876Speter if ((flags = apr_file_flags_get(f)) & APR_FOPEN_XTHREAD) { 97251876Speter /* this file descriptor is shared across multiple threads and 98251876Speter * this OS doesn't support that natively, so as a workaround 99251876Speter * we must reopen the file into a->readpool */ 100251876Speter const char *fname; 101251876Speter apr_file_name_get(&fname, f); 102251876Speter 103251876Speter rv = apr_file_open(&f, fname, (flags & ~APR_FOPEN_XTHREAD), 0, a->readpool); 104251876Speter if (rv != APR_SUCCESS) 105251876Speter return rv; 106251876Speter 107251876Speter a->fd = f; 108251876Speter } 109251876Speter#endif 110251876Speter 111251876Speter *str = NULL; /* in case we die prematurely */ 112362181Sdim *len = (filelength > a->read_size) ? a->read_size : filelength; 113251876Speter buf = apr_bucket_alloc(*len, e->list); 114251876Speter 115251876Speter /* Handle offset ... */ 116251876Speter rv = apr_file_seek(f, APR_SET, &fileoffset); 117251876Speter if (rv != APR_SUCCESS) { 118251876Speter apr_bucket_free(buf); 119251876Speter return rv; 120251876Speter } 121251876Speter rv = apr_file_read(f, buf, len); 122251876Speter if (rv != APR_SUCCESS && rv != APR_EOF) { 123251876Speter apr_bucket_free(buf); 124251876Speter return rv; 125251876Speter } 126251876Speter filelength -= *len; 127251876Speter /* 128251876Speter * Change the current bucket to refer to what we read, 129251876Speter * even if we read nothing because we hit EOF. 130251876Speter */ 131251876Speter apr_bucket_heap_make(e, buf, *len, apr_bucket_free); 132251876Speter 133251876Speter /* If we have more to read from the file, then create another bucket */ 134251876Speter if (filelength > 0 && rv != APR_EOF) { 135251876Speter /* for efficiency, we can just build a new apr_bucket struct 136251876Speter * to wrap around the existing file bucket */ 137251876Speter b = apr_bucket_alloc(sizeof(*b), e->list); 138251876Speter b->start = fileoffset + (*len); 139251876Speter b->length = filelength; 140251876Speter b->data = a; 141251876Speter b->type = &apr_bucket_type_file; 142251876Speter b->free = apr_bucket_free; 143251876Speter b->list = e->list; 144251876Speter APR_BUCKET_INSERT_AFTER(e, b); 145251876Speter } 146251876Speter else { 147251876Speter file_bucket_destroy(a); 148251876Speter } 149251876Speter 150251876Speter *str = buf; 151251876Speter return rv; 152251876Speter} 153251876Speter 154251876SpeterAPU_DECLARE(apr_bucket *) apr_bucket_file_make(apr_bucket *b, apr_file_t *fd, 155251876Speter apr_off_t offset, 156251876Speter apr_size_t len, apr_pool_t *p) 157251876Speter{ 158251876Speter apr_bucket_file *f; 159251876Speter 160251876Speter f = apr_bucket_alloc(sizeof(*f), b->list); 161251876Speter f->fd = fd; 162251876Speter f->readpool = p; 163251876Speter#if APR_HAS_MMAP 164251876Speter f->can_mmap = 1; 165251876Speter#endif 166362181Sdim f->read_size = APR_BUCKET_BUFF_SIZE; 167251876Speter 168251876Speter b = apr_bucket_shared_make(b, f, offset, len); 169251876Speter b->type = &apr_bucket_type_file; 170251876Speter 171251876Speter return b; 172251876Speter} 173251876Speter 174251876SpeterAPU_DECLARE(apr_bucket *) apr_bucket_file_create(apr_file_t *fd, 175251876Speter apr_off_t offset, 176251876Speter apr_size_t len, apr_pool_t *p, 177251876Speter apr_bucket_alloc_t *list) 178251876Speter{ 179251876Speter apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); 180251876Speter 181251876Speter APR_BUCKET_INIT(b); 182251876Speter b->free = apr_bucket_free; 183251876Speter b->list = list; 184251876Speter return apr_bucket_file_make(b, fd, offset, len, p); 185251876Speter} 186251876Speter 187251876SpeterAPU_DECLARE(apr_status_t) apr_bucket_file_enable_mmap(apr_bucket *e, 188251876Speter int enabled) 189251876Speter{ 190251876Speter#if APR_HAS_MMAP 191251876Speter apr_bucket_file *a = e->data; 192251876Speter a->can_mmap = enabled; 193251876Speter return APR_SUCCESS; 194251876Speter#else 195251876Speter return APR_ENOTIMPL; 196251876Speter#endif /* APR_HAS_MMAP */ 197251876Speter} 198251876Speter 199362181SdimAPU_DECLARE(apr_status_t) apr_bucket_file_set_buf_size(apr_bucket *e, 200362181Sdim apr_size_t size) 201362181Sdim{ 202362181Sdim apr_bucket_file *a = e->data; 203251876Speter 204362181Sdim if (size <= APR_BUCKET_BUFF_SIZE) { 205362181Sdim a->read_size = APR_BUCKET_BUFF_SIZE; 206362181Sdim } 207362181Sdim else { 208362181Sdim apr_size_t floor = apr_bucket_alloc_aligned_floor(e->list, size); 209362181Sdim a->read_size = (size < floor) ? size : floor; 210362181Sdim } 211362181Sdim 212362181Sdim return APR_SUCCESS; 213362181Sdim} 214362181Sdim 215251876Speterstatic apr_status_t file_bucket_setaside(apr_bucket *data, apr_pool_t *reqpool) 216251876Speter{ 217251876Speter apr_bucket_file *a = data->data; 218251876Speter apr_file_t *fd = NULL; 219251876Speter apr_file_t *f = a->fd; 220251876Speter apr_pool_t *curpool = apr_file_pool_get(f); 221251876Speter 222251876Speter if (apr_pool_is_ancestor(curpool, reqpool)) { 223251876Speter return APR_SUCCESS; 224251876Speter } 225251876Speter 226251876Speter if (!apr_pool_is_ancestor(a->readpool, reqpool)) { 227251876Speter a->readpool = reqpool; 228251876Speter } 229251876Speter 230251876Speter apr_file_setaside(&fd, f, reqpool); 231251876Speter a->fd = fd; 232251876Speter return APR_SUCCESS; 233251876Speter} 234251876Speter 235251876SpeterAPU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_file = { 236251876Speter "FILE", 5, APR_BUCKET_DATA, 237251876Speter file_bucket_destroy, 238251876Speter file_bucket_read, 239251876Speter file_bucket_setaside, 240251876Speter apr_bucket_shared_split, 241251876Speter apr_bucket_shared_copy 242251876Speter}; 243