This is the mail archive of the libc-hacker@sources.redhat.com mailing list for the glibc project.

Note that libc-hacker is a closed list. You may look at the archives of this list, but subscription and posting are not open.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

First ldconfig -r without chroot attempt


Hi!

Below is my first chroot -r without chroot() attempt.
I haven't tested it thouroughly, just ran it a few times (with the chroot()
call commented out in main) to see if it created the same ld.so.cache as old
ldconfig and did not mess any symlinks and it looked fine, but it needs both
a proof reading and more testing.
Basically, I hacked canonicalize.c, so that it prepends a chroot prefix to
the path and whenever some symlink is absolute or there are too many ../
path items, it limits the path into that "chroot".
This is slightly slower than using chroot(), that's why I try in main to use
chroot and only if it fails use this canonicalization.
Also, so that the user is not too surprised, I try to print in all messages
the user given paths and not the canonicalized ones (e.g. if in /mnt chroot
there is a /usr/lib -> /foo/bar/lib symlink, I don't print /mnt/foo/bar/lib
or /foo/bar/lib paths, but /usr/lib). The user given paths are needed in
some cases anyway (e.g. those paths are stored into the cache).
Also, I think ldconfig should be consistent in what paths it prints, and as
95% of ldconfig with -r /mnt prints the paths without the leading /mnt
prefix, I have removed the only place where Ulrich added it. As those paths
should be e.g. present in the ldconfig -p printout, it would be e.g. weird
if normal ldconfig output uses paths without prefix and stderr with prefix.
Comments/testing welcome :)

2000-09-29  Jakub Jelinek  <jakub@redhat.com>

	* elf/chroot_canon.c: New file.
	* elf/ldconfig.h (process_file): Add real_file_name argument.
	(chroot_canon): Add prototype.
	* elf/ldconfig.c (cache_file): Remove const.
	(chroot_stat): New.
	(create_links): Add real_path argument.
	If opt_chroot, maintain both real and given filenames.
	(manual_link): Likewise.
	(search_dir): Likewise.
	(parse_conf): If opt_chroot, use chroot_canon to find the real
	config file.
	(main): For -r, try to use chroot, if it fails, leave opt_chroot set
	and use chroot_canon where appropriate to do the same as if chroot
	succeeded.
	* elf/readlib.c (process_file): Add real_file_name argument, pass it
	to fopen.

--- libc/elf/chroot_canon.c.jj	Fri Sep 29 13:51:00 2000
+++ libc/elf/chroot_canon.c	Fri Sep 29 14:40:18 2000
@@ -0,0 +1,164 @@
+/* Return the canonical absolute name of a given file inside chroot.
+   Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stddef.h>
+#include "ldconfig.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+/* Return the canonical absolute name of file NAME as if chroot(CHROOT) was
+   done first.  A canonical name does not contain any `.', `..' components
+   nor any repeated path separators ('/') or symlinks.  All path components
+   must exist and NAME must be absolute filename.  The result is malloc'd.
+   The returned name includes the CHROOT prefix.  */
+
+char *
+chroot_canon (const char *chroot, const char *name)
+{
+  char *rpath, *dest, *extra_buf = NULL, *rpath_root;
+  const char *start, *end, *rpath_limit;
+  int num_links = 0;
+  size_t chroot_len = strlen(chroot);
+
+  if (chroot_len < 1)
+    {
+      __set_errno (EINVAL);
+      return NULL;
+    }
+
+  rpath = malloc (chroot_len + PATH_MAX);
+  rpath_limit = rpath + chroot_len + PATH_MAX;
+
+  strcpy (rpath, chroot);
+  rpath_root = rpath + chroot_len - 1;
+  if (*rpath_root != '/')
+    *++rpath_root = '/';
+  dest = rpath_root + 1;
+
+  for (start = end = name; *start; start = end)
+    {
+      struct stat64 st;
+      int n;
+
+      /* Skip sequence of multiple path-separators.  */
+      while (*start == '/')
+	++start;
+
+      /* Find end of path component.  */
+      for (end = start; *end && *end != '/'; ++end)
+	/* Nothing.  */;
+
+      if (end - start == 0)
+	break;
+      else if (end - start == 1 && start[0] == '.')
+	/* nothing */;
+      else if (end - start == 2 && start[0] == '.' && start[1] == '.')
+	{
+	  /* Back up to previous component, ignore if at root already.  */
+	  if (dest > rpath_root + 1)
+	    while ((--dest)[-1] != '/');
+	}
+      else
+	{
+	  size_t new_size;
+
+	  if (dest[-1] != '/')
+	    *dest++ = '/';
+
+	  if (dest + (end - start) >= rpath_limit)
+	    {
+	      ptrdiff_t dest_offset = dest - rpath;
+
+	      new_size = rpath_limit - rpath;
+	      if (end - start + 1 > PATH_MAX)
+		new_size += end - start + 1;
+	      else
+		new_size += PATH_MAX;
+	      rpath = realloc (rpath, new_size);
+	      rpath_limit = rpath + new_size;
+	      if (rpath == NULL)
+		return NULL;
+
+	      dest = rpath + dest_offset;
+	    }
+
+	  dest = mempcpy (dest, start, end - start);
+	  *dest = '\0';
+
+	  if (lstat64 (rpath, &st) < 0)
+	    goto error;
+
+	  if (S_ISLNK (st.st_mode))
+	    {
+	      char *buf = alloca (PATH_MAX);
+	      size_t len;
+
+	      if (++num_links > MAXSYMLINKS)
+		{
+		  __set_errno (ELOOP);
+		  goto error;
+		}
+
+	      n = readlink (rpath, buf, PATH_MAX);
+	      if (n < 0)
+		goto error;
+	      buf[n] = '\0';
+
+	      if (!extra_buf)
+		extra_buf = alloca (PATH_MAX);
+
+	      len = strlen (end);
+	      if ((long int) (n + len) >= PATH_MAX)
+		{
+		  __set_errno (ENAMETOOLONG);
+		  goto error;
+		}
+
+	      /* Careful here, end may be a pointer into extra_buf... */
+	      memmove (&extra_buf[n], end, len + 1);
+	      name = end = memcpy (extra_buf, buf, n);
+
+	      if (buf[0] == '/')
+		dest = rpath_root + 1;	/* It's an absolute symlink */
+	      else
+		/* Back up to previous component, ignore if at root already: */
+		if (dest > rpath_root + 1)
+		  while ((--dest)[-1] != '/');
+	    }
+	}
+    }
+  if (dest > rpath_root + 1 && dest[-1] == '/')
+    --dest;
+  *dest = '\0';
+
+  return rpath;
+
+error:
+  free (rpath);
+  return NULL;
+}
--- libc/elf/ldconfig.h.jj	Wed May 31 23:02:38 2000
+++ libc/elf/ldconfig.h	Fri Sep 29 17:39:52 2000
@@ -41,14 +41,17 @@ extern void add_to_cache (const char *pa
 			  unsigned long int hwcap);
 
 /* Declared in readlib.c.  */
