source: rtems/cpukit/posix/src/mmap.c @ a330c5d

5
Last change on this file since a330c5d was a330c5d, checked in by Gedare Bloom <gedare@…>, on 05/16/17 at 15:37:27

posix: clarify expression with parentheses

Close #3010.

  • Property mode set to 100644
File size: 9.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 *
9 * The license and distribution terms for this file may be
10 * found in the file LICENSE in this distribution or at
11 * http://www.rtems.org/license/LICENSE.
12 */
13
14#if HAVE_CONFIG_H
15#include "config.h"
16#endif
17
18#include <rtems.h>
19#include <errno.h>
20#include <stdlib.h>
21#include <sys/mman.h>
22#include <sys/stat.h>
23#include <unistd.h>
24
25#include "rtems/libio_.h"
26
27#include <rtems/posix/mmanimpl.h>
28#include <rtems/posix/shmimpl.h>
29
30#define RTEMS_MUTEX_ATTRIBS \
31  (RTEMS_PRIORITY | RTEMS_SIMPLE_BINARY_SEMAPHORE | \
32   RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL)
33
34/**
35 * Mmap chain of mappings.
36 */
37rtems_chain_control mmap_mappings;
38
39/**
40 * The id of the MMAP lock.
41 */
42static rtems_id mmap_mappings_lock;
43
44/**
45 * Create the lock.
46 */
47static
48bool mmap_mappings_lock_create(
49  void
50)
51{
52  /*
53   * Lock the mapping table. We only create a lock if a call is made. First we
54   * test if a mapping lock is present. If one is present we lock it. If not
55   * the libio lock is locked and we then test the mapping lock again. If not
56   * present we create the mapping lock then release libio lock.
57   */
58  /* FIXME: double-checked locking anti-pattern. */
59  if ( mmap_mappings_lock == 0 ) {
60    rtems_status_code sc = RTEMS_SUCCESSFUL;
61    rtems_chain_initialize_empty( &mmap_mappings );
62    rtems_semaphore_obtain( rtems_libio_semaphore,
63                            RTEMS_WAIT, RTEMS_NO_TIMEOUT );
64    /* FIXME: account for semaphore in confdefs, or maybe just use the
65     * rtems_libio_semaphore? */
66    if ( mmap_mappings_lock == 0 )
67      sc = rtems_semaphore_create( rtems_build_name( 'M', 'M', 'A', 'P' ),
68                                   1,
69                                   RTEMS_MUTEX_ATTRIBS,
70                                   RTEMS_NO_PRIORITY,
71                                   &mmap_mappings_lock );
72    rtems_semaphore_release( rtems_libio_semaphore );
73    if ( sc != RTEMS_SUCCESSFUL ) {
74      errno = EINVAL;
75      return false;
76    }
77  }
78  return true;
79}
80
81bool mmap_mappings_lock_obtain(
82  void
83)
84{
85  if ( mmap_mappings_lock_create( ) ) {
86    rtems_status_code sc;
87    sc = rtems_semaphore_obtain( mmap_mappings_lock,
88                                 RTEMS_WAIT, RTEMS_NO_TIMEOUT );
89    if ( sc != RTEMS_SUCCESSFUL ) {
90      errno = EINVAL;
91      return false;
92    }
93  }
94  return true;
95}
96
97bool mmap_mappings_lock_release(
98  void
99)
100{
101  rtems_status_code sc;
102  sc = rtems_semaphore_release( mmap_mappings_lock );
103  if (( sc != RTEMS_SUCCESSFUL ) && ( errno == 0 )) {
104    errno = EINVAL;
105    return false;
106  }
107  return true;
108}
109
110/* Helper function only gets called for mmap mappings of shared memory objects
111 * with the MAP_SHARED flag.
112 */
113static void *shm_mmap( rtems_libio_t *iop, size_t len, int prot, off_t off)
114{
115  POSIX_Shm_Control *shm = iop_to_shm( iop );
116  void *m;
117
118  _Objects_Allocator_lock();
119
120  m = (*shm->shm_object.ops->object_mmap)( &shm->shm_object, len, prot, off);
121  if ( m != NULL ) {
122    /* Keep a reference in the shared memory to prevent its removal. */
123    ++shm->reference_count;
124
125    /* Update atime */
126    _POSIX_Shm_Update_atime(shm);
127  }
128
129  _Objects_Allocator_unlock();
130
131  return m;
132}
133
134void *mmap(
135  void *addr, size_t len, int prot, int flags, int fildes, off_t off
136)
137{
138  struct stat     sb;
139  mmap_mapping   *mapping;
140  ssize_t         r;
141  rtems_libio_t  *iop;
142  bool            map_fixed;
143
144  map_fixed = (flags & MAP_FIXED) == MAP_FIXED;
145
146  /* Clear errno. */
147  errno = 0;
148
149  /*
150   * Get a stat of the file to get the dev + inode number and to make sure the
151   * fd is ok. The normal libio calls cannot be used because we need to return
152   * MAP_FAILED on error and they return -1 directly without coming back to
153   * here.
154   */
155  if ( fstat( fildes, &sb ) < 0 ) {
156    errno = EBADF;
157    return MAP_FAILED;
158  }
159
160  /* fstat ensures we have a good file descriptor. Hold on to iop. */
161  iop = rtems_libio_iop( fildes );
162
163  if ( len == 0 ) {
164    errno = EINVAL;
165    return MAP_FAILED;
166  }
167
168  /* Check the type of file we have and make sure it is supported. */
169  if ( S_ISDIR( sb.st_mode ) || S_ISLNK( sb.st_mode )) {
170    errno = ENODEV;
171    return MAP_FAILED;
172  }
173
174  /*
175   * We can provide read, write and execute because the memory in RTEMS does
176   * not normally have protections but we cannot hide access to memory.
177   */
178  if ( prot == PROT_NONE ) {
179    errno = ENOTSUP;
180    return MAP_FAILED;
181  }
182
183  /* Either MAP_SHARED or MAP_PRIVATE must be defined, but not both */
184  if ( (flags & MAP_SHARED) == MAP_SHARED ) {
185    if ( (flags & MAP_PRIVATE) == MAP_PRIVATE ) {
186      errno = EINVAL;
187      return MAP_FAILED;
188    }
189  } else if ( (flags & MAP_PRIVATE) != MAP_PRIVATE ) {
190    errno = EINVAL;
191    return MAP_FAILED;
192  }
193
194  /*
195   * We can not normally provide restriction of write access. Reject any
196   * attempt to map without write permission, since we are not able to
197   * prevent a write from succeeding.
198   */
199  if ( PROT_WRITE != (prot & PROT_WRITE) ) {
200    errno = ENOTSUP;
201    return MAP_FAILED;
202  }
203
204  /* Check to see if the mapping is valid for a regular file. */
205  if ( S_ISREG( sb.st_mode )
206  /* FIXME: Should this be using strict inequality (>) comparisons? It would
207   * be valid to map a region exactly equal to the st_size, e.g. see below. */
208       && (( off >= sb.st_size ) || (( off + len ) >= sb.st_size ))) {
209    errno = EOVERFLOW;
210    return MAP_FAILED;
211  }
212
213  /*
214   * Check to see if the mapping is valid for other file/object types.
215   * Does this satisfy for devices?
216   */
217  if ( sb.st_size < off + len ) {
218    errno = ENXIO;
219    return MAP_FAILED;
220  }
221
222  /* Do not seek on character devices, pipes, sockets, or memory objects. */
223  if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ) {
224    if ( lseek( fildes, off, SEEK_SET ) < 0 ) {
225      return MAP_FAILED;
226    }
227  }
228
229  /* Create the mapping */
230  mapping = malloc( sizeof( mmap_mapping ));
231  if ( !mapping ) {
232    errno = ENOMEM;
233    return NULL;
234  }
235  memset( mapping, 0, sizeof( mmap_mapping ));
236  mapping->len = len;
237  mapping->flags = flags;
238  mapping->iop = iop;
239
240  /*
241   * HACK: We should have a better generic way to distinguish between
242   * shm objects and other mmap'd files. We need to know at munmap time
243   * if the mapping is to a shared memory object in order to refcnt shms.
244   * We could do this by providing mmap in the file operations if needed.
245   */
246  if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ||
247       S_ISCHR( sb.st_mode ) || S_ISFIFO( sb.st_mode ) ||
248       S_ISSOCK( sb.st_mode ) ) {
249    mapping->is_shared_shm = false;
250  } else {
251    mapping->is_shared_shm = true;
252  }
253
254  /*
255   * MAP_SHARED currently is only supported for shared memory objects.
256   */
257  if ( (MAP_SHARED == (flags & MAP_SHARED)) && (mapping->is_shared_shm == false) ) {
258    free( mapping );
259    errno = ENOTSUP;
260    return MAP_FAILED;
261  }
262
263  if ( map_fixed ) {
264    mapping->addr = addr;
265  } else if ( MAP_PRIVATE == (flags & MAP_PRIVATE) ) {
266    /* private mappings of shared memory do not need special treatment. */
267    mapping->is_shared_shm = false;
268    mapping->addr = malloc( len );
269    if ( !mapping->addr ) {
270      free( mapping );
271      errno = ENOMEM;
272      return MAP_FAILED;
273    }
274  }
275
276  /* MAP_FIXED is not supported for shared memory objects with MAP_SHARED. */
277  if ( map_fixed && mapping->is_shared_shm ) {
278    free( mapping );
279    errno = ENOTSUP;
280    return MAP_FAILED;
281  }
282
283  /* Lock access to mmap_mappings. Sets errno on failure. */
284  if ( !mmap_mappings_lock_obtain( ) )
285    return MAP_FAILED;
286
287  if ( map_fixed ) {
288    rtems_chain_node* node = rtems_chain_first (&mmap_mappings);
289    while ( !rtems_chain_is_tail( &mmap_mappings, node )) {
290      /*
291       * If the map is fixed see if this address is already mapped. At this
292       * point in time if there is an overlap in the mappings we return an
293       * error. POSIX allows us to also return successfully by unmapping
294       * the overlapping prior mappings.
295       */
296      mapping = (mmap_mapping*) node;
297      if ( ( addr >= mapping->addr ) &&
298           ( addr < ( mapping->addr + mapping->len )) ) {
299        free( mapping );
300        mmap_mappings_lock_release( );
301        errno = ENXIO;
302        return MAP_FAILED;
303      }
304      node = rtems_chain_next( node );
305    }
306  }
307
308  /* Populate the data */
309  if ( MAP_PRIVATE == (flags & MAP_PRIVATE) ) {
310    /*
311     * Use read() for private mappings. This updates atime as needed.
312     * Changes to the underlying object will NOT be reflected in the mapping.
313     * The underlying object can be removed while the mapping exists.
314     */
315    r = read( fildes, mapping->addr, len );
316
317    if ( r != len ) {
318      mmap_mappings_lock_release( );
319      if ( !map_fixed ) {
320        free( mapping->addr );
321      }
322      free( mapping );
323      errno = ENXIO;
324      return MAP_FAILED;
325    }
326  } else if ( MAP_SHARED == (flags & MAP_SHARED) ) {
327    /* Currently only shm objects can be MAP_SHARED. */
328    mapping->addr = shm_mmap(iop, len, prot, off);
329  }
330
331  /* add an extra reference to the file associated with fildes that
332   * is not removed by a subsequent close().  This reference shall be removed
333   * when there are no more mappings to the file. */
334  rtems_libio_increment_mapping_refcnt(iop);
335
336  rtems_chain_append( &mmap_mappings, &mapping->node );
337
338  mmap_mappings_lock_release( );
339
340  return mapping->addr;
341}
Note: See TracBrowser for help on using the repository browser.