[v2,1/1] ldconfig: add --install option

Message ID xnzf1ic3t8.fsf@greed.delorie.com (mailing list archive)
State Superseded
Headers
Series [v2,1/1] ldconfig: add --install option |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
redhat-pt-bot/TryBot-32bit fail Patch series failed to apply
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 fail Build failed
linaro-tcwg-bot/tcwg_glibc_build--master-arm fail Build failed

Commit Message

DJ Delorie May 29, 2026, 3:06 a.m. UTC
  Add --install option, which copies a pre-built ld.so.cache into place,
honoring the cache and root options and defaults.  This gives the user
a canonical "correct" way to install a pre-built cache.
---
 NEWS           |   2 +
 elf/ldconfig.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 140 insertions(+), 2 deletions(-)
  

Comments

Florian Weimer June 3, 2026, 3:25 p.m. UTC | #1
* DJ Delorie:

> Add --install option, which copies a pre-built ld.so.cache into place,
> honoring the cache and root options and defaults.  This gives the user
> a canonical "correct" way to install a pre-built cache.

It avoids truncating the file in place.  This is what cp does, leading
to crashes when /etc/ld.so.conf is read.

> +      /* If the two files are on the same filesystem, we can just
> +	 rename it.  */
> +      if (rename (source, cache_file) == 0)
> +	{
> +	  /* A simple rename was sufficient.  */
> +	  close (src_fd);
> +	  exit (0);
> +	}

Can we please always make a copy, so that the original file does not get
deleted mysteriously?

Thanks,
Florian
  

Patch

diff --git a/NEWS b/NEWS
index e2173fa1aa..484d09e6b3 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,8 @@  Major new features:
 * Static PIE is now supported for arm-*-linux-gnueabi.  It requires toolchain
   support to correctly set the expected linker options.
 
+* Pre-built ld.so.cache files can be installed with ldconfig.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * Although malloc and related functions currently return pointers
diff --git a/elf/ldconfig.c b/elf/ldconfig.c
index 070e933df6..df225df6e9 100644
--- a/elf/ldconfig.c
+++ b/elf/ldconfig.c
@@ -104,6 +104,9 @@  static int opt_manual_link;
 /* Should we ignore an old auxiliary cache file?  */
 static int opt_ignore_aux_cache;
 
+/* Install a pre-existing cache file instead of generating a new one.  */
+static int opt_install;
+
 /* Cache file to use.  */
 static char *cache_file;
 
@@ -132,6 +135,7 @@  static const struct argp_option options[] =
   { NULL, 'l', NULL, 0, N_("Manually link individual libraries."), 0},
   { "format", 'c', N_("FORMAT"), 0, N_("Format to use: new (default), old, or compat"), 0},
   { "ignore-aux-cache", 'i', NULL, 0, N_("Ignore auxiliary cache file"), 0},
+  { "install", 'I', NULL, 0, N_("install pre-existing cache file"), 0},
   { NULL, 0, NULL, 0, NULL, 0 }
 };
 
@@ -197,6 +201,9 @@  parse_opt (int key, char *arg, struct argp_state *state)
       else if (strcmp (arg, "new") == 0)
 	opt_format = opt_format_new;
       break;
+    case 'I':
+      opt_install = 1;
+      break;
     default:
       return ARGP_ERR_UNKNOWN;
     }
@@ -1045,8 +1052,8 @@  main (int argc, char **argv)
   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
 
   /* Remaining arguments are additional directories if opt_manual_link
-     is not set.  */
-  if (remaining != argc && !opt_manual_link)
+     and opt_install are not set.  */
+  if (remaining != argc && !opt_manual_link && !opt_install)
     {
       int i;
       for (i = remaining; i < argc; ++i)
@@ -1139,6 +1146,135 @@  main (int argc, char **argv)
       exit (0);
     }
 
