Back to index

glibc  2.9
gconv_cache.c
Go to the documentation of this file.
00001 /* Cache handling for iconv modules.
00002    Copyright (C) 2001, 2002, 2003, 2005, 2007 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@cygnus.com>, 2001.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #include <dlfcn.h>
00022 #include <errno.h>
00023 #include <fcntl.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <unistd.h>
00027 #include <sys/mman.h>
00028 #include <sys/stat.h>
00029 
00030 #include <gconv_int.h>
00031 #include <iconvconfig.h>
00032 #include <not-cancel.h>
00033 
00034 #include "../intl/hash-string.h"
00035 
00036 static void *gconv_cache;
00037 static size_t cache_size;
00038 static int cache_malloced;
00039 
00040 
00041 void *
00042 __gconv_get_cache (void)
00043 {
00044   return gconv_cache;
00045 }
00046 
00047 
00048 int
00049 internal_function
00050 __gconv_load_cache (void)
00051 {
00052   int fd;
00053   struct stat64 st;
00054   struct gconvcache_header *header;
00055 
00056   /* We cannot use the cache if the GCONV_PATH environment variable is
00057      set.  */
00058   __gconv_path_envvar = getenv ("GCONV_PATH");
00059   if (__gconv_path_envvar != NULL)
00060     return -1;
00061 
00062   /* See whether the cache file exists.  */
00063   fd = open_not_cancel (GCONV_MODULES_CACHE, O_RDONLY, 0);
00064   if (__builtin_expect (fd, 0) == -1)
00065     /* Not available.  */
00066     return -1;
00067 
00068   /* Get information about the file.  */
00069   if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0
00070       /* We do not have to start looking at the file if it cannot contain
00071         at least the cache header.  */
00072       || (size_t) st.st_size < sizeof (struct gconvcache_header))
00073     {
00074     close_and_exit:
00075       close_not_cancel_no_status (fd);
00076       return -1;
00077     }
00078 
00079   /* Make the file content available.  */
00080   cache_size = st.st_size;
00081 #ifdef _POSIX_MAPPED_FILES
00082   gconv_cache = __mmap (NULL, cache_size, PROT_READ, MAP_SHARED, fd, 0);
00083   if (__builtin_expect (gconv_cache == MAP_FAILED, 0))
00084 #endif
00085     {
00086       size_t already_read;
00087 
00088       gconv_cache = malloc (cache_size);
00089       if (gconv_cache == NULL)
00090        goto close_and_exit;
00091 
00092       already_read = 0;
00093       do
00094        {
00095          ssize_t n = __read (fd, (char *) gconv_cache + already_read,
00096                            cache_size - already_read);
00097          if (__builtin_expect (n, 0) == -1)
00098            {
00099              free (gconv_cache);
00100              gconv_cache = NULL;
00101              goto close_and_exit;
00102            }
00103 
00104          already_read += n;
00105        }
00106       while (already_read < cache_size);
00107 
00108       cache_malloced = 1;
00109     }
00110 
00111   /* We don't need the file descriptor anymore.  */
00112   close_not_cancel_no_status (fd);
00113 
00114   /* Check the consistency.  */
00115   header = (struct gconvcache_header *) gconv_cache;
00116   if (__builtin_expect (header->magic, GCONVCACHE_MAGIC) != GCONVCACHE_MAGIC
00117       || __builtin_expect (header->string_offset >= cache_size, 0)
00118       || __builtin_expect (header->hash_offset >= cache_size, 0)
00119       || __builtin_expect (header->hash_size == 0, 0)
00120       || __builtin_expect ((header->hash_offset
00121                          + header->hash_size * sizeof (struct hash_entry))
00122                         > cache_size, 0)
00123       || __builtin_expect (header->module_offset >= cache_size, 0)
00124       || __builtin_expect (header->otherconv_offset > cache_size, 0))
00125     {
00126       if (cache_malloced)
00127        {
00128          free (gconv_cache);
00129          cache_malloced = 0;
00130        }
00131 #ifdef _POSIX_MAPPED_FILES
00132       else
00133        __munmap (gconv_cache, cache_size);
00134 #endif
00135       gconv_cache = NULL;
00136 
00137       return -1;
00138     }
00139 
00140   /* That worked.  */
00141   return 0;
00142 }
00143 
00144 
00145 static int
00146 internal_function
00147 find_module_idx (const char *str, size_t *idxp)
00148 {
00149   unsigned int idx;
00150   unsigned int hval;
00151   unsigned int hval2;
00152   const struct gconvcache_header *header;
00153   const char *strtab;
00154   const struct hash_entry *hashtab;
00155   unsigned int limit;
00156 
00157   header = (const struct gconvcache_header *) gconv_cache;
00158   strtab = (char *) gconv_cache + header->string_offset;
00159   hashtab = (struct hash_entry *) ((char *) gconv_cache
00160                                + header->hash_offset);
00161 
00162   hval = __hash_string (str);
00163   idx = hval % header->hash_size;
00164   hval2 = 1 + hval % (header->hash_size - 2);
00165 
00166   limit = cache_size - header->string_offset;
00167   while (hashtab[idx].string_offset != 0)
00168     if (hashtab[idx].string_offset < limit
00169        && strcmp (str, strtab + hashtab[idx].string_offset) == 0)
00170       {
00171        *idxp = hashtab[idx].module_idx;
00172        return 0;
00173       }
00174     else
00175       if ((idx += hval2) >= header->hash_size)
00176        idx -= header->hash_size;
00177 
00178   /* Nothing found.  */
00179   return -1;
00180 }
00181 
00182 
00183 #ifndef STATIC_GCONV
00184 static int
00185 internal_function
00186 find_module (const char *directory, const char *filename,
00187             struct __gconv_step *result)
00188 {
00189   size_t dirlen = strlen (directory);
00190   size_t fnamelen = strlen (filename) + 1;
00191   char fullname[dirlen + fnamelen];
00192   int status = __GCONV_NOCONV;
00193 
00194   memcpy (__mempcpy (fullname, directory, dirlen), filename, fnamelen);
00195 
00196   result->__shlib_handle = __gconv_find_shlib (fullname);
00197   if (result->__shlib_handle != NULL)
00198     {
00199       status = __GCONV_OK;
00200 
00201       result->__modname = NULL;
00202       result->__fct = result->__shlib_handle->fct;
00203       result->__init_fct = result->__shlib_handle->init_fct;
00204       result->__end_fct = result->__shlib_handle->end_fct;
00205 
00206       /* These settings can be overridden by the init function.  */
00207       result->__btowc_fct = NULL;
00208       result->__data = NULL;
00209 
00210       /* Call the init function.  */
00211       if (result->__init_fct != NULL)
00212        {
00213          __gconv_init_fct init_fct = result->__init_fct;
00214 #ifdef PTR_DEMANGLE
00215          PTR_DEMANGLE (init_fct);
00216 #endif
00217          status = DL_CALL_FCT (init_fct, (result));
00218 
00219 #ifdef PTR_MANGLE
00220          if (result->__btowc_fct != NULL)
00221            PTR_MANGLE (result->__btowc_fct);
00222 #endif
00223        }
00224     }
00225 
00226   return status;
00227 }
00228 #endif
00229 
00230 
00231 int
00232 internal_function
00233 __gconv_compare_alias_cache (const char *name1, const char *name2, int *result)
00234 {
00235   size_t name1_idx;
00236   size_t name2_idx;
00237 
00238   if (gconv_cache == NULL)
00239     return -1;
00240 
00241   if (find_module_idx (name1, &name1_idx) != 0
00242       || find_module_idx (name2, &name2_idx) != 0)
00243     *result = strcmp (name1, name2);
00244   else
00245     *result = (int) (name1_idx - name2_idx);
00246 
00247   return 0;
00248 }
00249 
00250 
00251 int
00252 internal_function
00253 __gconv_lookup_cache (const char *toset, const char *fromset,
00254                     struct __gconv_step **handle, size_t *nsteps, int flags)
00255 {
00256   const struct gconvcache_header *header;
00257   const char *strtab;
00258   size_t fromidx;
00259   size_t toidx;
00260   const struct module_entry *modtab;
00261   const struct module_entry *from_module;
00262   const struct module_entry *to_module;
00263   struct __gconv_step *result;
00264 
00265   if (gconv_cache == NULL)
00266     /* We have no cache available.  */
00267     return __GCONV_NODB;
00268 
00269   header = (const struct gconvcache_header *) gconv_cache;
00270   strtab = (char *) gconv_cache + header->string_offset;
00271   modtab = (const struct module_entry *) ((char *) gconv_cache
00272                                      + header->module_offset);
00273 
00274   if (find_module_idx (fromset, &fromidx) != 0
00275       || (header->module_offset + (fromidx + 1) * sizeof (struct module_entry)
00276          > cache_size))
00277     return __GCONV_NOCONV;
00278   from_module = &modtab[fromidx];
00279 
00280   if (find_module_idx (toset, &toidx) != 0
00281       || (header->module_offset + (toidx + 1) * sizeof (struct module_entry)
00282          > cache_size))
00283     return __GCONV_NOCONV;
00284   to_module = &modtab[toidx];
00285 
00286   /* Avoid copy-only transformations if the user requests.   */
00287   if (__builtin_expect (flags & GCONV_AVOID_NOCONV, 0) && fromidx == toidx)
00288     return __GCONV_NULCONV;
00289 
00290   /* If there are special conversions available examine them first.  */
00291   if (fromidx != 0 && toidx != 0
00292       && __builtin_expect (from_module->extra_offset, 0) != 0)
00293     {
00294       /* Search through the list to see whether there is a module
00295         matching the destination character set.  */
00296       const struct extra_entry *extra;
00297 
00298       /* Note the -1.  This is due to the offset added in iconvconfig.
00299         See there for more explanations.  */
00300       extra = (const struct extra_entry *) ((char *) gconv_cache
00301                                        + header->otherconv_offset
00302                                        + from_module->extra_offset - 1);
00303       while (extra->module_cnt != 0
00304             && extra->module[extra->module_cnt - 1].outname_offset != toidx)
00305        extra = (const struct extra_entry *) ((char *) extra
00306                                          + sizeof (struct extra_entry)
00307                                          + (extra->module_cnt
00308                                            * sizeof (struct extra_entry_module)));
00309 
00310       if (extra->module_cnt != 0)
00311        {
00312          /* Use the extra module.  First determine how many steps.  */
00313          char *fromname;
00314          int idx;
00315 
00316          *nsteps = extra->module_cnt;
00317          *handle = result =
00318            (struct __gconv_step *) malloc (extra->module_cnt
00319                                        * sizeof (struct __gconv_step));
00320          if (result == NULL)
00321            return __GCONV_NOMEM;
00322 
00323          fromname = (char *) strtab + from_module->canonname_offset;
00324          idx = 0;
00325          do
00326            {
00327              result[idx].__from_name = fromname;
00328              fromname = result[idx].__to_name =
00329               (char *) strtab + modtab[extra->module[idx].outname_offset].canonname_offset;
00330 
00331              result[idx].__counter = 1;
00332              result[idx].__data = NULL;
00333 
00334 #ifndef STATIC_GCONV
00335              if (strtab[extra->module[idx].dir_offset] != '\0')
00336               {
00337                 /* Load the module, return handle for it.  */
00338                 int res;
00339 
00340                 res = find_module (strtab + extra->module[idx].dir_offset,
00341                                  strtab + extra->module[idx].name_offset,
00342                                  &result[idx]);
00343                 if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK)
00344                   {
00345                     /* Something went wrong.  */
00346                     free (result);
00347                     goto try_internal;
00348                   }
00349               }
00350              else
00351 #endif
00352               /* It's a builtin transformation.  */
00353               __gconv_get_builtin_trans (strtab
00354                                       + extra->module[idx].name_offset,
00355                                       &result[idx]);
00356 
00357            }
00358          while (++idx < extra->module_cnt);
00359 
00360          return __GCONV_OK;
00361        }
00362     }
00363 
00364  try_internal:
00365   /* See whether we can convert via the INTERNAL charset.  */
00366   if ((fromidx != 0 && __builtin_expect (from_module->fromname_offset, 1) == 0)
00367       || (toidx != 0 && __builtin_expect (to_module->toname_offset, 1) == 0)
00368       || (fromidx == 0 && toidx == 0))
00369     /* Not possible.  Nothing we can do.  */
00370     return __GCONV_NOCONV;
00371 
00372   /* We will use up to two modules.  Always allocate room for two.  */
00373   result = (struct __gconv_step *) malloc (2 * sizeof (struct __gconv_step));
00374   if (result == NULL)
00375     return __GCONV_NOMEM;
00376 
00377   *handle = result;
00378   *nsteps = 0;
00379 
00380   /* Generate data structure for conversion to INTERNAL.  */
00381   if (fromidx != 0)
00382     {
00383       result[0].__from_name = (char *) strtab + from_module->canonname_offset;
00384       result[0].__to_name = (char *) "INTERNAL";
00385 
00386       result[0].__counter = 1;
00387       result[0].__data = NULL;
00388 
00389 #ifndef STATIC_GCONV
00390       if (strtab[from_module->todir_offset] != '\0')
00391        {
00392          /* Load the module, return handle for it.  */
00393          int res = find_module (strtab + from_module->todir_offset,
00394                              strtab + from_module->toname_offset,
00395                              &result[0]);
00396          if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK)
00397            {
00398              /* Something went wrong.  */
00399              free (result);
00400              return res;
00401            }
00402        }
00403       else
00404 #endif
00405        /* It's a builtin transformation.  */
00406        __gconv_get_builtin_trans (strtab + from_module->toname_offset,
00407                                &result[0]);
00408 
00409       ++*nsteps;
00410     }
00411 
00412   /* Generate data structure for conversion from INTERNAL.  */
00413   if (toidx != 0)
00414     {
00415       int idx = *nsteps;
00416 
00417       result[idx].__from_name = (char *) "INTERNAL";
00418       result[idx].__to_name = (char *) strtab + to_module->canonname_offset;
00419 
00420       result[idx].__counter = 1;
00421       result[idx].__data = NULL;
00422 
00423 #ifndef STATIC_GCONV
00424       if (strtab[to_module->fromdir_offset] != '\0')
00425        {
00426          /* Load the module, return handle for it.  */
00427          int res = find_module (strtab + to_module->fromdir_offset,
00428                              strtab + to_module->fromname_offset,
00429                              &result[idx]);
00430          if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK)
00431            {
00432              /* Something went wrong.  */
00433              if (idx != 0)
00434               __gconv_release_step (&result[0]);
00435              free (result);
00436              return res;
00437            }
00438        }
00439       else
00440 #endif
00441        /* It's a builtin transformation.  */
00442        __gconv_get_builtin_trans (strtab + to_module->fromname_offset,
00443                                &result[idx]);
00444 
00445       ++*nsteps;
00446     }
00447 
00448   return __GCONV_OK;
00449 }
00450 
00451 
00452 /* Free memory allocated for the transformation record.  */
00453 void
00454 internal_function
00455 __gconv_release_cache (struct __gconv_step *steps, size_t nsteps)
00456 {
00457   if (gconv_cache != NULL)
00458     /* The only thing we have to deallocate is the record with the
00459        steps.  */
00460     free (steps);
00461 }
00462 
00463 
00464 /* Free all resources if necessary.  */
00465 libc_freeres_fn (free_mem)
00466 {
00467   if (cache_malloced)
00468     free (gconv_cache);
00469 #ifdef _POSIX_MAPPED_FILES
00470   else if (gconv_cache != NULL)
00471     __munmap (gconv_cache, cache_size);
00472 #endif
00473 }