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