/* * 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 #include #include #include #include #include #include #include #include /* * 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); }