Blame elf/cache.c

Packit 6c4009
/* Copyright (C) 1999-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Andreas Jaeger <aj@suse.de>, 1999.
Packit 6c4009
Packit 6c4009
   This program is free software; you can redistribute it and/or modify
Packit 6c4009
   it under the terms of the GNU General Public License as published
Packit 6c4009
   by the Free Software Foundation; version 2 of the License, or
Packit 6c4009
   (at your option) any later version.
Packit 6c4009
Packit 6c4009
   This program is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 6c4009
   GNU General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU General Public License
Packit 6c4009
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit Service a1e539
#include <assert.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <error.h>
Packit 6c4009
#include <dirent.h>
Packit 6c4009
#include <inttypes.h>
Packit 6c4009
#include <libgen.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
#include <sys/fcntl.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
#include <sys/types.h>
Packit 6c4009
Packit 6c4009
#include <ldconfig.h>
Packit 6c4009
#include <dl-cache.h>
Packit Service a1e539
#include <version.h>
Packit 6c4009
Packit 6c4009
struct cache_entry
Packit 6c4009
{
Packit 6c4009
  char *lib;			/* Library name.  */
Packit 6c4009
  char *path;			/* Path to find library.  */
Packit 6c4009
  int flags;			/* Flags to indicate kind of library.  */
Packit 6c4009
  unsigned int osversion;	/* Required OS version.  */
Packit 6c4009
  uint64_t hwcap;		/* Important hardware capabilities.  */
Packit 6c4009
  int bits_hwcap;		/* Number of bits set in hwcap.  */
Packit 6c4009
  struct cache_entry *next;	/* Next entry in list.  */
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* List of all cache entries.  */
Packit 6c4009
static struct cache_entry *entries;
Packit 6c4009
Packit 6c4009
static const char *flag_descr[] =
Packit 6c4009
{ "libc4", "ELF", "libc5", "libc6"};
Packit 6c4009
Packit 6c4009
/* Print a single entry.  */
Packit 6c4009
static void
Packit 6c4009
print_entry (const char *lib, int flag, unsigned int osversion,
Packit 6c4009
	     uint64_t hwcap, const char *key)
