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.h"
18#include "apr_private.h"
19#include "apr_general.h"
20#include "apr_strings.h"
21#include "apr_mmap.h"
22#include "apr_errno.h"
23#include "apr_arch_file_io.h"
24#include "apr_portable.h"
25
26/* System headers required for the mmap library */
27#ifdef BEOS
28#include <kernel/OS.h>
29#endif
30#if APR_HAVE_STRING_H
31#include <string.h>
32#endif
33#if APR_HAVE_STDIO_H
34#include <stdio.h>
35#endif
36#ifdef HAVE_SYS_STAT_H
37#include <sys/stat.h>
38#endif
39#ifdef HAVE_SYS_MMAN_H
40#include <sys/mman.h>
41#endif
42
43#if APR_HAS_MMAP || defined(BEOS)
44
45static apr_status_t mmap_cleanup(void *themmap)
46{
47    apr_mmap_t *mm = themmap;
48    apr_mmap_t *next = APR_RING_NEXT(mm,link);
49    int rv = 0;
50
51    /* we no longer refer to the mmaped region */
52    APR_RING_REMOVE(mm,link);
53    APR_RING_NEXT(mm,link) = NULL;
54    APR_RING_PREV(mm,link) = NULL;
55
56    if (next != mm) {
57        /* more references exist, so we're done */
58        return APR_SUCCESS;
59    }
60
61#ifdef BEOS
62    rv = delete_area(mm->area);
63#else
64    rv = munmap(mm->mm, mm->size);
65#endif
66    mm->mm = (void *)-1;
67
68    if (rv == 0) {
69        return APR_SUCCESS;
70    }
71    return errno;
72}
73
74APR_DECLARE(apr_status_t) apr_mmap_create(apr_mmap_t **new,
75                                          apr_file_t *file, apr_off_t offset,
76                                          apr_size_t size, apr_int32_t flag,
77                                          apr_pool_t *cont)
78{
79    void *mm;
80#ifdef BEOS
81    area_id aid = -1;
82    uint32 pages = 0;
83#else
84    apr_int32_t native_flags = 0;
85#endif
86
87#if APR_HAS_LARGE_FILES && defined(HAVE_MMAP64)
88#define mmap mmap64
89#elif APR_HAS_LARGE_FILES && SIZEOF_OFF_T == 4
90    /* LFS but no mmap64: check for overflow */
91    if ((apr_int64_t)offset + size > INT_MAX)
92        return APR_EINVAL;
93#endif
94
95    if (size == 0)
96        return APR_EINVAL;
97
98    if (file == NULL || file->filedes == -1 || file->buffered)
99        return APR_EBADF;
100    (*new) = (apr_mmap_t *)apr_pcalloc(cont, sizeof(apr_mmap_t));
101
102#ifdef BEOS
103    /* XXX: mmap shouldn't really change the seek offset */
104    apr_file_seek(file, APR_SET, &offset);
105
106    /* There seems to be some strange interactions that mean our area must
107     * be set as READ & WRITE or writev will fail!  Go figure...
108     * So we ignore the value in flags and always ask for both READ and WRITE
109     */
110    pages = (size + B_PAGE_SIZE -1) / B_PAGE_SIZE;
111    aid = create_area("apr_mmap", &mm , B_ANY_ADDRESS, pages * B_PAGE_SIZE,
112        B_NO_LOCK, B_WRITE_AREA|B_READ_AREA);
113
114    if (aid < B_NO_ERROR) {
115        /* we failed to get an area we can use... */
116        *new = NULL;
117        return APR_ENOMEM;
118    }
119
120    if (aid >= B_NO_ERROR)
121        read(file->filedes, mm, size);
122
123    (*new)->area = aid;
124#else
125
126    if (flag & APR_MMAP_WRITE) {
127        native_flags |= PROT_WRITE;
128    }
129    if (flag & APR_MMAP_READ) {
130        native_flags |= PROT_READ;
131    }
132
133    mm = mmap(NULL, size, native_flags, MAP_SHARED, file->filedes, offset);
134
135    if (mm == (void *)-1) {
136        /* we failed to get an mmap'd file... */
137        *new = NULL;
138        return errno;
139    }
140#endif
141
142    (*new)->mm = mm;
143    (*new)->size = size;
144    (*new)->cntxt = cont;
145    APR_RING_ELEM_INIT(*new, link);
146
147    /* register the cleanup... */
148    apr_pool_cleanup_register((*new)->cntxt, (void*)(*new), mmap_cleanup,
149             apr_pool_cleanup_null);
150    return APR_SUCCESS;
151}
152
153APR_DECLARE(apr_status_t) apr_mmap_dup(apr_mmap_t **new_mmap,
154                                       apr_mmap_t *old_mmap,
155                                       apr_pool_t *p)
156{
157    *new_mmap = (apr_mmap_t *)apr_pmemdup(p, old_mmap, sizeof(apr_mmap_t));
158    (*new_mmap)->cntxt = p;
159
160    APR_RING_INSERT_AFTER(old_mmap, *new_mmap, link);
161
162    apr_pool_cleanup_register(p, *new_mmap, mmap_cleanup,
163                              apr_pool_cleanup_null);
164    return APR_SUCCESS;
165}
166
167APR_DECLARE(apr_status_t) apr_mmap_delete(apr_mmap_t *mm)
168{
169    return apr_pool_cleanup_run(mm->cntxt, mm, mmap_cleanup);
170}
171
172#endif
173