diff options
Diffstat (limited to 'elf')
-rw-r--r-- | elf/Makefile | 3 | ||||
-rw-r--r-- | elf/cache.c | 4 | ||||
-rw-r--r-- | elf/chroot_canon.c | 172 | ||||
-rw-r--r-- | elf/ldconfig.c | 250 | ||||
-rw-r--r-- | elf/ldconfig.h | 7 | ||||
-rw-r--r-- | elf/readlib.c | 10 |
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); |