Packit 6c4009
{
Packit 6c4009
  printf ("\t%s (", lib);
Packit 6c4009
  switch (flag & FLAG_TYPE_MASK)
Packit 6c4009
    {
Packit 6c4009
    case FLAG_LIBC4:
Packit 6c4009
    case FLAG_ELF:
Packit 6c4009
    case FLAG_ELF_LIBC5:
Packit 6c4009
    case FLAG_ELF_LIBC6:
Packit 6c4009
      fputs (flag_descr[flag & FLAG_TYPE_MASK], stdout);
Packit 6c4009
      break;
Packit 6c4009
    default:
Packit 6c4009
      fputs (_("unknown"), stdout);
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
  switch (flag & FLAG_REQUIRED_MASK)
Packit 6c4009
    {
Packit 6c4009
    case FLAG_SPARC_LIB64:
Packit 6c4009
      fputs (",64bit", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_IA64_LIB64:
Packit 6c4009
      fputs (",IA-64", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_X8664_LIB64:
Packit 6c4009
      fputs (",x86-64", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_S390_LIB64:
Packit 6c4009
      fputs (",64bit", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_POWERPC_LIB64:
Packit 6c4009
      fputs (",64bit", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_MIPS64_LIBN32:
Packit 6c4009
      fputs (",N32", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_MIPS64_LIBN64:
Packit 6c4009
      fputs (",64bit", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_X8664_LIBX32:
Packit 6c4009
      fputs (",x32", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_ARM_LIBHF:
Packit 6c4009
      fputs (",hard-float", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_AARCH64_LIB64:
Packit 6c4009
      fputs (",AArch64", stdout);
Packit 6c4009
      break;
Packit 6c4009
    /* Uses the ARM soft-float ABI.  */
Packit 6c4009
    case FLAG_ARM_LIBSF:
Packit 6c4009
      fputs (",soft-float", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_MIPS_LIB32_NAN2008:
Packit 6c4009
      fputs (",nan2008", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_MIPS64_LIBN32_NAN2008:
Packit 6c4009
      fputs (",N32,nan2008", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_MIPS64_LIBN64_NAN2008:
Packit 6c4009
      fputs (",64bit,nan2008", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_RISCV_FLOAT_ABI_SOFT:
Packit 6c4009
      fputs (",soft-float", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_RISCV_FLOAT_ABI_DOUBLE:
Packit 6c4009
      fputs (",double-float", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case 0:
Packit 6c4009
      break;
Packit 6c4009
    default:
Packit 6c4009
      printf (",%d", flag & FLAG_REQUIRED_MASK);
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
  if (hwcap != 0)
Packit 6c4009
    printf (", hwcap: %#.16" PRIx64, hwcap);
Packit 6c4009
  if (osversion != 0)
Packit 6c4009
    {
Packit 6c4009
      static const char *const abi_tag_os[] =
Packit 6c4009
      {
Packit 6c4009
	[0] = "Linux",
Packit 6c4009
	[1] = "Hurd",
Packit 6c4009
	[2] = "Solaris",
Packit 6c4009
	[3] = "FreeBSD",
Packit 6c4009
	[4] = "kNetBSD",
Packit 6c4009
	[5] = "Syllable",
Packit 6c4009
	[6] = N_("Unknown OS")
Packit 6c4009
      };
Packit 6c4009
#define MAXTAG (sizeof abi_tag_os / sizeof abi_tag_os[0] - 1)
Packit 6c4009
      unsigned int os = osversion >> 24;
Packit 6c4009
Packit 6c4009
      printf (_(", OS ABI: %s %d.%d.%d"),
Packit 6c4009
	      _(abi_tag_os[os > MAXTAG ? MAXTAG : os]),
Packit 6c4009
	      (osversion >> 16) & 0xff,
Packit 6c4009
	      (osversion >> 8) & 0xff,
Packit 6c4009
	      osversion & 0xff);
Packit 6c4009
    }
Packit 6c4009
  printf (") => %s\n", key);
Packit 6c4009
}
Packit 6c4009
Packit Service 74bf73
/* Print an error and exit if the new-file cache is internally
Packit Service 74bf73
   inconsistent.  */
Packit Service 74bf73
static void
Packit Service 74bf73
check_new_cache (struct cache_file_new *cache)
Packit Service 74bf73
{
Packit Service 74bf73
  if (! cache_file_new_matches_endian (cache))
Packit Service 74bf73
    error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n"));
Packit Service 74bf73
}
Packit 6c4009
Packit Service a1e539
/* Print the extension information at the cache at start address
Packit Service a1e539
   FILE_BASE, of length FILE_SIZE bytes.  The new-format cache header
Packit Service a1e539
   is at CACHE, and the file name for diagnostics is CACHE_NAME.  */
Packit Service a1e539
static void
Packit Service a1e539
print_extensions (struct cache_extension_all_loaded *ext)
Packit Service a1e539
{
Packit Service a1e539
  if (ext->sections[cache_extension_tag_generator].base != NULL)
Packit Service a1e539
    {
Packit Service a1e539
      fputs (_("Cache generated by: "), stdout);
Packit Service a1e539
      fwrite (ext->sections[cache_extension_tag_generator].base, 1,
Packit Service a1e539
	      ext->sections[cache_extension_tag_generator].size, stdout);
Packit Service a1e539
      putchar ('\n');
Packit Service a1e539
    }
Packit Service a1e539
}
Packit Service a1e539
Packit 6c4009
/* Print the whole cache file, if a file contains the new cache format
Packit 6c4009
   hidden in the old one, print the contents of the new format.  */
Packit 6c4009
void
Packit 6c4009
print_cache (const char *cache_name)
Packit 6c4009
{
Packit 6c4009
  int fd = open (cache_name, O_RDONLY);
Packit 6c4009
  if (fd < 0)
Packit 6c4009
    error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"), cache_name);
Packit 6c4009
Packit 6c4009
  struct stat64 st;
Packit 6c4009
  if (fstat64 (fd, &st) < 0
Packit 6c4009
      /* No need to map the file if it is empty.  */
Packit 6c4009
      || st.st_size == 0)
Packit 6c4009
    {
Packit 6c4009
      close (fd);
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct cache_file *cache
Packit 6c4009
    = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
Packit 6c4009
  if (cache == MAP_FAILED)
Packit 6c4009
    error (EXIT_FAILURE, errno, _("mmap of cache file failed.\n"));
Packit 6c4009
Packit 6c4009
  size_t cache_size = st.st_size;
Packit 6c4009
  if (cache_size < sizeof (struct cache_file))
Packit 6c4009
    error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
Packit 6c4009
Packit 6c4009
  struct cache_file_new *cache_new = NULL;
Packit 6c4009
  const char *cache_data;
Packit 6c4009
  int format = 0;
Packit 6c4009
Packit 6c4009
  if (memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
Packit 6c4009
    {
Packit 6c4009
      /* This can only be the new format without the old one.  */
Packit 6c4009
      cache_new = (struct cache_file_new *) cache;
Packit 6c4009
Packit 6c4009
      if (memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1)
Packit 6c4009
	  || memcmp (cache_new->version, CACHE_VERSION,
Packit 6c4009
		      sizeof CACHE_VERSION - 1))
Packit 6c4009
	error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
Packit Service 74bf73
      check_new_cache (cache_new);
Packit 6c4009
      format = 1;
Packit 6c4009
      /* This is where the strings start.  */
Packit 6c4009
      cache_data = (const char *) cache_new;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit Service badb26
      /* Check for corruption, avoiding overflow.  */
Packit Service badb26
      if ((cache_size - sizeof (struct cache_file)) / sizeof (struct file_entry)
Packit Service badb26
	  < cache->nlibs)
Packit Service badb26
	error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
Packit Service badb26
Packit 6c4009
      size_t offset = ALIGN_CACHE (sizeof (struct cache_file)
Packit 6c4009
				   + (cache->nlibs
Packit 6c4009
				      * sizeof (struct file_entry)));
Packit 6c4009
      /* This is where the strings start.  */
Packit 6c4009
      cache_data = (const char *) &cache->libs[cache->nlibs];
Packit 6c4009
Packit 6c4009
      /* Check for a new cache embedded in the old format.  */
Packit 6c4009
      if (cache_size >
Packit 6c4009
	  (offset + sizeof (struct cache_file_new)))
Packit 6c4009
	{
Packit 6c4009
Packit 6c4009
	  cache_new = (struct cache_file_new *) ((void *)cache + offset);
Packit 6c4009
Packit 6c4009
	  if (memcmp (cache_new->magic, CACHEMAGIC_NEW,
Packit 6c4009
		      sizeof CACHEMAGIC_NEW - 1) == 0
Packit 6c4009
	      && memcmp (cache_new->version, CACHE_VERSION,
Packit 6c4009
			 sizeof CACHE_VERSION - 1) == 0)
Packit 6c4009
	    {
Packit Service 74bf73
	      check_new_cache (cache_new);
Packit 6c4009
	      cache_data = (const char *) cache_new;
Packit 6c4009
	      format = 1;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (format == 0)
Packit 6c4009
    {
Packit 6c4009
      printf (_("%d libs found in cache `%s'\n"), cache->nlibs, cache_name);
Packit 6c4009
Packit 6c4009
      /* Print everything.  */
Packit 6c4009
      for (unsigned int i = 0; i < cache->nlibs; i++)
Packit 6c4009
	print_entry (cache_data + cache->libs[i].key,
Packit 6c4009
		     cache->libs[i].flags, 0, 0,
Packit 6c4009
		     cache_data + cache->libs[i].value);
Packit 6c4009
    }
Packit 6c4009
  else if (format == 1)
Packit 6c4009
    {
Packit Service a1e539
      struct cache_extension_all_loaded ext;
Packit Service a1e539
      if (!cache_extension_load (cache_new, cache, cache_size, &ext))
Packit Service a1e539
	error (EXIT_FAILURE, 0,
Packit Service a1e539
	       _("Malformed extension data in cache file %s\n"), cache_name);
Packit Service a1e539
Packit 6c4009
      printf (_("%d libs found in cache `%s'\n"),
Packit 6c4009
	      cache_new->nlibs, cache_name);
Packit 6c4009
Packit 6c4009
      /* Print everything.  */
Packit 6c4009
      for (unsigned int i = 0; i < cache_new->nlibs; i++)
Packit 6c4009
	print_entry (cache_data + cache_new->libs[i].key,
Packit 6c4009
		     cache_new->libs[i].flags,
Packit 6c4009
		     cache_new->libs[i].osversion,
Packit 6c4009
		     cache_new->libs[i].hwcap,
Packit 6c4009
		     cache_data + cache_new->libs[i].value);
Packit Service a1e539
      print_extensions (&ext;;
Packit 6c4009
    }
Packit 6c4009
  /* Cleanup.  */
Packit 6c4009
  munmap (cache, cache_size);
Packit 6c4009
  close (fd);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Initialize cache data structures.  */
Packit 6c4009
void
Packit 6c4009
init_cache (void)
Packit 6c4009
{
Packit 6c4009
  entries = NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
compare (const struct cache_entry *e1, const struct cache_entry *e2)
Packit 6c4009
{
Packit 6c4009
  /* We need to swap entries here to get the correct sort order.  */
Packit 6c4009
  int res = _dl_cache_libcmp (e2->lib, e1->lib);
Packit 6c4009
  if (res == 0)
Packit 6c4009
    {
Packit 6c4009
      if (e1->flags < e2->flags)
Packit 6c4009
	return 1;
Packit 6c4009
      else if (e1->flags > e2->flags)
Packit 6c4009
	return -1;
Packit 6c4009
      /* Sort by most specific hwcap.  */
Packit 6c4009
      else if (e2->bits_hwcap > e1->bits_hwcap)
Packit 6c4009
	return 1;
Packit 6c4009
      else if (e2->bits_hwcap < e1->bits_hwcap)
Packit 6c4009
	return -1;
Packit 6c4009
      else if (e2->hwcap > e1->hwcap)
Packit 6c4009
	return 1;
Packit 6c4009
      else if (e2->hwcap < e1->hwcap)
Packit 6c4009
	return -1;
Packit 6c4009
      if (e2->osversion > e1->osversion)
Packit 6c4009
	return 1;
Packit 6c4009
      if (e2->osversion < e1->osversion)
Packit 6c4009
	return -1;
Packit 6c4009
    }
Packit 6c4009
  return res;
Packit 6c4009
}
Packit 6c4009
Packit Service a1e539
/* Size of the cache extension directory.  All tags are assumed to be
Packit Service a1e539
   present.  */
Packit Service a1e539
enum
Packit Service a1e539
  {
Packit Service a1e539
   cache_extension_size = (offsetof (struct cache_extension, sections)
Packit Service a1e539
			   + (cache_extension_count
Packit Service a1e539
			      * sizeof (struct cache_extension_section)))
Packit Service a1e539
  };
Packit Service a1e539
Packit Service a1e539
/* Write the cache extensions to FD.  The extension directory is
Packit Service a1e539
   assumed to be located at CACHE_EXTENSION_OFFSET.  */
Packit Service a1e539
static void
Packit Service a1e539
write_extensions (int fd, uint32_t cache_extension_offset)
Packit Service a1e539
{
Packit Service a1e539
  assert ((cache_extension_offset % 4) == 0);
Packit Service a1e539
Packit Service a1e539
  struct cache_extension *ext = xmalloc (cache_extension_size);
Packit Service a1e539
  ext->magic = cache_extension_magic;
Packit Service a1e539
  ext->count = cache_extension_count;
Packit Service a1e539
Packit Service a1e539
  for (int i = 0; i < cache_extension_count; ++i)
Packit Service a1e539
    {
Packit Service a1e539
      ext->sections[i].tag = i;
Packit Service a1e539
      ext->sections[i].flags = 0;
Packit Service a1e539
    }
Packit Service a1e539
Packit Service a1e539
  const char *generator
Packit Service a1e539
    = "ldconfig " PKGVERSION RELEASE " release version " VERSION;
Packit Service a1e539
  ext->sections[cache_extension_tag_generator].offset
Packit Service a1e539
    = cache_extension_offset + cache_extension_size;
Packit Service a1e539
  ext->sections[cache_extension_tag_generator].size = strlen (generator);
Packit Service a1e539
Packit Service a1e539
  if (write (fd, ext, cache_extension_size) != cache_extension_size
Packit Service a1e539
      || write (fd, generator, strlen (generator)) != strlen (generator))
Packit Service a1e539
    error (EXIT_FAILURE, errno, _("Writing of cache extension data failed"));
Packit Service a1e539
Packit Service a1e539
  free (ext);
Packit Service a1e539
}
Packit Service a1e539
Packit 6c4009
/* Save the contents of the cache.  */
Packit 6c4009
void
Packit 6c4009
save_cache (const char *cache_name)
Packit 6c4009
{
Packit 6c4009
  /* The cache entries are sorted already, save them in this order. */
Packit 6c4009
Packit 6c4009
  /* Count the length of all strings.  */
Packit 6c4009
  /* The old format doesn't contain hwcap entries and doesn't contain
Packit 6c4009
     libraries in subdirectories with hwcaps entries.  Count therefore
Packit 6c4009
     also all entries with hwcap == 0.  */
Packit 6c4009
  size_t total_strlen = 0;
Packit 6c4009
  struct cache_entry *entry;
Packit 6c4009
  /* Number of cache entries.  */
Packit 6c4009
  int cache_entry_count = 0;
Packit 6c4009
  /* Number of normal cache entries.  */
Packit 6c4009
  int cache_entry_old_count = 0;
Packit 6c4009
Packit 6c4009
  for (entry = entries; entry != NULL; entry = entry->next)
Packit 6c4009
    {
Packit 6c4009
      /* Account the final NULs.  */
Packit 6c4009
      total_strlen += strlen (entry->lib) + strlen (entry->path) + 2;
Packit 6c4009
      ++cache_entry_count;
Packit 6c4009
      if (entry->hwcap == 0)
Packit 6c4009
	++cache_entry_old_count;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Create the on disk cache structure.  */
Packit 6c4009
  struct cache_file *file_entries = NULL;
Packit 6c4009
  size_t file_entries_size = 0;
Packit 6c4009
Packit Service 73996b
  if (opt_format != opt_format_new)
Packit 6c4009
    {
Packit 6c4009
      /* struct cache_file_new is 64-bit aligned on some arches while
Packit 6c4009
	 only 32-bit aligned on other arches.  Duplicate last old
Packit 6c4009
	 cache entry so that new cache in ld.so.cache can be used by
Packit 6c4009
	 both.  */
Packit Service 73996b
      if (opt_format != opt_format_old)
Packit 6c4009
	cache_entry_old_count = (cache_entry_old_count + 1) & ~1;
Packit 6c4009
Packit 6c4009
      /* And the list of all entries in the old format.  */
Packit 6c4009
      file_entries_size = sizeof (struct cache_file)
Packit 6c4009
	+ cache_entry_old_count * sizeof (struct file_entry);
Packit 6c4009
      file_entries = xmalloc (file_entries_size);
Packit 6c4009
Packit 6c4009
      /* Fill in the header.  */
Packit 6c4009
      memset (file_entries, '\0', sizeof (struct cache_file));
Packit 6c4009
      memcpy (file_entries->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1);
Packit 6c4009
Packit 6c4009
      file_entries->nlibs = cache_entry_old_count;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct cache_file_new *file_entries_new = NULL;
Packit 6c4009
  size_t file_entries_new_size = 0;
Packit 6c4009
Packit Service 73996b
  if (opt_format != opt_format_old)
Packit 6c4009
    {
Packit 6c4009
      /* And the list of all entries in the new format.  */
Packit 6c4009
      file_entries_new_size = sizeof (struct cache_file_new)
Packit 6c4009
	+ cache_entry_count * sizeof (struct file_entry_new);
Packit 6c4009
      file_entries_new = xmalloc (file_entries_new_size);
Packit 6c4009
Packit 6c4009
      /* Fill in the header.  */
Packit 6c4009
      memset (file_entries_new, '\0', sizeof (struct cache_file_new));
Packit 6c4009
      memcpy (file_entries_new->magic, CACHEMAGIC_NEW,
Packit 6c4009
	      sizeof CACHEMAGIC_NEW - 1);
Packit 6c4009
      memcpy (file_entries_new->version, CACHE_VERSION,
Packit 6c4009
	      sizeof CACHE_VERSION - 1);
Packit 6c4009
Packit 6c4009
      file_entries_new->nlibs = cache_entry_count;
Packit 6c4009
      file_entries_new->len_strings = total_strlen;
Packit Service 74bf73
      file_entries_new->flags = cache_file_new_flags_endian_current;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Pad for alignment of cache_file_new.  */
Packit 6c4009
  size_t pad = ALIGN_CACHE (file_entries_size) - file_entries_size;
Packit 6c4009
Packit 6c4009
  /* If we have both formats, we hide the new format in the strings
Packit 6c4009
     table, we have to adjust all string indices for this so that
Packit 6c4009
     old libc5/glibc 2 dynamic linkers just ignore them.  */
Packit 6c4009
  unsigned int str_offset;
Packit Service 73996b
  if (opt_format != opt_format_old)
Packit 6c4009
    str_offset = file_entries_new_size;
Packit 6c4009
  else
Packit 6c4009
    str_offset = 0;
Packit 6c4009
Packit 6c4009
  /* An array for all strings.  */
Packit 6c4009
  char *strings = xmalloc (total_strlen);
Packit 6c4009
  char *str = strings;
Packit 6c4009
  int idx_old;
Packit 6c4009
  int idx_new;
Packit 6c4009
Packit 6c4009
  for (idx_old = 0, idx_new = 0, entry = entries; entry != NULL;
Packit 6c4009
       entry = entry->next, ++idx_new)
Packit 6c4009
    {
Packit 6c4009
      /* First the library.  */
Packit Service 73996b
      if (opt_format != opt_format_new && entry->hwcap == 0)
Packit 6c4009
	{
Packit 6c4009
	  file_entries->libs[idx_old].flags = entry->flags;
Packit 6c4009
	  /* XXX: Actually we can optimize here and remove duplicates.  */
Packit 6c4009
	  file_entries->libs[idx_old].key = str_offset + pad;
Packit 6c4009
	}
Packit Service 73996b
      if (opt_format != opt_format_old)
Packit 6c4009
	{
Packit 6c4009
	  /* We could subtract file_entries_new_size from str_offset -
Packit 6c4009
	     not doing so makes the code easier, the string table
Packit 6c4009
	     always begins at the beginning of the new cache
Packit 6c4009
	     struct.  */
Packit 6c4009
	  file_entries_new->libs[idx_new].flags = entry->flags;
Packit 6c4009
	  file_entries_new->libs[idx_new].osversion = entry->osversion;
Packit 6c4009
	  file_entries_new->libs[idx_new].hwcap = entry->hwcap;
Packit 6c4009
	  file_entries_new->libs[idx_new].key = str_offset;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      size_t len = strlen (entry->lib) + 1;
Packit 6c4009
      str = mempcpy (str, entry->lib, len);
Packit 6c4009
      str_offset += len;
Packit 6c4009
      /* Then the path.  */
Packit Service 73996b
      if (opt_format != opt_format_new && entry->hwcap == 0)
Packit 6c4009
	file_entries->libs[idx_old].value = str_offset + pad;
Packit Service 73996b
      if (opt_format != opt_format_old)
Packit 6c4009
	file_entries_new->libs[idx_new].value = str_offset;
Packit 6c4009
      len = strlen (entry->path) + 1;
Packit 6c4009
      str = mempcpy (str, entry->path, len);
Packit 6c4009
      str_offset += len;
Packit 6c4009
      /* Ignore entries with hwcap for old format.  */
Packit 6c4009
      if (entry->hwcap == 0)
Packit 6c4009
	++idx_old;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Duplicate last old cache entry if needed.  */
Packit Service 73996b
  if (opt_format != opt_format_new
Packit 6c4009
      && idx_old < cache_entry_old_count)
Packit 6c4009
    file_entries->libs[idx_old] = file_entries->libs[idx_old - 1];
Packit 6c4009
Packit Service a1e539
  /* Compute the location of the extension directory.  This
Packit Service a1e539
     implementation puts the directory after the string table.  The
Packit Service a1e539
     size computation matches the write calls below.  The extension
Packit Service a1e539
     directory does not exist with format 0, so the value does not
Packit Service a1e539
     matter.  */
Packit Service a1e539
  uint32_t extension_offset = 0;
Packit Service a1e539
  if (opt_format != opt_format_new)
Packit Service a1e539
    extension_offset += file_entries_size;
Packit Service a1e539
  if (opt_format != opt_format_old)
Packit Service a1e539
    {
Packit Service a1e539
      if (opt_format != opt_format_new)
Packit Service a1e539
	extension_offset += pad;
Packit Service a1e539
      extension_offset += file_entries_new_size;
Packit Service a1e539
    }
Packit Service a1e539
  extension_offset += total_strlen;
Packit Service a1e539
  extension_offset = roundup (extension_offset, 4); /* Provide alignment.  */
Packit Service a1e539
  if (opt_format != opt_format_old)
Packit Service a1e539
    file_entries_new->extension_offset = extension_offset;
Packit Service a1e539
Packit 6c4009
  /* Write out the cache.  */
Packit 6c4009
Packit 6c4009
  /* Write cache first to a temporary file and rename it later.  */
Packit 6c4009
  char *temp_name = xmalloc (strlen (cache_name) + 2);
Packit 6c4009
  sprintf (temp_name, "%s~", cache_name);
Packit 6c4009
Packit 6c4009
  /* Create file.  */
Packit 6c4009
  int fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW,
Packit 6c4009
		 S_IRUSR|S_IWUSR);
Packit 6c4009
  if (fd < 0)
Packit 6c4009
    error (EXIT_FAILURE, errno, _("Can't create temporary cache file %s"),
Packit 6c4009
	   temp_name);
Packit 6c4009
Packit 6c4009
  /* Write contents.  */
Packit Service 73996b
  if (opt_format != opt_format_new)
Packit 6c4009
    {
Packit 6c4009
      if (write (fd, file_entries, file_entries_size)
Packit 6c4009
	  != (ssize_t) file_entries_size)
Packit 6c4009
	error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
Packit 6c4009
    }
Packit Service 73996b
  if (opt_format != opt_format_old)
Packit 6c4009
    {
Packit 6c4009
      /* Align cache.  */
Packit Service 73996b
      if (opt_format != opt_format_new)
Packit 6c4009
	{
Packit 6c4009
	  char zero[pad];
Packit 6c4009
	  memset (zero, '\0', pad);
Packit 6c4009
	  if (write (fd, zero, pad) != (ssize_t) pad)
Packit 6c4009
	    error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
Packit 6c4009
	}
Packit 6c4009
      if (write (fd, file_entries_new, file_entries_new_size)
Packit 6c4009
	  != (ssize_t) file_entries_new_size)
Packit 6c4009
	error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (write (fd, strings, total_strlen) != (ssize_t) total_strlen)
Packit 6c4009
    error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
Packit 6c4009
Packit Service a1e539
  if (opt_format != opt_format_old)
Packit Service a1e539
    {
Packit Service a1e539
      /* Align file position to 4.  */
Packit Service a1e539
      off64_t old_offset = lseek64 (fd, extension_offset, SEEK_SET);
Packit Service a1e539
      assert ((unsigned long long int) (extension_offset - old_offset) < 4);
Packit Service a1e539
      write_extensions (fd, extension_offset);
Packit Service a1e539
    }
Packit Service a1e539
Packit 6c4009
  /* Make sure user can always read cache file */
Packit 6c4009
  if (chmod (temp_name, S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR))
Packit 6c4009
    error (EXIT_FAILURE, errno,
Packit 6c4009
	   _("Changing access rights of %s to %#o failed"), temp_name,
Packit 6c4009
	   S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR);
Packit 6c4009
Packit 6c4009
  /* Make sure that data is written to disk.  */
Packit 6c4009
  if (fsync (fd) != 0 || close (fd) != 0)
Packit 6c4009
    error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
Packit 6c4009
Packit 6c4009
  /* Move temporary to its final location.  */
Packit 6c4009
  if (rename (temp_name, cache_name))
Packit 6c4009
    error (EXIT_FAILURE, errno, _("Renaming of %s to %s failed"), temp_name,
Packit 6c4009
	   cache_name);
Packit 6c4009
Packit 6c4009
  /* Free all allocated memory.  */
Packit 6c4009
  free (file_entries_new);
Packit 6c4009
  free (file_entries);
Packit 6c4009
  free (strings);
Packit 6c4009
Packit 6c4009
  while (entries)
Packit 6c4009
    {
Packit 6c4009
      entry = entries;
Packit 6c4009
      entries = entries->next;
Packit 6c4009
      free (entry);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Add one library to the cache.  */
Packit 6c4009
void
Packit 6c4009
add_to_cache (const char *path, const char *lib, int flags,
Packit 6c4009
	      unsigned int osversion, uint64_t hwcap)
Packit 6c4009
{
Packit 6c4009
  size_t liblen = strlen (lib) + 1;
Packit 6c4009
  size_t len = liblen + strlen (path) + 1;
Packit 6c4009
  struct cache_entry *new_entry
Packit 6c4009
    = xmalloc (sizeof (struct cache_entry) + liblen + len);
Packit 6c4009
Packit 6c4009
  new_entry->lib = memcpy ((char *) (new_entry + 1), lib, liblen);
Packit 6c4009
  new_entry->path = new_entry->lib + liblen;
Packit 6c4009
  snprintf (new_entry->path, len, "%s/%s", path, lib);
Packit 6c4009
  new_entry->flags = flags;
Packit 6c4009
  new_entry->osversion = osversion;
Packit 6c4009
  new_entry->hwcap = hwcap;
Packit 6c4009
  new_entry->bits_hwcap = 0;
Packit 6c4009
Packit 6c4009
  /* Count the number of bits set in the masked value.  */
Packit 6c4009
  for (size_t i = 0;
Packit 6c4009
       (~((1ULL << i) - 1) & hwcap) != 0 && i < 8 * sizeof (hwcap); ++i)
Packit 6c4009
    if ((hwcap & (1ULL << i)) != 0)
Packit 6c4009
      ++new_entry->bits_hwcap;
Packit 6c4009
Packit 6c4009
Packit 6c4009
  /* Keep the list sorted - search for right place to insert.  */
Packit 6c4009
  struct cache_entry *ptr = entries;
Packit 6c4009
  struct cache_entry *prev = entries;
Packit 6c4009
  while (ptr != NULL)
Packit 6c4009
    {
Packit 6c4009
      if (compare (ptr, new_entry) > 0)
Packit 6c4009
	break;
Packit 6c4009
      prev = ptr;
Packit 6c4009
      ptr = ptr->next;
Packit 6c4009
    }
Packit 6c4009
  /* Is this the first entry?  */
Packit 6c4009
  if (ptr == entries)
Packit 6c4009
    {
Packit 6c4009
      new_entry->next = entries;
Packit 6c4009
      entries = new_entry;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      new_entry->next = prev->next;
Packit 6c4009
      prev->next = new_entry;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Auxiliary cache.  */
Packit 6c4009
Packit 6c4009
struct aux_cache_entry_id
Packit 6c4009
{
Packit 6c4009
  uint64_t ino;
Packit 6c4009
  uint64_t ctime;
Packit 6c4009
  uint64_t size;
Packit 6c4009
  uint64_t dev;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
struct aux_cache_entry
Packit 6c4009
{
Packit 6c4009
  struct aux_cache_entry_id id;
Packit 6c4009
  int flags;
Packit 6c4009
  unsigned int osversion;
Packit 6c4009
  int used;
Packit 6c4009
  char *soname;
Packit 6c4009
  struct aux_cache_entry *next;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
#define AUX_CACHEMAGIC		"glibc-ld.so.auxcache-1.0"
Packit 6c4009
Packit 6c4009
struct aux_cache_file_entry
Packit 6c4009
{
Packit 6c4009
  struct aux_cache_entry_id id;	/* Unique id of entry.  */
Packit 6c4009
  int32_t flags;		/* This is 1 for an ELF library.  */
Packit 6c4009
  uint32_t soname;		/* String table indice.  */
Packit 6c4009
  uint32_t osversion;		/* Required OS version.	 */
Packit 6c4009
  int32_t pad;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* ldconfig maintains an auxiliary cache file that allows
Packit 6c4009
   only reading those libraries that have changed since the last iteration.
Packit 6c4009
   For this for each library some information is cached in the auxiliary
Packit 6c4009
   cache.  */
Packit 6c4009
struct aux_cache_file
Packit 6c4009
{
Packit 6c4009
  char magic[sizeof AUX_CACHEMAGIC - 1];
Packit 6c4009
  uint32_t nlibs;		/* Number of entries.  */
Packit 6c4009
  uint32_t len_strings;		/* Size of string table. */
Packit 6c4009
  struct aux_cache_file_entry libs[0]; /* Entries describing libraries.  */
Packit 6c4009
  /* After this the string table of size len_strings is found.	*/
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static const unsigned int primes[] =
Packit 6c4009
{
Packit 6c4009
  1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139,
Packit 6c4009
  524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393,
Packit 6c4009
  67108859, 134217689, 268435399, 536870909, 1073741789, 2147483647
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static size_t aux_hash_size;
Packit 6c4009
static struct aux_cache_entry **aux_hash;
Packit 6c4009
Packit 6c4009
/* Simplistic hash function for aux_cache_entry_id.  */
Packit 6c4009
static unsigned int
Packit 6c4009
aux_cache_entry_id_hash (struct aux_cache_entry_id *id)
Packit 6c4009
{
Packit 6c4009
  uint64_t ret = ((id->ino * 11 + id->ctime) * 11 + id->size) * 11 + id->dev;
Packit 6c4009
  return ret ^ (ret >> 32);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static size_t nextprime (size_t x)
Packit 6c4009
{
Packit 6c4009
  for (unsigned int i = 0; i < sizeof (primes) / sizeof (primes[0]); ++i)
Packit 6c4009
    if (primes[i] >= x)
Packit 6c4009
      return primes[i];
Packit 6c4009
  return x;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
init_aux_cache (void)
Packit 6c4009
{
Packit 6c4009
  aux_hash_size = primes[3];
Packit 6c4009
  aux_hash = xcalloc (aux_hash_size, sizeof (struct aux_cache_entry *));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
search_aux_cache (struct stat64 *stat_buf, int *flags,
Packit 6c4009
		  unsigned int *osversion, char **soname)
Packit 6c4009
{
Packit 6c4009
  struct aux_cache_entry_id id;
Packit 6c4009
  id.ino = (uint64_t) stat_buf->st_ino;
Packit 6c4009
  id.ctime = (uint64_t) stat_buf->st_ctime;
Packit 6c4009
  id.size = (uint64_t) stat_buf->st_size;
Packit 6c4009
  id.dev = (uint64_t) stat_buf->st_dev;
Packit 6c4009
Packit 6c4009
  unsigned int hash = aux_cache_entry_id_hash (&id;;
Packit 6c4009
  struct aux_cache_entry *entry;
Packit 6c4009
  for (entry = aux_hash[hash % aux_hash_size]; entry; entry = entry->next)
Packit 6c4009
    if (id.ino == entry->id.ino
Packit 6c4009
	&& id.ctime == entry->id.ctime
Packit 6c4009
	&& id.size == entry->id.size
Packit 6c4009
	&& id.dev == entry->id.dev)
Packit 6c4009
      {
Packit 6c4009
	*flags = entry->flags;
Packit 6c4009
	*osversion = entry->osversion;
Packit 6c4009
	if (entry->soname != NULL)
Packit 6c4009
	  *soname = xstrdup (entry->soname);
Packit 6c4009
	else
Packit 6c4009
	  *soname = NULL;
Packit 6c4009
	entry->used = 1;
Packit 6c4009
	return 1;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
insert_to_aux_cache (struct aux_cache_entry_id *id, int flags,
Packit 6c4009
		     unsigned int osversion, const char *soname, int used)
Packit 6c4009
{
Packit 6c4009
  size_t hash = aux_cache_entry_id_hash (id) % aux_hash_size;
Packit 6c4009
  struct aux_cache_entry *entry;
Packit 6c4009
  for (entry = aux_hash[hash]; entry; entry = entry->next)
Packit 6c4009
    if (id->ino == entry->id.ino
Packit 6c4009
	&& id->ctime == entry->id.ctime
Packit 6c4009
	&& id->size == entry->id.size
Packit 6c4009
	&& id->dev == entry->id.dev)
Packit 6c4009
      abort ();
Packit 6c4009
Packit 6c4009
  size_t len = soname ? strlen (soname) + 1 : 0;
Packit 6c4009
  entry = xmalloc (sizeof (struct aux_cache_entry) + len);
Packit 6c4009
  entry->id = *id;
Packit 6c4009
  entry->flags = flags;
Packit 6c4009
  entry->osversion = osversion;
Packit 6c4009
  entry->used = used;
Packit 6c4009
  if (soname != NULL)
Packit 6c4009
    entry->soname = memcpy ((char *) (entry + 1), soname, len);
Packit 6c4009
  else
Packit 6c4009
    entry->soname = NULL;
Packit 6c4009
  entry->next = aux_hash[hash];
Packit 6c4009
  aux_hash[hash] = entry;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
add_to_aux_cache (struct stat64 *stat_buf, int flags,
Packit 6c4009
		  unsigned int osversion, const char *soname)
Packit 6c4009
{
Packit 6c4009
  struct aux_cache_entry_id id;
Packit 6c4009
  id.ino = (uint64_t) stat_buf->st_ino;
Packit 6c4009
  id.ctime = (uint64_t) stat_buf->st_ctime;
Packit 6c4009
  id.size = (uint64_t) stat_buf->st_size;
Packit 6c4009
  id.dev = (uint64_t) stat_buf->st_dev;
Packit 6c4009
  insert_to_aux_cache (&id, flags, osversion, soname, 1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Load auxiliary cache to search for unchanged entries.   */
Packit 6c4009
void
Packit 6c4009
load_aux_cache (const char *aux_cache_name)
Packit 6c4009
{
Packit 6c4009
  int fd = open (aux_cache_name, O_RDONLY);
Packit 6c4009
  if (fd < 0)
Packit 6c4009
    {
Packit 6c4009
      init_aux_cache ();
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct stat64 st;
Packit 6c4009
  if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (struct aux_cache_file))
Packit 6c4009
    {
Packit 6c4009
      close (fd);
Packit 6c4009
      init_aux_cache ();
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  size_t aux_cache_size = st.st_size;
Packit 6c4009
  struct aux_cache_file *aux_cache
Packit 6c4009
    = mmap (NULL, aux_cache_size, PROT_READ, MAP_PRIVATE, fd, 0);
Packit 6c4009
  if (aux_cache == MAP_FAILED
Packit 6c4009
      || aux_cache_size < sizeof (struct aux_cache_file)
Packit 6c4009
      || memcmp (aux_cache->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1)
Packit 6c4009
      || aux_cache_size != (sizeof(struct aux_cache_file) +
Packit 6c4009
			    aux_cache->nlibs * sizeof(struct aux_cache_file_entry) +
Packit 6c4009
			    aux_cache->len_strings))
Packit 6c4009
    {
Packit 6c4009
      close (fd);
Packit 6c4009
      init_aux_cache ();
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  aux_hash_size = nextprime (aux_cache->nlibs);
Packit 6c4009
  aux_hash = xcalloc (aux_hash_size, sizeof (struct aux_cache_entry *));
Packit 6c4009
Packit 6c4009
  const char *aux_cache_data
Packit 6c4009
    = (const char *) &aux_cache->libs[aux_cache->nlibs];
Packit 6c4009
  for (unsigned int i = 0; i < aux_cache->nlibs; ++i)
Packit 6c4009
    insert_to_aux_cache (&aux_cache->libs[i].id,
Packit 6c4009
			 aux_cache->libs[i].flags,
Packit 6c4009
			 aux_cache->libs[i].osversion,
Packit 6c4009
			 aux_cache->libs[i].soname == 0
Packit 6c4009
			 ? NULL : aux_cache_data + aux_cache->libs[i].soname,
Packit 6c4009
			 0);
Packit 6c4009
Packit 6c4009
  munmap (aux_cache, aux_cache_size);
Packit 6c4009
  close (fd);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Save the contents of the auxiliary cache.  */
Packit 6c4009
void
Packit 6c4009
save_aux_cache (const char *aux_cache_name)
Packit 6c4009
{
Packit 6c4009
  /* Count the length of all sonames.  We start with empty string.  */
Packit 6c4009
  size_t total_strlen = 1;
Packit 6c4009
  /* Number of cache entries.  */
Packit 6c4009
  int cache_entry_count = 0;
Packit 6c4009
Packit 6c4009
  for (size_t i = 0; i < aux_hash_size; ++i)
Packit 6c4009
    for (struct aux_cache_entry *entry = aux_hash[i];
Packit 6c4009
	 entry != NULL; entry = entry->next)
Packit 6c4009
      if (entry->used)
Packit 6c4009
	{
Packit 6c4009
	  ++cache_entry_count;
Packit 6c4009
	  if (entry->soname != NULL)
Packit 6c4009
	    total_strlen += strlen (entry->soname) + 1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
  /* Auxiliary cache.  */
Packit 6c4009
  size_t file_entries_size
Packit 6c4009
    = sizeof (struct aux_cache_file)
Packit 6c4009
      + cache_entry_count * sizeof (struct aux_cache_file_entry);
Packit 6c4009
  struct aux_cache_file *file_entries
Packit 6c4009
    = xmalloc (file_entries_size + total_strlen);
Packit 6c4009
Packit 6c4009
  /* Fill in the header of the auxiliary cache.  */
Packit 6c4009
  memset (file_entries, '\0', sizeof (struct aux_cache_file));
Packit 6c4009
  memcpy (file_entries->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1);
Packit 6c4009
Packit 6c4009
  file_entries->nlibs = cache_entry_count;
Packit 6c4009
  file_entries->len_strings = total_strlen;
Packit 6c4009
Packit 6c4009
  /* Initial String offset for auxiliary cache is always after the
Packit 6c4009
     special empty string.  */
Packit 6c4009
  unsigned int str_offset = 1;
Packit 6c4009
Packit 6c4009
  /* An array for all strings.  */
Packit 6c4009
  char *str = (char *) file_entries + file_entries_size;
Packit 6c4009
  *str++ = '\0';
Packit 6c4009
Packit 6c4009
  size_t idx = 0;
Packit 6c4009
  for (size_t i = 0; i < aux_hash_size; ++i)
Packit 6c4009
    for (struct aux_cache_entry *entry = aux_hash[i];
Packit 6c4009
	 entry != NULL; entry = entry->next)
Packit 6c4009
      if (entry->used)
Packit 6c4009
	{
Packit 6c4009
	  file_entries->libs[idx].id = entry->id;
Packit 6c4009
	  file_entries->libs[idx].flags = entry->flags;
Packit 6c4009
	  if (entry->soname == NULL)
Packit 6c4009
	    file_entries->libs[idx].soname = 0;
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      file_entries->libs[idx].soname = str_offset;
Packit 6c4009
Packit 6c4009
	      size_t len = strlen (entry->soname) + 1;
Packit 6c4009
	      str = mempcpy (str, entry->soname, len);
Packit 6c4009
	      str_offset += len;
Packit 6c4009
	    }
Packit 6c4009
	  file_entries->libs[idx].osversion = entry->osversion;
Packit 6c4009
	  file_entries->libs[idx++].pad = 0;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
  /* Write out auxiliary cache file.  */
Packit 6c4009
  /* Write auxiliary cache first to a temporary file and rename it later.  */
Packit 6c4009
Packit 6c4009
  char *temp_name = xmalloc (strlen (aux_cache_name) + 2);
Packit 6c4009
  sprintf (temp_name, "%s~", aux_cache_name);
Packit 6c4009
Packit 6c4009
  /* Check that directory exists and create if needed.  */
Packit 6c4009
  char *dir = strdupa (aux_cache_name);
Packit 6c4009
  dir = dirname (dir);
Packit 6c4009
Packit 6c4009
  struct stat64 st;
Packit 6c4009
  if (stat64 (dir, &st) < 0)
Packit 6c4009
    {
Packit 6c4009
      if (mkdir (dir, 0700) < 0)
Packit 6c4009
	goto out_fail;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Create file.  */
Packit 6c4009
  int fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW,
Packit 6c4009
		 S_IRUSR|S_IWUSR);
Packit 6c4009
  if (fd < 0)
Packit 6c4009
    goto out_fail;
Packit 6c4009
Packit 6c4009
  if (write (fd, file_entries, file_entries_size + total_strlen)
Packit 6c4009
      != (ssize_t) (file_entries_size + total_strlen)
Packit 6c4009
      || fdatasync (fd) != 0
Packit 6c4009
      || close (fd) != 0)
Packit 6c4009
    {
Packit 6c4009
      unlink (temp_name);
Packit 6c4009
      goto out_fail;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Move temporary to its final location.  */
Packit 6c4009
  if (rename (temp_name, aux_cache_name))
Packit 6c4009
    unlink (temp_name);
Packit 6c4009
Packit 6c4009
out_fail:
Packit 6c4009
  /* Free allocated memory.  */
Packit 6c4009
  free (temp_name);
Packit 6c4009
  free (file_entries);
Packit 6c4009
}