Home | History | Annotate | Line # | Download | only in sysinst
aout2elf.c revision 1.1
      1 /*	$NetBSD: aout2elf.c,v 1.1 2014/07/26 19:30:44 dholland 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 	msg_display(MSG_aoutfail);
    199 	process_menu(MENU_ok, NULL);
    200 	exit(1);
    201 }
    202 
    203 int
    204 move_aout_libs(void)
    205 {
    206 	int n, backedup = 0;
    207 	char prefix[MAXPATHLEN], src[MAXPATHLEN];
    208 	struct stat st;
    209 
    210 	n = handle_aout_libs(target_expand("/usr/lib"), LIB_COUNT, NULL);
    211 	if (n <= 0)
    212 		return n;
    213 
    214 	/*
    215 	 * See if /emul/aout already exists, taking symlinks into
    216 	 * account. If so, no need to create it, just use it.
    217 	 */
    218 	if (target_realpath("/emul/aout", prefix) != NULL && stat(prefix, &st) == 0)
    219 		goto domove;
    220 
    221 	/*
    222 	 * See if /emul exists. If not, create it.
    223 	 */
    224 	if (target_realpath("/emul", prefix) == NULL || stat(prefix, &st) < 0) {
    225 		strlcpy(prefix, target_expand("/emul"), sizeof(prefix));
    226 		if (lstat(prefix, &st) == 0) {
    227 			run_program(0, "mv -f %s %s", prefix,
    228 			    target_expand("/emul.old"));
    229 			backedup = 1;
    230 		}
    231 		scripting_fprintf(NULL, "mkdir %s\n", prefix);
    232 		mkdir(prefix, 0755);
    233 	}
    234 
    235 	/*
    236 	 * Can use strcpy, target_expand has made sure it fits into
    237 	 * MAXPATHLEN. XXX all this copying is because concat_paths
    238 	 * returns a pointer to a static buffer.
    239 	 *
    240 	 * If an old aout link exists (apparently pointing to nowhere),
    241 	 * move it out of the way.
    242 	 */
    243 	strlcpy(src, concat_paths(prefix, "aout"), sizeof(src));
    244 	if (lstat(src, &st) == 0) {
    245 		run_program(0, "mv -f %s %s", src,
    246 		    concat_paths(prefix, "aout.old"));
    247 		backedup = 1;
    248 	}
    249 
    250 	/*
    251 	 * We have created /emul if needed. Since no previous /emul/aout
    252 	 * existed, we'll use a symbolic link in /emul to /usr/aout, to
    253 	 * avoid overflowing the root partition.
    254 	 */
    255 	strlcpy(prefix, target_expand("/usr/aout"), sizeof(prefix));
    256 	if (run_program(0, "mkdir -p %s", prefix))
    257 		abort_libupdate();
    258 	if (run_program(0, "ln -s %s %s", "/usr/aout", src))
    259 		abort_libupdate();
    260 
    261 domove:
    262 	/*
    263 	 * Rename etc and usr/lib if they already existed, so that we
    264 	 * do not overwrite old files.
    265 	 *
    266 	 * Then, move /etc/ld.so.conf to /emul/aout/etc/ld.so.conf,
    267 	 * and all a.out dynamic libraries from /usr/lib to
    268 	 * /emul/aout/usr/lib. This is where the a.out code in ldconfig
    269 	 * and ld.so respectively will find them.
    270 	 */
    271 	strlcpy(src, concat_paths(prefix, "usr/lib"), sizeof(src));
    272 	run_program(0, "mv -f %s %s", src, concat_paths(prefix, "usr/lib.old"));
    273 	strlcpy(src, concat_paths(prefix, "etc/ld.so.conf"), sizeof(src));
    274 	run_program(0, "mv -f %s %s",
    275 	    src, concat_paths(prefix, "etc/ld.so.conf.old"));
    276 	if (run_program(0, "mkdir -p %s ", concat_paths(prefix, "usr/lib")))
    277 		abort_libupdate();
    278 	if (run_program(0, "mkdir -p %s ", concat_paths(prefix, "etc")))
    279 		abort_libupdate();
    280 
    281 	strlcpy(src, target_expand("/etc/ld.so.conf"), sizeof(src));
    282 	if (run_program(0, "mv -f %s %s",
    283 	    src, concat_paths(prefix, "etc/ld.so.conf")))
    284 		abort_libupdate();
    285 
    286 	strlcpy(src, target_expand("/usr/lib"), sizeof(src));
    287 	n = handle_aout_libs(src, LIB_MOVE, concat_paths(prefix, "usr/lib"));
    288 
    289 	if (run_program(0, "mkdir -p %s ",
    290 	    concat_paths(prefix, "usr/X11R6/lib")))
    291 		abort_libupdate();
    292 
    293 	strlcpy(src, target_expand("/usr/X11R6/lib"), sizeof(src));
    294 	handle_aout_x_libs(src, concat_paths(prefix, "usr/X11R6/lib"));
    295 
    296 	if (backedup) {
    297 		msg_display(MSG_emulbackup);
    298 		process_menu(MENU_ok, NULL);
    299 	}
    300 
    301 	return n;
    302 }
    303 
    304 /*
    305  * XXXX had to include this to deal with symlinks in some places.
    306  * When the target * disk is mounted under /targetroot, absolute symlinks
    307  * on it don't work right.
    308  * This function will resolve them using the mountpoint as prefix.
    309  * Copied verbatim from libc, with added prefix handling.
    310  *
    311  * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
    312  *
    313  * Find the real name of path, by removing all ".", ".." and symlink
    314  * components.  Returns (resolved) on success, or (NULL) on failure,
    315  * in which case the path which caused trouble is left in (resolved).
    316  */
    317 static char *
    318 target_realpath(const char *path, char *resolved)
    319 {
    320 	struct stat sb;
    321 	int fd, n, rootd, serrno, nlnk = 0;
    322 	char *p, *q, wbuf[MAXPATHLEN];
    323 	char solidus[2], empty[1];
    324 	solidus[0] = '/';
    325 	solidus[1] = '\0';
    326 	empty[0] = '\0';
    327 
    328 	/* Save the starting point. */
    329 	if ((fd = open(".", O_RDONLY)) < 0) {
    330 		(void)strlcpy(resolved, ".", MAXPATHLEN);
    331 		return (NULL);
    332 	}
    333 
    334 	/*
    335 	 * Find the dirname and basename from the path to be resolved.
    336 	 * Change directory to the dirname component.
    337 	 * lstat the basename part.
    338 	 *     if it is a symlink, read in the value and loop.
    339 	 *     if it is a directory, then change to that directory.
    340 	 * get the current directory name and append the basename.
    341 	 */
    342 	if (target_prefix() != NULL && strcmp(target_prefix(), "") != 0)
    343 		snprintf(resolved, MAXPATHLEN, "%s/%s", target_prefix(), path);
    344 	else
    345 		if (strlcpy(resolved, path, MAXPATHLEN) >= MAXPATHLEN) {
    346 			errno = ENAMETOOLONG;
    347 			goto err1;
    348 		}
    349 loop:
    350 	q = strrchr(resolved, '/');
    351 	if (q != NULL) {
    352 		p = q + 1;
    353 		if (q == resolved)
    354 			q = solidus;
    355 		else {
    356 			do {
    357 				--q;
    358 			} while (q > resolved && *q == '/');
    359 			q[1] = '\0';
    360 			q = resolved;
    361 		}
    362 		if (chdir(q) < 0)
    363 			goto err1;
    364 	} else
    365 		p = resolved;
    366 
    367 	/* Deal with the last component. */
    368 	if (lstat(p, &sb) == 0) {
    369 		if (S_ISLNK(sb.st_mode)) {
    370 			if (nlnk++ >= MAXSYMLINKS) {
    371 				errno = ELOOP;
    372 				goto err1;
    373 			}
    374 			n = readlink(p, wbuf, MAXPATHLEN - 1);
    375 			if (n < 0)
    376 				goto err1;
    377 			wbuf[n] = '\0';
    378 			if (wbuf[0] == '/')
    379 				snprintf(resolved, MAXPATHLEN, "%s%s",
    380 				    target_prefix(), wbuf);
    381 			else
    382 				strlcpy(resolved, wbuf, MAXPATHLEN);
    383 			goto loop;
    384 		}
    385 		if (S_ISDIR(sb.st_mode)) {
    386 			if (chdir(p) < 0)
    387 				goto err1;
    388 			p = empty;
    389 		}
    390 	}
    391 
    392 	/*
    393 	 * Save the last component name and get the full pathname of
    394 	 * the current directory.
    395 	 */
    396 	if (strlcpy(wbuf, p, sizeof(wbuf)) >= sizeof(wbuf)) {
    397 		errno = ENAMETOOLONG;
    398 		goto err1;
    399 	}
    400 
    401 	/*
    402 	 * Call the internal internal version of getcwd which
    403 	 * does a physical search rather than using the $PWD short-cut
    404 	 */
    405 	if (getcwd(resolved, MAXPATHLEN) == 0)
    406 		goto err1;
    407 
    408 	/*
    409 	 * Join the two strings together, ensuring that the right thing
    410 	 * happens if the last component is empty, or the dirname is root.
    411 	 */
    412 	if (resolved[0] == '/' && resolved[1] == '\0')
    413 		rootd = 1;
    414 	else
    415 		rootd = 0;
    416 
    417 	if (*wbuf) {
    418 		if (strlen(resolved) + strlen(wbuf) + (rootd ? 0 : 1) + 1 >
    419 		    MAXPATHLEN) {
    420 			errno = ENAMETOOLONG;
    421 			goto err1;
    422 		}
    423 		if (rootd == 0)
    424 			if (strlcat(resolved, "/", MAXPATHLEN) >= MAXPATHLEN) {
    425 				errno = ENAMETOOLONG;
    426 				goto err1;
    427 			}
    428 		if (strlcat(resolved, wbuf, MAXPATHLEN) >= MAXPATHLEN) {
    429 			errno = ENAMETOOLONG;
    430 			goto err1;
    431 		}
    432 	}
    433 
    434 	/* Go back to where we came from. */
    435 	if (fchdir(fd) < 0) {
    436 		serrno = errno;
    437 		goto err2;
    438 	}
    439 
    440 	/* It's okay if the close fails, what's an fd more or less? */
    441 	(void)close(fd);
    442 	return (resolved);
    443 
    444 err1:	serrno = errno;
    445 	(void)fchdir(fd);
    446 err2:	(void)close(fd);
    447 	errno = serrno;
    448 	return (NULL);
    449 }
    450