(root)/
glibc-2.38/
sysdeps/
pthread/
sem_open.c
       1  /* Copyright (C) 2002-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 <fcntl.h>
      19  #include <semaphore.h>
      20  #include <stdarg.h>
      21  #include <unistd.h>
      22  #include <sys/mman.h>
      23  #include "semaphoreP.h"
      24  #include <shm-directory.h>
      25  #include <sem_routines.h>
      26  #include <futex-internal.h>
      27  #include <libc-lock.h>
      28  
      29  #if !PTHREAD_IN_LIBC
      30  /* The private names are not exported from libc.  */
      31  # define __link link
      32  # define __unlink unlink
      33  #endif
      34  
      35  sem_t *
      36  __sem_open (const char *name, int oflag, ...)
      37  {
      38    int fd;
      39    int open_flags;
      40    sem_t *result;
      41  
      42    /* Check that shared futexes are supported.  */
      43    int err = futex_supports_pshared (PTHREAD_PROCESS_SHARED);
      44    if (err != 0)
      45      {
      46        __set_errno (err);
      47        return SEM_FAILED;
      48      }
      49  
      50    struct shmdir_name dirname;
      51    int ret = __shm_get_name (&dirname, name, true);
      52    if (ret != 0)
      53      {
      54        __set_errno (ret);
      55        return SEM_FAILED;
      56      }
      57  
      58    /* Disable asynchronous cancellation.  */
      59  #ifdef __libc_ptf_call
      60    int state;
      61    __libc_ptf_call (__pthread_setcancelstate,
      62                     (PTHREAD_CANCEL_DISABLE, &state), 0);
      63  #endif
      64  
      65    /* If the semaphore object has to exist simply open it.  */
      66    if ((oflag & O_CREAT) == 0 || (oflag & O_EXCL) == 0)
      67      {
      68        open_flags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
      69        open_flags |= (oflag & ~(O_CREAT|O_ACCMODE));
      70      try_again:
      71        fd = __open (dirname.name, open_flags);
      72  
      73        if (fd == -1)
      74  	{
      75  	  /* If we are supposed to create the file try this next.  */
      76  	  if ((oflag & O_CREAT) != 0 && errno == ENOENT)
      77  	    goto try_create;
      78  
      79  	  /* Return.  errno is already set.  */
      80  	}
      81        else
      82  	/* Check whether we already have this semaphore mapped and
      83  	   create one if necessary.  */
      84  	result = __sem_check_add_mapping (name, fd, SEM_FAILED);
      85      }
      86    else
      87      {
      88        /* We have to open a temporary file first since it must have the
      89  	 correct form before we can start using it.  */
      90        mode_t mode;
      91        unsigned int value;
      92        va_list ap;
      93  
      94      try_create:
      95        va_start (ap, oflag);
      96  
      97        mode = va_arg (ap, mode_t);
      98        value = va_arg (ap, unsigned int);
      99  
     100        va_end (ap);
     101  
     102        if (value > SEM_VALUE_MAX)
     103  	{
     104  	  __set_errno (EINVAL);
     105  	  result = SEM_FAILED;
     106  	  goto out;
     107  	}
     108  
     109        /* Create the initial file content.  */
     110        union
     111        {
     112  	sem_t initsem;
     113  	struct new_sem newsem;
     114        } sem;
     115  
     116        __new_sem_open_init (&sem.newsem, value);
     117  
     118        /* Initialize the remaining bytes as well.  */
     119        memset ((char *) &sem.initsem + sizeof (struct new_sem), '\0',
     120  	      sizeof (sem_t) - sizeof (struct new_sem));
     121  
     122        char tmpfname[] = SHMDIR "sem.XXXXXX";
     123        int retries = 0;
     124  #define NRETRIES 50
     125        while (1)
     126  	{
     127  	  /* We really want to use mktemp here.  We cannot use mkstemp
     128  	     since the file must be opened with a specific mode.  The
     129  	     mode cannot later be set since then we cannot apply the
     130  	     file create mask.  */
     131  	  if (__mktemp (tmpfname) == NULL)
     132  	    {
     133  	      result = SEM_FAILED;
     134  	      goto out;
     135  	    }
     136  
     137  	  /* Open the file.  Make sure we do not overwrite anything.  */
     138  	  open_flags = O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC;
     139  	  fd = __open (tmpfname, open_flags, mode);
     140  	  if (fd == -1)
     141  	    {
     142  	      if (errno == EEXIST)
     143  		{
     144  		  if (++retries < NRETRIES)
     145  		    {
     146  		      /* Restore the six placeholder bytes before the
     147  			 null terminator before the next attempt.  */
     148  		      memcpy (tmpfname + sizeof (tmpfname) - 7, "XXXXXX", 6);
     149  		      continue;
     150  		    }
     151  
     152  		  __set_errno (EAGAIN);
     153  		}
     154  
     155  	      result = SEM_FAILED;
     156  	      goto out;
     157  	    }
     158  
     159  	  /* We got a file.  */
     160  	  break;
     161  	}
     162  
     163        if (TEMP_FAILURE_RETRY (write (fd, &sem.initsem, sizeof (sem_t)))
     164  	  == sizeof (sem_t)
     165  	  /* Map the sem_t structure from the file.  */
     166  	  && (result = (sem_t *) __mmap (NULL, sizeof (sem_t),
     167  					 PROT_READ | PROT_WRITE, MAP_SHARED,
     168  					 fd, 0)) != MAP_FAILED)
     169  	{
     170  	  /* Create the file.  Don't overwrite an existing file.  */
     171  	  if (__link (tmpfname, dirname.name) != 0)
     172  	    {
     173  	      /* Undo the mapping.  */
     174  	      __munmap (result, sizeof (sem_t));
     175  
     176  	      /* Reinitialize 'result'.  */
     177  	      result = SEM_FAILED;
     178  
     179  	      /* This failed.  If O_EXCL is not set and the problem was
     180  		 that the file exists, try again.  */
     181  	      if ((oflag & O_EXCL) == 0 && errno == EEXIST)
     182  		{
     183  		  /* Remove the file.  */
     184  		  __unlink (tmpfname);
     185  
     186  		  /* Close the file.  */
     187  		  __close (fd);
     188  
     189  		  goto try_again;
     190  		}
     191  	    }
     192  	  else
     193  	    /* Insert the mapping into the search tree.  This also
     194  	       determines whether another thread sneaked by and already
     195  	       added such a mapping despite the fact that we created it.  */
     196  	    result = __sem_check_add_mapping (name, fd, result);
     197  	}
     198  
     199        /* Now remove the temporary name.  This should never fail.  If
     200  	 it fails we leak a file name.  Better fix the kernel.  */
     201        __unlink (tmpfname);
     202      }
     203  
     204    /* Map the mmap error to the error we need.  */
     205    if (MAP_FAILED != (void *) SEM_FAILED && result == MAP_FAILED)
     206      result = SEM_FAILED;
     207  
     208    /* We don't need the file descriptor anymore.  */
     209    if (fd != -1)
     210      {
     211        /* Do not disturb errno.  */
     212        int save = errno;
     213        __close (fd);
     214        errno = save;
     215      }
     216  
     217  out:
     218  #ifdef __libc_ptf_call
     219    __libc_ptf_call (__pthread_setcancelstate, (state, NULL), 0);
     220  #endif
     221  
     222    return result;
     223  }
     224  #if PTHREAD_IN_LIBC
     225  versioned_symbol (libc, __sem_open, sem_open, GLIBC_2_34);
     226  # if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_1_1, GLIBC_2_34)
     227  compat_symbol (libpthread, __sem_open, sem_open, GLIBC_2_1_1);
     228  # endif
     229  #else /* !PTHREAD_IN_LIBC */
     230  strong_alias (__sem_open, sem_open)
     231  #endif