file.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* Copyright (c) 1987, 1988 Microsoft Corporation */
/* All Rights Reserved */
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#define _LARGEFILE64_SOURCE
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <libelf.h>
#include <stdlib.h>
#include <limits.h>
#include <locale.h>
#include <wctype.h>
#include <string.h>
#include <errno.h>
#include <door.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/mkdev.h>
#include <sys/stat.h>
#include <sys/elf.h>
#include <sys/elf_M32.h>
#include <sys/elf_SPARC.h>
#include <procfs.h>
#include <sys/core.h>
#include <sys/dumphdr.h>
#include <netinet/in.h>
#include <gelf.h>
#include <elfcap.h>
#include "file.h"
typedef Elf64_Nhdr GElf_Nhdr;
/*
* Misc
*/
#define FBSZ 512
#define MLIST_SZ 12
/*
* The 0x8FCA0102 magic string was used in crash dumps generated by releases
* prior to Solaris 7.
*/
#define OLD_DUMP_MAGIC 0x8FCA0102
#if defined(__sparc)
#define NATIVE_ISA "SPARC"
#define OTHER_ISA "Intel"
#else
#define NATIVE_ISA "Intel"
#define OTHER_ISA "SPARC"
#endif
/* Assembly language comment char */
#ifdef pdp11
#define ASCOMCHAR '/'
#else
#define ASCOMCHAR '!'
#endif
#pragma align 16(fbuf)
static char fbuf[FBSZ];
/*
* Magic file variables
*/
static intmax_t maxmagicoffset;
static intmax_t tmpmax;
static char *magicbuf;
static char *dfile;
static char *troff[] = { /* new troff intermediate lang */
"x", "T", "res", "init", "font", "202", "V0", "p1", 0};
static char *fort[] = { /* FORTRAN */
"function", "subroutine", "common", "dimension", "block",
"integer", "real", "data", "double",
"FUNCTION", "SUBROUTINE", "COMMON", "DIMENSION", "BLOCK",
"INTEGER", "REAL", "DATA", "DOUBLE", 0};
static char *asc[] = { /* Assembler Commands */
"sys", "mov", "tst", "clr", "jmp", "cmp", "set", "inc",
"dec", 0};
static char *c[] = { /* C Language */
"int", "char", "float", "double", "short", "long", "unsigned",
"register", "static", "struct", "extern", 0};
static char *as[] = { /* Assembler Pseudo Ops, prepended with '.' */
"globl", "global", "ident", "file", "byte", "even",
"text", "data", "bss", "comm", 0};
/*
* The line and debug section names are used by the strip command.
* Any changes in the strip implementation need to be reflected here.
*/
static char *debug_sections[] = { /* Debug sections in a ELF file */
".debug", ".stab", ".dwarf", ".line", NULL};
/* start for MB env */
static wchar_t wchar;
static int length;
static int IS_ascii;
static int Max;
/* end for MB env */
static int i; /* global index into first 'fbsz' bytes of file */
static int fbsz;
static int ifd = -1;
static int elffd = -1;
static int tret;
static int hflg;
static int dflg;
static int mflg;
static int M_flg;
static int iflg;
static struct stat64 mbuf;
static char **mlist1; /* 1st ordered list of magic files */
static char **mlist2; /* 2nd ordered list of magic files */
static size_t mlist1_sz; /* number of ptrs allocated for mlist1 */
static size_t mlist2_sz; /* number of ptrs allocated for mlist2 */
static char **mlist1p; /* next entry in mlist1 */
static char **mlist2p; /* next entry in mlist2 */
static ssize_t mread;
static void is_stripped(Elf *elf);
static Elf *is_elf_file(int elffd);
static void ar_coff_or_aout(int ifd);
static int type(char *file);
static int def_position_tests(void);
static void def_context_tests(void);
static int troffint(char *bp, int n);
static int lookup(char **tab);
static int ccom(void);
static int ascom(void);
static int sccs(void);
static int english(char *bp, int n);
static int old_core(Elf *elf, GElf_Ehdr *ehdr, int format);
static int core(Elf *elf, GElf_Ehdr *ehdr, int format);
static int shellscript(char buf[], struct stat64 *sb);
static int elf_check(Elf *elf);
static int get_door_target(char *, char *, size_t);
static int zipfile(char *, int);
static int is_crash_dump(const char *, int);
static void print_dumphdr(const int, const dumphdr_t *, uint32_t (*)(uint32_t),
const char *);
static uint32_t swap_uint32(uint32_t);
static uint32_t return_uint32(uint32_t);
static int is_in_list(char *[], char *);
static void usage(void);
static void default_magic(void);
static void add_to_mlist(char *, int);
static void fd_cleanup(void);
#ifdef XPG4
/* SUSv3 requires a single <space> after the colon */
#define prf(x) (void) printf("%s: ", x);
#else /* !XPG4 */
#define prf(x) (void) printf("%s:%s", x, (int)strlen(x) > 6 ? "\t" : "\t\t");
#endif /* XPG4 */
int
main(int argc, char **argv)
{
char *p;
int ch;
FILE *fl;
int cflg = 0;
int eflg = 0;
int fflg = 0;
char *ap = NULL;
int pathlen;
char **filep;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
#endif
(void) textdomain(TEXT_DOMAIN);
while ((ch = getopt(argc, argv, "M:cdf:him:")) != EOF) {
switch (ch) {
case 'M':
add_to_mlist(optarg, !dflg);
M_flg++;
break;
case 'c':
cflg++;
break;
case 'd':
if (!dflg) {
default_magic();
add_to_mlist(dfile, 0);
dflg++;
}
break;
case 'f':
fflg++;
if ((fl = fopen(optarg, "r")) == NULL) {
(void) fprintf(stderr,
gettext("cannot open %s\n"), optarg);
usage();
}
pathlen = pathconf("/", _PC_PATH_MAX);
if (pathlen == -1) {
(void) fprintf(stderr,
gettext("pathconf: cannot determine "
"maximum path length\n"));
exit(1);
}
pathlen += 2; /* for null and newline in fgets */
ap = malloc(pathlen * sizeof (char));
if (ap == NULL) {
perror("malloc");
exit(1);
}
break;
case 'h':
hflg++;
break;
case 'i':
iflg++;
break;
case 'm':
add_to_mlist(optarg, !dflg);
mflg++;
break;
case '?':
eflg++;
break;
}
}
if (!cflg && !fflg && (eflg || optind == argc))
usage();
if (iflg && (dflg || mflg || M_flg)) {
usage();
}
if (iflg && cflg) {
usage();
}
if (!dflg && !mflg && !M_flg && !iflg) {
/* no -d, -m, nor -M option; also -i option doesn't need magic */
default_magic();
if (f_mkmtab(dfile, cflg, 0) == -1) {
exit(2);
}
}
else if (mflg && !M_flg && !dflg) {
/* -m specified without -d nor -M */
#ifdef XPG4 /* For SUSv3 only */
/*
* The default position-dependent magic file tests
* in /etc/magic will follow all the -m magic tests.
*/
for (filep = mlist1; filep < mlist1p; filep++) {
if (f_mkmtab(*filep, cflg, 1) == -1) {
exit(2);
}
}
default_magic();
if (f_mkmtab(dfile, cflg, 0) == -1) {
exit(2);
}
#else /* !XPG4 */
/*
* Retain Solaris file behavior for -m before SUSv3,
* when the new -d and -M options are not specified.
* Use the -m file specified in place of the default
* /etc/magic file. Solaris file will
* now allow more than one magic file to be specified
* with multiple -m options, for consistency with
* other behavior.
*
* Put the magic table(s) specified by -m into
* the second magic table instead of the first
* (as indicated by the last argument to f_mkmtab()),
* since they replace the /etc/magic tests and
* must be executed alongside the default
* position-sensitive tests.
*/
for (filep = mlist1; filep < mlist1p; filep++) {
if (f_mkmtab(*filep, cflg, 0) == -1) {
exit(2);
}
}
#endif /* XPG4 */
} else {
/*
* For any other combination of -d, -m, and -M,
* use the magic files in command-line order.
* Store the entries from the two separate lists of magic
* files, if any, into two separate magic file tables.
* mlist1: magic tests executed before default magic tests
* mlist2: default magic tests and after
*/
for (filep = mlist1; filep && (filep < mlist1p); filep++) {
if (f_mkmtab(*filep, cflg, 1) == -1) {
exit(2);
}
}
for (filep = mlist2; filep && (filep < mlist2p); filep++) {
if (f_mkmtab(*filep, cflg, 0) == -1) {
exit(2);
}
}
}
/* Initialize the magic file variables; check both magic tables */
tmpmax = f_getmaxoffset(1);
maxmagicoffset = f_getmaxoffset(0);
if (maxmagicoffset < tmpmax) {
maxmagicoffset = tmpmax;
}
if (maxmagicoffset < (intmax_t)FBSZ)
maxmagicoffset = (intmax_t)FBSZ;
if ((magicbuf = (char *)malloc(maxmagicoffset)) == NULL) {
(void) fprintf(stderr, gettext("malloc failed\n"));
exit(2);
}
if (cflg) {
f_prtmtab();
if (ferror(stdout) != 0) {
(void) fprintf(stderr, gettext("file: error writing to "
"stdout\n"));
exit(1);
}
if (fclose(stdout) != 0) {
perror(gettext("file: fclose failed"));
exit(1);
}
exit(0);
}
for (; fflg || optind < argc; optind += !fflg) {
register int l;
if (fflg) {
if ((p = fgets(ap, pathlen, fl)) == NULL) {
fflg = 0;
optind--;
continue;
}
l = strlen(p);
if (l > 0)
p[l - 1] = '\0';
} else
p = argv[optind];
prf(p); /* print "file_name:<tab>" */
if (type(p))
tret = 1;
}
if (ap != NULL)
free(ap);
if (tret != 0) {
exit(tret);
}
if (ferror(stdout) != 0) {
(void) fprintf(stderr, gettext("file: error writing to "
"stdout\n"));
exit(1);
}
if (fclose(stdout) != 0) {
perror(gettext("file: fclose failed"));
exit(1);
}
return (0);
}
static int
type(char *file)
{
int cc;
char buf[BUFSIZ];
int (*statf)() = hflg ? lstat64 : stat64;
i = 0; /* reset index to beginning of file */
ifd = -1;
if ((*statf)(file, &mbuf) < 0) {
if (statf == lstat64 || lstat64(file, &mbuf) < 0) {
(void) printf(gettext("cannot open: %s\n"),
strerror(errno));
return (0); /* POSIX.2 */
}
}
switch (mbuf.st_mode & S_IFMT) {
case S_IFREG:
if (iflg) {
(void) printf(gettext("regular file\n"));
return (0);
}
break;
case S_IFCHR:
(void) printf(gettext("character"));
goto spcl;
case S_IFDIR:
(void) printf(gettext("directory\n"));
return (0);
case S_IFIFO:
(void) printf(gettext("fifo\n"));
return (0);
case S_IFLNK:
if ((cc = readlink(file, buf, BUFSIZ)) < 0) {
(void) printf(gettext("readlink error: %s\n"),
strerror(errno));
return (1);
}
buf[cc] = '\0';
(void) printf(gettext("symbolic link to %s\n"), buf);
return (0);
case S_IFBLK:
(void) printf(gettext("block"));
/* major and minor, see sys/mkdev.h */
spcl:
(void) printf(gettext(" special (%d/%d)\n"),
major(mbuf.st_rdev), minor(mbuf.st_rdev));
return (0);
case S_IFSOCK:
(void) printf("socket\n");
/* FIXME, should open and try to getsockname. */
return (0);
case S_IFDOOR:
if (get_door_target(file, buf, sizeof (buf)) == 0)
(void) printf(gettext("door to %s\n"), buf);
else
(void) printf(gettext("door\n"));
return (0);
}
if (elf_version(EV_CURRENT) == EV_NONE) {
(void) printf(gettext("libelf is out of date\n"));
return (1);
}
ifd = open64(file, O_RDONLY);
if (ifd < 0) {
(void) printf(gettext("cannot open: %s\n"), strerror(errno));
return (0); /* POSIX.2 */
}
/* need another fd for elf, since we might want to read the file too */
elffd = open64(file, O_RDONLY);
if (elffd < 0) {
(void) printf(gettext("cannot open: %s\n"), strerror(errno));
(void) close(ifd);
ifd = -1;
return (0); /* POSIX.2 */
}
if ((fbsz = read(ifd, fbuf, FBSZ)) == -1) {
(void) printf(gettext("cannot read: %s\n"), strerror(errno));
(void) close(ifd);
ifd = -1;
return (0); /* POSIX.2 */
}
if (fbsz == 0) {
(void) printf(gettext("empty file\n"));
fd_cleanup();
return (0);
}
/*
* First try user-specified position-dependent magic tests, if any,
* which need to execute before the default tests.
*/
if ((mread = pread(ifd, (void*)magicbuf, (size_t)maxmagicoffset,
(off_t)0)) == -1) {
(void) printf(gettext("cannot read: %s\n"), strerror(errno));
fd_cleanup();
return (0);
}
/*
* ChecK against Magic Table entries.
* Check first magic table for magic tests to be applied
* before default tests.
* If no default tests are to be applied, all magic tests
* should occur in this magic table.
*/
switch (f_ckmtab(magicbuf, mread, 1)) {
case -1: /* Error */
exit(2);
break;
case 0: /* Not magic */
break;
default: /* Switch is magic index */
(void) putchar('\n');
fd_cleanup();
return (0);
/* NOTREACHED */
break;
}
if (dflg || !M_flg) {
/*
* default position-dependent tests,
* plus non-default magic tests, if any
*/
switch (def_position_tests()) {
case -1: /* error */
fd_cleanup();
return (1);
case 1: /* matching type found */
fd_cleanup();
return (0);
/* NOTREACHED */
break;
case 0: /* no matching type found */
break;
}
/* default context-sensitive tests */
def_context_tests();
} else {
/* no more tests to apply; no match was found */
(void) printf(gettext("data\n"));
}
fd_cleanup();
return (0);
}
/*
* def_position_tests() - applies default position-sensitive tests,
* looking for values in specific positions in the file.
* These are followed by default (followed by possibly some
* non-default) magic file tests.
*
* All position-sensitive tests, default or otherwise, must
* be applied before context-sensitive tests, to avoid
* false context-sensitive matches.
*
* Returns -1 on error which should result in error (non-zero)
* exit status for the file utility.
* Returns 0 if no matching file type found.
* Returns 1 if matching file type found.
*/
static int
def_position_tests(void)
{
Elf *elf;
if (sccs()) { /* look for "1hddddd" where d is a digit */
(void) printf("sccs \n");
return (1);
}
if (fbuf[0] == '#' && fbuf[1] == '!' && shellscript(fbuf+2, &mbuf))
return (1);
if ((elf = is_elf_file(elffd)) != NULL) {
(void) elf_check(elf);
(void) elf_end(elf);
(void) putchar('\n');
return (1);
/* LINTED: pointer cast may result in improper alignment */
} else if (*(int *)fbuf == CORE_MAGIC) {
/* LINTED: pointer cast may result in improper alignment */
struct core *corep = (struct core *)fbuf;
(void) printf("a.out core file");
if (*(corep->c_cmdname) != '\0')
(void) printf(" from '%s'", corep->c_cmdname);
(void) putchar('\n');
return (1);
}
/*
* ZIP files, JAR files, and Java executables
*/
if (zipfile(fbuf, ifd))
return (1);
if (is_crash_dump(fbuf, ifd))
return (1);
/*
* ChecK against Magic Table entries.
* The magic entries checked here always start with default
* magic tests and may be followed by other, non-default magic
* tests. If no default tests are to be executed, all the
* magic tests should have been in the first magic table.
*/
switch (f_ckmtab(magicbuf, mread, 0)) {
case -1: /* Error */
exit(2);
break;
case 0: /* Not magic */
return (0);
/* NOTREACHED */
break;
default: /* Switch is magic index */
/*
* f_ckmtab recognizes file type,
* check if it is PostScript.
* if not, check if elf or a.out
*/
if (magicbuf[0] == '%' && magicbuf[1] == '!') {
(void) putchar('\n');
} else {
/*
* Check that the file is executable (dynamic
* objects must be executable to be exec'ed,
* shared objects need not be, but by convention
* should be executable).
*
* Note that we should already have processed
* the file if it was an ELF file.
*/
ar_coff_or_aout(elffd);
(void) putchar('\n');
}
return (1);
/* NOTREACHED */
break;
}
return (0); /* file was not identified */
}
/*
* def_context_tests() - default context-sensitive tests.
* These are the last tests to be applied.
* If no match is found, prints out "data".
*/
static void
def_context_tests(void)
{
int j;
int nl;
char ch;
int len;
if (ccom() == 0)
goto notc;
while (fbuf[i] == '#') {
j = i;
while (fbuf[i++] != '\n') {
if (i - j > 255) {
(void) printf(gettext("data\n"));
return;
}
if (i >= fbsz)
goto notc;
}
if (ccom() == 0)
goto notc;
}
check:
if (lookup(c) == 1) {
while ((ch = fbuf[i]) != ';' && ch != '{') {
if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0)
len = 1;
i += len;
if (i >= fbsz)
goto notc;
}
(void) printf(gettext("c program text"));
goto outa;
}
nl = 0;
while (fbuf[i] != '(') {
if (fbuf[i] <= 0)
goto notas;
if (fbuf[i] == ';') {
i++;
goto check;
}
if (fbuf[i++] == '\n')
if (nl++ > 6)
goto notc;
if (i >= fbsz)
goto notc;
}
while (fbuf[i] != ')') {
if (fbuf[i++] == '\n')
if (nl++ > 6)
goto notc;
if (i >= fbsz)
goto notc;
}
while (fbuf[i] != '{') {
if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0)
len = 1;
if (fbuf[i] == '\n')
if (nl++ > 6)
goto notc;
i += len;
if (i >= fbsz)
goto notc;
}
(void) printf(gettext("c program text"));
goto outa;
notc:
i = 0; /* reset to begining of file again */
while (fbuf[i] == 'c' || fbuf[i] == 'C'|| fbuf[i] == '!' ||
fbuf[i] == '*' || fbuf[i] == '\n') {
while (fbuf[i++] != '\n')
if (i >= fbsz)
goto notfort;
}
if (lookup(fort) == 1) {
(void) printf(gettext("fortran program text"));
goto outa;
}
notfort: /* looking for assembler program */
i = 0; /* reset to beginning of file again */
if (ccom() == 0) /* assembler programs may contain */
/* c-style comments */
goto notas;
if (ascom() == 0)
goto notas;
j = i - 1;
if (fbuf[i] == '.') {
i++;
if (lookup(as) == 1) {
(void) printf(gettext("assembler program text"));
goto outa;
} else if (j != -1 && fbuf[j] == '\n' && isalpha(fbuf[j + 2])) {
(void) printf(
gettext("[nt]roff, tbl, or eqn input text"));
goto outa;
}
}
while (lookup(asc) == 0) {
if (ccom() == 0)
goto notas;
if (ascom() == 0)
goto notas;
while (fbuf[i] != '\n' && fbuf[i++] != ':') {
if (i >= fbsz)
goto notas;
}
while (fbuf[i] == '\n' || fbuf[i] == ' ' || fbuf[i] == '\t')
if (i++ >= fbsz)
goto notas;
j = i - 1;
if (fbuf[i] == '.') {
i++;
if (lookup(as) == 1) {
(void) printf(
gettext("assembler program text"));
goto outa;
} else if (fbuf[j] == '\n' && isalpha(fbuf[j+2])) {
(void) printf(
gettext("[nt]roff, tbl, or eqn input "
"text"));
goto outa;
}
}
}
(void) printf(gettext("assembler program text"));
goto outa;
notas:
/* start modification for multibyte env */
IS_ascii = 1;
if (fbsz < FBSZ)
Max = fbsz;
else
Max = FBSZ - MB_LEN_MAX; /* prevent cut of wchar read */
/* end modification for multibyte env */
for (i = 0; i < Max; /* null */)
if (fbuf[i] & 0200) {
IS_ascii = 0;
if (fbuf[0] == '\100' && fbuf[1] == '\357') {
(void) printf(gettext("troff output\n"));
return;
}
/* start modification for multibyte env */
if ((length = mbtowc(&wchar, &fbuf[i], MB_CUR_MAX))
<= 0 || !iswprint(wchar)) {
(void) printf(gettext("data\n"));
return;
}
i += length;
}
else
i++;
i = fbsz;
/* end modification for multibyte env */
if (mbuf.st_mode&(S_IXUSR|S_IXGRP|S_IXOTH))
(void) printf(gettext("commands text"));
else if (troffint(fbuf, fbsz))
(void) printf(gettext("troff intermediate output text"));
else if (english(fbuf, fbsz))
(void) printf(gettext("English text"));
else if (IS_ascii)
(void) printf(gettext("ascii text"));
else
(void) printf(gettext("text")); /* for multibyte env */
outa:
/*
* This code is to make sure that no MB char is cut in half
* while still being used.
*/
fbsz = (fbsz < FBSZ ? fbsz : fbsz - MB_CUR_MAX + 1);
while (i < fbsz) {
if (isascii(fbuf[i])) {
i++;
continue;
} else {
if ((length = mbtowc(&wchar, &fbuf[i], MB_CUR_MAX))
<= 0 || !iswprint(wchar)) {
(void) printf(gettext(" with garbage\n"));
return;
}
i = i + length;
}
}
(void) printf("\n");
}
static int
troffint(char *bp, int n)
{
int k;
i = 0;
for (k = 0; k < 6; k++) {
if (lookup(troff) == 0)
return (0);
if (lookup(troff) == 0)
return (0);
while (i < n && bp[i] != '\n')
i++;
if (i++ >= n)
return (0);
}
return (1);
}
/*
* Determine if the passed descriptor describes an ELF file.
* If so, return the Elf handle.
*/
static Elf *
is_elf_file(int elffd)
{
Elf *elf;
elf = elf_begin(elffd, ELF_C_READ, (Elf *)0);
switch (elf_kind(elf)) {
case ELF_K_ELF:
break;
default:
(void) elf_end(elf);
elf = NULL;
break;
}
return (elf);
}
static void
ar_coff_or_aout(int elffd)
{
Elf *elf;
/*
* Get the files elf descriptor and process it as an elf or
* a.out (4.x) file.
*/
elf = elf_begin(elffd, ELF_C_READ, (Elf *)0);
switch (elf_kind(elf)) {
case ELF_K_AR :
(void) printf(gettext(", not a dynamic executable "
"or shared object"));
break;
case ELF_K_COFF:
(void) printf(gettext(", unsupported or unknown "
"file type"));
break;
default:
/*
* This is either an unknown file or an aout format
* At this time, we don't print dynamic/stripped
* info. on a.out or non-Elf binaries.
*/
break;
}
(void) elf_end(elf);
}
static void
print_elf_type(Elf *elf, GElf_Ehdr *ehdr, int format)
{
switch (ehdr->e_type) {
case ET_NONE:
(void) printf(" %s", gettext("unknown type"));
break;
case ET_REL:
(void) printf(" %s", gettext("relocatable"));
break;
case ET_EXEC:
(void) printf(" %s", gettext("executable"));
break;
case ET_DYN:
(void) printf(" %s", gettext("dynamic lib"));
break;
case ET_CORE:
if (old_core(elf, ehdr, format))
(void) printf(" %s", gettext("pre-2.6 core file"));
else
(void) printf(" %s", gettext("core file"));
break;
default:
break;
}
}
static void
print_elf_machine(int machine)
{
switch (machine) {
case EM_NONE:
(void) printf(" %s", gettext("unknown machine"));
break;
case EM_M32:
(void) printf(" %s", gettext("WE32100"));
break;
case EM_SPARC:
(void) printf(" %s", gettext("SPARC"));
break;
case EM_386:
(void) printf(" %s", gettext("80386"));
break;
case EM_68K:
(void) printf(" %s", gettext("M68000"));
break;
case EM_88K:
(void) printf(" %s", gettext("M88000"));
break;
case EM_486:
(void) printf(" %s", gettext("80486"));
break;
case EM_860:
(void) printf(" %s", gettext("i860"));
break;
case EM_MIPS:
(void) printf(" %s", gettext("MIPS RS3000 Big-Endian"));
break;
case EM_MIPS_RS3_LE:
(void) printf(" %s", gettext("MIPS RS3000 Little-Endian"));
break;
case EM_RS6000:
(void) printf(" %s", gettext("MIPS RS6000"));
break;
case EM_PA_RISC:
(void) printf(" %s", gettext("PA-RISC"));
break;
case EM_nCUBE:
(void) printf(" %s", gettext("nCUBE"));
break;
case EM_VPP500:
(void) printf(" %s", gettext("VPP500"));
break;
case EM_SPARC32PLUS:
(void) printf(" %s", gettext("SPARC32PLUS"));
break;
case EM_PPC:
(void) printf(" %s", gettext("PowerPC"));
break;
case EM_SPARCV9:
(void) printf(" %s", gettext("SPARCV9"));
break;
case EM_IA_64:
(void) printf(" %s", gettext("IA64"));
break;
case EM_AMD64:
(void) printf(" %s", gettext("AMD64"));
break;
default:
break;
}
}
static void
print_elf_datatype(int datatype)
{
switch (datatype) {
case ELFDATA2LSB:
(void) printf(" %s", gettext("LSB"));
break;
case ELFDATA2MSB:
(void) printf(" %s", gettext("MSB"));
break;
default:
break;
}
}
static void
print_elf_class(int class)
{
switch (class) {
case ELFCLASS32:
(void) printf(" %s", gettext("32-bit"));
break;
case ELFCLASS64:
(void) printf(" %s", gettext("64-bit"));
break;
default:
break;
}
}
static void
print_elf_flags(int machine, unsigned int flags)
{
switch (machine) {
case EM_M32:
if (flags & EF_M32_MAU)
(void) printf("%s", gettext(", MAU Required"));
break;
case EM_SPARCV9:
if (flags & EF_SPARC_EXT_MASK) {
if (flags & EF_SPARC_SUN_US3) {
(void) printf("%s", gettext(
", UltraSPARC3 Extensions Required"));
} else if (flags & EF_SPARC_SUN_US1) {
(void) printf("%s", gettext(
", UltraSPARC1 Extensions Required"));
}
if (flags & EF_SPARC_HAL_R1)
(void) printf("%s", gettext(
", HaL R1 Extensions Required"));
}
break;
case EM_SPARC32PLUS:
if (flags & EF_SPARC_32PLUS)
(void) printf("%s", gettext(", V8+ Required"));
if (flags & EF_SPARC_SUN_US3) {
(void) printf("%s",
gettext(", UltraSPARC3 Extensions Required"));
} else if (flags & EF_SPARC_SUN_US1) {
(void) printf("%s",
gettext(", UltraSPARC1 Extensions Required"));
}
if (flags & EF_SPARC_HAL_R1)
(void) printf("%s",
gettext(", HaL R1 Extensions Required"));
break;
default:
break;
}
}
static int
print_cap(Elf *elf, GElf_Ehdr *ehdr)
{
Elf_Scn *scn = 0;
/*
* Traverse the files sections to see if any software/hardware
* capabilities are available.
*/
while ((scn = elf_nextscn(elf, scn)) != 0) {
GElf_Word ndx, capn;
GElf_Shdr shdr;
Elf_Data *data;
if (gelf_getshdr(scn, &shdr) == 0) {
(void) fprintf(stderr,
gettext("can't read ELF section header\n"));
return (1);
}
if (shdr.sh_type != SHT_SUNW_cap)
continue;
/*
* Get the data associated with the .cap section.
*/
if ((data = elf_getdata(scn, 0)) == 0) {
(void) fprintf(stderr,
gettext("can't read ELF section data\n"));
return (1);
}
capn = (GElf_Word)(shdr.sh_size / shdr.sh_entsize);
for (ndx = 0; ndx < capn; ndx++) {
char str[100];
GElf_Cap cap;
if (gelf_getcap(data, ndx, &cap) == NULL) {
(void) fprintf(stderr,
gettext("can't read capabilities data\n"));
return (1);
}
if (cap.c_tag != CA_SUNW_NULL) {
(void) cap_val2str(cap.c_tag, cap.c_un.c_val,
str, sizeof (str), 0, ehdr->e_machine);
(void) printf(" [%s]", str);
}
}
}
return (0);
}
static int
elf_check(Elf *elf)
{
GElf_Ehdr ehdr;
GElf_Phdr phdr;
int dynamic, cnt;
char *ident;
size_t size;
/*
* verify information in file header
*/
if (gelf_getehdr(elf, &ehdr) == (GElf_Ehdr *)0) {
(void) fprintf(stderr, gettext("can't read ELF header\n"));
return (1);
}
ident = elf_getident(elf, &size);
(void) printf("%s", gettext("ELF"));
print_elf_class(ident[EI_CLASS]);
print_elf_datatype(ident[EI_DATA]);
print_elf_type(elf, &ehdr, ident[EI_DATA]);
print_elf_machine(ehdr.e_machine);
if (ehdr.e_version == 1)
(void) printf(" %s %d",
gettext("Version"), (int)ehdr.e_version);
print_elf_flags(ehdr.e_machine, ehdr.e_flags);
if (core(elf, &ehdr, ident[EI_DATA])) /* check for core file */
return (0);
if (print_cap(elf, &ehdr))
return (1);
/*
* check type
*/
if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN))
return (1);
/*
* read program header and check for dynamic section
*/
if (ehdr.e_phnum == 0) {
(void) fprintf(stderr, gettext("can't read program header\n"));
return (1);
}
for (dynamic = 0, cnt = 0; cnt < (int)ehdr.e_phnum; cnt++) {
if (gelf_getphdr(elf, cnt, &phdr) == NULL) {
(void) fprintf(stderr,
gettext("can't read program header\n"));
return (1);
}
if (phdr.p_type == PT_DYNAMIC) {
dynamic = 1;
break;
}
}
if (dynamic)
(void) printf(gettext(", dynamically linked"));
else
(void) printf(gettext(", statically linked"));
is_stripped(elf);
return (0);
}
/*
* is_stripped prints information on whether the executable has
* been stripped.
*/
static void
is_stripped(Elf *elf)
{
GElf_Shdr shdr;
GElf_Ehdr ehdr;
Elf_Scn *scn, *nextscn;
char *section_name;
int symtab = 0;
int debuginfo = 0;
if (gelf_getehdr(elf, &ehdr) == NULL) {
return;
}
/*
* Definition time:
* - "not stripped" means that an executable file
* contains a Symbol Table (.symtab)
* - "stripped" means that an executable file
* does not contain a Symbol Table.
* When strip -l or strip -x is run, it strips the
* debugging information (.line section name (strip -l),
* .line, .debug*, .stabs*, .dwarf* section names
* and SHT_SUNW_DEBUGSTR and SHT_SUNW_DEBUG
* section types (strip -x), however the Symbol
* Table will still be present.
* Therefore, if
* - No Symbol Table present, then report
* "stripped"
* - Symbol Table present with debugging
* information (line number or debug section names,
* or SHT_SUNW_DEBUGSTR or SHT_SUNW_DEBUG section
* types) then report:
* "not stripped"
* - Symbol Table present with no debugging
* information (line number or debug section names,
* or SHT_SUNW_DEBUGSTR or SHT_SUNW_DEBUG section
* types) then report:
* "not stripped, no debugging information
* available"
*/
scn = NULL;
while ((nextscn = elf_nextscn(elf, scn)) != NULL) {
if (symtab && debuginfo) {
break;
}
scn = nextscn;
if (gelf_getshdr(scn, &shdr) == NULL) {
continue;
}
if (!symtab && (shdr.sh_type == SHT_SYMTAB)) {
symtab++;
continue;
}
if (!debuginfo &&
((shdr.sh_type == SHT_SUNW_DEBUG) ||
(shdr.sh_type == SHT_SUNW_DEBUGSTR) ||
(((section_name = elf_strptr(elf, ehdr.e_shstrndx,
(size_t)shdr.sh_name)) != NULL) &&
(is_in_list(debug_sections, section_name))))) {
debuginfo++;
}
}
/*
* Now that we've scanned all sections, print out the appropriate
* diagnostic.
*/
if (symtab) {
(void) printf(gettext(", not stripped"));
if (!debuginfo) {
(void) printf(gettext(
", no debugging information available"));
}
} else {
(void) printf(gettext(", stripped"));
}
}
/*
* lookup -
* Attempts to match one of the strings from a list, 'tab',
* with what is in the file, starting at the current index position 'i'.
* Looks past any initial whitespace and expects whitespace or other
* delimiting characters to follow the matched string.
* A match identifies the file as being 'assembler', 'fortran', 'c', etc.
* Returns 1 for a successful match, 0 otherwise.
*/
static int
lookup(char **tab)
{
register char r;
register int k, j, l;
while (fbuf[i] == ' ' || fbuf[i] == '\t' || fbuf[i] == '\n')
i++;
for (j = 0; tab[j] != 0; j++) {
l = 0;
for (k = i; ((r = tab[j][l++]) == fbuf[k] && r != '\0'); k++);
if (r == '\0')
if (fbuf[k] == ' ' || fbuf[k] == '\n' ||
fbuf[k] == '\t' || fbuf[k] == '{' ||
fbuf[k] == '/') {
i = k;
return (1);
}
}
return (0);
}
/*
* ccom -
* Increments the current index 'i' into the file buffer 'fbuf' past any
* whitespace lines and C-style comments found, starting at the current
* position of 'i'. Returns 1 as long as we don't increment i past the
* size of fbuf (fbsz). Otherwise, returns 0.
*/
static int
ccom(void)
{
register char cc;
int len;
while ((cc = fbuf[i]) == ' ' || cc == '\t' || cc == '\n')
if (i++ >= fbsz)
return (0);
if (fbuf[i] == '/' && fbuf[i+1] == '*') {
i += 2;
while (fbuf[i] != '*' || fbuf[i+1] != '/') {
if (fbuf[i] == '\\')
i++;
if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0)
len = 1;
i += len;
if (i >= fbsz)
return (0);
}
if ((i += 2) >= fbsz)
return (0);
}
if (fbuf[i] == '\n')
if (ccom() == 0)
return (0);
return (1);
}
/*
* ascom -
* Increments the current index 'i' into the file buffer 'fbuf' past
* consecutive assembler program comment lines starting with ASCOMCHAR,
* starting at the current position of 'i'.
* Returns 1 as long as we don't increment i past the
* size of fbuf (fbsz). Otherwise returns 0.
*/
static int
ascom(void)
{
while (fbuf[i] == ASCOMCHAR) {
i++;
while (fbuf[i++] != '\n')
if (i >= fbsz)
return (0);
while (fbuf[i] == '\n')
if (i++ >= fbsz)
return (0);
}
return (1);
}
static int
sccs(void)
{ /* look for "1hddddd" where d is a digit */
register int j;
if (fbuf[0] == 1 && fbuf[1] == 'h') {
for (j = 2; j <= 6; j++) {
if (isdigit(fbuf[j]))
continue;
else
return (0);
}
} else {
return (0);
}
return (1);
}
static int
english(char *bp, int n)
{
#define NASC 128 /* number of ascii char ?? */
register int j, vow, freq, rare, len;
register int badpun = 0, punct = 0;
int ct[NASC];
if (n < 50)
return (0); /* no point in statistics on squibs */
for (j = 0; j < NASC; j++)
ct[j] = 0;
for (j = 0; j < n; j += len) {
if ((unsigned char)bp[j] < NASC)
ct[bp[j]|040]++;
switch (bp[j]) {
case '.':
case ',':
case ')':
case '%':
case ';':
case ':':
case '?':
punct++;
if (j < n-1 && bp[j+1] != ' ' && bp[j+1] != '\n')
badpun++;
}
if ((len = mblen(&bp[j], MB_CUR_MAX)) <= 0)
len = 1;
}
if (badpun*5 > punct)
return (0);
vow = ct['a'] + ct['e'] + ct['i'] + ct['o'] + ct['u'];
freq = ct['e'] + ct['t'] + ct['a'] + ct['i'] + ct['o'] + ct['n'];
rare = ct['v'] + ct['j'] + ct['k'] + ct['q'] + ct['x'] + ct['z'];
if (2*ct[';'] > ct['e'])
return (0);
if ((ct['>'] + ct['<'] + ct['/']) > ct['e'])
return (0); /* shell file test */
return (vow * 5 >= n - ct[' '] && freq >= 10 * rare);
}
/*
* Convert a word from an elf file to native format.
* This is needed because there's no elf routine to
* get and decode a Note section header.
*/
static void
convert_gelf_word(Elf *elf, GElf_Word *data, int version, int format)
{
Elf_Data src, dst;
dst.d_buf = data;
dst.d_version = version;
dst.d_size = sizeof (GElf_Word);
dst.d_type = ELF_T_WORD;
src.d_buf = data;
src.d_version = version;
src.d_size = sizeof (GElf_Word);
src.d_type = ELF_T_WORD;
(void) gelf_xlatetom(elf, &dst, &src, format);
}
static void
convert_gelf_nhdr(Elf *elf, GElf_Nhdr *nhdr, GElf_Word version, int format)
{
convert_gelf_word(elf, &nhdr->n_namesz, version, format);
convert_gelf_word(elf, &nhdr->n_descsz, version, format);
convert_gelf_word(elf, &nhdr->n_type, version, format);
}
/*
* Return true if it is an old (pre-restructured /proc) core file.
*/
static int
old_core(Elf *elf, GElf_Ehdr *ehdr, int format)
{
register int inx;
GElf_Phdr phdr;
GElf_Phdr nphdr;
GElf_Nhdr nhdr;
off_t offset;
if (ehdr->e_type != ET_CORE)
return (0);
for (inx = 0; inx < (int)ehdr->e_phnum; inx++) {
if (gelf_getphdr(elf, inx, &phdr) == NULL) {
return (0);
}
if (phdr.p_type == PT_NOTE) {
/*
* If the next segment is also a note, use it instead.
*/
if (gelf_getphdr(elf, inx+1, &nphdr) == NULL) {
return (0);
}
if (nphdr.p_type == PT_NOTE)
phdr = nphdr;
offset = (off_t)phdr.p_offset;
(void) pread(ifd, &nhdr, sizeof (GElf_Nhdr), offset);
convert_gelf_nhdr(elf, &nhdr, ehdr->e_version, format);
/*
* Old core files have type NT_PRPSINFO.
*/
if (nhdr.n_type == NT_PRPSINFO)
return (1);
return (0);
}
}
return (0);
}
/*
* If it's a core file, print out the name of the file that dumped core.
*/
static int
core(Elf *elf, GElf_Ehdr *ehdr, int format)
{
register int inx;
char *psinfo;
GElf_Phdr phdr;
GElf_Phdr nphdr;
GElf_Nhdr nhdr;
off_t offset;
if (ehdr->e_type != ET_CORE)
return (0);
for (inx = 0; inx < (int)ehdr->e_phnum; inx++) {
if (gelf_getphdr(elf, inx, &phdr) == NULL) {
(void) fprintf(stderr,
gettext("can't read program header\n"));
return (0);
}
if (phdr.p_type == PT_NOTE) {
char *fname;
size_t size;
/*
* If the next segment is also a note, use it instead.
*/
if (gelf_getphdr(elf, inx+1, &nphdr) == NULL) {
(void) fprintf(stderr,
gettext("can't read program header\n"));
return (0);
}
if (nphdr.p_type == PT_NOTE)
phdr = nphdr;
offset = (off_t)phdr.p_offset;
(void) pread(ifd, &nhdr, sizeof (GElf_Nhdr), offset);
convert_gelf_nhdr(elf, &nhdr, ehdr->e_version, format);
/*
* Note: the ABI states that n_namesz must
* be rounded up to a 4 byte boundary.
*/
offset += sizeof (GElf_Nhdr) +
((nhdr.n_namesz + 0x03) & ~0x3);
size = nhdr.n_descsz;
psinfo = malloc(size);
(void) pread(ifd, psinfo, size, offset);
/*
* We want to print the string contained
* in psinfo->pr_fname[], where 'psinfo'
* is either an old NT_PRPSINFO structure
* or a new NT_PSINFO structure.
*
* Old core files have only type NT_PRPSINFO.
* New core files have type NT_PSINFO.
*
* These structures are also different by
* virtue of being contained in a core file
* of either 32-bit or 64-bit type.
*
* To further complicate matters, we ourself
* might be compiled either 32-bit or 64-bit.
*
* For these reason, we just *know* the offsets of
* pr_fname[] into the four different structures
* here, regardless of how we are compiled.
*/
if (gelf_getclass(elf) == ELFCLASS32) {
/* 32-bit core file, 32-bit structures */
if (nhdr.n_type == NT_PSINFO)
fname = psinfo + 88;
else /* old: NT_PRPSINFO */
fname = psinfo + 84;
} else if (gelf_getclass(elf) == ELFCLASS64) {
/* 64-bit core file, 64-bit structures */
if (nhdr.n_type == NT_PSINFO)
fname = psinfo + 136;
else /* old: NT_PRPSINFO */
fname = psinfo + 120;
} else {
free(psinfo);
break;
}
(void) printf(gettext(", from '%s'"), fname);
free(psinfo);
break;
}
}
return (1);
}
static int
shellscript(char buf[], struct stat64 *sb)
{
char *tp, *cp, *xp, *up, *gp;
cp = strchr(buf, '\n');
if (cp == NULL || cp - fbuf > fbsz)
return (0);
for (tp = buf; tp != cp && isspace((unsigned char)*tp); tp++)
if (!isascii(*tp))
return (0);
for (xp = tp; tp != cp && !isspace((unsigned char)*tp); tp++)
if (!isascii(*tp))
return (0);
if (tp == xp)
return (0);
if (sb->st_mode & S_ISUID)
up = gettext("set-uid ");
else
up = "";
if (sb->st_mode & S_ISGID)
gp = gettext("set-gid ");
else
gp = "";
if (strncmp(xp, "/bin/sh", tp - xp) == 0)
xp = gettext("shell");
else if (strncmp(xp, "/bin/csh", tp - xp) == 0)
xp = gettext("c-shell");
else if (strncmp(xp, "/usr/sbin/dtrace", tp - xp) == 0)
xp = gettext("DTrace");
else
*tp = '\0';
/*
* TRANSLATION_NOTE
* This message is printed by file command for shell scripts.
* The first %s is for the translation for "set-uid " (if the script
* has the set-uid bit set), or is for an empty string (if the
* script does not have the set-uid bit set).
* Similarly, the second %s is for the translation for "set-gid ",
* or is for an empty string.
* The third %s is for the translation for either: "shell", "c-shell",
* or "DTrace", or is for the pathname of the program the script
* executes.
*/
(void) printf(gettext("%s%sexecutable %s script\n"), up, gp, xp);
return (1);
}
static int
get_door_target(char *file, char *buf, size_t bufsize)
{
int fd;
door_info_t di;
psinfo_t psinfo;
if ((fd = open64(file, O_RDONLY)) < 0 ||
door_info(fd, &di) != 0) {
if (fd >= 0)
(void) close(fd);
return (-1);
}
(void) close(fd);
(void) sprintf(buf, "/proc/%ld/psinfo", di.di_target);
if ((fd = open64(buf, O_RDONLY)) < 0 ||
read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) {
if (fd >= 0)
(void) close(fd);
return (-1);
}
(void) close(fd);
(void) snprintf(buf, bufsize, "%s[%ld]", psinfo.pr_fname, di.di_target);
return (0);
}
/*
* ZIP file header information
*/
#define SIGSIZ 4
#define LOCSIG "PK\003\004"
#define LOCHDRSIZ 30
#define CH(b, n) (((unsigned char *)(b))[n])
#define SH(b, n) (CH(b, n) | (CH(b, n+1) << 8))
#define LG(b, n) (SH(b, n) | (SH(b, n+2) << 16))
#define LOCNAM(b) (SH(b, 26)) /* filename size */
#define LOCEXT(b) (SH(b, 28)) /* extra field size */
#define XFHSIZ 4 /* header id, data size */
#define XFHID(b) (SH(b, 0)) /* extract field header id */
#define XFDATASIZ(b) (SH(b, 2)) /* extract field data size */
#define XFJAVASIG 0xcafe /* java executables */
static int
zipfile(char *fbuf, int fd)
{
off_t xoff, xoff_end;
if (strncmp(fbuf, LOCSIG, SIGSIZ) != 0)
return (0);
xoff = LOCHDRSIZ + LOCNAM(fbuf);
xoff_end = xoff + LOCEXT(fbuf);
while (xoff < xoff_end) {
char xfhdr[XFHSIZ];
if (pread(fd, xfhdr, XFHSIZ, xoff) != XFHSIZ)
break;
if (XFHID(xfhdr) == XFJAVASIG) {
(void) printf("%s\n", gettext("java program"));
return (1);
}
xoff += sizeof (xfhdr) + XFDATASIZ(xfhdr);
}
/*
* We could just print "ZIP archive" here.
*
* However, customers may be using their own entries in
* /etc/magic to distinguish one kind of ZIP file from another, so
* let's defer the printing of "ZIP archive" to there.
*/
return (0);
}
static int
is_crash_dump(const char *buf, int fd)
{
/* LINTED: pointer cast may result in improper alignment */
const dumphdr_t *dhp = (const dumphdr_t *)buf;
/*
* The current DUMP_MAGIC string covers Solaris 7 and later releases.
* The utsname struct is only present in dumphdr_t's with dump_version
* greater than or equal to 9.
*/
if (dhp->dump_magic == DUMP_MAGIC) {
print_dumphdr(fd, dhp, return_uint32, NATIVE_ISA);
} else if (dhp->dump_magic == swap_uint32(DUMP_MAGIC)) {
print_dumphdr(fd, dhp, swap_uint32, OTHER_ISA);
} else if (dhp->dump_magic == OLD_DUMP_MAGIC ||
dhp->dump_magic == swap_uint32(OLD_DUMP_MAGIC)) {
char *isa = (dhp->dump_magic == OLD_DUMP_MAGIC ?
NATIVE_ISA : OTHER_ISA);
(void) printf(gettext("SunOS 32-bit %s crash dump\n"), isa);
} else {
return (0);
}
return (1);
}
static void
print_dumphdr(const int fd, const dumphdr_t *dhp, uint32_t (*swap)(uint32_t),
const char *isa)
{
dumphdr_t dh;
/*
* A dumphdr_t is bigger than FBSZ, so we have to manually read the
* rest of it.
*/
if (swap(dhp->dump_version) > 8 && pread(fd, &dh, sizeof (dumphdr_t),
(off_t)0) == sizeof (dumphdr_t)) {
(void) printf(gettext(
"%s %s %s %u-bit %s crash dump from '%s'\n"),
dh.dump_utsname.sysname, dh.dump_utsname.release,
dh.dump_utsname.version, swap(dh.dump_wordsize), isa,
dh.dump_utsname.nodename);
} else {
(void) printf(gettext("SunOS %u-bit %s crash dump\n"),
swap(dhp->dump_wordsize), isa);
}
}
static void
usage(void)
{
(void) fprintf(stderr, gettext(
"usage: file [-dh] [-M mfile] [-m mfile] [-f ffile] file ...\n"
" file [-dh] [-M mfile] [-m mfile] -f ffile\n"
" file -i [-h] [-f ffile] file ...\n"
" file -i [-h] -f ffile\n"
" file -c [-d] [-M mfile] [-m mfile]\n"));
exit(2);
}
static uint32_t
swap_uint32(uint32_t in)
{
uint32_t out;
out = (in & 0x000000ff) << 24;
out |= (in & 0x0000ff00) << 8; /* >> 8 << 16 */
out |= (in & 0x00ff0000) >> 8; /* >> 16 << 8 */
out |= (in & 0xff000000) >> 24;
return (out);
}
static uint32_t
return_uint32(uint32_t in)
{
return (in);
}
/*
* Check if str is in the string list str_list.
*/
static int
is_in_list(char *str_list[], char *str)
{
int i;
/*
* Only need to compare the strlen(str_list[i]) bytes.
* That way .stab will match on .stab* sections, and
* .debug will match on .debug* sections.
*/
for (i = 0; str_list[i] != NULL; i++) {
if (strncmp(str_list[i], str, strlen(str_list[i])) == 0) {
return (1);
}
}
return (0);
}
/*
* default_magic -
* allocate space for and create the default magic file
* name string.
*/
static void
default_magic(void)
{
const char *msg_locale = setlocale(LC_MESSAGES, NULL);
struct stat statbuf;
if ((dfile = (char *)malloc(strlen(msg_locale) + 35)) == NULL) {
perror("file");
exit(2);
}
(void) snprintf(dfile, strlen(msg_locale) + 35,
"/usr/lib/locale/%s/LC_MESSAGES/magic", msg_locale);
if (stat(dfile, &statbuf) != 0) {
(void) strcpy(dfile, "/etc/magic");
}
}
/*
* add_to_mlist -
* Add the given magic_file filename string to the list of magic
* files (mlist). This list of files will later be examined, and
* each magic file's entries will be added in order to
* the mtab table.
*
* The first flag is set to 1 to add to the first list, mlist1.
* The first flag is set to 0 to add to the second list, mlist2.
*/
static void
add_to_mlist(char *magic_file, int first)
{
char **mlist; /* ordered list of magic files */
size_t mlist_sz; /* number of pointers allocated for mlist */
char **mlistp; /* next entry in mlist */
size_t mlistp_off;
if (first) {
mlist = mlist1;
mlist_sz = mlist1_sz;
mlistp = mlist1p;
} else {
mlist = mlist2;
mlist_sz = mlist2_sz;
mlistp = mlist2p;
}
if (mlist == NULL) { /* initial mlist allocation */
if ((mlist = (char **)calloc(MLIST_SZ, sizeof (char *)))
== NULL) {
perror("file");
exit(2);
}
mlist_sz = MLIST_SZ;
mlistp = mlist;
}
if ((mlistp - mlist) >= mlist_sz) {
mlistp_off = mlistp - mlist;
mlist_sz *= 2;
if ((mlist = (char **)realloc(mlist,
mlist_sz * sizeof (char *))) == NULL) {
perror("file");
exit(2);
}
mlistp = mlist + mlistp_off;
}
/*
* now allocate memory for and copy the
* magic file name string
*/
if ((*mlistp = malloc(strlen(magic_file) + 1)) == NULL) {
perror("file");
exit(2);
}
(void) strlcpy(*mlistp, magic_file, strlen(magic_file) + 1);
mlistp++;
if (first) {
mlist1 = mlist;
mlist1_sz = mlist_sz;
mlist1p = mlistp;
} else {
mlist2 = mlist;
mlist2_sz = mlist_sz;
mlist2p = mlistp;
}
}
static void
fd_cleanup(void)
{
if (ifd != -1) {
(void) close(ifd);
ifd = -1;
}
if (elffd != -1) {
(void) close(elffd);
elffd = -1;
}
}