LCOV - code coverage report
Current view: top level - libpkg - elfhints.c (source / functions) Hit Total Coverage
Test: cov.info Lines: 77 249 30.9 %
Date: 2015-08-15 Functions: 8 15 53.3 %

          Line data    Source code
       1             : /*-
       2             :  * Copyright (c) 1998 John D. Polstra
       3             :  * Copyright (c) 2012 Matthew Seaman <matthew@FreeBSD.org>
       4             :  * All rights reserved.
       5             :  *
       6             :  * Redistribution and use in source and binary forms, with or without
       7             :  * modification, are permitted provided that the following conditions
       8             :  * are met:
       9             :  * 1. Redistributions of source code must retain the above copyright
      10             :  *    notice, this list of conditions and the following disclaimer.
      11             :  * 2. Redistributions in binary form must reproduce the above copyright
      12             :  *    notice, this list of conditions and the following disclaimer in the
      13             :  *    documentation and/or other materials provided with the distribution.
      14             :  *
      15             :  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
      16             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      17             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      18             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
      19             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      20             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      21             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      22             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      23             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      24             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      25             :  * SUCH DAMAGE.
      26             :  *
      27             :  * $FreeBSD: stable/8/sbin/ldconfig/elfhints.c 76224 2001-05-02 23:56:21Z obrien $
      28             :  */
      29             : 
      30             : #include <bsd_compat.h>
      31             : #include <sys/mman.h>
      32             : #include <sys/stat.h>
      33             : 
      34             : #include <assert.h>
      35             : #include <ctype.h>
      36             : #include <dirent.h>
      37             : #include <err.h>
      38             : #include <errno.h>
      39             : #include <fcntl.h>
      40             : #include <stdio.h>
      41             : #include <stdlib.h>
      42             : #include <string.h>
      43             : #include <unistd.h>
      44             : #include <uthash.h>
      45             : 
      46             : #include "pkg.h"
      47             : #include "private/ldconfig.h"
      48             : 
      49             : #define MAXDIRS         1024            /* Maximum directories in path */
      50             : #define MAXFILESIZE     (16*1024)       /* Maximum hints file size */
      51             : 
      52             : struct shlib_list {
      53             :         UT_hash_handle   hh;
      54             :         const char      *name;
      55             :         char             path[];
      56             : };
      57             : 
      58             : static int      shlib_list_add(struct shlib_list **shlib_list,
      59             :                                 const char *dir, const char *shlib_file);
      60             : static int      scan_dirs_for_shlibs(struct shlib_list **shlib_list,
      61             :                                      int numdirs, const char **dirlist,
      62             :                                      bool strictnames);
      63             : static void     add_dir(const char *, const char *, int);
      64             : static void     read_dirs_from_file(const char *, const char *);
      65             : static void     read_elf_hints(const char *, int);
      66             : static void     write_elf_hints(const char *);
      67             : 
      68             : static const char       *dirs[MAXDIRS];
      69             : static int               ndirs;
      70             : int                      insecure;
      71             : 
      72             : /* Known shlibs on the standard system search path.  Persistent,
      73             :    common to all applications */
      74             : static struct shlib_list *shlibs = NULL;
      75             : 
      76             : /* Known shlibs on the specific RPATH or RUNPATH of one binary.
      77             :    Evanescent. */
      78             : static struct shlib_list *rpath = NULL;
      79             : 
      80             : void
      81          71 : shlib_list_init(void)
      82             : {
      83          71 :         assert(HASH_COUNT(shlibs) == 0);
      84          71 : }
      85             : 
      86             : void
      87           0 : rpath_list_init(void)
      88             : {
      89           0 :         assert(HASH_COUNT(rpath) == 0);
      90           0 : }
      91             : 
      92             : static int
      93      174234 : shlib_list_add(struct shlib_list **shlib_list, const char *dir,
      94             :     const char *shlib_file)
      95             : {
      96             :         struct shlib_list       *sl;
      97             :         size_t  path_len, dir_len;
      98             : 
      99             :         /* If shlib_file is already in the shlib_list table, don't try
     100             :          * and add it again */
     101      174234 :         HASH_FIND_STR(*shlib_list, shlib_file, sl);
     102      174234 :         if (sl != NULL)
     103        1704 :                 return (EPKG_OK);
     104             : 
     105      172530 :         path_len = strlen(dir) + strlen(shlib_file) + 2;
     106             : 
     107      172530 :         sl = calloc(1, sizeof(struct shlib_list) + path_len);
     108      172530 :         if (sl == NULL) {
     109           0 :                 warnx("Out of memory");
     110           0 :                 return (EPKG_FATAL);
     111             :         }
     112             : 
     113      172530 :         strlcpy(sl->path, dir, path_len);
     114      172530 :         dir_len = strlcat(sl->path, "/", path_len);
     115      172530 :         strlcat(sl->path, shlib_file, path_len);
     116             :         
     117      172530 :         sl->name = sl->path + dir_len;
     118             : 
     119      172530 :         HASH_ADD_KEYPTR(hh, *shlib_list, sl->name,
     120             :                         strlen(sl->name), sl);
     121             : 
     122      172530 :         return (EPKG_OK);
     123             : }
     124             : 
     125             : const char *
     126           0 : shlib_list_find_by_name(const char *shlib_file)
     127             : {
     128             :         struct shlib_list *sl;
     129             : 
     130           0 :         if (HASH_COUNT(shlibs) == 0)
     131           0 :                 return (NULL);
     132             : 
     133           0 :         HASH_FIND_STR(rpath, shlib_file, sl);
     134           0 :         if (sl != NULL)
     135           0 :                 return (sl->path);
     136             : 
     137           0 :         HASH_FIND_STR(shlibs, shlib_file, sl);
     138           0 :         if (sl != NULL)
     139           0 :                 return (sl->path);
     140             :                 
     141           0 :         return (NULL);
     142             : }
     143             : 
     144             : void
     145          71 : shlib_list_free(void)
     146             : {
     147             :         struct shlib_list       *sl1, *sl2;
     148             : 
     149      172601 :         HASH_ITER(hh, shlibs, sl1, sl2) {
     150      172530 :                 HASH_DEL(shlibs, sl1);
     151      172530 :                 free(sl1);
     152             :         }
     153          71 :         shlibs = NULL;
     154          71 : }
     155             : 
     156             : void
     157           2 : rpath_list_free(void)
     158             : {
     159             :         struct shlib_list       *sl1, *sl2;
     160             : 
     161           2 :         HASH_ITER(hh, rpath, sl1, sl2) {
     162           0 :                 HASH_DEL(rpath, sl1);
     163           0 :                 free(sl1);
     164             :         }
     165           2 :         rpath = NULL;
     166           2 : }
     167             : 
     168             : static void
     169        1065 : add_dir(const char *hintsfile, const char *name, int trusted)
     170             : {
     171             :         struct stat     stbuf;
     172             :         int             i;
     173             : 
     174             :         /* Do some security checks */
     175        1065 :         if (!trusted && !insecure) {
     176           0 :                 if (stat(name, &stbuf) == -1) {
     177           0 :                         warn("%s", name);
     178           0 :                         return;
     179             :                 }
     180           0 :                 if (stbuf.st_uid != 0) {
     181           0 :                         warnx("%s: ignoring directory not owned by root", name);
     182           0 :                         return;
     183             :                 }
     184           0 :                 if ((stbuf.st_mode & S_IWOTH) != 0) {
     185           0 :                         warnx("%s: ignoring world-writable directory", name);
     186           0 :                         return;
     187             :                 }
     188           0 :                 if ((stbuf.st_mode & S_IWGRP) != 0) {
     189           0 :                         warnx("%s: ignoring group-writable directory", name);
     190           0 :                         return;
     191             :                 }
     192             :         }
     193             : 
     194        8520 :         for (i = 0;  i < ndirs;  i++)
     195        7455 :                 if (strcmp(dirs[i], name) == 0)
     196           0 :                         return;
     197        1065 :         if (ndirs >= MAXDIRS)
     198           0 :                 errx(1, "\"%s\": Too many directories in path", hintsfile);
     199        1065 :         dirs[ndirs++] = name;
     200             : }
     201             : 
     202             : static int
     203          71 : scan_dirs_for_shlibs(struct shlib_list **shlib_list, int numdirs,
     204             :                      const char **dirlist, bool strictnames)
     205             : {
     206             :         int     i;
     207             : 
     208             :         /* Expect shlibs to follow the name pattern libfoo.so.N if
     209             :            strictnames is true -- ie. when searching the default
     210             :            library search path.
     211             : 
     212             :            Otherwise, allow any name ending in .so or .so.N --
     213             :            ie. when searching RPATH or RUNPATH and assuming it
     214             :            contains private shared libraries which can follow just
     215             :            about any naming convention */
     216             : 
     217        1136 :         for (i = 0;  i < numdirs;  i++) {
     218             :                 DIR             *dirp;
     219             :                 struct dirent   *dp;
     220             : 
     221        1065 :                 if ((dirp = opendir(dirlist[i])) == NULL)
     222           0 :                         continue;
     223      286343 :                 while ((dp = readdir(dirp)) != NULL) {
     224             :                         int              len;
     225             :                         int              ret;
     226             :                         const char      *vers;
     227             : 
     228             :                         /* Only regular files and sym-links. On some
     229             :                            filesystems d_type is not set, on these the d_type
     230             :                            field will be DT_UNKNOWN. */
     231      293372 :                         if (dp->d_type != DT_REG && dp->d_type != DT_LNK &&
     232        9159 :                             dp->d_type != DT_UNKNOWN)
     233        9159 :                                 continue;
     234             : 
     235      275054 :                         len = strlen(dp->d_name);
     236      275054 :                         if (strictnames) {
     237             :                                 /* Name can't be shorter than "libx.so" */
     238      549043 :                                 if (len < 7 ||
     239      273989 :                                     strncmp(dp->d_name, "lib", 3) != 0)
     240       16472 :                                         continue;
     241             :                         }
     242             : 
     243      258582 :                         vers = dp->d_name + len;
     244     1615818 :                         while (vers > dp->d_name &&
     245     1135006 :                                (isdigit(*(vers-1)) || *(vers-1) == '.'))
     246      420036 :                                 vers--;
     247      258582 :                         if (vers == dp->d_name + len) {
     248      152011 :                                 if (strncmp(vers - 3, ".so", 3) != 0)
     249       84348 :                                         continue;
     250      213142 :                         } else if (vers < dp->d_name + 3 ||
     251      106571 :                             strncmp(vers - 3, ".so.", 4) != 0)
     252           0 :                                 continue;
     253             : 
     254             :                         /* We have a valid shared library name. */
     255      174234 :                         ret = shlib_list_add(shlib_list, dirlist[i],
     256      174234 :                                               dp->d_name);
     257      174234 :                         if (ret != EPKG_OK) {
     258           0 :                                 closedir(dirp);
     259           0 :                                 return ret;
     260             :                         }
     261             :                 }
     262        1065 :                 closedir(dirp);
     263             :         }
     264          71 :         return 0;
     265             : }
     266             : 
     267             : #define ORIGIN  "$ORIGIN"
     268             : 
     269           0 : int shlib_list_from_rpath(const char *rpath_str, const char *dirpath)
     270             : {
     271             :         const char    **dirlist;
     272             :         char           *buf;
     273             :         size_t          buflen;
     274             :         int             i, numdirs;
     275             :         int             ret;
     276             :         const char     *c, *cstart;
     277             :         
     278             :         /* The special token $ORIGIN should be replaced by the
     279             :            dirpath: adjust buflen calculation to account for this */
     280             : 
     281           0 :         numdirs = 1;
     282           0 :         for (c = rpath_str; *c != '\0'; c++)
     283           0 :                 if (*c == ':')
     284           0 :                         numdirs++;
     285           0 :         buflen = numdirs * sizeof(char *) + strlen(rpath_str) + 1;
     286           0 :         i = strlen(dirpath) - strlen(ORIGIN);
     287           0 :         if (i > 0)
     288           0 :                 buflen += i * numdirs;
     289             : 
     290           0 :         dirlist = calloc(1, buflen);
     291           0 :         if (dirlist == NULL) {
     292           0 :                 warnx("Out of memory");
     293           0 :                 return (EPKG_FATAL);
     294             :         }
     295           0 :         buf = (char *)dirlist + numdirs * sizeof(char *);
     296             : 
     297           0 :         buf[0] = '\0';
     298           0 :         cstart = rpath_str;
     299           0 :         while ( (c = strstr(cstart, ORIGIN)) != NULL ) {
     300           0 :                 strncat(buf, cstart, c - cstart);
     301           0 :                 strlcat(buf, dirpath, buflen);
     302           0 :                 cstart = c + strlen(ORIGIN);
     303             :         }
     304           0 :         strlcat(buf, cstart, buflen);
     305             : 
     306           0 :         i = 0;
     307           0 :         while ((c = strsep(&buf, ":")) != NULL) {
     308           0 :                 if (strlen(c) > 0)
     309           0 :                         dirlist[i++] = c;
     310             :         }
     311             : 
     312           0 :         assert(i <= numdirs);
     313             : 
     314           0 :         ret = scan_dirs_for_shlibs(&rpath, i, dirlist, false);
     315             : 
     316           0 :         free(dirlist);
     317             : 
     318           0 :         return (ret);
     319             : }
     320             : 
     321             : int 
     322          71 : shlib_list_from_elf_hints(const char *hintsfile)
     323             : {
     324             : #ifndef __linux__
     325          71 :         read_elf_hints(hintsfile, 1);
     326             : #endif
     327             : 
     328          71 :         return (scan_dirs_for_shlibs(&shlibs, ndirs, dirs, true));
     329             : }
     330             : 
     331             : void
     332           0 : list_elf_hints(const char *hintsfile)
     333             : {
     334             :         int     i;
     335             :         int     nlibs;
     336             : 
     337           0 :         read_elf_hints(hintsfile, 1);
     338           0 :         printf("%s:\n", hintsfile);
     339           0 :         printf("\tsearch directories:");
     340           0 :         for (i = 0;  i < ndirs;  i++)
     341           0 :                 printf("%c%s", i == 0 ? ' ' : ':', dirs[i]);
     342           0 :         printf("\n");
     343             : 
     344           0 :         nlibs = 0;
     345           0 :         for (i = 0;  i < ndirs;  i++) {
     346             :                 DIR             *dirp;
     347             :                 struct dirent   *dp;
     348             : 
     349           0 :                 if ((dirp = opendir(dirs[i])) == NULL)
     350           0 :                         continue;
     351           0 :                 while ((dp = readdir(dirp)) != NULL) {
     352             :                         int              len;
     353             :                         int              namelen;
     354             :                         const char      *name;
     355             :                         const char      *vers;
     356             : 
     357             :                         /* Name can't be shorter than "libx.so.0" */
     358           0 :                         if ((len = strlen(dp->d_name)) < 9 ||
     359           0 :                             strncmp(dp->d_name, "lib", 3) != 0)
     360           0 :                                 continue;
     361           0 :                         name = dp->d_name + 3;
     362           0 :                         vers = dp->d_name + len;
     363           0 :                         while (vers > dp->d_name && isdigit(*(vers-1)))
     364           0 :                                 vers--;
     365           0 :                         if (vers == dp->d_name + len)
     366           0 :                                 continue;
     367           0 :                         if (vers < dp->d_name + 4 ||
     368           0 :                             strncmp(vers - 4, ".so.", 4) != 0)
     369           0 :                                 continue;
     370             : 
     371             :                         /* We have a valid shared library name. */
     372           0 :                         namelen = (vers - 4) - name;
     373           0 :                         printf("\t%d:-l%.*s.%s => %s/%s\n", nlibs,
     374           0 :                             namelen, name, vers, dirs[i], dp->d_name);
     375           0 :                         nlibs++;
     376             :                 }
     377           0 :                 closedir(dirp);
     378             :         }
     379           0 : }
     380             : 
     381             : static void
     382           0 : read_dirs_from_file(const char *hintsfile, const char *listfile)
     383             : {
     384             :         FILE    *fp;
     385             :         char     buf[MAXPATHLEN];
     386             :         int      linenum;
     387             : 
     388           0 :         if ((fp = fopen(listfile, "r")) == NULL)
     389           0 :                 err(1, "%s", listfile);
     390             : 
     391           0 :         linenum = 0;
     392           0 :         while (fgets(buf, sizeof buf, fp) != NULL) {
     393             :                 char    *cp, *sp;
     394             : 
     395           0 :                 linenum++;
     396           0 :                 cp = buf;
     397             :                 /* Skip leading white space. */
     398           0 :                 while (isspace(*cp))
     399           0 :                         cp++;
     400           0 :                 if (*cp == '#' || *cp == '\0')
     401           0 :                         continue;
     402           0 :                 sp = cp;
     403             :                 /* Advance over the directory name. */
     404           0 :                 while (!isspace(*cp) && *cp != '\0')
     405           0 :                         cp++;
     406             :                 /* Terminate the string and skip trailing white space. */
     407           0 :                 if (*cp != '\0') {
     408           0 :                         *cp++ = '\0';
     409           0 :                         while (isspace(*cp))
     410           0 :                                 cp++;
     411             :                 }
     412             :                 /* Now we had better be at the end of the line. */
     413           0 :                 if (*cp != '\0')
     414           0 :                         warnx("%s:%d: trailing characters ignored",
     415             :                             listfile, linenum);
     416             : 
     417           0 :                 if ((sp = strdup(sp)) == NULL)
     418           0 :                         errx(1, "Out of memory");
     419           0 :                 add_dir(hintsfile, sp, 0);
     420             :         }
     421             : 
     422           0 :         fclose(fp);
     423           0 : }
     424             : 
     425             : static void
     426          71 : read_elf_hints(const char *hintsfile, int must_exist)
     427             : {
     428             :         int                      fd;
     429             :         struct stat              s;
     430             :         void                    *mapbase;
     431             :         struct elfhints_hdr     *hdr;
     432             :         char                    *strtab;
     433             :         char                    *dirlist;
     434             :         char                    *p;
     435             : 
     436          71 :         if ((fd = open(hintsfile, O_RDONLY)) == -1) {
     437           0 :                 if (errno == ENOENT && !must_exist)
     438          71 :                         return;
     439           0 :                 err(1, "Cannot open \"%s\"", hintsfile);
     440             :         }
     441          71 :         if (fstat(fd, &s) == -1)
     442           0 :                 err(1, "Cannot stat \"%s\"", hintsfile);
     443          71 :         if (s.st_size > MAXFILESIZE)
     444           0 :                 errx(1, "\"%s\" is unreasonably large", hintsfile);
     445             :         /*
     446             :          * We use a read-write, private mapping so that we can null-terminate
     447             :          * some strings in it without affecting the underlying file.
     448             :          */
     449          71 :         mapbase = mmap(NULL, s.st_size, PROT_READ|PROT_WRITE,
     450             :             MAP_PRIVATE, fd, 0);
     451          71 :         if (mapbase == MAP_FAILED)
     452           0 :                 err(1, "Cannot mmap \"%s\"", hintsfile);
     453          71 :         close(fd);
     454             : 
     455          71 :         hdr = (struct elfhints_hdr *)mapbase;
     456          71 :         if (hdr->magic != ELFHINTS_MAGIC)
     457           0 :                 errx(1, "\"%s\": invalid file format", hintsfile);
     458          71 :         if (hdr->version != 1)
     459           0 :                 errx(1, "\"%s\": unrecognized file version (%d)", hintsfile,
     460             :                     hdr->version);
     461             : 
     462          71 :         strtab = (char *)mapbase + hdr->strtab;
     463          71 :         dirlist = strtab + hdr->dirlist;
     464             : 
     465          71 :         if (*dirlist != '\0')
     466        1207 :                 while ((p = strsep(&dirlist, ":")) != NULL)
     467        1065 :                         add_dir(hintsfile, p, 1);
     468             : }
     469             : 
     470             : void
     471           0 : update_elf_hints(const char *hintsfile, int argc, char **argv, int merge)
     472             : {
     473             :         int     i;
     474             : 
     475           0 :         if (merge)
     476           0 :                 read_elf_hints(hintsfile, 0);
     477           0 :         for (i = 0;  i < argc;  i++) {
     478             :                 struct stat     s;
     479             : 
     480           0 :                 if (stat(argv[i], &s) == -1)
     481           0 :                         warn("warning: %s", argv[i]);
     482           0 :                 else if (S_ISREG(s.st_mode))
     483           0 :                         read_dirs_from_file(hintsfile, argv[i]);
     484             :                 else
     485           0 :                         add_dir(hintsfile, argv[i], 0);
     486             :         }
     487           0 :         write_elf_hints(hintsfile);
     488           0 : }
     489             : 
     490             : static void
     491           0 : write_elf_hints(const char *hintsfile)
     492             : {
     493             :         struct elfhints_hdr      hdr;
     494             :         char                    *tempname;
     495             :         int                      fd;
     496             :         FILE                    *fp;
     497             :         int                      i;
     498             : 
     499           0 :         if (asprintf(&tempname, "%s.XXXXXX", hintsfile) == -1)
     500           0 :                 errx(1, "Out of memory");
     501           0 :         if ((fd = mkstemp(tempname)) ==  -1)
     502           0 :                 err(1, "mkstemp(%s)", tempname);
     503           0 :         if (fchmod(fd, 0444) == -1)
     504           0 :                 err(1, "fchmod(%s)", tempname);
     505           0 :         if ((fp = fdopen(fd, "wb")) == NULL)
     506           0 :                 err(1, "fdopen(%s)", tempname);
     507             : 
     508           0 :         hdr.magic = ELFHINTS_MAGIC;
     509           0 :         hdr.version = 1;
     510           0 :         hdr.strtab = sizeof hdr;
     511           0 :         hdr.strsize = 0;
     512           0 :         hdr.dirlist = 0;
     513           0 :         memset(hdr.spare, 0, sizeof hdr.spare);
     514             : 
     515             :         /* Count up the size of the string table. */
     516           0 :         if (ndirs > 0) {
     517           0 :                 hdr.strsize += strlen(dirs[0]);
     518           0 :                 for (i = 1;  i < ndirs;  i++)
     519           0 :                         hdr.strsize += 1 + strlen(dirs[i]);
     520             :         }
     521           0 :         hdr.dirlistlen = hdr.strsize;
     522           0 :         hdr.strsize++;  /* For the null terminator */
     523             : 
     524             :         /* Write the header. */
     525           0 :         if (fwrite(&hdr, 1, sizeof hdr, fp) != sizeof hdr)
     526           0 :                 err(1, "%s: write error", tempname);
     527             :         /* Write the strings. */
     528           0 :         if (ndirs > 0) {
     529           0 :                 if (fputs(dirs[0], fp) == EOF)
     530           0 :                         err(1, "%s: write error", tempname);
     531           0 :                 for (i = 1;  i < ndirs;  i++)
     532           0 :                         if (fprintf(fp, ":%s", dirs[i]) < 0)
     533           0 :                                 err(1, "%s: write error", tempname);
     534             :         }
     535           0 :         if (putc('\0', fp) == EOF || fclose(fp) == EOF)
     536           0 :                 err(1, "%s: write error", tempname);
     537             : 
     538           0 :         if (rename(tempname, hintsfile) == -1)
     539           0 :                 err(1, "rename %s to %s", tempname, hintsfile);
     540           0 :         free(tempname);
     541           0 : }

Generated by: LCOV version 1.10