(root)/
glibc-2.38/
sysdeps/
pthread/
sem_routines.c
       1  /* Helper code for POSIX semaphore implementation.
       2     Copyright (C) 2021-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <search.h>
      20  #include <semaphoreP.h>
      21  #include <sys/mman.h>
      22  #include <sem_routines.h>
      23  
      24  /* Keeping track of currently used mappings.  */
      25  struct inuse_sem
      26  {
      27    dev_t dev;
      28    ino64_t ino;
      29    int refcnt;
      30    sem_t *sem;
      31    char name[];
      32  };
      33  
      34  struct search_sem
      35  {
      36    dev_t dev;
      37    ino64_t ino;
      38    int refcnt;
      39    sem_t *sem;
      40    char name[NAME_MAX + 1];
      41  };
      42  
      43  /* Comparison function for search of existing mapping.  */
      44  static int
      45  sem_search (const void *a, const void *b)
      46  {
      47    const struct inuse_sem *as = (const struct inuse_sem *) a;
      48    const struct inuse_sem *bs = (const struct inuse_sem *) b;
      49  
      50    if (as->ino != bs->ino)
      51      /* Cannot return the difference the type is larger than int.  */
      52      return as->ino < bs->ino ? -1 : (as->ino == bs->ino ? 0 : 1);
      53  
      54    if (as->dev != bs->dev)
      55      /* Cannot return the difference the type is larger than int.  */
      56      return as->dev < bs->dev ? -1 : (as->dev == bs->dev ? 0 : 1);
      57  
      58    return strcmp (as->name, bs->name);
      59  }
      60  
      61  /* The search tree for existing mappings.  */
      62  static void *sem_mappings;
      63  
      64  /* Lock to protect the search tree.  */
      65  static int sem_mappings_lock = LLL_LOCK_INITIALIZER;
      66  
      67  
      68  /* Search for existing mapping and if possible add the one provided.  */
      69  sem_t *
      70  __sem_check_add_mapping (const char *name, int fd, sem_t *existing)
      71  {
      72    size_t namelen = strlen (name);
      73    if (namelen > NAME_MAX)
      74      return SEM_FAILED;
      75    namelen += 1;
      76  
      77    sem_t *result = SEM_FAILED;
      78  
      79    /* Get the information about the file.  */
      80    struct __stat64_t64 st;
      81    if (__fstat64_time64 (fd, &st) == 0)
      82      {
      83        /* Get the lock.  */
      84        lll_lock (sem_mappings_lock, LLL_PRIVATE);
      85  
      86        /* Search for an existing mapping given the information we have.  */
      87        struct search_sem fake;
      88        memcpy (fake.name, name, namelen);
      89        fake.dev = st.st_dev;
      90        fake.ino = st.st_ino;
      91  
      92        struct inuse_sem **foundp = __tfind (&fake, &sem_mappings, sem_search);
      93        if (foundp != NULL)
      94  	{
      95  	  /* There is already a mapping.  Use it.  */
      96  	  result = (*foundp)->sem;
      97  	  ++(*foundp)->refcnt;
      98  	}
      99        else
     100  	{
     101  	  /* We haven't found a mapping.  Install ione.  */
     102  	  struct inuse_sem *newp;
     103  
     104  	  newp = (struct inuse_sem *) malloc (sizeof (*newp) + namelen);
     105  	  if (newp != NULL)
     106  	    {
     107  	      /* If the caller hasn't provided any map it now.  */
     108  	      if (existing == SEM_FAILED)
     109  		existing = (sem_t *) __mmap (NULL, sizeof (sem_t),
     110  					     PROT_READ | PROT_WRITE,
     111  					     MAP_SHARED, fd, 0);
     112  
     113  	      newp->dev = st.st_dev;
     114  	      newp->ino = st.st_ino;
     115  	      newp->refcnt = 1;
     116  	      newp->sem = existing;
     117  	      memcpy (newp->name, name, namelen);
     118  
     119  	      /* Insert the new value.  */
     120  	      if (existing != MAP_FAILED
     121  		  && __tsearch (newp, &sem_mappings, sem_search) != NULL)
     122  		/* Successful.  */
     123  		result = existing;
     124  	      else
     125  		/* Something went wrong while inserting the new
     126  		   value.  We fail completely.  */
     127  		free (newp);
     128  	    }
     129  	}
     130  
     131        /* Release the lock.  */
     132        lll_unlock (sem_mappings_lock, LLL_PRIVATE);
     133      }
     134  
     135    if (result != existing && existing != SEM_FAILED && existing != MAP_FAILED)
     136      {
     137        /* Do not disturb errno.  */
     138        int save = errno;
     139        __munmap (existing, sizeof (sem_t));
     140        errno = save;
     141      }
     142  
     143    return result;
     144  }
     145  
     146  struct walk_closure
     147  {
     148    sem_t *the_sem;
     149    struct inuse_sem *rec;
     150  };
     151  
     152  static void
     153  walker (const void *inodep, VISIT which, void *closure0)
     154  {
     155    struct walk_closure *closure = closure0;
     156    struct inuse_sem *nodep = *(struct inuse_sem **) inodep;
     157  
     158    if (nodep->sem == closure->the_sem)
     159      closure->rec = nodep;
     160  }
     161  
     162  bool
     163  __sem_remove_mapping (sem_t *sem)
     164  {
     165    bool ret = true;
     166  
     167    /* Get the lock.  */
     168    lll_lock (sem_mappings_lock, LLL_PRIVATE);
     169  
     170    /* Locate the entry for the mapping the caller provided.  */
     171    struct inuse_sem *rec;
     172    {
     173      struct walk_closure closure = { .the_sem = sem, .rec = NULL };
     174      __twalk_r (sem_mappings, walker, &closure);
     175      rec = closure.rec;
     176    }
     177    if (rec != NULL)
     178      {
     179        /* Check the reference counter.  If it is going to be zero, free
     180  	 all the resources.  */
     181        if (--rec->refcnt == 0)
     182  	{
     183  	  /* Remove the record from the tree.  */
     184  	  __tdelete (rec, &sem_mappings, sem_search);
     185  
     186  	  if (__munmap (rec->sem, sizeof (sem_t)) == -1)
     187  	    ret = false;
     188  
     189  	  free (rec);
     190  	}
     191      }
     192    else
     193      ret = false;
     194  
     195    /* Release the lock.  */
     196    lll_unlock (sem_mappings_lock, LLL_PRIVATE);
     197  
     198    return ret;
     199  }