magicutils.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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* Copyright (c) 1987, 1988 Microsoft Corporation */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <inttypes.h>
#include <sys/types.h>
#include <libintl.h>
/*
* Types
*/
#define BYTE 1
#define SHORT 2
#define LONG 4
#define LLONG 8
#define UBYTE 16
#define USHORT 32
#define ULONG 64
#define ULLONG 128
#define STR 256
/*
* Opcodes
*/
#define EQ 0
#define GT 1
#define LT 2
#define STRC 3 /* string compare */
#define ANY 4
#define AND 5
#define NSET 6 /* True if bit is not set */
#define SUB 64 /* or'ed in, SUBstitution string, for example */
/* %ld, %s, %lo mask: with bit 6 on, used to locate */
/* print formats */
/*
* Misc
*/
#define BSZ 128
#define NENT 200
/*
* Structure of magic file entry
*/
struct entry {
char e_level; /* 0 or 1 */
off_t e_off; /* in bytes */
uint32_t e_type; /* BYTE, SHORT, STR, et al */
char e_opcode; /* EQ, GT, LT, ANY, AND, NSET */
uint64_t e_mask; /* if non-zero, mask value with this */
union {
uint64_t num;
char *str;
} e_value;
const char *e_str;
};
typedef struct entry Entry;
static Entry *mtab1; /* 1st magic table, applied before default tests */
/*
* 2nd magic table, includes default tests and magic entries
* to be applied after default position-sensitive tests
*/
static Entry *mtab2;
static Entry *mend1; /* one past last-allocated entry in mtab1 */
static Entry *mend2; /* one past last-allocated entry in mtab2 */
static Entry *ep1; /* current entry in mtab1 */
static Entry *ep2; /* current entry in mtab2 */
static char *
getstr(char *p)
{
char *newstr;
char *s;
long val;
int base;
newstr = (char *)malloc((strlen(p) + 1) * sizeof (char));
if (newstr == NULL) {
perror(gettext("magic table string allocation"));
return (NULL);
}
s = newstr;
while (*p != '\0') {
if (*p != '\\') {
*s++ = *p++;
continue;
}
p++;
if (*p == '\0')
break;
if (isdigit(*p)) {
if (*p == '0' && (*(p+1) == 'x' || *(p+1) == 'X')) {
/* hex */
base = 16;
} else {
base = 8;
}
errno = 0;
val = strtol(p, &p, base);
if (val > UCHAR_MAX || val < 0 || errno != 0) {
perror(gettext("magic table invalid "
"string value"));
return (NULL);
}
*s++ = (char)val;
} else {
/* escape the character */
switch (*p) {
case 'n':
*s = '\n';
break;
case 'r':
*s = '\r';
break;
case 'a':
*s = '\a';
break;
case 'b':
*s = '\b';
break;
case 'f':
*s = '\f';
break;
case 't':
*s = '\t';
break;
case 'v':
*s = '\v';
break;
default:
*s = *p;
break;
}
p++;
s++;
}
}
*s = '\0';
return (newstr);
}
/*
* f_mkmtab - fills mtab array of magic table entries with
* values from the file magfile.
* May be called more than once if multiple magic
* files were specified.
* Stores entries sequentially in one of two magic
* tables: mtab1, if first = 1; mtab2 otherwise.
*
* If -c option is specified, cflg is non-zero, and
* f_mkmtab() reports on errors in the magic file.
*
* Two magic tables may need to be created. The first
* one (mtab1) contains magic entries to be checked before
* the programmatic default position-sensitive tests in
* def_position_tests().
* The second one (mtab2) should start with the default
* /etc/magic file entries and is to be checked after
* the programmatic default position-sensitive tests in
* def_position_tests(). The parameter "first" would
* be 1 for the former set of tables, 0 for the latter
* set of magic tables.
* No mtab2 should be created if file will not be
* applying default tests; in that case, all magic table
* entries should be in mtab1.
*
* f_mkmtab returns 0 on success, -1 on error. The calling
* program is not expected to proceed after f_mkmtab()
* returns an error.
*/
int
f_mkmtab(char *magfile, int cflg, int first)
{
Entry *mtab; /* generic magic table pointer */
Entry *ep; /* current magic table entry */
Entry *mend; /* one past last-allocated entry of mtab */
FILE *fp;
int lcnt = 0;
char buf[BSZ];
size_t tbsize;
size_t oldsize;
if (first) {
mtab = mtab1;
mend = mend1;
ep = ep1;
} else {
mtab = mtab2;
mend = mend2;
ep = ep2;
}
/* mtab may have been allocated on a previous f_mkmtab call */
if (mtab == (Entry *)NULL) {
mtab = (Entry *) calloc(sizeof (Entry), NENT);
if (mtab == (Entry *)NULL) {
(void) fprintf(stderr,
gettext("no memory for magic table\n"));
return (-1);
}
ep = mtab;
mend = &mtab[NENT];
}
fp = fopen(magfile, "r");
if (fp == NULL) {
(void) fprintf(stderr,
gettext("cannot open magic file <%s>.\n"),
magfile);
return (-1);
}
while (fgets(buf, BSZ, fp) != NULL) {
char *p = buf;
char *p2;
char *p3;
char opc;
/*
* ensure we have one extra entry allocated
* to mark end of the table, after the while loop
*/
if (ep >= (mend - 1)) {
oldsize = mend - mtab;
tbsize = (NENT + oldsize) * sizeof (Entry);
if ((mtab = realloc(mtab, tbsize)) == NULL) {
perror(gettext("magic table overflow"));
return (-1);
} else {
(void) memset(mtab + oldsize, 0,
sizeof (Entry) * NENT);
mend = &mtab[tbsize / sizeof (Entry)];
ep = &mtab[oldsize-1];
}
}
lcnt++;
if (*p == '\n' || *p == '#')
continue;
/* LEVEL */
if (*p == '>') {
ep->e_level = 1;
p++;
}
/* OFFSET */
p2 = strchr(p, '\t');
if (p2 == NULL) {
if (cflg)
(void) fprintf(stderr,
gettext("fmt error, no tab after %s on "
"line %d of %s\n"), p, lcnt, magfile);
continue;
}
*p2++ = NULL;
ep->e_off = strtol((const char *)p, (char **)NULL, 0);
while (*p2 == '\t')
p2++;
/* TYPE */
p = p2;
p2 = strchr(p, '\t');
if (p2 == NULL) {
if (cflg)
(void) fprintf(stderr,
gettext("fmt error, no tab after %s on "
"line %d of %s\n"), p, lcnt, magfile);
continue;
}
*p2++ = NULL;
p3 = strchr(p, '&');
if (p3 != NULL) {
*p3++ = '\0';
ep->e_mask = strtoull((const char *)p3, (char **)NULL,
0); /* returns 0 or ULLONG_MAX on error */
} else {
ep->e_mask = 0ULL;
}
switch (*p) {
case 'd':
if (*(p+1) == NULL) {
/* d */
ep->e_type = LONG;
} else if (*(p+2) == NULL) { /* d? */
switch (*(p+1)) {
case 'C':
case '1':
/* dC, d1 */
ep->e_type = BYTE;
break;
case 'S':
case '2':
/* dS, d2 */
ep->e_type = SHORT;
break;
case 'I':
case 'L':
case '4':
/* dI, dL, d4 */
ep->e_type = LONG;
break;
case '8':
/* d8 */
ep->e_type = LLONG;
break;
default:
ep->e_type = LONG;
break;
}
}
break;
case 'l':
if (*(p+1) == 'l') { /* llong */
ep->e_type = LLONG;
} else { /* long */
ep->e_type = LONG;
}
break;
case 's':
if (*(p+1) == 'h') {
/* short */
ep->e_type = SHORT;
} else {
/* s or string */
ep->e_type = STR;
}
break;
case 'u':
if (*(p+1) == NULL) {
/* u */
ep->e_type = ULONG;
} else if (*(p+2) == NULL) { /* u? */
switch (*(p+1)) {
case 'C':
case '1':
/* uC, u1 */
ep->e_type = UBYTE;
break;
case 'S':
case '2':
/* uS, u2 */
ep->e_type = USHORT;
break;
case 'I':
case 'L':
case '4':
/* uI, uL, u4 */
ep->e_type = ULONG;
break;
case '8':
/* u8 */
ep->e_type = ULLONG;
break;
default:
ep->e_type = ULONG;
break;
}
} else { /* u?* */
switch (*(p+1)) {
case 'b': /* ubyte */
ep->e_type = UBYTE;
break;
case 's': /* ushort */
ep->e_type = USHORT;
break;
case 'l':
if (*(p+2) == 'l') {
/* ullong */
ep->e_type = ULLONG;
} else {
/* ulong */
ep->e_type = ULONG;
}
break;
default:
/* default, same as "u" */
ep->e_type = ULONG;
break;
}
}
break;
default:
/* retain (undocumented) default type */
ep->e_type = BYTE;
break;
}
if (ep->e_type == 0) {
ep->e_type = BYTE; /* default */
}
while (*p2 == '\t')
p2++;
/* OP-VALUE */
p = p2;
p2 = strchr(p, '\t');
if (p2 == NULL) {
if (cflg)
(void) fprintf(stderr,
gettext("fmt error, no tab after %s on "
"line %d of %s\n"), p, lcnt, magfile);
continue;
}
*p2++ = NULL;
if (ep->e_type != STR) {
opc = *p++;
switch (opc) {
case '=':
ep->e_opcode = EQ;
break;
case '>':
ep->e_opcode = GT;
break;
case '<':
ep->e_opcode = LT;
break;
case 'x':
ep->e_opcode = ANY;
break;
case '&':
ep->e_opcode = AND;
break;
case '^':
ep->e_opcode = NSET;
break;
default: /* EQ (i.e. 0) is default */
p--; /* since global ep->e_opcode=0 */
}
}
if (ep->e_opcode != ANY) {
if (ep->e_type != STR) {
ep->e_value.num = strtoull((const char *)p,
(char **)NULL, 0);
} else if ((ep->e_value.str = getstr(p)) == NULL) {
return (-1);
}
}
p2 += strspn(p2, "\t");
/* STRING */
if ((ep->e_str = strdup(p2)) == NULL) {
perror(gettext("magic table message allocation"));
return (-1);
} else {
if ((p = strchr(ep->e_str, '\n')) != NULL)
*p = '\0';
if (strchr(ep->e_str, '%') != NULL)
ep->e_opcode |= SUB;
}
ep++;
} /* end while (fgets) */
ep->e_off = -1L; /* mark end of table */
if (first) {
mtab1 = mtab;
mend1 = mend;
ep1 = ep;
} else {
mtab2 = mtab;
mend2 = mend;
ep2 = ep;
}
if (fclose(fp) == EOF) {
perror(magfile);
return (-1);
}
return (0);
}
/*
* Check for Magic Table entries in the file.
*
* Since there may be two sets of magic tables, first = 1
* for the first magic table (mtab1) and 0 for the second magic
* table (mtab2).
*/
int
f_ckmtab(char *buf, int bufsize, int first)
{
int result;
Entry *mtab;
Entry *ep;
char *p;
int lev1 = 0;
uint16_t u16_val;
uint32_t u32_val;
uint64_t u64_val;
if (first) {
mtab = mtab1;
} else {
mtab = mtab2;
}
if (mtab == (Entry *)NULL) {
return (0); /* no magic file tests in this table */
}
for (ep = mtab; ep->e_off != -1L; ep++) { /* -1 offset marks end of */
if (lev1) { /* valid magic file entries */
if (ep->e_level != 1)
break;
} else if (ep->e_level == 1) {
continue;
}
if (ep->e_off > (off_t)bufsize)
continue;
p = &buf[ep->e_off];
switch (ep->e_type) {
case STR:
{
if (strncmp(p, ep->e_value.str,
strlen(ep->e_value.str)))
continue;
if (lev1) {
(void) putchar(' ');
}
if (ep->e_opcode & SUB)
(void) printf(ep->e_str,
ep->e_value.str);
else
(void) printf(ep->e_str);
lev1 = 1;
continue;
/*
* We've matched the string and printed the message;
* no STR processing occurs beyond this point.
*/
}
case BYTE:
case UBYTE:
u64_val = (uint64_t)(uint8_t)(*p);
break;
case SHORT:
case USHORT:
(void) memcpy(&u16_val, p, sizeof (uint16_t));
u64_val = (uint64_t)u16_val;
break;
case LONG:
case ULONG:
(void) memcpy(&u32_val, p, sizeof (uint32_t));
u64_val = (uint64_t)u32_val;
break;
case LLONG:
case ULLONG:
(void) memcpy(&(u64_val), p, sizeof (uint64_t));
break;
}
if (ep->e_mask) {
u64_val &= ep->e_mask;
}
/*
* Compare the values according to the size and sign
* of the type. For =, &, and ^ operators, the sign
* does not have any effect, so these are always compared
* unsigned. Only for < and > operators is the
* sign significant.
* If the file value was masked, the compare should
* be unsigned.
*/
switch (ep->e_opcode & ~SUB) {
case EQ:
switch (ep->e_type) {
case BYTE:
case UBYTE:
if ((uint8_t)u64_val !=
(uint8_t)(ep->e_value.num))
continue;
break;
case SHORT:
case USHORT:
if ((uint16_t)u64_val !=
(uint16_t)(ep->e_value.num))
continue;
break;
case LONG:
case ULONG:
if ((uint32_t)u64_val !=
(uint32_t)(ep->e_value.num))
continue;
break;
case LLONG:
case ULLONG:
if (u64_val != ep->e_value.num)
continue;
break;
default:
continue;
}
break;
case GT:
switch (ep->e_type) {
case BYTE:
if (ep->e_mask == 0) {
if ((int8_t)u64_val <=
(int8_t)(ep->e_value.num))
continue;
break;
}
/*FALLTHROUGH*/
case UBYTE:
if ((uint8_t)u64_val <=
(uint8_t)(ep->e_value.num))
continue;
break;
case SHORT:
if (ep->e_mask == 0) {
if ((int16_t)u64_val <=
(int16_t)(ep->e_value.num))
continue;
break;
}
/*FALLTHROUGH*/
case USHORT:
if ((uint16_t)u64_val <=
(uint16_t)(ep->e_value.num))
continue;
break;
case LONG:
if (ep->e_mask == 0) {
if ((int32_t)u64_val <=
(int32_t)(ep->e_value.num))
continue;
break;
}
/*FALLTHROUGH*/
case ULONG:
if ((uint32_t)u64_val <=
(uint32_t)(ep->e_value.num))
continue;
break;
case LLONG:
if (ep->e_mask == 0) {
if ((int64_t)u64_val <=
(int64_t)(ep->e_value.num))
continue;
break;
}
/*FALLTHROUGH*/
case ULLONG:
if (u64_val <= ep->e_value.num)
continue;
break;
default:
continue;
}
break;
case LT:
switch (ep->e_type) {
case BYTE:
if (ep->e_mask == 0) {
if ((int8_t)u64_val >=
(int8_t)(ep->e_value.num))
continue;
break;
}
/*FALLTHROUGH*/
case UBYTE:
if ((uint8_t)u64_val >=
(uint8_t)(ep->e_value.num))
continue;
break;
case SHORT:
if (ep->e_mask == 0) {
if ((int16_t)u64_val >=
(int16_t)(ep->e_value.num))
continue;
break;
}
/*FALLTHROUGH*/
case USHORT:
if ((uint16_t)u64_val >=
(uint16_t)(ep->e_value.num))
continue;
break;
case LONG:
if (ep->e_mask == 0) {
if ((int32_t)u64_val >=
(int32_t)(ep->e_value.num))
continue;
break;
}
/*FALLTHROUGH*/
case ULONG:
if ((uint32_t)u64_val >=
(uint32_t)(ep->e_value.num))
continue;
break;
case LLONG:
if (ep->e_mask == 0) {
if ((int64_t)u64_val >=
(int64_t)(ep->e_value.num))
continue;
break;
}
/*FALLTHROUGH*/
case ULLONG:
if (u64_val >= ep->e_value.num)
continue;
break;
default:
continue;
}
break;
case AND:
switch (ep->e_type) {
case BYTE:
case UBYTE:
if (((uint8_t)u64_val &
(uint8_t)(ep->e_value.num)) ==
(uint8_t)(ep->e_value.num))
break;
continue;
case SHORT:
case USHORT:
if (((uint16_t)u64_val &
(uint16_t)(ep->e_value.num)) ==
(uint16_t)(ep->e_value.num))
break;
continue;
case LONG:
case ULONG:
if (((uint32_t)u64_val &
(uint32_t)(ep->e_value.num)) ==
(uint32_t)(ep->e_value.num))
break;
continue;
case LLONG:
case ULLONG:
if ((u64_val & ep->e_value.num) ==
ep->e_value.num)
break;
continue;
default:
continue;
}
break;
case NSET:
switch (ep->e_type) {
case BYTE:
case UBYTE:
if (((uint8_t)u64_val &
(uint8_t)(ep->e_value.num)) !=
(uint8_t)(ep->e_value.num))
break;
continue;
case SHORT:
case USHORT:
if (((uint16_t)u64_val &
(uint16_t)(ep->e_value.num)) !=
(uint16_t)(ep->e_value.num))
break;
continue;
case LONG:
case ULONG:
if (((uint32_t)u64_val &
(uint32_t)(ep->e_value.num)) !=
(uint32_t)(ep->e_value.num))
break;
continue;
case LLONG:
case ULLONG:
if ((u64_val & ep->e_value.num) !=
ep->e_value.num)
break;
continue;
default:
continue;
}
break;
case ANY: /* matches anything */
break;
default: /* shouldn't occur; ignore it */
continue;
}
if (lev1)
(void) putchar(' ');
if (ep->e_opcode & SUB) {
switch (ep->e_type) {
case LLONG:
#ifdef XPG4
if (ep->e_mask == 0) {
(void) printf(ep->e_str,
(int64_t)u64_val);
break;
}
#endif /* XPG4 */
/*FALLTHROUGH*/
case ULLONG:
(void) printf(ep->e_str, u64_val);
break;
case LONG:
#ifdef XPG4
if (ep->e_mask == 0) {
(void) printf(ep->e_str,
(int32_t)u64_val);
break;
}
#endif /* XPG4 */
/*FALLTHROUGH*/
case ULONG:
(void) printf(ep->e_str,
(uint32_t)u64_val);
break;
case SHORT:
#ifdef XPG4
if (ep->e_mask == 0) {
(void) printf(ep->e_str,
(int16_t)u64_val);
break;
}
#endif /* XPG4 */
/*FALLTHROUGH*/
case USHORT:
(void) printf(ep->e_str,
(uint16_t)u64_val);
break;
case BYTE:
#ifdef XPG4
if (ep->e_mask == 0) {
(void) printf(ep->e_str,
(int8_t)u64_val);
break;
}
#endif /* XPG4 */
/*FALLTHROUGH*/
case UBYTE:
(void) printf(ep->e_str,
(uint8_t)u64_val);
break;
case STR:
/*
* Note: Currently can't get type
* STR here because we already
* did a 'continue' out of the
* loop earlier for case STR
*/
break;
}
} else
(void) printf(ep->e_str);
lev1 = 1;
}
result = lev1 ? (int)(1 + ep - mtab) : 0;
return (result);
}
static void
showstr(char *s, int width)
{
char c;
while ((c = *s++) != '\0')
if (c >= 040 && c < 0176) {
(void) putchar(c);
width--;
} else {
(void) putchar('\\');
switch (c) {
case '\n':
(void) putchar('n');
width -= 2;
break;
case '\r':
(void) putchar('r');
width -= 2;
break;
case '\a':
(void) putchar('a');
width -= 2;
break;
case '\b':
(void) putchar('b');
width -= 2;
break;
case '\t':
(void) putchar('t');
width -= 2;
break;
case '\f':
(void) putchar('f');
width -= 2;
break;
case '\v':
(void) putchar('v');
width -= 2;
break;
default:
(void) printf("%.3o", c & 0377);
width -= 4;
break;
}
}
while (width >= 0) {
(void) putchar(' ');
width--;
};
}
static char *
type_to_name(Entry *ep)
{
static char buf[20];
char *s;
switch (ep->e_type) {
case BYTE:
s = "byte";
break;
case SHORT:
s = "short";
break;
case LONG:
s = "long";
break;
case LLONG:
s = "llong";
break;
case UBYTE:
s = "ubyte";
break;
case USHORT:
s = "ushort";
break;
case ULONG:
s = "ulong";
break;
case ULLONG:
s = "ullong";
break;
case STR:
return ("string");
default:
/* more of an emergency measure .. */
(void) sprintf(buf, "%d", ep->e_type);
return (buf);
}
if (ep->e_mask) {
(void) snprintf(buf, sizeof (buf), "%s&0x%llx", s, ep->e_mask);
return (buf);
} else
return (s);
}
static char
op_to_name(char op)
{
char c;
switch (op & ~SUB) {
case EQ:
case STRC:
c = '=';
break;
case GT:
c = '>';
break;
case LT:
c = '<';
break;
case ANY:
c = 'x';
break;
case AND:
c = '&';
break;
case NSET:
c = '^';
break;
default:
c = '?';
break;
}
return (c);
}
/*
* f_prtmtab - Prints out a header, then entries from both magic
* tables, mtab1 and mtab2, if any exist.
*/
void
f_prtmtab(void)
{
Entry *mtab;
Entry *ep;
int count;
(void) printf("%-7s %-7s %-10s %-7s %-11s %s\n",
"level", "off", "type", "opcode", "value", "string");
for (mtab = mtab1, count = 1; count <= 2; count++, mtab = mtab2) {
if (mtab == (Entry *)NULL) {
continue;
}
for (ep = mtab; ep->e_off != -1L; ep++) {
(void) printf("%-7d %-7ld %-10s %-7c ",
ep->e_level,
ep->e_off, type_to_name(ep),
op_to_name(ep->e_opcode));
if (ep->e_type == STR) {
showstr(ep->e_value.str, 10);
} else { /* numeric */
(void) printf("%-#11llo", ep->e_value.num);
}
(void) printf(" %s", ep->e_str);
if (ep->e_opcode & SUB)
(void) printf("\tsubst");
(void) printf("\n");
}
}
}
intmax_t
f_getmaxoffset(int first)
{
Entry *mtab;
Entry *ep;
intmax_t cur;
intmax_t max = 0;
if (first) {
mtab = mtab1;
} else {
mtab = mtab2;
}
if (mtab == (Entry *)NULL) {
return (0);
}
for (ep = mtab; ep->e_off != -1L; ep++) {
cur = ep->e_off;
switch (ep->e_type) {
case STR:
cur += strlen(ep->e_value.str);
break;
case BYTE:
case UBYTE:
cur += sizeof (uchar_t);
break;
case SHORT:
case USHORT:
cur += sizeof (uint16_t);
break;
case LONG:
case ULONG:
cur += sizeof (uint32_t);
break;
case LLONG:
case ULLONG:
cur += sizeof (uint64_t);
break;
}
if (cur <= INT_MAX && cur > max) {
max = cur;
}
}
return (max);
}