Back to index

glibc  2.9
cache.c
Go to the documentation of this file.
00001 /* Copyright (C) 1999-2003,2005,2006,2007 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Andreas Jaeger <aj@suse.de>, 1999.
00004 
00005    This program is free software; you can redistribute it and/or modify
00006    it under the terms of the GNU General Public License as published
00007    by the Free Software Foundation; version 2 of the License, or
00008    (at your option) any later version.
00009 
00010    This program 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
00013    GNU General Public License for more details.
00014 
00015    You should have received a copy of the GNU General Public License
00016    along with this program; if not, write to the Free Software Foundation,
00017    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00018 
00019 #include <errno.h>
00020 #include <error.h>
00021 #include <dirent.h>
00022 #include <inttypes.h>
00023 #include <libgen.h>
00024 #include <libintl.h>
00025 #include <stdio.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <unistd.h>
00029 #include <sys/fcntl.h>
00030 #include <sys/mman.h>
00031 #include <sys/stat.h>
00032 #include <sys/types.h>
00033 
00034 #include <ldconfig.h>
00035 #include <dl-cache.h>
00036 
00037 struct cache_entry
00038 {
00039   char *lib;                /* Library name.  */
00040   char *path;               /* Path to find library.  */
00041   int flags;                /* Flags to indicate kind of library.  */
00042   unsigned int osversion;   /* Required OS version.  */
00043   uint64_t hwcap;           /* Important hardware capabilities.  */
00044   int bits_hwcap;           /* Number of bits set in hwcap.  */
00045   struct cache_entry *next; /* Next entry in list.  */
00046 };
00047 
00048 /* List of all cache entries.  */
00049 static struct cache_entry *entries;
00050 
00051 static const char *flag_descr[] =
00052 { "libc4", "ELF", "libc5", "libc6"};
00053 
00054 /* Print a single entry.  */
00055 static void
00056 print_entry (const char *lib, int flag, unsigned int osversion,
00057             uint64_t hwcap, const char *key)
00058 {
00059   printf ("\t%s (", lib);
00060   switch (flag & FLAG_TYPE_MASK)
00061     {
00062     case FLAG_LIBC4:
00063     case FLAG_ELF:
00064     case FLAG_ELF_LIBC5:
00065     case FLAG_ELF_LIBC6:
00066       fputs (flag_descr[flag & FLAG_TYPE_MASK], stdout);
00067       break;
00068     default:
00069       fputs (_("unknown"), stdout);
00070       break;
00071     }
00072   switch (flag & FLAG_REQUIRED_MASK)
00073     {
00074     case FLAG_SPARC_LIB64:
00075       fputs (",64bit", stdout);
00076       break;
00077     case FLAG_IA64_LIB64:
00078       fputs (",IA-64", stdout);
00079       break;
00080     case FLAG_X8664_LIB64:
00081       fputs (",x86-64", stdout);
00082       break;
00083     case FLAG_S390_LIB64:
00084       fputs (",64bit", stdout);
00085       break;
00086     case FLAG_POWERPC_LIB64:
00087       fputs (",64bit", stdout);
00088       break;
00089     case FLAG_MIPS64_LIBN32:
00090       fputs (",N32", stdout);
00091       break;
00092     case FLAG_MIPS64_LIBN64:
00093       fputs (",64bit", stdout);
00094     case 0:
00095       break;
00096     default:
00097       printf (",%d", flag & FLAG_REQUIRED_MASK);
00098       break;
00099     }
00100   if (hwcap != 0)
00101     printf (", hwcap: %#.16" PRIx64, hwcap);
00102   if (osversion != 0)
00103     {
00104       static const char *const abi_tag_os[] =
00105       {
00106        [0] = "Linux",
00107        [1] = "Hurd",
00108        [2] = "Solaris",
00109        [3] = "FreeBSD",
00110        [4] = "kNetBSD",
00111        [5] = "Syllable",
00112        [6] = N_("Unknown OS")
00113       };
00114 #define MAXTAG (sizeof abi_tag_os / sizeof abi_tag_os[0] - 1)
00115       unsigned int os = osversion >> 24;
00116 
00117       printf (_(", OS ABI: %s %d.%d.%d"),
00118              _(abi_tag_os[os > MAXTAG ? MAXTAG : os]),
00119              (osversion >> 16) & 0xff,
00120              (osversion >> 8) & 0xff,
00121              osversion & 0xff);
00122     }
00123   printf (") => %s\n", key);
00124 }
00125 
00126 
00127 /* Print the whole cache file, if a file contains the new cache format
00128    hidden in the old one, print the contents of the new format.  */
00129 void
00130 print_cache (const char *cache_name)
00131 {
00132   int fd = open (cache_name, O_RDONLY);
00133   if (fd < 0)
00134     error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"), cache_name);
00135 
00136   struct stat64 st;
00137   if (fstat64 (fd, &st) < 0
00138       /* No need to map the file if it is empty.  */
00139       || st.st_size == 0)
00140     {
00141       close (fd);
00142       return;
00143     }
00144 
00145   struct cache_file *cache
00146     = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
00147   if (cache == MAP_FAILED)
00148     error (EXIT_FAILURE, errno, _("mmap of cache file failed.\n"));
00149 
00150   size_t cache_size = st.st_size;
00151   if (cache_size < sizeof (struct cache_file))
00152     error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
00153 
00154   struct cache_file_new *cache_new = NULL;
00155   const char *cache_data;
00156   int format = 0;
00157 
00158   if (memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
00159     {
00160       /* This can only be the new format without the old one.  */
00161       cache_new = (struct cache_file_new *) cache;
00162 
00163       if (memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1)
00164          || memcmp (cache_new->version, CACHE_VERSION,
00165                     sizeof CACHE_VERSION - 1))
00166        error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
00167       format = 1;
00168       /* This is where the strings start.  */
00169       cache_data = (const char *) cache_new;
00170     }
00171   else
00172     {
00173       size_t offset = ALIGN_CACHE (sizeof (struct cache_file)
00174                                + (cache->nlibs
00175                                   * sizeof (struct file_entry)));
00176       /* This is where the strings start.  */
00177       cache_data = (const char *) &cache->libs[cache->nlibs];
00178 
00179       /* Check for a new cache embedded in the old format.  */
00180       if (cache_size >
00181          (offset + sizeof (struct cache_file_new)))
00182        {
00183 
00184          cache_new = (struct cache_file_new *) ((void *)cache + offset);
00185 
00186          if (memcmp (cache_new->magic, CACHEMAGIC_NEW,
00187                     sizeof CACHEMAGIC_NEW - 1) == 0
00188              && memcmp (cache_new->version, CACHE_VERSION,
00189                       sizeof CACHE_VERSION - 1) == 0)
00190            {
00191              cache_data = (const char *) cache_new;
00192              format = 1;
00193            }
00194        }
00195     }
00196 
00197   if (format == 0)
00198     {
00199       printf (_("%d libs found in cache `%s'\n"), cache->nlibs, cache_name);
00200 
00201       /* Print everything.  */
00202       for (unsigned int i = 0; i < cache->nlibs; i++)
00203        print_entry (cache_data + cache->libs[i].key,
00204                    cache->libs[i].flags, 0, 0,
00205                    cache_data + cache->libs[i].value);
00206     }
00207   else if (format == 1)
00208     {
00209       printf (_("%d libs found in cache `%s'\n"),
00210              cache_new->nlibs, cache_name);
00211 
00212       /* Print everything.  */
00213       for (unsigned int i = 0; i < cache_new->nlibs; i++)
00214        print_entry (cache_data + cache_new->libs[i].key,
00215                    cache_new->libs[i].flags,
00216                    cache_new->libs[i].osversion,
00217                    cache_new->libs[i].hwcap,
00218                    cache_data + cache_new->libs[i].value);
00219     }
00220   /* Cleanup.  */
00221   munmap (cache, cache_size);
00222   close (fd);
00223 }
00224 
00225 /* Initialize cache data structures.  */
00226 void
00227 init_cache (void)
00228 {
00229   entries = NULL;
00230 }
00231 
00232 static int
00233 compare (const struct cache_entry *e1, const struct cache_entry *e2)
00234 {
00235   /* We need to swap entries here to get the correct sort order.  */
00236   int res = _dl_cache_libcmp (e2->lib, e1->lib);
00237   if (res == 0)
00238     {
00239       if (e1->flags < e2->flags)
00240        return 1;
00241       else if (e1->flags > e2->flags)
00242        return -1;
00243       /* Sort by most specific hwcap.  */
00244       else if (e2->bits_hwcap > e1->bits_hwcap)
00245        return 1;
00246       else if (e2->bits_hwcap < e1->bits_hwcap)
00247        return -1;
00248       else if (e2->hwcap > e1->hwcap)
00249        return 1;
00250       else if (e2->hwcap < e1->hwcap)
00251        return -1;
00252       if (e2->osversion > e1->osversion)
00253        return 1;
00254       if (e2->osversion < e1->osversion)
00255        return -1;
00256     }
00257   return res;
00258 }
00259 
00260 /* Save the contents of the cache.  */
00261 void
00262 save_cache (const char *cache_name)
00263 {
00264   /* The cache entries are sorted already, save them in this order. */
00265 
00266   /* Count the length of all strings.  */
00267   /* The old format doesn't contain hwcap entries and doesn't contain
00268      libraries in subdirectories with hwcaps entries.  Count therefore
00269      also all entries with hwcap == 0.  */
00270   size_t total_strlen = 0;
00271   struct cache_entry *entry;
00272   /* Number of cache entries.  */
00273   int cache_entry_count = 0;
00274   /* Number of normal cache entries.  */
00275   int cache_entry_old_count = 0;
00276 
00277   for (entry = entries; entry != NULL; entry = entry->next)
00278     {
00279       /* Account the final NULs.  */
00280       total_strlen += strlen (entry->lib) + strlen (entry->path) + 2;
00281       ++cache_entry_count;
00282       if (entry->hwcap == 0)
00283        ++cache_entry_old_count;
00284     }
00285 
00286   /* Create the on disk cache structure.  */
00287   struct cache_file *file_entries = NULL;
00288   size_t file_entries_size = 0;
00289 
00290   if (opt_format != 2)
00291     {
00292       /* struct cache_file_new is 64-bit aligned on some arches while
00293         only 32-bit aligned on other arches.  Duplicate last old
00294         cache entry so that new cache in ld.so.cache can be used by
00295         both.  */
00296       if (opt_format != 0)
00297        cache_entry_old_count = (cache_entry_old_count + 1) & ~1;
00298 
00299       /* And the list of all entries in the old format.  */
00300       file_entries_size = sizeof (struct cache_file)
00301        + cache_entry_old_count * sizeof (struct file_entry);
00302       file_entries = xmalloc (file_entries_size);
00303 
00304       /* Fill in the header.  */
00305       memset (file_entries, '\0', sizeof (struct cache_file));
00306       memcpy (file_entries->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1);
00307 
00308       file_entries->nlibs = cache_entry_old_count;
00309     }
00310 
00311   struct cache_file_new *file_entries_new = NULL;
00312   size_t file_entries_new_size = 0;
00313 
00314   if (opt_format != 0)
00315     {
00316       /* And the list of all entries in the new format.  */
00317       file_entries_new_size = sizeof (struct cache_file_new)
00318        + cache_entry_count * sizeof (struct file_entry_new);
00319       file_entries_new = xmalloc (file_entries_new_size);
00320 
00321       /* Fill in the header.  */
00322       memset (file_entries_new, '\0', sizeof (struct cache_file_new));
00323       memcpy (file_entries_new->magic, CACHEMAGIC_NEW,
00324              sizeof CACHEMAGIC_NEW - 1);
00325       memcpy (file_entries_new->version, CACHE_VERSION,
00326              sizeof CACHE_VERSION - 1);
00327 
00328       file_entries_new->nlibs = cache_entry_count;
00329       file_entries_new->len_strings = total_strlen;
00330     }
00331 
00332   /* Pad for alignment of cache_file_new.  */
00333   size_t pad = ALIGN_CACHE (file_entries_size) - file_entries_size;
00334 
00335   /* If we have both formats, we hide the new format in the strings
00336      table, we have to adjust all string indices for this so that
00337      old libc5/glibc 2 dynamic linkers just ignore them.  */
00338   unsigned int str_offset;
00339   if (opt_format != 0)
00340     str_offset = file_entries_new_size;
00341   else
00342     str_offset = 0;
00343 
00344   /* An array for all strings.  */
00345   char *strings = xmalloc (total_strlen);
00346   char *str = strings;
00347   int idx_old;
00348   int idx_new;
00349 
00350   for (idx_old = 0, idx_new = 0, entry = entries; entry != NULL;
00351        entry = entry->next, ++idx_new)
00352     {
00353       /* First the library.  */
00354       if (opt_format != 2 && entry->hwcap == 0)
00355        {
00356          file_entries->libs[idx_old].flags = entry->flags;
00357          /* XXX: Actually we can optimize here and remove duplicates.  */
00358          file_entries->libs[idx_old].key = str_offset + pad;
00359        }
00360       if (opt_format != 0)
00361        {
00362          /* We could subtract file_entries_new_size from str_offset -
00363             not doing so makes the code easier, the string table
00364             always begins at the beginning of the the new cache
00365             struct.  */
00366          file_entries_new->libs[idx_new].flags = entry->flags;
00367          file_entries_new->libs[idx_new].osversion = entry->osversion;
00368          file_entries_new->libs[idx_new].hwcap = entry->hwcap;
00369          file_entries_new->libs[idx_new].key = str_offset;
00370        }
00371 
00372       size_t len = strlen (entry->lib) + 1;
00373       str = mempcpy (str, entry->lib, len);
00374       str_offset += len;
00375       /* Then the path.  */
00376       if (opt_format != 2 && entry->hwcap == 0)
00377        file_entries->libs[idx_old].value = str_offset + pad;
00378       if (opt_format != 0)
00379        file_entries_new->libs[idx_new].value = str_offset;
00380       len = strlen (entry->path) + 1;
00381       str = mempcpy (str, entry->path, len);
00382       str_offset += len;
00383       /* Ignore entries with hwcap for old format.  */
00384       if (entry->hwcap == 0)
00385        ++idx_old;
00386     }
00387 
00388   /* Duplicate last old cache entry if needed.  */
00389   if (opt_format != 2
00390       && idx_old < cache_entry_old_count)
00391     file_entries->libs[idx_old] = file_entries->libs[idx_old - 1];
00392 
00393   /* Write out the cache.  */
00394 
00395   /* Write cache first to a temporary file and rename it later.  */
00396   char *temp_name = xmalloc (strlen (cache_name) + 2);
00397   sprintf (temp_name, "%s~", cache_name);
00398 
00399   /* Create file.  */
00400   int fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW,
00401                S_IRUSR|S_IWUSR);
00402   if (fd < 0)
00403     error (EXIT_FAILURE, errno, _("Can't create temporary cache file %s"),
00404           temp_name);
00405 
00406   /* Write contents.  */
00407   if (opt_format != 2)
00408     {
00409       if (write (fd, file_entries, file_entries_size)
00410          != (ssize_t) file_entries_size)
00411        error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
00412     }
00413   if (opt_format != 0)
00414     {
00415       /* Align cache.  */
00416       if (opt_format != 2)
00417        {
00418          char zero[pad];
00419          memset (zero, '\0', pad);
00420          if (write (fd, zero, pad) != (ssize_t) pad)
00421            error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
00422        }
00423       if (write (fd, file_entries_new, file_entries_new_size)
00424          != (ssize_t) file_entries_new_size)
00425        error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
00426     }
00427 
00428   if (write (fd, strings, total_strlen) != (ssize_t) total_strlen
00429       || close (fd))
00430     error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
00431 
00432   /* Make sure user can always read cache file */
00433   if (chmod (temp_name, S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR))
00434     error (EXIT_FAILURE, errno,
00435           _("Changing access rights of %s to %#o failed"), temp_name,
00436           S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR);
00437 
00438   /* Move temporary to its final location.  */
00439   if (rename (temp_name, cache_name))
00440     error (EXIT_FAILURE, errno, _("Renaming of %s to %s failed"), temp_name,
00441           cache_name);
00442 
00443   /* Free all allocated memory.  */
00444   free (file_entries_new);
00445   free (file_entries);
00446   free (strings);
00447 
00448   while (entries)
00449     {
00450       entry = entries;
00451       entries = entries->next;
00452       free (entry);
00453     }
00454 }
00455 
00456 
00457 /* Add one library to the cache.  */
00458 void
00459 add_to_cache (const char *path, const char *lib, int flags,
00460              unsigned int osversion, uint64_t hwcap)
00461 {
00462   size_t liblen = strlen (lib) + 1;
00463   size_t len = liblen + strlen (path) + 1;
00464   struct cache_entry *new_entry
00465     = xmalloc (sizeof (struct cache_entry) + liblen + len);
00466 
00467   new_entry->lib = memcpy ((char *) (new_entry + 1), lib, liblen);
00468   new_entry->path = new_entry->lib + liblen;
00469   snprintf (new_entry->path, len, "%s/%s", path, lib);
00470   new_entry->flags = flags;
00471   new_entry->osversion = osversion;
00472   new_entry->hwcap = hwcap;
00473   new_entry->bits_hwcap = 0;
00474 
00475   /* Count the number of bits set in the masked value.  */
00476   for (size_t i = 0;
00477        (~((1ULL << i) - 1) & hwcap) != 0 && i < 8 * sizeof (hwcap); ++i)
00478     if ((hwcap & (1ULL << i)) != 0)
00479       ++new_entry->bits_hwcap;
00480 
00481 
00482   /* Keep the list sorted - search for right place to insert.  */
00483   struct cache_entry *ptr = entries;
00484   struct cache_entry *prev = entries;
00485   while (ptr != NULL)
00486     {
00487       if (compare (ptr, new_entry) > 0)
00488        break;
00489       prev = ptr;
00490       ptr = ptr->next;
00491     }
00492   /* Is this the first entry?  */
00493   if (ptr == entries)
00494     {
00495       new_entry->next = entries;
00496       entries = new_entry;
00497     }
00498   else
00499     {
00500       new_entry->next = prev->next;
00501       prev->next = new_entry;
00502     }
00503 }
00504 
00505 
00506 /* Auxiliary cache.  */
00507 
00508 struct aux_cache_entry_id
00509 {
00510   uint64_t ino;
00511   uint64_t ctime;
00512   uint64_t size;
00513   uint64_t dev;
00514 };
00515 
00516 struct aux_cache_entry
00517 {
00518   struct aux_cache_entry_id id;
00519   int flags;
00520   unsigned int osversion;
00521   int used;
00522   char *soname;
00523   struct aux_cache_entry *next;
00524 };
00525 
00526 #define AUX_CACHEMAGIC             "glibc-ld.so.auxcache-1.0"
00527 
00528 struct aux_cache_file_entry
00529 {
00530   struct aux_cache_entry_id id;    /* Unique id of entry.  */
00531   int32_t flags;            /* This is 1 for an ELF library.  */
00532   uint32_t soname;          /* String table indice.  */
00533   uint32_t osversion;              /* Required OS version.      */
00534   int32_t pad;
00535 };
00536 
00537 /* ldconfig maintains an auxiliary cache file that allows
00538    only reading those libraries that have changed since the last iteration.
00539    For this for each library some information is cached in the auxiliary
00540    cache.  */
00541 struct aux_cache_file
00542 {
00543   char magic[sizeof AUX_CACHEMAGIC - 1];
00544   uint32_t nlibs;           /* Number of entries.  */
00545   uint32_t len_strings;            /* Size of string table. */
00546   struct aux_cache_file_entry libs[0]; /* Entries describing libraries.  */
00547   /* After this the string table of size len_strings is found. */
00548 };
00549 
00550 static const unsigned int primes[] =
00551 {
00552   1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139,
00553   524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393,
00554   67108859, 134217689, 268435399, 536870909, 1073741789, 2147483647
00555 };
00556 
00557 static size_t aux_hash_size;
00558 static struct aux_cache_entry **aux_hash;
00559 
00560 /* Simplistic hash function for aux_cache_entry_id.  */
00561 static unsigned int
00562 aux_cache_entry_id_hash (struct aux_cache_entry_id *id)
00563 {
00564   uint64_t ret = ((id->ino * 11 + id->ctime) * 11 + id->size) * 11 + id->dev;
00565   return ret ^ (ret >> 32);
00566 }
00567 
00568 static size_t nextprime (size_t x)
00569 {
00570   for (unsigned int i = 0; i < sizeof (primes) / sizeof (primes[0]); ++i)
00571     if (primes[i] >= x)
00572       return primes[i];
00573   return x;
00574 }
00575 
00576 void
00577 init_aux_cache (void)
00578 {
00579   aux_hash_size = primes[3];
00580   aux_hash = xcalloc (aux_hash_size, sizeof (struct aux_cache_entry *));
00581 }
00582 
00583 int
00584 search_aux_cache (struct stat64 *stat_buf, int *flags,
00585                 unsigned int *osversion, char **soname)
00586 {
00587   struct aux_cache_entry_id id;
00588   id.ino = (uint64_t) stat_buf->st_ino;
00589   id.ctime = (uint64_t) stat_buf->st_ctime;
00590   id.size = (uint64_t) stat_buf->st_size;
00591   id.dev = (uint64_t) stat_buf->st_dev;
00592 
00593   unsigned int hash = aux_cache_entry_id_hash (&id);
00594   struct aux_cache_entry *entry;
00595   for (entry = aux_hash[hash % aux_hash_size]; entry; entry = entry->next)
00596     if (id.ino == entry->id.ino
00597        && id.ctime == entry->id.ctime
00598        && id.size == entry->id.size
00599        && id.dev == entry->id.dev)
00600       {
00601        *flags = entry->flags;
00602        *osversion = entry->osversion;
00603        if (entry->soname != NULL)
00604          *soname = xstrdup (entry->soname);
00605        else
00606          *soname = NULL;
00607        entry->used = 1;
00608        return 1;
00609       }
00610 
00611   return 0;
00612 }
00613 
00614 static void
00615 insert_to_aux_cache (struct aux_cache_entry_id *id, int flags,
00616                    unsigned int osversion, const char *soname, int used)
00617 {
00618   size_t hash = aux_cache_entry_id_hash (id) % aux_hash_size;
00619   struct aux_cache_entry *entry;
00620   for (entry = aux_hash[hash]; entry; entry = entry->next)
00621     if (id->ino == entry->id.ino
00622        && id->ctime == entry->id.ctime
00623        && id->size == entry->id.size
00624        && id->dev == entry->id.dev)
00625       abort ();
00626 
00627   size_t len = soname ? strlen (soname) + 1 : 0;
00628   entry = xmalloc (sizeof (struct aux_cache_entry) + len);
00629   entry->id = *id;
00630   entry->flags = flags;
00631   entry->osversion = osversion;
00632   entry->used = used;
00633   if (soname != NULL)
00634     entry->soname = memcpy ((char *) (entry + 1), soname, len);
00635   else
00636     entry->soname = NULL;
00637   entry->next = aux_hash[hash];
00638   aux_hash[hash] = entry;
00639 }
00640 
00641 void
00642 add_to_aux_cache (struct stat64 *stat_buf, int flags,
00643                 unsigned int osversion, const char *soname)
00644 {
00645   struct aux_cache_entry_id id;
00646   id.ino = (uint64_t) stat_buf->st_ino;
00647   id.ctime = (uint64_t) stat_buf->st_ctime;
00648   id.size = (uint64_t) stat_buf->st_size;
00649   id.dev = (uint64_t) stat_buf->st_dev;
00650   insert_to_aux_cache (&id, flags, osversion, soname, 1);
00651 }
00652 
00653 /* Load auxiliary cache to search for unchanged entries.   */
00654 void
00655 load_aux_cache (const char *aux_cache_name)
00656 {
00657   int fd = open (aux_cache_name, O_RDONLY);
00658   if (fd < 0)
00659     {
00660       init_aux_cache ();
00661       return;
00662     }
00663 
00664   struct stat64 st;
00665   if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (struct aux_cache_file))
00666     {
00667       close (fd);
00668       init_aux_cache ();
00669       return;
00670     }
00671 
00672   size_t aux_cache_size = st.st_size;
00673   struct aux_cache_file *aux_cache
00674     = mmap (NULL, aux_cache_size, PROT_READ, MAP_PRIVATE, fd, 0);
00675   if (aux_cache == MAP_FAILED
00676       || aux_cache_size < sizeof (struct aux_cache_file)
00677       || memcmp (aux_cache->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1)
00678       || aux_cache->nlibs < 0
00679       || aux_cache->nlibs >= aux_cache_size)
00680     {
00681       close (fd);
00682       init_aux_cache ();
00683       return;
00684     }
00685 
00686   aux_hash_size = nextprime (aux_cache->nlibs);
00687   aux_hash = xcalloc (aux_hash_size, sizeof (struct aux_cache_entry *));
00688 
00689   const char *aux_cache_data
00690     = (const char *) &aux_cache->libs[aux_cache->nlibs];
00691   for (unsigned int i = 0; i < aux_cache->nlibs; ++i)
00692     insert_to_aux_cache (&aux_cache->libs[i].id,
00693                       aux_cache->libs[i].flags,
00694                       aux_cache->libs[i].osversion,
00695                       aux_cache->libs[i].soname == 0
00696                       ? NULL : aux_cache_data + aux_cache->libs[i].soname,
00697                       0);
00698 
00699   munmap (aux_cache, aux_cache_size);
00700   close (fd);
00701 }
00702 
00703 /* Save the contents of the auxiliary cache.  */
00704 void
00705 save_aux_cache (const char *aux_cache_name)
00706 {
00707   /* Count the length of all sonames.  We start with empty string.  */
00708   size_t total_strlen = 1;
00709   /* Number of cache entries.  */
00710   int cache_entry_count = 0;
00711 
00712   for (size_t i = 0; i < aux_hash_size; ++i)
00713     for (struct aux_cache_entry *entry = aux_hash[i];
00714         entry != NULL; entry = entry->next)
00715       if (entry->used)
00716        {
00717          ++cache_entry_count;
00718          if (entry->soname != NULL)
00719            total_strlen += strlen (entry->soname) + 1;
00720        }
00721 
00722   /* Auxiliary cache.  */
00723   size_t file_entries_size
00724     = sizeof (struct aux_cache_file)
00725       + cache_entry_count * sizeof (struct aux_cache_file_entry);
00726   struct aux_cache_file *file_entries
00727     = xmalloc (file_entries_size + total_strlen);
00728 
00729   /* Fill in the header of the auxiliary cache.  */
00730   memset (file_entries, '\0', sizeof (struct aux_cache_file));
00731   memcpy (file_entries->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1);
00732 
00733   file_entries->nlibs = cache_entry_count;
00734   file_entries->len_strings = total_strlen;
00735 
00736   /* Initial String offset for auxiliary cache is always after the
00737      special empty string.  */
00738   unsigned int str_offset = 1;
00739 
00740   /* An array for all strings.  */
00741   char *str = (char *) file_entries + file_entries_size;
00742   *str++ = '\0';
00743 
00744   size_t idx = 0;
00745   for (size_t i = 0; i < aux_hash_size; ++i)
00746     for (struct aux_cache_entry *entry = aux_hash[i];
00747         entry != NULL; entry = entry->next)
00748       if (entry->used)
00749        {
00750          file_entries->libs[idx].id = entry->id;
00751          file_entries->libs[idx].flags = entry->flags;
00752          if (entry->soname == NULL)
00753            file_entries->libs[idx].soname = 0;
00754          else
00755            {
00756              file_entries->libs[idx].soname = str_offset;
00757 
00758              size_t len = strlen (entry->soname) + 1;
00759              str = mempcpy (str, entry->soname, len);
00760              str_offset += len;
00761            }
00762          file_entries->libs[idx].osversion = entry->osversion;
00763          file_entries->libs[idx++].pad = 0;
00764        }
00765 
00766   /* Write out auxiliary cache file.  */
00767   /* Write auxiliary cache first to a temporary file and rename it later.  */
00768 
00769   char *temp_name = xmalloc (strlen (aux_cache_name) + 2);
00770   sprintf (temp_name, "%s~", aux_cache_name);
00771 
00772   /* Check that directory exists and create if needed.  */
00773   char *dir = strdupa (aux_cache_name);
00774   dir = dirname (dir);
00775 
00776   struct stat64 st;
00777   if (stat64 (dir, &st) < 0)
00778     {
00779       if (mkdir (dir, 0700) < 0)
00780        goto out_fail;
00781     }
00782 
00783   /* Create file.  */
00784   int fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW,
00785                S_IRUSR|S_IWUSR);
00786   if (fd < 0)
00787     goto out_fail;
00788 
00789   if (write (fd, file_entries, file_entries_size + total_strlen)
00790       != (ssize_t) (file_entries_size + total_strlen)
00791       || close (fd))
00792     {
00793       unlink (temp_name);
00794       goto out_fail;
00795     }
00796 
00797   /* Move temporary to its final location.  */
00798   if (rename (temp_name, aux_cache_name))
00799     unlink (temp_name);
00800 
00801 out_fail:
00802   /* Free allocated memory.  */
00803   free (temp_name);
00804   free (file_entries);
00805 }