(root)/
glibc-2.38/
sysdeps/
mach/
hurd/
mremap.c
       1  /* Copyright (C) 2020-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <sys/types.h>
      19  #include <sys/mman.h>
      20  #include <errno.h>
      21  #include <stdarg.h>
      22  #include <hurd.h>
      23  
      24  #include <stdio.h>
      25  
      26  /* Remap pages mapped by the range [ADDR,ADDR+OLD_LEN) to new length
      27     NEW_LEN.  If MREMAP_MAYMOVE is set in FLAGS the returned address
      28     may differ from ADDR.  If MREMAP_FIXED is set in FLAGS the function
      29     takes another parameter which is a fixed address at which the block
      30     resides after a successful call.  */
      31  
      32  void *
      33  __mremap (void *addr, size_t old_len, size_t new_len, int flags, ...)
      34  {
      35    error_t err;
      36    vm_address_t vm_addr = (vm_address_t) addr;
      37    vm_offset_t new_vm_addr = 0;
      38  
      39    vm_address_t begin = vm_addr;
      40    vm_address_t end;
      41    vm_size_t len;
      42    vm_prot_t prot;
      43    vm_prot_t max_prot;
      44    vm_inherit_t inherit;
      45    boolean_t shared;
      46    memory_object_name_t obj;
      47    vm_offset_t offset;
      48  
      49    if ((flags & ~(MREMAP_MAYMOVE | MREMAP_FIXED)) ||
      50        ((flags & MREMAP_FIXED) && !(flags & MREMAP_MAYMOVE)) ||
      51        (old_len == 0 && !(flags & MREMAP_MAYMOVE)))
      52      return (void *) (long int) __hurd_fail (EINVAL);
      53  
      54    if (flags & MREMAP_FIXED)
      55      {
      56        va_list arg;
      57        va_start (arg, flags);
      58        new_vm_addr = (vm_offset_t) va_arg (arg, void *);
      59        va_end (arg);
      60      }
      61  
      62    err = __vm_region (__mach_task_self (),
      63  		     &begin, &len, &prot, &max_prot, &inherit,
      64  		     &shared, &obj, &offset);
      65    if (err)
      66      return (void *) (uintptr_t) __hurd_fail (err);
      67  
      68    if (begin > vm_addr)
      69      {
      70        err = EFAULT;
      71        goto out;
      72      }
      73  
      74    if (begin < vm_addr || (old_len != 0 && old_len != len))
      75      {
      76        err = EINVAL;
      77        goto out;
      78      }
      79  
      80    end = begin + len;
      81  
      82    if ((flags & MREMAP_FIXED) &&
      83        ((new_vm_addr + new_len > vm_addr && new_vm_addr < end)))
      84      {
      85      /* Overlapping is not supported, like in Linux.  */
      86        err = EINVAL;
      87        goto out;
      88      }
      89  
      90    /* FIXME: locked memory.  */
      91  
      92    if (old_len != 0 && !(flags & MREMAP_FIXED))
      93      {
      94        /* A mere change of the existing map.  */
      95  
      96        if (new_len == len)
      97  	{
      98  	  new_vm_addr = vm_addr;
      99  	  goto out;
     100  	}
     101  
     102        if (new_len < len)
     103  	{
     104  	  /* Shrink.  */
     105  	  __mach_port_deallocate (__mach_task_self (), obj);
     106  	  err = __vm_deallocate (__mach_task_self (),
     107  				 begin + new_len, len - new_len);
     108  	  new_vm_addr = vm_addr;
     109  	  goto out;
     110  	}
     111  
     112        /* Try to expand.  */
     113        err = __vm_map (__mach_task_self (),
     114  		      &end, new_len - len, 0, 0,
     115  		      obj, offset + len, 0, prot, max_prot, inherit);
     116        if (!err)
     117  	{
     118  	  /* Ok, that worked.  Now coalesce them.  */
     119  	  new_vm_addr = vm_addr;
     120  
     121  	  /* XXX this is not atomic as it is in unix! */
     122  	  err = __vm_deallocate (__mach_task_self (), begin, new_len);
     123  	  if (err)
     124  	    {
     125  	      __vm_deallocate (__mach_task_self (), end, new_len - len);
     126  	      goto out;
     127  	    }
     128  
     129  	  err = __vm_map (__mach_task_self (),
     130  			  &begin, new_len, 0, 0,
     131  			  obj, offset, 0, prot, max_prot, inherit);
     132  	  if (err)
     133  	    {
     134  	      /* Oops, try to remap before reporting.  */
     135  	      __vm_map (__mach_task_self (),
     136  			&begin, len, 0, 0,
     137  			obj, offset, 0, prot, max_prot, inherit);
     138  	    }
     139  
     140  	  goto out;
     141  	}
     142      }
     143  
     144    if (!(flags & MREMAP_MAYMOVE))
     145      {
     146        /* Can not map here */
     147        err = ENOMEM;
     148        goto out;
     149      }
     150  
     151    err = __vm_map (__mach_task_self (),
     152  		  &new_vm_addr, new_len, 0,
     153  		  new_vm_addr == 0, obj, offset,
     154  		  old_len == 0, prot, max_prot, inherit);
     155  
     156    if (err == KERN_NO_SPACE && (flags & MREMAP_FIXED))
     157      {
     158        /* XXX this is not atomic as it is in unix! */
     159        /* The region is already allocated; deallocate it first.  */
     160        err = __vm_deallocate (__mach_task_self (), new_vm_addr, new_len);
     161        if (! err)
     162  	err = __vm_map (__mach_task_self (),
     163  			&new_vm_addr, new_len, 0,
     164  			0, obj, offset,
     165  			old_len == 0, prot, max_prot, inherit);
     166      }
     167  
     168    if (!err)
     169      /* Alright, can remove old mapping.  */
     170      __vm_deallocate (__mach_task_self (), begin, len);
     171  
     172  out:
     173    __mach_port_deallocate (__mach_task_self (), obj);
     174    if (err)
     175      return (void *) (uintptr_t) __hurd_fail (err);
     176    return (void *) new_vm_addr;
     177  }
     178  
     179  libc_hidden_def (__mremap)
     180  weak_alias (__mremap, mremap)