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 */ 55static int is_aout_shared_lib(const char *name); 56static void handle_aout_x_libs(const char *srcdir, const char *tgtdir); 57static int handle_aout_libs(const char *dir, int op, const void *arg); 58static 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 */ 67const 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 83static int 84is_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 111static void 112handle_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 */ 132static int 133handle_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 186endloop: 187 free(full_name); 188 } 189 190 closedir(dd); 191 192 return n; 193} 194 195__dead static void 196abort_libupdate(void) 197{ 198 hit_enter_to_continue(MSG_aoutfail, NULL); 199 exit(1); 200} 201 202int 203move_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 260domove: 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 */ 315static char * 316target_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 } 347loop: 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 442err1: serrno = errno; 443 (void)fchdir(fd); 444err2: (void)close(fd); 445 errno = serrno; 446 return (NULL); 447} 448