1 /* 2 * Copyright (c) Ian F. Darwin 1986-1995. 3 * Software written by Ian F. Darwin and others; 4 * maintained 1995-present by Christos Zoulas and others. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice immediately at the beginning of the file, without modification, 11 * this list of conditions, and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 /* 29 * file - find type of a file or files - main program. 30 */ 31 32 #include "file.h" 33 34 #ifndef lint 35 FILE_RCSID("@(#)$File: file.c,v 1.184 2019/08/03 11:51:59 christos Exp $") 36 #endif /* lint */ 37 38 #include "magic.h" 39 40 #include <stdlib.h> 41 #include <unistd.h> 42 #include <string.h> 43 #ifdef RESTORE_TIME 44 # if (__COHERENT__ >= 0x420) 45 # include <sys/utime.h> 46 # else 47 # ifdef USE_UTIMES 48 # include <sys/time.h> 49 # else 50 # include <utime.h> 51 # endif 52 # endif 53 #endif 54 #ifdef HAVE_UNISTD_H 55 #include <unistd.h> /* for read() */ 56 #endif 57 #ifdef HAVE_WCHAR_H 58 #include <wchar.h> 59 #endif 60 61 #if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION) 62 # include <getopt.h> 63 # ifndef HAVE_GETOPT_LONG 64 int getopt_long(int, char * const *, const char *, 65 const struct option *, int *); 66 # endif 67 # else 68 # include "mygetopt.h" 69 #endif 70 71 #ifdef S_IFLNK 72 # define IFLNK_h "h" 73 # define IFLNK_L "L" 74 #else 75 # define IFLNK_h "" 76 # define IFLNK_L "" 77 #endif 78 79 #define FILE_FLAGS "bcCdE" IFLNK_h "ik" IFLNK_L "lNnprsSvzZ0" 80 #define OPTSTRING "bcCde:Ef:F:hiklLm:nNpP:rsSvzZ0" 81 82 # define USAGE \ 83 "Usage: %s [-" FILE_FLAGS "] [--apple] [--extension] [--mime-encoding]\n" \ 84 " [--mime-type] [-e <testname>] [-F <separator>] " \ 85 " [-f <namefile>]\n" \ 86 " [-m <magicfiles>] [-P <parameter=value>] <file> ...\n" \ 87 " %s -C [-m <magicfiles>]\n" \ 88 " %s [--help]\n" 89 90 private int /* Global command-line options */ 91 bflag = 0, /* brief output format */ 92 nopad = 0, /* Don't pad output */ 93 nobuffer = 0, /* Do not buffer stdout */ 94 nulsep = 0; /* Append '\0' to the separator */ 95 96 private const char *separator = ":"; /* Default field separator */ 97 private const struct option long_options[] = { 98 #define OPT_HELP 1 99 #define OPT_APPLE 2 100 #define OPT_EXTENSIONS 3 101 #define OPT_MIME_TYPE 4 102 #define OPT_MIME_ENCODING 5 103 #define OPT(shortname, longname, opt, def, doc) \ 104 {longname, opt, NULL, shortname}, 105 #define OPT_LONGONLY(longname, opt, def, doc, id) \ 106 {longname, opt, NULL, id}, 107 #include "file_opts.h" 108 #undef OPT 109 #undef OPT_LONGONLY 110 {0, 0, NULL, 0} 111 }; 112 113 private const struct { 114 const char *name; 115 int value; 116 } nv[] = { 117 { "apptype", MAGIC_NO_CHECK_APPTYPE }, 118 { "ascii", MAGIC_NO_CHECK_ASCII }, 119 { "cdf", MAGIC_NO_CHECK_CDF }, 120 { "compress", MAGIC_NO_CHECK_COMPRESS }, 121 { "csv", MAGIC_NO_CHECK_CSV }, 122 { "elf", MAGIC_NO_CHECK_ELF }, 123 { "encoding", MAGIC_NO_CHECK_ENCODING }, 124 { "soft", MAGIC_NO_CHECK_SOFT }, 125 { "tar", MAGIC_NO_CHECK_TAR }, 126 { "json", MAGIC_NO_CHECK_JSON }, 127 { "text", MAGIC_NO_CHECK_TEXT }, /* synonym for ascii */ 128 { "tokens", MAGIC_NO_CHECK_TOKENS }, /* OBSOLETE: ignored for backwards compatibility */ 129 }; 130 131 private struct { 132 const char *name; 133 int tag; 134 size_t value; 135 int set; 136 } pm[] = { 137 { "indir", MAGIC_PARAM_INDIR_MAX, 0, 0 }, 138 { "name", MAGIC_PARAM_NAME_MAX, 0, 0 }, 139 { "elf_phnum", MAGIC_PARAM_ELF_PHNUM_MAX, 0, 0 }, 140 { "elf_shnum", MAGIC_PARAM_ELF_SHNUM_MAX, 0, 0 }, 141 { "elf_notes", MAGIC_PARAM_ELF_NOTES_MAX, 0, 0 }, 142 { "regex", MAGIC_PARAM_REGEX_MAX, 0, 0 }, 143 { "bytes", MAGIC_PARAM_BYTES_MAX, 0, 0 }, 144 }; 145 146 private int posixly; 147 148 #ifdef __dead 149 __dead 150 #endif 151 private void usage(void); 152 private void docprint(const char *, int); 153 #ifdef __dead 154 __dead 155 #endif 156 private void help(void); 157 158 private int unwrap(struct magic_set *, const char *); 159 private int process(struct magic_set *ms, const char *, int); 160 private struct magic_set *load(const char *, int); 161 private void setparam(const char *); 162 private void applyparam(magic_t); 163 164 165 /* 166 * main - parse arguments and handle options 167 */ 168 int 169 main(int argc, char *argv[]) 170 { 171 int c; 172 size_t i; 173 int action = 0, didsomefiles = 0, errflg = 0; 174 int flags = 0, e = 0; 175 #ifdef HAVE_LIBSECCOMP 176 int sandbox = 1; 177 #endif 178 struct magic_set *magic = NULL; 179 int longindex; 180 const char *magicfile = NULL; /* where the magic is */ 181 char *progname; 182 183 /* makes islower etc work for other langs */ 184 (void)setlocale(LC_CTYPE, ""); 185 186 #ifdef __EMX__ 187 /* sh-like wildcard expansion! Shouldn't hurt at least ... */ 188 _wildcard(&argc, &argv); 189 #endif 190 191 if ((progname = strrchr(argv[0], '/')) != NULL) 192 progname++; 193 else 194 progname = argv[0]; 195 196 file_setprogname(progname); 197 198 199 #ifdef S_IFLNK 200 posixly = getenv("POSIXLY_CORRECT") != NULL; 201 flags |= posixly ? MAGIC_SYMLINK : 0; 202 #endif 203 while ((c = getopt_long(argc, argv, OPTSTRING, long_options, 204 &longindex)) != -1) 205 switch (c) { 206 case OPT_HELP: 207 help(); 208 break; 209 case OPT_APPLE: 210 flags |= MAGIC_APPLE; 211 break; 212 case OPT_EXTENSIONS: 213 flags |= MAGIC_EXTENSION; 214 break; 215 case OPT_MIME_TYPE: 216 flags |= MAGIC_MIME_TYPE; 217 break; 218 case OPT_MIME_ENCODING: 219 flags |= MAGIC_MIME_ENCODING; 220 break; 221 case '0': 222 nulsep++; 223 break; 224 case 'b': 225 bflag++; 226 break; 227 case 'c': 228 action = FILE_CHECK; 229 break; 230 case 'C': 231 action = FILE_COMPILE; 232 break; 233 case 'd': 234 flags |= MAGIC_DEBUG|MAGIC_CHECK; 235 break; 236 case 'E': 237 flags |= MAGIC_ERROR; 238 break; 239 case 'e': 240 for (i = 0; i < __arraycount(nv); i++) 241 if (strcmp(nv[i].name, optarg) == 0) 242 break; 243 244 if (i == __arraycount(nv)) 245 errflg++; 246 else 247 flags |= nv[i].value; 248 break; 249 250 case 'f': 251 if(action) 252 usage(); 253 if (magic == NULL) 254 if ((magic = load(magicfile, flags)) == NULL) 255 return 1; 256 applyparam(magic); 257 e |= unwrap(magic, optarg); 258 ++didsomefiles; 259 break; 260 case 'F': 261 separator = optarg; 262 break; 263 case 'i': 264 flags |= MAGIC_MIME; 265 break; 266 case 'k': 267 flags |= MAGIC_CONTINUE; 268 break; 269 case 'l': 270 action = FILE_LIST; 271 break; 272 case 'm': 273 magicfile = optarg; 274 break; 275 case 'n': 276 ++nobuffer; 277 break; 278 case 'N': 279 ++nopad; 280 break; 281 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES) 282 case 'p': 283 flags |= MAGIC_PRESERVE_ATIME; 284 break; 285 #endif 286 case 'P': 287 setparam(optarg); 288 break; 289 case 'r': 290 flags |= MAGIC_RAW; 291 break; 292 case 's': 293 flags |= MAGIC_DEVICES; 294 break; 295 case 'S': 296 #ifdef HAVE_LIBSECCOMP 297 sandbox = 0; 298 #endif 299 break; 300 case 'v': 301 if (magicfile == NULL) 302 magicfile = magic_getpath(magicfile, action); 303 (void)fprintf(stdout, "%s-%s\n", file_getprogname(), 304 VERSION); 305 (void)fprintf(stdout, "magic file from %s\n", 306 magicfile); 307 #ifdef HAVE_LIBSECCOMP 308 (void)fprintf(stdout, "seccomp support included\n"); 309 #endif 310 return 0; 311 case 'z': 312 flags |= MAGIC_COMPRESS; 313 break; 314 315 case 'Z': 316 flags |= MAGIC_COMPRESS|MAGIC_COMPRESS_TRANSP; 317 break; 318 #ifdef S_IFLNK 319 case 'L': 320 flags |= MAGIC_SYMLINK; 321 break; 322 case 'h': 323 flags &= ~MAGIC_SYMLINK; 324 break; 325 #endif 326 case '?': 327 default: 328 errflg++; 329 break; 330 } 331 332 if (errflg) { 333 usage(); 334 } 335 if (e) 336 return e; 337 338 #ifdef HAVE_LIBSECCOMP 339 #if 0 340 if (sandbox && enable_sandbox_basic() == -1) 341 #else 342 if (sandbox && enable_sandbox_full() == -1) 343 #endif 344 file_err(EXIT_FAILURE, "SECCOMP initialisation failed"); 345 #endif /* HAVE_LIBSECCOMP */ 346 347 if (MAGIC_VERSION != magic_version()) 348 file_warnx("Compiled magic version [%d] " 349 "does not match with shared library magic version [%d]\n", 350 MAGIC_VERSION, magic_version()); 351 352 switch(action) { 353 case FILE_CHECK: 354 case FILE_COMPILE: 355 case FILE_LIST: 356 /* 357 * Don't try to check/compile ~/.magic unless we explicitly 358 * ask for it. 359 */ 360 magic = magic_open(flags|MAGIC_CHECK); 361 if (magic == NULL) { 362 file_warn("Can't create magic"); 363 return 1; 364 } 365 366 367 switch(action) { 368 case FILE_CHECK: 369 c = magic_check(magic, magicfile); 370 break; 371 case FILE_COMPILE: 372 c = magic_compile(magic, magicfile); 373 break; 374 case FILE_LIST: 375 c = magic_list(magic, magicfile); 376 break; 377 default: 378 abort(); 379 } 380 if (c == -1) { 381 file_warnx("%s", magic_error(magic)); 382 e = 1; 383 goto out; 384 } 385 goto out; 386 default: 387 if (magic == NULL) 388 if ((magic = load(magicfile, flags)) == NULL) 389 return 1; 390 applyparam(magic); 391 } 392 393 if (optind == argc) { 394 if (!didsomefiles) 395 usage(); 396 } 397 else { 398 size_t j, wid, nw; 399 for (wid = 0, j = CAST(size_t, optind); j < CAST(size_t, argc); 400 j++) { 401 nw = file_mbswidth(argv[j]); 402 if (nw > wid) 403 wid = nw; 404 } 405 /* 406 * If bflag is only set twice, set it depending on 407 * number of files [this is undocumented, and subject to change] 408 */ 409 if (bflag == 2) { 410 bflag = optind >= argc - 1; 411 } 412 for (; optind < argc; optind++) 413 e |= process(magic, argv[optind], wid); 414 } 415 416 out: 417 if (magic) 418 magic_close(magic); 419 return e; 420 } 421 422 private void 423 applyparam(magic_t magic) 424 { 425 size_t i; 426 427 for (i = 0; i < __arraycount(pm); i++) { 428 if (!pm[i].set) 429 continue; 430 if (magic_setparam(magic, pm[i].tag, &pm[i].value) == -1) 431 file_err(EXIT_FAILURE, "Can't set %s", pm[i].name); 432 } 433 } 434 435 private void 436 setparam(const char *p) 437 { 438 size_t i; 439 char *s; 440 441 if ((s = strchr(p, '=')) == NULL) 442 goto badparm; 443 444 for (i = 0; i < __arraycount(pm); i++) { 445 if (strncmp(p, pm[i].name, s - p) != 0) 446 continue; 447 pm[i].value = atoi(s + 1); 448 pm[i].set = 1; 449 return; 450 } 451 badparm: 452 file_errx(EXIT_FAILURE, "Unknown param %s", p); 453 } 454 455 private struct magic_set * 456 /*ARGSUSED*/ 457 load(const char *magicfile, int flags) 458 { 459 struct magic_set *magic = magic_open(flags); 460 const char *e; 461 462 if (magic == NULL) { 463 file_warn("Can't create magic"); 464 return NULL; 465 } 466 if (magic_load(magic, magicfile) == -1) { 467 file_warn("%s", magic_error(magic)); 468 magic_close(magic); 469 return NULL; 470 } 471 if ((e = magic_error(magic)) != NULL) 472 file_warn("%s", e); 473 return magic; 474 } 475 476 /* 477 * unwrap -- read a file of filenames, do each one. 478 */ 479 private int 480 unwrap(struct magic_set *ms, const char *fn) 481 { 482 FILE *f; 483 ssize_t len; 484 char *line = NULL; 485 size_t llen = 0; 486 int wid = 0, cwid; 487 int e = 0; 488 489 if (strcmp("-", fn) == 0) { 490 f = stdin; 491 wid = 1; 492 } else { 493 if ((f = fopen(fn, "r")) == NULL) { 494 file_warn("Cannot open `%s'", fn); 495 return 1; 496 } 497 498 while ((len = getline(&line, &llen, f)) > 0) { 499 if (line[len - 1] == '\n') 500 line[len - 1] = '\0'; 501 cwid = file_mbswidth(line); 502 if (cwid > wid) 503 wid = cwid; 504 } 505 506 rewind(f); 507 } 508 509 while ((len = getline(&line, &llen, f)) > 0) { 510 if (line[len - 1] == '\n') 511 line[len - 1] = '\0'; 512 e |= process(ms, line, wid); 513 if(nobuffer) 514 (void)fflush(stdout); 515 } 516 517 free(line); 518 (void)fclose(f); 519 return e; 520 } 521 522 /* 523 * Called for each input file on the command line (or in a list of files) 524 */ 525 private int 526 process(struct magic_set *ms, const char *inname, int wid) 527 { 528 const char *type, c = nulsep > 1 ? '\0' : '\n'; 529 int std_in = strcmp(inname, "-") == 0; 530 531 if (wid > 0 && !bflag) { 532 (void)printf("%s", std_in ? "/dev/stdin" : inname); 533 if (nulsep) 534 (void)putc('\0', stdout); 535 if (nulsep < 2) { 536 (void)printf("%s", separator); 537 (void)printf("%*s ", CAST(int, nopad ? 0 538 : (wid - file_mbswidth(inname))), ""); 539 } 540 } 541 542 type = magic_file(ms, std_in ? NULL : inname); 543 544 if (type == NULL) { 545 (void)printf("ERROR: %s%c", magic_error(ms), c); 546 return 1; 547 } else { 548 (void)printf("%s%c", type, c); 549 return 0; 550 } 551 } 552 553 protected size_t 554 file_mbswidth(const char *s) 555 { 556 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) 557 size_t bytesconsumed, old_n, n, width = 0; 558 mbstate_t state; 559 wchar_t nextchar; 560 (void)memset(&state, 0, sizeof(mbstate_t)); 561 old_n = n = strlen(s); 562 563 while (n > 0) { 564 bytesconsumed = mbrtowc(&nextchar, s, n, &state); 565 if (bytesconsumed == CAST(size_t, -1) || 566 bytesconsumed == CAST(size_t, -2)) { 567 /* Something went wrong, return something reasonable */ 568 return old_n; 569 } 570 if (s[0] == '\n') { 571 /* 572 * do what strlen() would do, so that caller 573 * is always right 574 */ 575 width++; 576 } else { 577 int w = wcwidth(nextchar); 578 if (w > 0) 579 width += w; 580 } 581 582 s += bytesconsumed, n -= bytesconsumed; 583 } 584 return width; 585 #else 586 return strlen(s); 587 #endif 588 } 589 590 private void 591 usage(void) 592 { 593 const char *pn = file_getprogname(); 594 (void)fprintf(stderr, USAGE, pn, pn, pn); 595 exit(EXIT_FAILURE); 596 } 597 598 private void 599 defprint(int def) 600 { 601 if (!def) 602 return; 603 if (((def & 1) && posixly) || ((def & 2) && !posixly)) 604 fprintf(stdout, " (default)"); 605 fputc('\n', stdout); 606 } 607 608 private void 609 docprint(const char *opts, int def) 610 { 611 size_t i; 612 int comma; 613 char *sp, *p; 614 615 p = strstr(opts, "%o"); 616 if (p == NULL) { 617 fprintf(stdout, "%s", opts); 618 defprint(def); 619 return; 620 } 621 622 for (sp = p - 1; sp > opts && *sp == ' '; sp--) 623 continue; 624 625 fprintf(stdout, "%.*s", CAST(int, p - opts), opts); 626 627 comma = 0; 628 for (i = 0; i < __arraycount(nv); i++) { 629 fprintf(stdout, "%s%s", comma++ ? ", " : "", nv[i].name); 630 if (i && i % 5 == 0 && i != __arraycount(nv) - 1) { 631 fprintf(stdout, ",\n%*s", CAST(int, p - sp - 1), ""); 632 comma = 0; 633 } 634 } 635 636 fprintf(stdout, "%s", opts + (p - opts) + 2); 637 } 638 639 private void 640 help(void) 641 { 642 (void)fputs( 643 "Usage: file [OPTION...] [FILE...]\n" 644 "Determine type of FILEs.\n" 645 "\n", stdout); 646 #define OPT(shortname, longname, opt, def, doc) \ 647 fprintf(stdout, " -%c, --" longname, shortname), \ 648 docprint(doc, def); 649 #define OPT_LONGONLY(longname, opt, def, doc, id) \ 650 fprintf(stdout, " --" longname), \ 651 docprint(doc, def); 652 #include "file_opts.h" 653 #undef OPT 654 #undef OPT_LONGONLY 655 fprintf(stdout, "\nReport bugs to https://bugs.astron.com/\n"); 656 exit(EXIT_SUCCESS); 657 } 658 659 private const char *file_progname; 660 661 protected void 662 file_setprogname(const char *progname) 663 { 664 file_progname = progname; 665 } 666 667 protected const char * 668 file_getprogname(void) 669 { 670 return file_progname; 671 } 672 673 protected void 674 file_err(int e, const char *fmt, ...) 675 { 676 va_list ap; 677 int se = errno; 678 679 va_start(ap, fmt); 680 fprintf(stderr, "%s: ", file_progname); 681 vfprintf(stderr, fmt, ap); 682 va_end(ap); 683 fprintf(stderr, " (%s)\n", strerror(se)); 684 exit(e); 685 } 686 687 protected void 688 file_errx(int e, const char *fmt, ...) 689 { 690 va_list ap; 691 692 va_start(ap, fmt); 693 fprintf(stderr, "%s: ", file_progname); 694 vfprintf(stderr, fmt, ap); 695 va_end(ap); 696 fprintf(stderr, "\n"); 697 exit(e); 698 } 699 700 protected void 701 file_warn(const char *fmt, ...) 702 { 703 va_list ap; 704 int se = errno; 705 706 va_start(ap, fmt); 707 fprintf(stderr, "%s: ", file_progname); 708 vfprintf(stderr, fmt, ap); 709 va_end(ap); 710 fprintf(stderr, " (%s)\n", strerror(se)); 711 errno = se; 712 } 713 714 protected void 715 file_warnx(const char *fmt, ...) 716 { 717 va_list ap; 718 int se = errno; 719 720 va_start(ap, fmt); 721 fprintf(stderr, "%s: ", file_progname); 722 vfprintf(stderr, fmt, ap); 723 va_end(ap); 724 fprintf(stderr, "\n"); 725 errno = se; 726 } 727