-extern int process_file (const char *file_name, const char *lib, int *flag,
-			 char **soname, int is_link);
+extern int process_file (const char *real_file_name, const char *file_name,
+			 const char *lib, int *flag, char **soname,
+			 int is_link);
 
 /* Declared in readelflib.c.  */
 extern int process_elf_file (const char *file_name, const char *lib, int *flag,
 			     char **soname, void *file_contents,
 			     size_t file_length);
 
+/* Declared in chroot_canon.c.  */
+extern char *chroot_canon (const char *chroot, const char *name);
 
 /* Declared in ldconfig.c.  */
 extern int opt_verbose;
--- libc/elf/ldconfig.c.jj	Fri Sep 29 12:56:56 2000
+++ libc/elf/ldconfig.c	Fri Sep 29 18:09:55 2000
@@ -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,11 +334,35 @@ add_dir (const char *line)
 }
 
 
+static int
+chroot_stat (const char *real_path, const char *path, struct stat *st)
+{
+  int ret;
+  char *canon_path;
+
+  if (!opt_chroot)
+    return stat (real_path, st);
+
+  ret = lstat (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 = stat (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;
+  char *real_full_libname, *real_full_soname;
   struct stat stat_lib, stat_so, lstat_so;
   int do_link = 1;
   int do_remove = 1;
@@ -349,11 +373,23 @@ create_links (const char *path, const ch
   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;
@@ -370,7 +406,7 @@ create_links (const char *path, const ch
 	  do_remove = 0;
 	}
     }
-  else if (lstat (full_soname, &lstat_so) != 0
+  else if (lstat (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 ch
     {
       /* 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,6 +446,8 @@ static void
 manual_link (char *library)
 {
   char *path;
+  char *real_path;
+  char *real_library;
   char *libname;
   char *soname;
   struct stat stat_buf;
@@ -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 (lstat (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,8 +569,8 @@ search_dir (const struct dir_entry *entr
 {
   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;
@@ -536,15 +591,28 @@ search_dir (const struct dir_entry *entr
 	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 *entr
 	{
 	  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 (lstat (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 *entr
 	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 *entr
     {
       /* 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 *entr
       dlibs = dlibs->next;
       free (dlib_ptr);
     }
+
+  if (opt_chroot && dir_name)
+    free (dir_name);
 }
 
 /* Search through all libraries.  */
@@ -726,16 +830,26 @@ search_dirs (void)
 static void
 parse_conf (const char *filename)
 {
-  FILE *file;
+  FILE *file = NULL;
   char *line = NULL;
+  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");
+	  free (canon);
+	}
+    }
+  else
+    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"), filename);
       return;
     }
 
@@ -783,33 +897,76 @@ 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 (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 (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 (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)
--- libc/elf/readlib.c.jj	Tue Sep 26 09:22:27 2000
+++ libc/elf/readlib.c	Fri Sep 29 17:37:00 2000
@@ -68,8 +68,8 @@ static struct known_names known_libs [] 
 
 /* Returns 0 if everything is ok, != 0 in case of error.  */
 int
-process_file (const char *file_name, const char *lib, int *flag,
-	      char **soname, int is_link)
+process_file (const char *real_file_name, const char *file_name,
+	      const char *lib, int *flag, char **soname, int is_link)
 {
   FILE *file;
   struct stat statbuf;
@@ -83,7 +83,7 @@ process_file (const char *file_name, con
   *flag = FLAG_ANY;
   *soname = NULL;
 
-  file = fopen (file_name, "rb");
+  file = fopen (real_file_name, "rb");
   if (file == NULL)
     {
       /* No error for stale symlink.  */

	Jakub

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]