Home | History | Annotate | Line # | Download | only in sysinst
      1 /*	$NetBSD: aout2elf.c,v 1.2 2019/06/12 06:20:17 martin Exp $
      2  *
      3  * Copyright 1997 Piermont Information Systems Inc.
      4  * All rights reserved.
      5  *
      6  * Written by Philip A. Nelson for Piermont Information Systems Inc.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. The name of Piermont Information Systems Inc. may not be used to endorse
     17  *    or promote products derived from this software without specific prior
     18  *    written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
     21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23  * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
     24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     30  * THE POSSIBILITY OF SUCH DAMAGE.
     31  *
     32  */
     33 
     34 /* aout2elf.c -- routines for upgrading an a.out system to ELF */
     35 
     36 #include <sys/param.h>
     37 #include <sys/exec.h>
     38 #include <sys/exec_aout.h>
     39 #include <sys/stat.h>
     40 #include <fcntl.h>
     41 #include <unistd.h>
     42 #include <dirent.h>
     43 #include <errno.h>
     44 #include <stdio.h>
     45 #include <stdlib.h>
     46 #include <string.h>
     47 #include <err.h>
     48 
     49 #include "defs.h"
     50 #include "md.h"
     51 #include "msg_defs.h"
     52 #include "menu_defs.h"
     53 
     54 /* Local prototypes */
     55 static int is_aout_shared_lib(const char *name);
     56 static void handle_aout_x_libs(const char *srcdir, const char *tgtdir);
     57 static int handle_aout_libs(const char *dir, int op, const void *arg);
     58 static char *target_realpath(const char *, char *);
     59 
     60 #define LIB_COUNT	0
     61 #define LIB_MOVE	1
     62 
     63 /* XXX NAH. This probably needs moving to arch/<foo>/md.h
     64  *
     65  * a.out X libraries to move. These have not changed since 1.3.x
     66  */
     67 const char *x_libs[] = {
     68 	"libICE.so.6.3",
     69 	"libPEX5.so.6.0",
     70 	"libSM.so.6.0",
     71 	"libX11.so.6.1",
     72 	"libXIE.so.6.0",
     73 	"libXaw.so.6.1",
     74 	"libXext.so.6.3",
     75 	"libXi.so.6.0",
     76 	"libXmu.so.6.0",
     77 	"libXp.so.6.2",
     78 	"libXt.so.6.0",
     79 	"libXtst.so.6.1",
     80 	"liboldX.so.6.0",
     81 };
     82 
     83 static int
     84 is_aout_shared_lib(const char *name)
     85 {
     86 	struct exec ex;
     87 	struct stat st;
     88 	int fd;
     89 
     90 	if (stat(name, &st) < 0)
     91 		return 0;
     92 	if ((st.st_mode & (S_IFREG|S_IFLNK)) == 0)
     93 		return 0;
     94 
     95 	fd = open(name, O_RDONLY);
     96 	if (fd < 0) {
     97 		return 0;
     98 	}
     99 	if (read(fd, &ex, sizeof ex) - sizeof ex != 0) {
    100 		close(fd);
    101 		return 0;
    102 	}
    103 	close(fd);
    104 	if (N_GETMAGIC(ex) != ZMAGIC ||
    105 	    (N_GETFLAG(ex) & EX_DYNAMIC) == 0)
    106 		return 0;
    107 
    108 	return 1;
    109 }
    110 
    111 static void
    112 handle_aout_x_libs(const char *srcdir, const char *tgtdir)
    113 {
    114 	char src[MAXPATHLEN];
    115 	unsigned int i;
    116 
    117 	for (i = 0; i < (sizeof x_libs / sizeof (const char *)); i++) {
    118 		snprintf(src, MAXPATHLEN, "%s/%s", srcdir, x_libs[i]);
    119 		if (!is_aout_shared_lib(src))
    120 			continue;
    121 		run_program(0, "mv -f %s %s", src, tgtdir);
    122 	}
    123 
    124 	/*
    125 	 * Don't care if it fails; X may not have been installed.
    126 	 */
    127 }
    128 
    129 /*
    130  * Function to count or move a.out shared libraries.
    131  */
    132 static int
    133 handle_aout_libs(const char *dir, int op, const void *arg)
    134 {
    135 	DIR *dd;
    136 	struct dirent *dp;
    137 	char *full_name;
    138 	const char *destdir = NULL; /* XXX -Wuninitialized [many] */
    139 	int n;
    140 
    141 	destdir = NULL;	/* XXX gcc */
    142 
    143 	dd = opendir(dir);
    144 	if (dd == NULL)
    145 		return -1;
    146 
    147 	n = 0;
    148 
    149 	switch (op) {
    150 	case LIB_COUNT:
    151 		break;
    152 	case LIB_MOVE:
    153 		destdir = (const char *)arg;
    154 		break;
    155 	default:
    156 		return -1;
    157 	}
    158 
    159 	while ((dp = readdir(dd)) != NULL) {
    160 		/*
    161 		 * strlen("libX.so")
    162 		 */
    163 		if (dp->d_namlen < 7)
    164 			continue;
    165 		if (strncmp(dp->d_name, "lib", 3) != 0)
    166 			continue;
    167 
    168 		if (asprintf(&full_name, "%s/%s", dir, dp->d_name) == -1)  {
    169 			warn("Out of memory");
    170 			continue;
    171 		}
    172 
    173 		if (!is_aout_shared_lib(full_name))
    174 			goto endloop;
    175 
    176 		switch (op) {
    177 		case LIB_COUNT:
    178 			n++;
    179 			break;
    180 		case LIB_MOVE:
    181 			run_program(0, "mv -f %s %s/%s",
    182 			    full_name, destdir, dp->d_name);
    183 			break;
    184 		}
    185 
    186 endloop:
    187 		free(full_name);
    188 	}
    189 
    190 	closedir(dd);
    191 
    192 	return n;
    193 }
    194 
    195 __dead static void
    196 abort_libupdate(void)
    197 {
    198 	hit_enter_to_continue(MSG_aoutfail, NULL);
    199 	exit(1);
    200 }
    201 
    202 int
    203 move_aout_libs(void)
    204 {
    205 	int n, backedup = 0;
    206 	char prefix[MAXPATHLEN], src[MAXPATHLEN];
    207 	struct stat st;
    208 
    209 	n = handle_aout_libs(target_expand("/usr/lib"), LIB_COUNT, NULL);
    210 	if (n <= 0)
    211 		return n;
    212 
    213 	/*
    214 	 * See if /emul/aout already exists, taking symlinks into
    215 	 * account. If so, no need to create it, just use it.
    216 	 */
    217 	if (target_realpath("/emul/aout", prefix) != NULL && stat(prefix, &st) == 0)
    218 		goto domove;
    219 
    220 	/*
    221 	 * See if /emul exists. If not, create it.
    222 	 */
    223 	if (target_realpath("/emul", prefix) == NULL || stat(prefix, &st) < 0) {
    224 		strlcpy(prefix, target_expand("/emul"), sizeof(prefix));
    225 		if (lstat(prefix, &st) == 0) {
    226 			run_program(0, "mv -f %s %s", prefix,
    227 			    target_expand("/emul.old"));
    228 			backedup = 1;
    229 		}
    230 		scripting_fprintf(NULL, "mkdir %s\n", prefix);
    231 		mkdir(prefix, 0755);
    232 	}
    233 
    234 	/*
    235 	 * Can use strcpy, target_expand has made sure it fits into
    236 	 * MAXPATHLEN. XXX all this copying is because concat_paths
    237 	 * returns a pointer to a static buffer.
    238 	 *
    239 	 * If an old aout link exists (apparently pointing to nowhere),
    240 	 * move it out of the way.
    241 	 */
    242 	strlcpy(src, concat_paths(prefix, "aout"), sizeof(src));
    243 	if (lstat(src, &st) == 0) {
    244 		run_program(0, "mv -f %s %s", src,
    245 		    concat_paths(prefix, "aout.old"));
    246 		backedup = 1;
    247 	}
    248 
    249 	/*
    250 	 * We have created /emul if needed. Since no previous /emul/aout
    251 	 * existed, we'll use a symbolic link in /emul to /usr/aout, to
    252 	 * avoid overflowing the root partition.
    253 	 */
    254 	strlcpy(prefix, target_expand("/usr/aout"), sizeof(prefix));
    255 	if (run_program(0, "mkdir -p %s", prefix))
    256 		abort_libupdate();
    257 	if (run_program(0, "ln -s %s %s", "/usr/aout", src))
    258 		abort_libupdate();
    259 
    260 domove:
    261 	/*
    262 	 * Rename etc and usr/lib if they already existed, so that we
    263 	 * do not overwrite old files.
    264 	 *
    265 	 * Then, move /etc/ld.so.conf to /emul/aout/etc/ld.so.conf,
    266 	 * and all a.out dynamic libraries from /usr/lib to
    267 	 * /emul/aout/usr/lib. This is where the a.out code in ldconfig
    268 	 * and ld.so respectively will find them.
    269 	 */
    270 	strlcpy(src, concat_paths(prefix, "usr/lib"), sizeof(src));
    271 	run_program(0, "mv -f %s %s", src, concat_paths(prefix, "usr/lib.old"));
    272 	strlcpy(src, concat_paths(prefix, "etc/ld.so.conf"), sizeof(src));
    273 	run_program(0, "mv -f %s %s",
    274 	    src, concat_paths(prefix, "etc/ld.so.conf.old"));
    275 	if (run_program(0, "mkdir -p %s ", concat_paths(prefix, "usr/lib")))
    276 		abort_libupdate();
    277 	if (run_program(0, "mkdir -p %s ", concat_paths(prefix, "etc")))
    278 		abort_libupdate();
    279 
    280 	strlcpy(src, target_expand("/etc/ld.so.conf"), sizeof(src));
    281 	if (run_program(0, "mv -f %s %s",
    282 	    src, concat_paths(prefix, "etc/ld.so.conf")))
    283 		abort_libupdate();
    284 
    285 	strlcpy(src, target_expand("/usr/lib"), sizeof(src));
    286 	n = handle_aout_libs(src, LIB_MOVE, concat_paths(prefix, "usr/lib"));
    287 
    288 	if (run_program(0, "mkdir -p %s ",
    289 	    concat_paths(prefix, "usr/X11R6/lib")))
    290 		abort_libupdate();
    291 
    292 	strlcpy(src, target_expand("/usr/X11R6/lib"), sizeof(src));
    293 	handle_aout_x_libs(src, concat_paths(prefix, "usr/X11R6/lib"));
    294 
    295 	if (backedup) {
    296 		hit_enter_to_continue(MSG_emulbackup, NULL);
    297 	}
    298 
    299 	return n;
    300 }
    301 
    302 /*
    303  * XXXX had to include this to deal with symlinks in some places.
    304  * When the target * disk is mounted under /targetroot, absolute symlinks
    305  * on it don't work right.
    306  * This function will resolve them using the mountpoint as prefix.
    307  * Copied verbatim from libc, with added prefix handling.
    308  *
    309  * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
    310  *
    311  * Find the real name of path, by removing all ".", ".." and symlink
    312  * components.  Returns (resolved) on success, or (NULL) on failure,
    313  * in which case the path which caused trouble is left in (resolved).
    314  */
    315 static char *
    316 target_realpath(const char *path, char *resolved)
    317 {
    318 	struct stat sb;
    319 	int fd, n, rootd, serrno, nlnk = 0;
    320 	char *p, *q, wbuf[MAXPATHLEN];
    321 	char solidus[2], empty[1];
    322 	solidus[0] = '/';
    323 	solidus[1] = '\0';
    324 	empty[0] = '\0';
    325 
    326 	/* Save the starting point. */
    327 	if ((fd = open(".", O_RDONLY)) < 0) {
    328 		(void)strlcpy(resolved, ".", MAXPATHLEN);
    329 		return (NULL);
    330 	}
    331 
    332 	/*
    333 	 * Find the dirname and basename from the path to be resolved.
    334 	 * Change directory to the dirname component.
    335 	 * lstat the basename part.
    336 	 *     if it is a symlink, read in the value and loop.
    337 	 *     if it is a directory, then change to that directory.
    338 	 * get the current directory name and append the basename.
    339 	 */
    340 	if (target_prefix() != NULL && strcmp(target_prefix(), "") != 0)
    341 		snprintf(resolved, MAXPATHLEN, "%s/%s", target_prefix(), path);
    342 	else
    343 		if (strlcpy(resolved, path, MAXPATHLEN) >= MAXPATHLEN) {
    344 			errno = ENAMETOOLONG;
    345 			goto err1;
    346 		}
    347 loop:
    348 	q = strrchr(resolved, '/');
    349 	if (q != NULL) {
    350 		p = q + 1;
    351 		if (q == resolved)
    352 			q = solidus;
    353 		else {
    354 			do {
    355 				--q;
    356 			} while (q > resolved && *q == '/');
    357 			q[1] = '\0';
    358 			q = resolved;
    359 		}
    360 		if (chdir(q) < 0)
    361 			goto err1;
    362 	} else
    363 		p = resolved;
    364 
    365 	/* Deal with the last component. */
    366 	if (lstat(p, &sb) == 0) {
    367 		if (S_ISLNK(sb.st_mode)) {
    368 			if (nlnk++ >= MAXSYMLINKS) {
    369 				errno = ELOOP;
    370 				goto err1;
    371 			}
    372 			n = readlink(p, wbuf, MAXPATHLEN - 1);
    373 			if (n < 0)
    374 				goto err1;
    375 			wbuf[n] = '\0';
    376 			if (wbuf[0] == '/')
    377 				snprintf(resolved, MAXPATHLEN, "%s%s",
    378 				    target_prefix(), wbuf);
    379 			else
    380 				strlcpy(resolved, wbuf, MAXPATHLEN);
    381 			goto loop;
    382 		}
    383 		if (S_ISDIR(sb.st_mode)) {
    384 			if (chdir(p) < 0)
    385 				goto err1;
    386 			p = empty;
    387 		}
    388 	}
    389 
    390 	/*
    391 	 * Save the last component name and get the full pathname of
    392 	 * the current directory.
    393 	 */
    394 	if (strlcpy(wbuf, p, sizeof(wbuf)) >= sizeof(wbuf)) {
    395 		errno = ENAMETOOLONG;
    396 		goto err1;
    397 	}
    398 
    399 	/*
    400 	 * Call the internal internal version of getcwd which
    401 	 * does a physical search rather than using the $PWD short-cut
    402 	 */
    403 	if (getcwd(resolved, MAXPATHLEN) == 0)
    404 		goto err1;
    405 
    406 	/*
    407 	 * Join the two strings together, ensuring that the right thing
    408 	 * happens if the last component is empty, or the dirname is root.
    409 	 */
    410 	if (resolved[0] == '/' && resolved[1] == '\0')
    411 		rootd = 1;
    412 	else
    413 		rootd = 0;
    414 
    415 	if (*wbuf) {
    416 		if (strlen(resolved) + strlen(wbuf) + (rootd ? 0 : 1) + 1 >
    417 		    MAXPATHLEN) {
    418 			errno = ENAMETOOLONG;
    419 			goto err1;
    420 		}
    421 		if (rootd == 0)
    422 			if (strlcat(resolved, "/", MAXPATHLEN) >= MAXPATHLEN) {
    423 				errno = ENAMETOOLONG;
    424 				goto err1;
    425 			}
    426 		if (strlcat(resolved, wbuf, MAXPATHLEN) >= MAXPATHLEN) {
    427 			errno = ENAMETOOLONG;
    428 			goto err1;
    429 		}
    430 	}
    431 
    432 	/* Go back to where we came from. */
    433 	if (fchdir(fd) < 0) {
    434 		serrno = errno;
    435 		goto err2;
    436 	}
    437 
    438 	/* It's okay if the close fails, what's an fd more or less? */
    439 	(void)close(fd);
    440 	return (resolved);
    441 
    442 err1:	serrno = errno;
    443 	(void)fchdir(fd);
    444 err2:	(void)close(fd);
    445 	errno = serrno;
    446 	return (NULL);
    447 }
    448