Back to index

glibc  2.9
open_catalog.c
Go to the documentation of this file.
00001 /* Copyright (C) 1996-2000, 2001, 2002, 2003 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Ulrich Drepper, <drepper@gnu.org>.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <byteswap.h>
00021 #include <endian.h>
00022 #include <errno.h>
00023 #include <fcntl.h>
00024 #include <string.h>
00025 #include <stdlib.h>
00026 #include <unistd.h>
00027 #ifdef _POSIX_MAPPED_FILES
00028 # include <sys/mman.h>
00029 #endif
00030 #include <sys/stat.h>
00031 
00032 #include "catgetsinfo.h"
00033 #include <not-cancel.h>
00034 
00035 
00036 #define SWAPU32(w) bswap_32 (w)
00037 
00038 
00039 int
00040 __open_catalog (const char *cat_name, const char *nlspath, const char *env_var,
00041               __nl_catd catalog)
00042 {
00043   int fd = -1;
00044   struct stat64 st;
00045   int swapping;
00046   size_t cnt;
00047   size_t max_offset;
00048   size_t tab_size;
00049   const char *lastp;
00050   int result = -1;
00051 
00052   if (strchr (cat_name, '/') != NULL || nlspath == NULL)
00053     fd = open_not_cancel_2 (cat_name, O_RDONLY);
00054   else
00055     {
00056       const char *run_nlspath = nlspath;
00057 #define ENOUGH(n)                                                    \
00058   if (__builtin_expect (bufact + (n) >= bufmax, 0))                         \
00059     {                                                                \
00060       char *old_buf = buf;                                           \
00061       bufmax += 256 + (n);                                           \
00062       buf = (char *) alloca (bufmax);                                       \
00063       memcpy (buf, old_buf, bufact);                                        \
00064     }
00065 
00066       /* The RUN_NLSPATH variable contains a colon separated list of
00067         descriptions where we expect to find catalogs.  We have to
00068         recognize certain % substitutions and stop when we found the
00069         first existing file.  */
00070       char *buf;
00071       size_t bufact;
00072       size_t bufmax;
00073       size_t len;
00074 
00075       buf = NULL;
00076       bufmax = 0;
00077 
00078       fd = -1;
00079       while (*run_nlspath != '\0')
00080        {
00081          bufact = 0;
00082 
00083          if (*run_nlspath == ':')
00084            {
00085              /* Leading colon or adjacent colons - treat same as %N.  */
00086              len = strlen (cat_name);
00087              ENOUGH (len);
00088              memcpy (&buf[bufact], cat_name, len);
00089              bufact += len;
00090            }
00091          else
00092            while (*run_nlspath != ':' && *run_nlspath != '\0')
00093              if (*run_nlspath == '%')
00094               {
00095                 const char *tmp;
00096 
00097                 ++run_nlspath;     /* We have seen the `%'.  */
00098                 switch (*run_nlspath++)
00099                   {
00100                   case 'N':
00101                     /* Use the catalog name.  */
00102                     len = strlen (cat_name);
00103                     ENOUGH (len);
00104                     memcpy (&buf[bufact], cat_name, len);
00105                     bufact += len;
00106                     break;
00107                   case 'L':
00108                     /* Use the current locale category value.  */
00109                     len = strlen (env_var);
00110                     ENOUGH (len);
00111                     memcpy (&buf[bufact], env_var, len);
00112                     bufact += len;
00113                     break;
00114                   case 'l':
00115                     /* Use language element of locale category value.  */
00116                     tmp = env_var;
00117                     do
00118                      {
00119                        ENOUGH (1);
00120                        buf[bufact++] = *tmp++;
00121                      }
00122                     while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
00123                     break;
00124                   case 't':
00125                     /* Use territory element of locale category value.  */
00126                     tmp = env_var;
00127                     do
00128                      ++tmp;
00129                     while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
00130                     if (*tmp == '_')
00131                      {
00132                        ++tmp;
00133                        do
00134                          {
00135                            ENOUGH (1);
00136                            buf[bufact++] = *tmp++;
00137                          }
00138                        while (*tmp != '\0' && *tmp != '.');
00139                      }
00140                     break;
00141                   case 'c':
00142                     /* Use code set element of locale category value.  */
00143                     tmp = env_var;
00144                     do
00145                      ++tmp;
00146                     while (*tmp != '\0' && *tmp != '.');
00147                     if (*tmp == '.')
00148                      {
00149                        ++tmp;
00150                        do
00151                          {
00152                            ENOUGH (1);
00153                            buf[bufact++] = *tmp++;
00154                          }
00155                        while (*tmp != '\0');
00156                      }
00157                     break;
00158                   case '%':
00159                     ENOUGH (1);
00160                     buf[bufact++] = '%';
00161                     break;
00162                   default:
00163                     /* Unknown variable: ignore this path element.  */
00164                     bufact = 0;
00165                     while (*run_nlspath != '\0' && *run_nlspath != ':')
00166                      ++run_nlspath;
00167                     break;
00168                   }
00169               }
00170              else
00171               {
00172                 ENOUGH (1);
00173                 buf[bufact++] = *run_nlspath++;
00174               }
00175 
00176          ENOUGH (1);
00177          buf[bufact] = '\0';
00178 
00179          if (bufact != 0)
00180            {
00181              fd = open_not_cancel_2 (buf, O_RDONLY);
00182              if (fd >= 0)
00183               break;
00184            }
00185 
00186          ++run_nlspath;
00187        }
00188     }
00189 
00190   /* Avoid dealing with directories and block devices */
00191   if (__builtin_expect (fd, 0) < 0)
00192     return -1;
00193 
00194   if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0)
00195     goto close_unlock_return;
00196 
00197   if (__builtin_expect (!S_ISREG (st.st_mode), 0)
00198       || (size_t) st.st_size < sizeof (struct catalog_obj))
00199     {
00200       /* `errno' is not set correctly but the file is not usable.
00201         Use an reasonable error value.  */
00202       __set_errno (EINVAL);
00203       goto close_unlock_return;
00204     }
00205 
00206   catalog->file_size = st.st_size;
00207 #ifdef _POSIX_MAPPED_FILES
00208 # ifndef MAP_COPY
00209     /* Linux seems to lack read-only copy-on-write.  */
00210 #  define MAP_COPY MAP_PRIVATE
00211 # endif
00212 # ifndef MAP_FILE
00213     /* Some systems do not have this flag; it is superfluous.  */
00214 #  define MAP_FILE 0
00215 # endif
00216   catalog->file_ptr =
00217     (struct catalog_obj *) __mmap (NULL, st.st_size, PROT_READ,
00218                                MAP_FILE|MAP_COPY, fd, 0);
00219   if (__builtin_expect (catalog->file_ptr != (struct catalog_obj *) MAP_FAILED,
00220                      1))
00221     /* Tell the world we managed to mmap the file.  */
00222     catalog->status = mmapped;
00223   else
00224 #endif /* _POSIX_MAPPED_FILES */
00225     {
00226       /* mmap failed perhaps because the system call is not
00227         implemented.  Try to load the file.  */
00228       size_t todo;
00229       catalog->file_ptr = malloc (st.st_size);
00230       if (catalog->file_ptr == NULL)
00231        goto close_unlock_return;
00232 
00233       todo = st.st_size;
00234       /* Save read, handle partial reads.  */
00235       do
00236        {
00237          size_t now = read_not_cancel (fd, (((char *) catalog->file_ptr)
00238                                         + (st.st_size - todo)), todo);
00239          if (now == 0 || now == (size_t) -1)
00240            {
00241 #ifdef EINTR
00242              if (now == (size_t) -1 && errno == EINTR)
00243               continue;
00244 #endif
00245              free ((void *) catalog->file_ptr);
00246              goto close_unlock_return;
00247            }
00248          todo -= now;
00249        }
00250       while (todo > 0);
00251       catalog->status = malloced;
00252     }
00253 
00254   /* Determine whether the file is a catalog file and if yes whether
00255      it is written using the correct byte order.  Else we have to swap
00256      the values.  */
00257   if (__builtin_expect (catalog->file_ptr->magic == CATGETS_MAGIC, 1))
00258     swapping = 0;
00259   else if (catalog->file_ptr->magic == SWAPU32 (CATGETS_MAGIC))
00260     swapping = 1;
00261   else
00262     {
00263     invalid_file:
00264       /* Invalid file.  Free the resources and mark catalog as not
00265         usable.  */
00266 #ifdef _POSIX_MAPPED_FILES
00267       if (catalog->status == mmapped)
00268        __munmap ((void *) catalog->file_ptr, catalog->file_size);
00269       else
00270 #endif /* _POSIX_MAPPED_FILES */
00271        free (catalog->file_ptr);
00272       goto close_unlock_return;
00273     }
00274 
00275 #define SWAP(x) (swapping ? SWAPU32 (x) : (x))
00276 
00277   /* Get dimensions of the used hashing table.  */
00278   catalog->plane_size = SWAP (catalog->file_ptr->plane_size);
00279   catalog->plane_depth = SWAP (catalog->file_ptr->plane_depth);
00280 
00281   /* The file contains two versions of the pointer tables.  Pick the
00282      right one for the local byte order.  */
00283 #if __BYTE_ORDER == __LITTLE_ENDIAN
00284   catalog->name_ptr = &catalog->file_ptr->name_ptr[0];
00285 #elif __BYTE_ORDER == __BIG_ENDIAN
00286   catalog->name_ptr = &catalog->file_ptr->name_ptr[catalog->plane_size
00287                                             * catalog->plane_depth
00288                                             * 3];
00289 #else
00290 # error Cannot handle __BYTE_ORDER byte order
00291 #endif
00292 
00293   /* The rest of the file contains all the strings.  They are
00294      addressed relative to the position of the first string.  */
00295   catalog->strings =
00296     (const char *) &catalog->file_ptr->name_ptr[catalog->plane_size
00297                                           * catalog->plane_depth * 3 * 2];
00298 
00299   /* Determine the largest string offset mentioned in the table.  */
00300   max_offset = 0;
00301   tab_size = 3 * catalog->plane_size * catalog->plane_depth;
00302   for (cnt = 2; cnt < tab_size; cnt += 3)
00303     if (catalog->name_ptr[cnt] > max_offset)
00304       max_offset = catalog->name_ptr[cnt];
00305 
00306   /* Now we can check whether the file is large enough to contain the
00307      tables it says it contains.  */
00308   if ((size_t) st.st_size
00309       <= (sizeof (struct catalog_obj) + 2 * tab_size + max_offset))
00310     /* The last string is not contained in the file.  */
00311     goto invalid_file;
00312 
00313   lastp = catalog->strings + max_offset;
00314   max_offset = (st.st_size
00315               - sizeof (struct catalog_obj) + 2 * tab_size + max_offset);
00316   while (*lastp != '\0')
00317     {
00318       if (--max_offset == 0)
00319        goto invalid_file;
00320       ++lastp;
00321     }
00322 
00323   /* We succeeded.  */
00324   result = 0;
00325 
00326   /* Release the lock again.  */
00327  close_unlock_return:
00328   close_not_cancel_no_status (fd);
00329 
00330   return result;
00331 }
00332 libc_hidden_def (__open_catalog)