aboutsummaryrefslogtreecommitdiff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile3
-rw-r--r--elf/cache.c4
-rw-r--r--elf/chroot_canon.c172
-rw-r--r--elf/ldconfig.c250
-rw-r--r--elf/ldconfig.h7
-rw-r--r--elf/readlib.c10
6 files changed, 393 insertions, 53 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 84815b30ea..4d786ec3ab 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -46,6 +46,7 @@ distribute := $(rtld-routines:=.c) dynamic-link.h do-rel.h dl-machine.h \
testobj1.c testobj2.c testobj3.c testobj4.c testobj5.c \
testobj6.c testobj1_1.c failobj.c unloadmod.c \
ldconfig.h ldconfig.c cache.c readlib.c readelflib.c \
+ chroot_canon.c \
dep1.c dep2.c dep3.c dep4.c dl-dtprocnum.h unsecvars.h \
vismain.c vismod1.c vismod2.c vismod3.c \
constload2.c constload3.c filtmod1.c filtmod2.c \
@@ -81,7 +82,7 @@ others-static += ldconfig
others += ldconfig
install-rootsbin += ldconfig
-ldconfig-modules := cache readlib xmalloc xstrdup
+ldconfig-modules := cache readlib xmalloc xstrdup chroot_canon
extra-objs += $(ldconfig-modules:=.o)
# To find xmalloc.c and xstrdup.c
diff --git a/elf/cache.c b/elf/cache.c
index ef0248f4f8..b84287332e 100644
--- a/elf/cache.c
+++ b/elf/cache.c
@@ -94,7 +94,7 @@ void
print_cache (const char *cache_name)
{
size_t cache_size;
- struct stat st;
+ struct stat64 st;
int fd;
unsigned int i;
struct cache_file *cache;
@@ -106,7 +106,7 @@ print_cache (const char *cache_name)
if (fd < 0)
error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"), cache_name);
- if (fstat (fd, &st) < 0
+ if (fstat64 (fd, &st) < 0
/* No need to map the file if it is empty. */
|| st.st_size == 0)
{
diff --git a/elf/chroot_canon.c b/elf/chroot_canon.c
new file mode 100644
index 0000000000..82cb8c4bf0
--- /dev/null
+++ b/elf/chroot_canon.c
@@ -0,0 +1,172 @@
+/* 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;
+
+ rpath_root = (char *) mempcpy (rpath, chroot, 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)
+ {
+ if (*end == '\0')
+ goto done;
+ 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)
+ {
+ if (*end == '\0')
+ goto done;
+ 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] != '/');
+ }
+ }
+ }
+ done:
+ if (dest > rpath_root + 1 && dest[-1] == '/')
+ --dest;
+ *dest = '\0';
+
+ return rpath;
+
+ error:
+ free (rpath);
+ return NULL;
+}
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. */
diff --git a/elf/ldconfig.h b/elf/ldconfig.h
index a94df64ad0..76284f6e67 100644
--- a/elf/ldconfig.h
+++ b/elf/ldconfig.h
@@ -41,14 +41,17 @@ extern void add_to_cache (const char *path, const char *lib, int flags,
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;
diff --git a/elf/readlib.c b/elf/readlib.c
index 5ec5a546be..70a77e2e72 100644
--- a/elf/readlib.c
+++ b/elf/readlib.c
@@ -68,11 +68,11 @@ 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;
+ struct stat64 statbuf;
void *file_contents;
int ret;
@@ -83,7 +83,7 @@ process_file (const char *file_name, const char *lib, int *flag,
*flag = FLAG_ANY;
*soname = NULL;
- file = fopen (file_name, "rb");
+ file = fopen (real_file_name, "rb");
if (file == NULL)
{
/* No error for stale symlink. */
@@ -93,7 +93,7 @@ process_file (const char *file_name, const char *lib, int *flag,
return 1;
}
- if (fstat (fileno (file), &statbuf) < 0)
+ if (fstat64 (fileno (file), &statbuf) < 0)
{
error (0, 0, _("Cannot fstat file %s.\n"), file_name);
fclose (file);