+  if (opt_install)
+    {
+      if (argv[remaining] == NULL)
+	error (EXIT_FAILURE, 0, _("Missing source file name"));
+
+      char *source = (opt_chroot
+		      ? chroot_canon (opt_chroot, argv[remaining])
+		      : argv[remaining]);
+      if (source == NULL)
+	error (EXIT_FAILURE, errno, _("Can't find %s"), argv[remaining]);
+
+      int src_fd = open (source, O_RDONLY);
+      if (src_fd < 0)
+	error (EXIT_FAILURE, errno, _("Can't open %s"), source);
+
+      char *dest = xmalloc (strlen (cache_file) + 1 + 1);
+
+      /* This matches the temp file created by cache.c, and should be
+	 on the same filesystem as the cache file.  */
+      sprintf(dest, "%s~", cache_file);
+      int dest_fd;
+
+      struct stat st;
+      if (fstat (src_fd, &st) < 0)
+	error (EXIT_FAILURE, errno, _("Can't stat %s"), source);
+
+      char buf[512];
+      int r, w, sz = 0;
+      char *bp = buf;
+
+      /* Read the first part of the file and verify it looks
+	 reasonable.  */
+      while (sz < sizeof (buf)
+	     && (r = read (src_fd, bp, sizeof (buf) - sz)) > 0)
+	{
+	  sz += r;
+	  bp += r;
+	}
+      if (r < 0)
+	error (EXIT_FAILURE, errno, _("Error reading file %s"), source);
+
+      if (! ((sz >= sizeof (CACHEMAGIC)
+	      && memcmp (buf, CACHEMAGIC,
+			 sizeof (CACHEMAGIC) - 1) == 0)
+	     || (sz >= sizeof (CACHEMAGIC_NEW)
+		 && memcmp (buf, CACHEMAGIC_NEW,
+			    sizeof (CACHEMAGIC_NEW) - 1) == 0)))
+	{
+	  error (EXIT_FAILURE, 0,
+		 _("File %s does not look like an ld.so.cache file"),
+		 source);
+	}
+
+      /* If the two files are on the same filesystem, we can just
+	 rename it.  */
+      if (rename (source, cache_file) == 0)
+	{
+	  /* A simple rename was sufficient.  */
+	  close (src_fd);
+	  exit (0);
+	}
+      /* Otherwise, a cross-filesystem copy/rename is needed.  */
+
+      /* Now write that first part out.  */
+      dest_fd = open (dest, O_WRONLY, 0644);
+      if (dest_fd < 0)
+	error (EXIT_FAILURE, errno, _("Can't create %s"), dest);
+
+      r = sz;
+      bp = buf;
+      while (r > 0 && (w = write (dest_fd, bp, r)) > 0)
+	{
+	  r -= w;
+	  bp += w;
+	}
+      if (w < 0)
+	{
+	  unlink (dest);
+	  close (dest_fd);
+	  error (EXIT_FAILURE, errno, _("Error writing file %s"), dest);
+	}
+
+      /* At this point, sz contains the number of bytes copied so far.
+	 Copy the rest of the file.  */
+      while ((r = read (src_fd, buf, sizeof(buf))) > 0)
+	{
+	  bp = buf;
+	  while (r > 0 && (w = write (dest_fd, bp, r)) > 0)
+	    {
+	      bp += r;
+	      r -= w;
+	      sz += w;
+	    }
+	  if (w <= 0)
+	    break;
+	}
+
+      close (src_fd);
+
+      /* Make sure we copied it all.  */
+      if (sz < st.st_size)
+	{
+	  unlink (dest);
+	  close (dest_fd);
+	  error (EXIT_FAILURE, errno, _("Unable to copy file %s to %s"),
+		 source, dest);
+	}
+
+      /* Make sure user can always read the cache file */
+      if (fchmod (dest_fd, S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR))
+	{
+	  unlink (dest);
+	  close (dest_fd);
+	  error (EXIT_FAILURE, errno,
+		 _("Changing access rights of %s to %#o failed"), dest,
+		 S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR);
+	}
+
+      fsync (dest_fd);
+      if (rename (dest, cache_file) < 0)
+	{
+	  unlink (dest);
+	  close (dest_fd);
+	  error (EXIT_FAILURE, errno, _("Can't rename %s to %s"), dest, cache_file);
+	}
+
+      close (dest_fd);
+      exit (0);
+    }
 
   if (opt_build_cache)
     init_cache ();