(root)/
glibc-2.38/
catgets/
open_catalog.c
       1  /* Copyright (C) 1996-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 <byteswap.h>
      19  #include <endian.h>
      20  #include <errno.h>
      21  #include <fcntl.h>
      22  #include <string.h>
      23  #include <stdlib.h>
      24  #include <unistd.h>
      25  #ifdef _POSIX_MAPPED_FILES
      26  # include <sys/mman.h>
      27  #endif
      28  #include <sys/stat.h>
      29  
      30  #include "catgetsinfo.h"
      31  #include <not-cancel.h>
      32  
      33  
      34  #define SWAPU32(w) bswap_32 (w)
      35  
      36  
      37  int
      38  __open_catalog (const char *cat_name, const char *nlspath, const char *env_var,
      39  		__nl_catd catalog)
      40  {
      41    int fd = -1;
      42    struct __stat64_t64 st;
      43    int swapping;
      44    size_t cnt;
      45    size_t max_offset;
      46    size_t tab_size;
      47    const char *lastp;
      48    int result = -1;
      49    char *buf = NULL;
      50  
      51    if (strchr (cat_name, '/') != NULL || nlspath == NULL)
      52      fd = __open_nocancel (cat_name, O_RDONLY | O_CLOEXEC);
      53    else
      54      {
      55        const char *run_nlspath = nlspath;
      56  #define ENOUGH(n)							      \
      57    if (__glibc_unlikely (bufact + (n) >= bufmax))			      \
      58      {									      \
      59        char *old_buf = buf;						      \
      60        bufmax += (bufmax < 256 + (n)) ? 256 + (n) : bufmax;		      \
      61        buf = realloc (buf, bufmax);					      \
      62        if (__glibc_unlikely (buf == NULL))				      \
      63  	{								      \
      64  	  free (old_buf);						      \
      65  	  return -1;							      \
      66  	}								      \
      67      }
      68  
      69        /* The RUN_NLSPATH variable contains a colon separated list of
      70  	 descriptions where we expect to find catalogs.  We have to
      71  	 recognize certain % substitutions and stop when we found the
      72  	 first existing file.  */
      73        size_t bufact;
      74        size_t bufmax = 0;
      75        size_t len;
      76  
      77        fd = -1;
      78        while (*run_nlspath != '\0')
      79  	{
      80  	  bufact = 0;
      81  
      82  	  if (*run_nlspath == ':')
      83  	    {
      84  	      /* Leading colon or adjacent colons - treat same as %N.  */
      85  	      len = strlen (cat_name);
      86  	      ENOUGH (len);
      87  	      memcpy (&buf[bufact], cat_name, len);
      88  	      bufact += len;
      89  	    }
      90  	  else
      91  	    while (*run_nlspath != ':' && *run_nlspath != '\0')
      92  	      if (*run_nlspath == '%')
      93  		{
      94  		  const char *tmp;
      95  
      96  		  ++run_nlspath;	/* We have seen the `%'.  */
      97  		  switch (*run_nlspath++)
      98  		    {
      99  		    case 'N':
     100  		      /* Use the catalog name.  */
     101  		      len = strlen (cat_name);
     102  		      ENOUGH (len);
     103  		      memcpy (&buf[bufact], cat_name, len);
     104  		      bufact += len;
     105  		      break;
     106  		    case 'L':
     107  		      /* Use the current locale category value.  */
     108  		      len = strlen (env_var);
     109  		      ENOUGH (len);
     110  		      memcpy (&buf[bufact], env_var, len);
     111  		      bufact += len;
     112  		      break;
     113  		    case 'l':
     114  		      /* Use language element of locale category value.  */
     115  		      tmp = env_var;
     116  		      do
     117  			{
     118  			  ENOUGH (1);
     119  			  buf[bufact++] = *tmp++;
     120  			}
     121  		      while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
     122  		      break;
     123  		    case 't':
     124  		      /* Use territory element of locale category value.  */
     125  		      tmp = env_var;
     126  		      do
     127  			++tmp;
     128  		      while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
     129  		      if (*tmp == '_')
     130  			{
     131  			  ++tmp;
     132  			  do
     133  			    {
     134  			      ENOUGH (1);
     135  			      buf[bufact++] = *tmp++;
     136  			    }
     137  			  while (*tmp != '\0' && *tmp != '.');
     138  			}
     139  		      break;
     140  		    case 'c':
     141  		      /* Use code set element of locale category value.  */
     142  		      tmp = env_var;
     143  		      do
     144  			++tmp;
     145  		      while (*tmp != '\0' && *tmp != '.');
     146  		      if (*tmp == '.')
     147  			{
     148  			  ++tmp;
     149  			  do
     150  			    {
     151  			      ENOUGH (1);
     152  			      buf[bufact++] = *tmp++;
     153  			    }
     154  			  while (*tmp != '\0');
     155  			}
     156  		      break;
     157  		    case '%':
     158  		      ENOUGH (1);
     159  		      buf[bufact++] = '%';
     160  		      break;
     161  		    default:
     162  		      /* Unknown variable: ignore this path element.  */
     163  		      bufact = 0;
     164  		      while (*run_nlspath != '\0' && *run_nlspath != ':')
     165  			++run_nlspath;
     166  		      break;
     167  		    }
     168  		}
     169  	      else
     170  		{
     171  		  ENOUGH (1);
     172  		  buf[bufact++] = *run_nlspath++;
     173  		}
     174  
     175  	  ENOUGH (1);
     176  	  buf[bufact] = '\0';
     177  
     178  	  if (bufact != 0)
     179  	    {
     180  	      fd = __open_nocancel (buf, O_RDONLY | O_CLOEXEC);
     181  	      if (fd >= 0)
     182  		break;
     183  	    }
     184  
     185  	  ++run_nlspath;
     186  	}
     187      }
     188  
     189    /* Avoid dealing with directories and block devices */
     190    if (__builtin_expect (fd, 0) < 0)
     191      {
     192        free (buf);
     193        return -1;
     194      }
     195  
     196    if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0))
     197      goto close_unlock_return;
     198  
     199    if (__builtin_expect (!S_ISREG (st.st_mode), 0)
     200        || (size_t) st.st_size < sizeof (struct catalog_obj))
     201      {
     202        /* `errno' is not set correctly but the file is not usable.
     203  	 Use an reasonable error value.  */
     204        __set_errno (EINVAL);
     205        goto close_unlock_return;
     206      }
     207  
     208    catalog->file_size = st.st_size;
     209  #ifdef _POSIX_MAPPED_FILES
     210  # ifndef MAP_COPY
     211      /* Linux seems to lack read-only copy-on-write.  */
     212  #  define MAP_COPY MAP_PRIVATE
     213  # endif
     214  # ifndef MAP_FILE
     215      /* Some systems do not have this flag; it is superfluous.  */
     216  #  define MAP_FILE 0
     217  # endif
     218    catalog->file_ptr =
     219      (struct catalog_obj *) __mmap (NULL, st.st_size, PROT_READ,
     220  				   MAP_FILE|MAP_COPY, fd, 0);
     221    if (__builtin_expect (catalog->file_ptr != (struct catalog_obj *) MAP_FAILED,
     222  			1))
     223      /* Tell the world we managed to mmap the file.  */
     224      catalog->status = mmapped;
     225    else
     226  #endif /* _POSIX_MAPPED_FILES */
     227      {
     228        /* mmap failed perhaps because the system call is not
     229  	 implemented.  Try to load the file.  */
     230        size_t todo;
     231        catalog->file_ptr = malloc (st.st_size);
     232        if (catalog->file_ptr == NULL)
     233  	goto close_unlock_return;
     234  
     235        todo = st.st_size;
     236        /* Save read, handle partial reads.  */
     237        do
     238  	{
     239  	  size_t now = __read_nocancel (fd, (((char *) catalog->file_ptr)
     240  					     + (st.st_size - todo)), todo);
     241  	  if (now == 0 || now == (size_t) -1)
     242  	    {
     243  #ifdef EINTR
     244  	      if (now == (size_t) -1 && errno == EINTR)
     245  		continue;
     246  #endif
     247  	      free ((void *) catalog->file_ptr);
     248  	      goto close_unlock_return;
     249  	    }
     250  	  todo -= now;
     251  	}
     252        while (todo > 0);
     253        catalog->status = malloced;
     254      }
     255  
     256    /* Determine whether the file is a catalog file and if yes whether
     257       it is written using the correct byte order.  Else we have to swap
     258       the values.  */
     259    if (__glibc_likely (catalog->file_ptr->magic == CATGETS_MAGIC))
     260      swapping = 0;
     261    else if (catalog->file_ptr->magic == SWAPU32 (CATGETS_MAGIC))
     262      swapping = 1;
     263    else
     264      {
     265      invalid_file:
     266        /* Invalid file.  Free the resources and mark catalog as not
     267  	 usable.  */
     268  #ifdef _POSIX_MAPPED_FILES
     269        if (catalog->status == mmapped)
     270  	__munmap ((void *) catalog->file_ptr, catalog->file_size);
     271        else
     272  #endif	/* _POSIX_MAPPED_FILES */
     273  	free (catalog->file_ptr);
     274        goto close_unlock_return;
     275      }
     276  
     277  #define SWAP(x) (swapping ? SWAPU32 (x) : (x))
     278  
     279    /* Get dimensions of the used hashing table.  */
     280    catalog->plane_size = SWAP (catalog->file_ptr->plane_size);
     281    catalog->plane_depth = SWAP (catalog->file_ptr->plane_depth);
     282  
     283    /* The file contains two versions of the pointer tables.  Pick the
     284       right one for the local byte order.  */
     285  #if __BYTE_ORDER == __LITTLE_ENDIAN
     286    catalog->name_ptr = &catalog->file_ptr->name_ptr[0];
     287  #elif __BYTE_ORDER == __BIG_ENDIAN
     288    catalog->name_ptr = &catalog->file_ptr->name_ptr[catalog->plane_size
     289  						  * catalog->plane_depth
     290  						  * 3];
     291  #else
     292  # error Cannot handle __BYTE_ORDER byte order
     293  #endif
     294  
     295    /* The rest of the file contains all the strings.  They are
     296       addressed relative to the position of the first string.  */
     297    catalog->strings =
     298      (const char *) &catalog->file_ptr->name_ptr[catalog->plane_size
     299  					       * catalog->plane_depth * 3 * 2];
     300  
     301    /* Determine the largest string offset mentioned in the table.  */
     302    max_offset = 0;
     303    tab_size = 3 * catalog->plane_size * catalog->plane_depth;
     304    for (cnt = 2; cnt < tab_size; cnt += 3)
     305      if (catalog->name_ptr[cnt] > max_offset)
     306        max_offset = catalog->name_ptr[cnt];
     307  
     308    /* Now we can check whether the file is large enough to contain the
     309       tables it says it contains.  */
     310    if ((size_t) st.st_size
     311        <= (sizeof (struct catalog_obj) + 2 * tab_size + max_offset))
     312      /* The last string is not contained in the file.  */
     313      goto invalid_file;
     314  
     315    lastp = catalog->strings + max_offset;
     316    max_offset = (st.st_size
     317  		- sizeof (struct catalog_obj) + 2 * tab_size + max_offset);
     318    while (*lastp != '\0')
     319      {
     320        if (--max_offset == 0)
     321  	goto invalid_file;
     322        ++lastp;
     323      }
     324  
     325    /* We succeeded.  */
     326    result = 0;
     327  
     328    /* Release the lock again.  */
     329   close_unlock_return:
     330    __close_nocancel_nostatus (fd);
     331    free (buf);
     332  
     333    return result;
     334  }
     335  libc_hidden_def (__open_catalog)