about summary refs log tree commit diff
path: root/elf/ldconfig.c
diff options
context:
space:
mode:
Diffstat (limited to 'elf/ldconfig.c')
-rw-r--r--elf/ldconfig.c250
1 files changed, 207 insertions, 43 deletions
diff --git a/elf/ldconfig.c b/elf/ldconfig.c
index 9202567a10..2e8746e75c 100644
--- a/elf/ldconfig.c
+++ b/elf/ldconfig.c
@@ -110,7 +110,7 @@ static char *opt_chroot;
 static int opt_manual_link = 0;
 
 /* Cache file to use.  */
-static const char *cache_file;
+static char *cache_file;
 
 /* Configuration file.  */
 static const char *config_file;
@@ -334,12 +334,36 @@ add_dir (const char *line)
 }
 
 
+static int
+chroot_stat (const char *real_path, const char *path, struct stat64 *st)
+{
+  int ret;
+  char *canon_path;
+
+  if (!opt_chroot)
+    return stat64 (real_path, st);
+
+  ret = lstat64 (real_path, st);
+  if (ret || !S_ISLNK (st->st_mode))
+    return ret;
+
+  canon_path = chroot_canon (opt_chroot, path);
+  if (canon_path == NULL)
+    return -1;
+
+  ret = stat64 (canon_path, st);
+  free (canon_path);
+  return ret;
+}
+
 /* Create a symbolic link from soname to libname in directory path.  */
 static void
