source: rtems/cpukit/posix/src/mmap.c @ 18b32d76

5
Last change on this file since 18b32d76 was b2ed712, checked in by Sebastian Huber <sebastian.huber@…>, on 08/25/17 at 08:58:58

Include missing <string.h>

Update #2133.

  • Property mode set to 100644
File size: 8.2 KB
Line 
1/**
2 * @file
3 */
4
5/*
6 * Copyright (c) 2012 Chris Johns (chrisj@rtems.org)
7 * Copyright (c) 2017 Gedare Bloom (gedare@rtems.org)
8 * Copyright (c) 2017 Kevin kirspel (kirspkt@gmail.com)
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15#if HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#include <rtems.h>
20#include <errno.h>
21#include <stdlib.h>
22#include <string.h>
23#include <sys/mman.h>
24#include <sys/stat.h>
25#include <unistd.h>
26
27#include "rtems/libio_.h"
28
29#include <rtems/posix/mmanimpl.h>
30#include <rtems/posix/shmimpl.h>
31
32
33/**
34 * mmap chain of mappings.
35 */
36CHAIN_DEFINE_EMPTY( mmap_mappings );
37
38void *mmap(
39  void *addr, size_t len, int prot, int flags, int fildes, off_t off
40)
41{
42  struct stat     sb;
43  mmap_mapping   *mapping;
44  mmap_mapping   *current_mapping;
45  ssize_t         r;
46  rtems_libio_t  *iop;
47  bool            map_fixed;
48  bool            map_anonymous;
49  bool            map_shared;
50  bool            map_private;
51  int             err;
52
53  map_fixed = (flags & MAP_FIXED) == MAP_FIXED;
54  map_anonymous = (flags & MAP_ANON) == MAP_ANON;
55  map_shared = (flags & MAP_SHARED) == MAP_SHARED;
56  map_private = (flags & MAP_PRIVATE) == MAP_PRIVATE;
57
58  /* Clear errno. */
59  errno = 0;
60  iop = NULL;
61
62  if ( len == 0 ) {
63    errno = EINVAL;
64    return MAP_FAILED;
65  }
66
67  /*
68   * We can provide read, write and execute because the memory in RTEMS does
69   * not normally have protections but we cannot hide access to memory.
70   */
71  if ( prot == PROT_NONE ) {
72    errno = ENOTSUP;
73    return MAP_FAILED;
74  }
75
76  /*
77   * We can not normally provide restriction of write access. Reject any
78   * attempt to map without write permission, since we are not able to
79   * prevent a write from succeeding.
80   */
81  if ( PROT_WRITE != (prot & PROT_WRITE) ) {
82    errno = ENOTSUP;
83    return MAP_FAILED;
84  }
85
86  /*
87   * Anonymous mappings must have file descriptor set to -1 and the offset
88   * set to 0. Shared mappings are not supported with Anonymous mappings at
89   * this time
90   */
91  if ( map_anonymous && (fildes != -1 || off != 0 || map_shared) ) {
92    errno = EINVAL;
93    return MAP_FAILED;
94  }
95
96  /*
97   * If MAP_ANON is declared without MAP_PRIVATE or MAP_SHARED,
98   * force MAP_PRIVATE
99   */
100  if ( map_anonymous && !map_private && !map_shared ) {
101    flags |= MAP_PRIVATE;
102    map_private = true;
103  }
104
105  /* Check for supported flags */
106  if ((flags & ~(MAP_SHARED | MAP_PRIVATE | MAP_FIXED | MAP_ANON)) != 0) {
107    errno = EINVAL;
108    return MAP_FAILED;
109  }
110
111  /* Either MAP_SHARED or MAP_PRIVATE must be defined, but not both */
112  if ( map_shared ) {
113    if ( map_private ) {
114      errno = EINVAL;
115      return MAP_FAILED;
116    }
117  } else if ( !map_private ) {
118    errno = EINVAL;
119    return MAP_FAILED;
120  }
121
122  /* Check for illegal addresses. Watch out for address wrap. */
123  if ( map_fixed ) {
124    if ((uintptr_t)addr & PAGE_MASK) {
125      errno = EINVAL;
126      return MAP_FAILED;
127    }
128    if ( addr == NULL ) {
129      errno = EINVAL;
130      return MAP_FAILED;
131    }
132    if (addr + len < addr) {
133      errno = EINVAL;
134      return MAP_FAILED;
135    }
136  }
137
138  if ( !map_anonymous ) {
139    /*
140     * Get a stat of the file to get the dev + inode number and to make sure the
141     * fd is ok. The normal libio calls cannot be used because we need to return
142     * MAP_FAILED on error and they return -1 directly without coming back to
143     * here.
144     */
145    if ( fstat( fildes, &sb ) < 0 ) {
146      errno = EBADF;
147      return MAP_FAILED;
148    }
149
150    /* fstat ensures we have a good file descriptor. Hold on to iop. */
151    iop = rtems_libio_iop( fildes );
152
153    /* Check the type of file we have and make sure it is supported. */
154    if ( S_ISDIR( sb.st_mode ) || S_ISLNK( sb.st_mode )) {
155      errno = ENODEV;
156      return MAP_FAILED;
157    }
158
159    /* Check to see if the mapping is valid for a regular file. */
160    if ( S_ISREG( sb.st_mode )
161    /* FIXME: Should this be using strict inequality (>) comparisons? It would
162     * be valid to map a region exactly equal to the st_size, e.g. see below. */
163         && (( off >= sb.st_size ) || (( off + len ) >= sb.st_size ))) {
164      errno = EOVERFLOW;
165      return MAP_FAILED;
166    }
167
168    /* Check to see if the mapping is valid for other file/object types. */
169    if ( !S_ISCHR( sb.st_mode ) && sb.st_size < off + len ) {
170      errno = ENXIO;
171      return MAP_FAILED;
172    }
173
174    /* Do not seek on character devices, pipes, sockets, or memory objects. */
175    if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ) {
176      if ( lseek( fildes, off, SEEK_SET ) < 0 ) {
177        return MAP_FAILED;
178      }
179    }
180
181    /* cdevs do not provide private mappings of any kind. */
182    if ( S_ISCHR( sb.st_mode ) && map_private ) {
183      errno = EINVAL;
184      return MAP_FAILED;
185    }
186  }
187
188  /* Create the mapping */
189  mapping = malloc( sizeof( mmap_mapping ));
190  if ( !mapping ) {
191    errno = ENOMEM;
192    return MAP_FAILED;
193  }
194  memset( mapping, 0, sizeof( mmap_mapping ));
195  mapping->len = len;
196  mapping->flags = flags;
197  mapping->iop = iop;
198
199  if ( !map_anonymous ) {
200    /*
201     * HACK: We should have a better generic way to distinguish between
202     * shm objects and other mmap'd files. We need to know at munmap time
203     * if the mapping is to a shared memory object in order to refcnt shms.
204     * We could do this by providing mmap in the file operations if needed.
205     */
206    if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ||
207         S_ISCHR( sb.st_mode ) || S_ISFIFO( sb.st_mode ) ||
208         S_ISSOCK( sb.st_mode ) ) {
209      mapping->is_shared_shm = false;
210    } else {
211      mapping->is_shared_shm = true;
212    }
213  } else {
214    mapping->is_shared_shm = false;
215  }
216
217  if ( map_fixed ) {
218    mapping->addr = addr;
219  } else if ( map_private ) {
220    /* private mappings of shared memory do not need special treatment. */
221    mapping->is_shared_shm = false;
222    posix_memalign( &mapping->addr, PAGE_SIZE, len );
223    if ( !mapping->addr ) {
224      free( mapping );
225      errno = ENOMEM;
226      return MAP_FAILED;
227    }
228  }
229
230  /* MAP_FIXED is not supported for shared memory objects with MAP_SHARED. */
231  if ( map_fixed && mapping->is_shared_shm ) {
232    free( mapping );
233    errno = ENOTSUP;
234    return MAP_FAILED;
235  }
236
237  mmap_mappings_lock_obtain();
238
239  if ( map_fixed ) {
240    rtems_chain_node* node = rtems_chain_first (&mmap_mappings);
241    while ( !rtems_chain_is_tail( &mmap_mappings, node )) {
242      /*
243       * If the map is fixed see if this address is already mapped. At this
244       * point in time if there is an overlap in the mappings we return an
245       * error. POSIX allows us to also return successfully by unmapping
246       * the overlapping prior mappings.
247       */
248      current_mapping = (mmap_mapping*) node;
249      if ( ( addr >= current_mapping->addr ) &&
250           ( addr < ( current_mapping->addr + current_mapping->len )) ) {
251        free( mapping );
252        mmap_mappings_lock_release( );
253        errno = ENXIO;
254        return MAP_FAILED;
255      }
256      node = rtems_chain_next( node );
257    }
258  }
259
260  /* Populate the data */
261  if ( map_private ) {
262    if ( !map_anonymous ) {
263      /*
264       * Use read() for private mappings. This updates atime as needed.
265       * Changes to the underlying object will NOT be reflected in the mapping.
266       * The underlying object can be removed while the mapping exists.
267       */
268      r = read( fildes, mapping->addr, len );
269
270      if ( r != len ) {
271        mmap_mappings_lock_release( );
272        if ( !map_fixed ) {
273          free( mapping->addr );
274        }
275        free( mapping );
276        errno = ENXIO;
277        return MAP_FAILED;
278      }
279    } else if ( !map_fixed ) {
280      memset( mapping->addr, 0, len );
281    }
282  } else if ( map_shared ) {
283    err = (*iop->pathinfo.handlers->mmap_h)(
284        iop, &mapping->addr, len, prot, off );
285    if ( err != 0 ) {
286      mmap_mappings_lock_release( );
287      free( mapping );
288      return MAP_FAILED;
289    }
290  }
291
292  if ( iop != NULL ) {
293    /* add an extra reference to the file associated with fildes that
294     * is not removed by a subsequent close().  This reference shall be removed
295     * when there are no more mappings to the file. */
296    rtems_libio_increment_mapping_refcnt(iop);
297  }
298
299  rtems_chain_append_unprotected( &mmap_mappings, &mapping->node );
300
301  mmap_mappings_lock_release( );
302
303  return mapping->addr;
304}
Note: See TracBrowser for help on using the repository browser.