-create_links (const char *path, const char *libname, const char *soname)
+create_links (const char *real_path, const char *path, const char *libname,
+	      const char *soname)
 {
   char *full_libname, *full_soname;
-  struct stat stat_lib, stat_so, lstat_so;
+  char *real_full_libname, *real_full_soname;
+  struct stat64 stat_lib, stat_so, lstat_so;
   int do_link = 1;
   int do_remove = 1;
   /* XXX: The logics in this function should be simplified.  */
@@ -349,11 +373,23 @@ create_links (const char *path, const char *libname, const char *soname)
   full_soname = alloca (strlen (path) + strlen (libname) + 2);
   sprintf (full_libname, "%s/%s", path, libname);
   sprintf (full_soname, "%s/%s", path, soname);
+  if (opt_chroot)
+    {
+      real_full_libname = alloca (strlen (real_path) + strlen (libname) + 2);
+      real_full_soname = alloca (strlen (real_path) + strlen (libname) + 2);
+      sprintf (real_full_libname, "%s/%s", real_path, libname);
+      sprintf (real_full_soname, "%s/%s", real_path, soname);
+    }
+  else
+    {
+      real_full_libname = full_libname;
+      real_full_soname = full_soname;
+    }
 
   /* Does soname already exist and point to the right library?  */
-  if (stat (full_soname, &stat_so) == 0)
+  if (chroot_stat (real_full_soname, full_soname, &stat_so) == 0)
     {
-      if (stat (full_libname, &stat_lib))
+      if (chroot_stat (real_full_libname, full_libname, &stat_lib))
 	{
 	  error (0, 0, _("Can't stat %s\n"), full_libname);
 	  return;
@@ -362,7 +398,7 @@ create_links (const char *path, const char *libname, const char *soname)
 	  && stat_lib.st_ino == stat_so.st_ino)
 	/* Link is already correct.  */
 	do_link = 0;
-      else if (lstat (full_soname, &lstat_so) == 0
+      else if (lstat64 (full_soname, &lstat_so) == 0
 	       && !S_ISLNK (lstat_so.st_mode))
 	{
 	  error (0, 0, _("%s is not a symbolic link\n"), full_soname);
@@ -370,7 +406,7 @@ create_links (const char *path, const char *libname, const char *soname)
 	  do_remove = 0;
 	}
     }
-  else if (lstat (full_soname, &lstat_so) != 0
+  else if (lstat64 (real_full_soname, &lstat_so) != 0
 	   || !S_ISLNK (lstat_so.st_mode))
     /* Unless it is a stale symlink, there is no need to remove.  */
     do_remove = 0;
@@ -382,13 +418,13 @@ create_links (const char *path, const char *libname, const char *soname)
     {
       /* Remove old link.  */
       if (do_remove)
-	if (unlink (full_soname))
+	if (unlink (real_full_soname))
 	  {
 	    error (0, 0, _("Can't unlink %s"), full_soname);
 	    do_link = 0;
 	  }
       /* Create symbolic link.  */
-      if (do_link && symlink (libname, full_soname))
+      if (do_link && symlink (libname, real_full_soname))
 	{
 	  error (0, 0, _("Can't link %s to %s"), full_soname, libname);
 	  do_link = 0;
@@ -410,9 +446,11 @@ static void
 manual_link (char *library)
 {
   char *path;
+  char *real_path;
+  char *real_library;
   char *libname;
   char *soname;
-  struct stat stat_buf;
+  struct stat64 stat_buf;
   int flag;
 
   /* Prepare arguments for create_links call.  Split library name in
@@ -445,8 +483,26 @@ manual_link (char *library)
       strcpy (path, ".");
     }
 
+  if (opt_chroot)
+    {
+      real_path = chroot_canon (opt_chroot, path);
+      if (real_path == NULL)
+	{
+	  error (0, errno, _("Can't find %s"), path);
+	  free (path);
+	  return;
+	}
+      real_library = alloca (strlen (real_path) + strlen (libname) + 2);
+      sprintf (real_library, "%s/%s", real_path, libname);
+    }
+  else
+    {
+      real_path = path;
+      real_library = library;
+    }
+
   /* Do some sanity checks first.  */
-  if (lstat (library, &stat_buf))
+  if (lstat64 (real_library, &stat_buf))
     {
       error (0, errno, _("Can't lstat %s"), library);
       free (path);
@@ -460,15 +516,14 @@ manual_link (char *library)
       free (path);
       return;
     }
-  libname = basename (library);
-  if (process_file (library, libname, &flag, &soname, 0))
+  if (process_file (real_library, library, libname, &flag, &soname, 0))
     {
       error (0, 0, _("No link created since soname could not be found for %s"),
 	     library);
       free (path);
       return;
     }
-  create_links (path, libname, soname);
+  create_links (real_path, path, libname, soname);
   free (soname);
   free (path);
 }
@@ -514,12 +569,12 @@ search_dir (const struct dir_entry *entry)
 {
   DIR *dir;
   struct dirent *direntry;
-  char *file_name;
-  int file_name_len, len;
+  char *file_name, *dir_name, *real_file_name, *real_name;
+  int file_name_len, real_file_name_len, len;
   char *soname;
   struct dlib_entry *dlibs;
   struct dlib_entry *dlib_ptr;
-  struct stat stat_buf;
+  struct stat64 stat_buf;
   int is_link;
   unsigned long int hwcap = path_hwcap (entry->path);
 
@@ -536,15 +591,28 @@ search_dir (const struct dir_entry *entry)
 	printf ("%s:\n", entry->path);
     }
 
-  dir = opendir (entry->path);
-  if (dir == NULL)
+  if (opt_chroot)
+    {
+      dir_name = chroot_canon (opt_chroot, entry->path);
+      real_file_name_len = PATH_MAX;
+      real_file_name = alloca (real_file_name_len);
+    }
+  else
+    {
+      dir_name = entry->path;
+      real_file_name_len = 0;
+      real_file_name = file_name;
+    }
+
+  if (dir_name == NULL || (dir = opendir (dir_name)) == NULL)
     {
       if (opt_verbose)
 	error (0, errno, _("Can't open directory %s"), entry->path);
+      if (opt_chroot && dir_name)
+	free (dir_name);
       return;
     }
 
-
   while ((direntry = readdir (dir)) != NULL)
     {
       int flag;
@@ -569,14 +637,26 @@ search_dir (const struct dir_entry *entry)
 	{
 	  file_name_len = len + 1;
 	  file_name = alloca (file_name_len);
+	  if (!opt_chroot)
+	    real_file_name = file_name;
+	}
+      sprintf (file_name, "%s/%s", entry->path, direntry->d_name);
+      if (opt_chroot)
+	{
+	  len = strlen (dir_name) + strlen (direntry->d_name);
+	  if (len > real_file_name_len)
+	    {
+	      real_file_name_len = len + 1;
+	      real_file_name = alloca (real_file_name_len);
+	    }
+	  sprintf (real_file_name, "%s/%s", dir_name, direntry->d_name);
 	}
-      sprintf (file_name , "%s/%s", entry->path, direntry->d_name);
 #ifdef _DIRENT_HAVE_D_TYPE
       if (direntry->d_type != DT_UNKNOWN)
 	stat_buf.st_mode = DTTOIF (direntry->d_type);
       else
 #endif
-	if (lstat (file_name, &stat_buf))
+	if (lstat64 (real_file_name, &stat_buf))
 	  {
 	    error (0, errno, _("Can't lstat %s"), file_name);
 	    continue;
@@ -598,9 +678,29 @@ search_dir (const struct dir_entry *entry)
 	continue;
 
       is_link = S_ISLNK (stat_buf.st_mode);
+      if (opt_chroot && is_link)
+	{
+	  real_name = chroot_canon (opt_chroot, file_name);
+	  if (real_name == NULL)
+	    {
+	      if (strstr (file_name, ".so") == NULL)
+		error (0, 0, _("Input file %s not found.\n"), file_name);
+	      continue;
+	    }
+	}
+      else
+	real_name = real_file_name;
 
-      if (process_file (file_name, direntry->d_name, &flag, &soname, is_link))
-	continue;
+      if (process_file (real_name, file_name, direntry->d_name, &flag,
+			&soname, is_link))
+	{
+	  if (real_name != real_file_name)
+	    free (real_name);
+	  continue;
+	}
+
+      if (real_name != real_file_name)
+	free (real_name);
 
       /* Links will just point to itself.  */
       if (is_link)
@@ -686,7 +786,8 @@ search_dir (const struct dir_entry *entry)
     {
       /* Don't create links to links.  */
       if (dlib_ptr->is_link == 0)
-	create_links (entry->path, dlib_ptr->name, dlib_ptr->soname);
+	create_links (dir_name, entry->path, dlib_ptr->name,
+		      dlib_ptr->soname);
       if (opt_build_cache)
 	add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag, hwcap);
     }
@@ -700,6 +801,9 @@ search_dir (const struct dir_entry *entry)
       dlibs = dlibs->next;
       free (dlib_ptr);
     }
+
+  if (opt_chroot && dir_name)
+    free (dir_name);
 }
 
 /* Search through all libraries.  */
@@ -726,19 +830,36 @@ search_dirs (void)
 static void
 parse_conf (const char *filename)
 {
-  FILE *file;
+  FILE *file = NULL;
   char *line = NULL;
+  const char *canon;
   size_t len = 0;
 
-  file = fopen (filename, "r");
+  if (opt_chroot)
+    {
+      canon = chroot_canon (opt_chroot, filename);
+      if (canon)
+	file = fopen (canon, "r");
+      else
+	canon = filename;
+    }
+  else
+    {
+      canon = filename;
+      file = fopen (filename, "r");
+    }
 
   if (file == NULL)
     {
-      error (0, errno, _("Can't open configuration file %s%s%s"),
-	     opt_chroot ?: "", opt_chroot ? "/" : "", filename);
+      error (0, errno, _("Can't open configuration file %s"), canon);
+      if (canon != filename)
+	free ((char *) canon);
       return;
     }
 
+  if (canon != filename)
+    free ((char *) canon);
+
   do
     {
       ssize_t n = getline (&line, &len, file);
@@ -783,35 +904,78 @@ main (int argc, char **argv)
 	add_dir (argv [i]);
     }
 
-  if (cache_file == NULL)
-    cache_file = LD_SO_CACHE;
-
-  if (config_file == NULL)
-    config_file = LD_SO_CONF;
-
-  /* Chroot first.  */
   if (opt_chroot)
     {
       /* Normalize the path a bit, we might need it for printing later.  */
       char *endp = strchr (opt_chroot, '\0');
-      while (endp > opt_chroot + 1 && endp[-1] == '/')
+      while (endp > opt_chroot && endp[-1] == '/')
 	--endp;
       *endp = '\0';
+      if (endp == opt_chroot)
+	opt_chroot = NULL;
 
-      if (chroot (opt_chroot))
-	/* Report failure and exit program.  */
-	error (EXIT_FAILURE, errno, _("Can't chroot to %s"), opt_chroot);
-      /* chroot doesn't change the working directory, let's play safe.  */
-      if (chdir ("/"))
-	error (EXIT_FAILURE, errno, _("Can't chdir to /"));
+      if (opt_chroot)
+	{
+	  /* It is faster to use chroot if we can.  */
+	  if (!chroot (opt_chroot))
+	    {
+	      if (chdir ("/"))
+		error (EXIT_FAILURE, errno, _("Can't chdir to /"));
+	      opt_chroot = NULL;
+	    }
+	}
+    }
+
+  if (cache_file == NULL)
+    {
+      cache_file = alloca (strlen (LD_SO_CACHE) + 1);
+      strcpy (cache_file, LD_SO_CACHE);
     }
 
+  if (config_file == NULL)
+    config_file = LD_SO_CONF;
+
   if (opt_print_cache)
     {
+      if (opt_chroot)
+	{
+	  char *p = chroot_canon (opt_chroot, cache_file);
+	  if (p == NULL)
+	    error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"),
+		   cache_file);
+	  cache_file = p;
+	}
       print_cache (cache_file);
+      if (opt_chroot)
+	free (cache_file);
       exit (0);
     }
 
+  if (opt_chroot)
+    {
+      /* Canonicalize the directory name of cache_file, not cache_file,
+         because we'll rename a temporary cache file to it.  */
+      char *p = strrchr (cache_file, '/');
+      char *canon = chroot_canon (opt_chroot,
+				  p ? (*p = '\0', cache_file) : "/");
+
+      if (canon == NULL)
+	{
+	  error (EXIT_FAILURE, errno,
+		 _("Can't open cache file directory %s\n"),
+		 p ? cache_file : "/");
+	}
+
+      if (p)
+	++p;
+      else
+	p = cache_file;
+
+      cache_file = alloca (strlen (canon) + strlen (p) + 2);
+      sprintf (cache_file, "%s/%s", canon, p);
+      free (canon);
+    }
+
   if (opt_manual_link)
     {
       /* Link all given libraries manually.  */