Annotation of src/usr.bin/file/file.c, Revision 1.46
1.46 ! tobias 1: /* $OpenBSD: file.c,v 1.45 2015/05/30 06:25:35 nicm Exp $ */
1.27 nicm 2:
1.14 tedu 3: /*
1.27 nicm 4: * Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.14 tedu 17: */
1.11 ian 18:
1.20 deraadt 19: #include <sys/types.h>
1.27 nicm 20: #include <sys/ioctl.h>
21: #include <sys/mman.h>
1.35 nicm 22: #include <sys/socket.h>
23: #include <sys/queue.h>
24: #include <sys/uio.h>
25: #include <sys/wait.h>
1.20 deraadt 26:
1.27 nicm 27: #include <errno.h>
1.35 nicm 28: #include <imsg.h>
1.27 nicm 29: #include <libgen.h>
30: #include <getopt.h>
31: #include <fcntl.h>
32: #include <pwd.h>
1.1 deraadt 33: #include <stdlib.h>
1.44 nicm 34: #include <time.h>
1.14 tedu 35: #include <unistd.h>
36:
1.27 nicm 37: #include "file.h"
38: #include "magic.h"
39: #include "xmalloc.h"
1.1 deraadt 40:
1.35 nicm 41: struct input_msg
42: {
43: int idx;
44:
45: struct stat sb;
46: int error;
47:
48: char link_path[PATH_MAX];
49: int link_error;
50: int link_target;
51: };
52:
53: struct input_ack
54: {
55: int idx;
56: };
57:
1.27 nicm 58: struct input_file
59: {
1.35 nicm 60: struct magic *m;
61: struct input_msg *msg;
1.1 deraadt 62:
1.35 nicm 63: const char *path;
64: int fd;
1.14 tedu 65:
1.35 nicm 66: void *base;
67: size_t size;
68: int mapped;
69: char *result;
1.27 nicm 70: };
1.1 deraadt 71:
1.27 nicm 72: extern char *__progname;
1.1 deraadt 73:
1.27 nicm 74: __dead void usage(void);
1.1 deraadt 75:
1.35 nicm 76: static void send_message(struct imsgbuf *, void *, size_t, int);
77: static int read_message(struct imsgbuf *, struct imsg *, pid_t);
78:
79: static void read_link(struct input_msg *, const char *);
80:
81: static __dead void child(int, pid_t, int, char **);
82:
83: static void test_file(struct input_file *, size_t);
1.27 nicm 84:
85: static int try_stat(struct input_file *);
86: static int try_empty(struct input_file *);
87: static int try_access(struct input_file *);
88: static int try_text(struct input_file *);
89: static int try_magic(struct input_file *);
90: static int try_unknown(struct input_file *);
91:
92: static int bflag;
93: static int cflag;
94: static int iflag;
95: static int Lflag;
96: static int sflag;
97: static int Wflag;
98:
1.35 nicm 99: static char *magicpath;
100: static FILE *magicfp;
101:
1.27 nicm 102: static struct option longopts[] = {
103: { "mime", no_argument, NULL, 'i' },
104: { "mime-type", no_argument, NULL, 'i' },
105: { NULL, 0, NULL, 0 }
106: };
1.14 tedu 107:
1.27 nicm 108: __dead void
109: usage(void)
110: {
1.39 jmc 111: fprintf(stderr, "usage: %s [-bchiLsW] file ...\n", __progname);
1.27 nicm 112: exit(1);
113: }
1.14 tedu 114:
1.1 deraadt 115: int
1.27 nicm 116: main(int argc, char **argv)
1.1 deraadt 117: {
1.35 nicm 118: int opt, pair[2], fd, idx;
119: char *home;
1.27 nicm 120: struct passwd *pw;
1.35 nicm 121: struct imsgbuf ibuf;
122: struct imsg imsg;
123: struct input_msg msg;
124: struct input_ack *ack;
125: pid_t pid, parent;
1.44 nicm 126:
127: tzset();
1.27 nicm 128:
129: for (;;) {
130: opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL);
131: if (opt == -1)
1.18 chl 132: break;
1.27 nicm 133: switch (opt) {
1.9 millert 134: case 'b':
1.27 nicm 135: bflag = 1;
1.9 millert 136: break;
1.1 deraadt 137: case 'c':
1.27 nicm 138: cflag = 1;
1.14 tedu 139: break;
1.27 nicm 140: case 'h':
141: Lflag = 0;
1.14 tedu 142: break;
1.19 chl 143: case 'i':
1.27 nicm 144: iflag = 1;
1.19 chl 145: break;
1.27 nicm 146: case 'L':
147: Lflag = 1;
1.14 tedu 148: break;
149: case 's':
1.27 nicm 150: sflag = 1;
1.14 tedu 151: break;
1.27 nicm 152: case 'W':
153: Wflag = 1;
1.18 chl 154: break;
1.1 deraadt 155: default:
1.27 nicm 156: usage();
1.1 deraadt 157: }
1.27 nicm 158: }
159: argc -= optind;
160: argv += optind;
161: if (cflag) {
162: if (argc != 0)
163: usage();
164: } else if (argc == 0)
165: usage();
1.1 deraadt 166:
1.35 nicm 167: magicfp = NULL;
1.32 nicm 168: if (geteuid() != 0 && !issetugid()) {
169: home = getenv("HOME");
170: if (home == NULL || *home == '\0') {
171: pw = getpwuid(getuid());
172: if (pw != NULL)
173: home = pw->pw_dir;
174: else
175: home = NULL;
176: }
177: if (home != NULL) {
1.35 nicm 178: xasprintf(&magicpath, "%s/.magic", home);
179: magicfp = fopen(magicpath, "r");
180: if (magicfp == NULL)
181: free(magicpath);
1.32 nicm 182: }
1.27 nicm 183: }
1.35 nicm 184: if (magicfp == NULL) {
185: magicpath = xstrdup("/etc/magic");
186: magicfp = fopen(magicpath, "r");
187: }
188: if (magicfp == NULL)
189: err(1, "%s", magicpath);
190:
191: parent = getpid();
192: if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
193: err(1, "socketpair");
1.36 nicm 194: pid = sandbox_fork(FILE_USER);
195: if (pid == 0) {
1.35 nicm 196: close(pair[0]);
197: child(pair[1], parent, argc, argv);
198: }
199: close(pair[1]);
200:
201: fclose(magicfp);
202: magicfp = NULL;
203:
204: if (cflag)
205: goto wait_for_child;
206:
207: imsg_init(&ibuf, pair[0]);
208: for (idx = 0; idx < argc; idx++) {
209: memset(&msg, 0, sizeof msg);
210: msg.idx = idx;
211:
1.45 nicm 212: if (strcmp(argv[idx], "-") == 0) {
213: if (fstat(STDIN_FILENO, &msg.sb) == -1) {
214: fd = -1;
215: msg.error = errno;
216: } else
217: fd = STDIN_FILENO;
218: } else if (lstat(argv[idx], &msg.sb) == -1) {
1.35 nicm 219: fd = -1;
220: msg.error = errno;
221: } else {
222: fd = open(argv[idx], O_RDONLY|O_NONBLOCK);
223: if (fd == -1 && (errno == ENFILE || errno == EMFILE))
224: err(1, "open");
225: if (S_ISLNK(msg.sb.st_mode))
226: read_link(&msg, argv[idx]);
227: }
228: send_message(&ibuf, &msg, sizeof msg, fd);
1.1 deraadt 229:
1.35 nicm 230: if (read_message(&ibuf, &imsg, pid) == 0)
231: break;
232: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *ack)
233: errx(1, "message too small");
234: ack = imsg.data;
235: if (ack->idx != idx)
236: errx(1, "index not expected");
237: imsg_free(&imsg);
1.1 deraadt 238: }
239:
1.35 nicm 240: wait_for_child:
241: close(pair[0]);
242: while (wait(NULL) == -1 && errno != ECHILD) {
243: if (errno != EINTR)
244: err(1, "wait");
1.30 nicm 245: }
1.35 nicm 246: _exit(0); /* let the child flush */
1.1 deraadt 247: }
248:
1.27 nicm 249: static void
1.35 nicm 250: send_message(struct imsgbuf *ibuf, void *msg, size_t msglen, int fd)
251: {
252: if (imsg_compose(ibuf, -1, -1, 0, fd, msg, msglen) != 1)
253: err(1, "imsg_compose");
254: if (imsg_flush(ibuf) != 0)
255: err(1, "imsg_flush");
256: }
257:
258: static int
259: read_message(struct imsgbuf *ibuf, struct imsg *imsg, pid_t from)
1.27 nicm 260: {
1.35 nicm 261: int n;
1.1 deraadt 262:
1.35 nicm 263: if ((n = imsg_read(ibuf)) == -1)
264: err(1, "imsg_read");
265: if (n == 0)
266: return (0);
1.27 nicm 267:
1.35 nicm 268: if ((n = imsg_get(ibuf, imsg)) == -1)
269: err(1, "imsg_get");
270: if (n == 0)
271: return (0);
1.31 nicm 272:
1.35 nicm 273: if ((pid_t)imsg->hdr.pid != from)
274: errx(1, "PIDs don't match");
1.27 nicm 275:
1.35 nicm 276: return (n);
1.27 nicm 277:
278: }
279:
280: static void
1.35 nicm 281: read_link(struct input_msg *msg, const char *path)
1.14 tedu 282: {
1.27 nicm 283: struct stat sb;
1.35 nicm 284: char lpath[PATH_MAX];
1.27 nicm 285: char *copy, *root;
286: int used;
287: ssize_t size;
288:
1.35 nicm 289: size = readlink(path, lpath, sizeof lpath);
1.27 nicm 290: if (size == -1) {
1.35 nicm 291: msg->link_error = errno;
1.14 tedu 292: return;
1.27 nicm 293: }
1.35 nicm 294: lpath[size] = '\0';
1.27 nicm 295:
1.35 nicm 296: if (*lpath == '/')
297: strlcpy(msg->link_path, lpath, sizeof msg->link_path);
1.27 nicm 298: else {
1.35 nicm 299: copy = xstrdup(path);
1.27 nicm 300:
301: root = dirname(copy);
302: if (*root == '\0' || strcmp(root, ".") == 0 ||
303: strcmp (root, "/") == 0)
1.35 nicm 304: strlcpy(msg->link_path, lpath, sizeof msg->link_path);
1.27 nicm 305: else {
1.35 nicm 306: used = snprintf(msg->link_path, sizeof msg->link_path,
307: "%s/%s", root, lpath);
308: if (used < 0 || (size_t)used >= sizeof msg->link_path) {
309: msg->link_error = ENAMETOOLONG;
1.37 lteo 310: free(copy);
1.27 nicm 311: return;
312: }
313: }
314:
315: free(copy);
316: }
317:
318: if (Lflag) {
1.35 nicm 319: if (stat(path, &msg->sb) == -1)
320: msg->error = errno;
1.27 nicm 321: } else {
1.35 nicm 322: if (stat(path, &sb) == -1)
323: msg->link_target = errno;
1.14 tedu 324: }
325: }
326:
1.35 nicm 327: static __dead void
328: child(int fd, pid_t parent, int argc, char **argv)
329: {
330: struct magic *m;
331: struct imsgbuf ibuf;
332: struct imsg imsg;
333: struct input_msg *msg;
334: struct input_ack ack;
335: struct input_file inf;
336: int i, idx;
337: size_t len, width = 0;
338:
339: m = magic_load(magicfp, magicpath, cflag || Wflag);
340: if (cflag) {
341: magic_dump(m);
342: exit(0);
343: }
344:
345: for (i = 0; i < argc; i++) {
346: len = strlen(argv[i]) + 1;
347: if (len > width)
348: width = len;
349: }
350:
351: imsg_init(&ibuf, fd);
352: for (;;) {
353: if (read_message(&ibuf, &imsg, parent) == 0)
354: break;
355: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *msg)
356: errx(1, "message too small");
357: msg = imsg.data;
358:
359: idx = msg->idx;
360: if (idx < 0 || idx >= argc)
361: errx(1, "index out of range");
362:
363: memset(&inf, 0, sizeof inf);
364: inf.m = m;
365: inf.msg = msg;
366:
367: inf.path = argv[idx];
368: inf.fd = imsg.fd;
369:
370: test_file(&inf, width);
371:
372: if (imsg.fd != -1)
373: close(imsg.fd);
374: imsg_free(&imsg);
375:
376: ack.idx = idx;
377: send_message(&ibuf, &ack, sizeof ack, -1);
378: }
379: exit(0);
380: }
381:
1.27 nicm 382: static void *
1.42 nicm 383: fill_buffer(int fd, size_t size, size_t *used)
1.1 deraadt 384: {
1.27 nicm 385: static void *buffer;
386: ssize_t got;
387: size_t left;
388: void *next;
389:
390: if (buffer == NULL)
391: buffer = xmalloc(FILE_READ_SIZE);
392:
393: next = buffer;
1.42 nicm 394: left = size;
1.27 nicm 395: while (left != 0) {
1.42 nicm 396: got = read(fd, next, left);
1.27 nicm 397: if (got == -1) {
398: if (errno == EINTR)
399: continue;
400: return NULL;
1.5 millert 401: }
1.27 nicm 402: if (got == 0)
403: break;
1.30 nicm 404: next = (char *)next + got;
1.27 nicm 405: left -= got;
406: }
1.42 nicm 407: *used = size - left;
1.27 nicm 408: return buffer;
409: }
410:
411: static int
412: load_file(struct input_file *inf)
413: {
1.42 nicm 414: size_t used;
415:
1.46 ! tobias 416: if (inf->msg->sb.st_size == 0 && S_ISREG(inf->msg->sb.st_mode))
1.43 nicm 417: return (0); /* empty file */
1.46 ! tobias 418: if (inf->msg->sb.st_size == 0 || inf->msg->sb.st_size > FILE_READ_SIZE)
1.27 nicm 419: inf->size = FILE_READ_SIZE;
1.46 ! tobias 420: else
! 421: inf->size = inf->msg->sb.st_size;
1.43 nicm 422:
423: if (!S_ISREG(inf->msg->sb.st_mode))
424: goto try_read;
1.27 nicm 425:
426: inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0);
1.43 nicm 427: if (inf->base == MAP_FAILED)
428: goto try_read;
429: inf->mapped = 1;
430: return (0);
431:
432: try_read:
433: inf->base = fill_buffer(inf->fd, inf->size, &used);
434: if (inf->base == NULL) {
435: xasprintf(&inf->result, "cannot read '%s' (%s)", inf->path,
436: strerror(errno));
437: return (1);
438: }
439: inf->size = used;
1.27 nicm 440: return (0);
441: }
1.5 millert 442:
1.27 nicm 443: static int
444: try_stat(struct input_file *inf)
445: {
1.35 nicm 446: if (inf->msg->error != 0) {
1.27 nicm 447: xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path,
1.35 nicm 448: strerror(inf->msg->error));
1.27 nicm 449: return (1);
450: }
1.45 nicm 451: if (sflag || strcmp(inf->path, "-") == 0) {
1.35 nicm 452: switch (inf->msg->sb.st_mode & S_IFMT) {
1.45 nicm 453: case S_IFIFO:
454: if (strcmp(inf->path, "-") != 0)
455: break;
1.27 nicm 456: case S_IFBLK:
457: case S_IFCHR:
458: case S_IFREG:
459: return (0);
1.5 millert 460: }
1.27 nicm 461: }
1.1 deraadt 462:
1.35 nicm 463: if (iflag && (inf->msg->sb.st_mode & S_IFMT) != S_IFREG) {
1.27 nicm 464: xasprintf(&inf->result, "application/x-not-regular-file");
465: return (1);
1.1 deraadt 466: }
467:
1.35 nicm 468: switch (inf->msg->sb.st_mode & S_IFMT) {
1.27 nicm 469: case S_IFDIR:
470: xasprintf(&inf->result, "directory");
471: return (1);
472: case S_IFLNK:
1.35 nicm 473: if (inf->msg->link_error != 0) {
1.27 nicm 474: xasprintf(&inf->result, "unreadable symlink '%s' (%s)",
1.35 nicm 475: inf->path, strerror(inf->msg->link_error));
1.27 nicm 476: return (1);
477: }
1.35 nicm 478: if (inf->msg->link_target == ELOOP)
1.27 nicm 479: xasprintf(&inf->result, "symbolic link in a loop");
1.35 nicm 480: else if (inf->msg->link_target != 0) {
1.27 nicm 481: xasprintf(&inf->result, "broken symbolic link to '%s'",
1.35 nicm 482: inf->msg->link_path);
1.27 nicm 483: } else {
484: xasprintf(&inf->result, "symbolic link to '%s'",
1.35 nicm 485: inf->msg->link_path);
1.27 nicm 486: }
487: return (1);
488: case S_IFSOCK:
489: xasprintf(&inf->result, "socket");
490: return (1);
491: case S_IFBLK:
492: xasprintf(&inf->result, "block special (%ld/%ld)",
1.35 nicm 493: (long)major(inf->msg->sb.st_rdev),
494: (long)minor(inf->msg->sb.st_rdev));
1.27 nicm 495: return (1);
496: case S_IFCHR:
497: xasprintf(&inf->result, "character special (%ld/%ld)",
1.35 nicm 498: (long)major(inf->msg->sb.st_rdev),
499: (long)minor(inf->msg->sb.st_rdev));
1.27 nicm 500: return (1);
501: case S_IFIFO:
502: xasprintf(&inf->result, "fifo (named pipe)");
503: return (1);
1.1 deraadt 504: }
1.27 nicm 505: return (0);
506: }
507:
508: static int
509: try_empty(struct input_file *inf)
510: {
511: if (inf->size != 0)
512: return (0);
513:
514: if (iflag)
515: xasprintf(&inf->result, "application/x-empty");
516: else
517: xasprintf(&inf->result, "empty");
518: return (1);
519: }
520:
521: static int
522: try_access(struct input_file *inf)
523: {
524: char tmp[256] = "";
525:
526: if (inf->fd != -1)
527: return (0);
1.1 deraadt 528:
1.35 nicm 529: if (inf->msg->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
1.27 nicm 530: strlcat(tmp, "writable, ", sizeof tmp);
1.35 nicm 531: if (inf->msg->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
1.27 nicm 532: strlcat(tmp, "executable, ", sizeof tmp);
1.35 nicm 533: if (S_ISREG(inf->msg->sb.st_mode))
1.27 nicm 534: strlcat(tmp, "regular file, ", sizeof tmp);
535: strlcat(tmp, "no read permission", sizeof tmp);
536:
537: inf->result = xstrdup(tmp);
538: return (1);
1.1 deraadt 539: }
540:
1.27 nicm 541: static int
542: try_text(struct input_file *inf)
1.14 tedu 543: {
1.27 nicm 544: const char *type, *s;
545: int flags;
546:
547: flags = MAGIC_TEST_TEXT;
548: if (iflag)
549: flags |= MAGIC_TEST_MIME;
550:
551: type = text_get_type(inf->base, inf->size);
552: if (type == NULL)
553: return (0);
1.14 tedu 554:
1.27 nicm 555: s = magic_test(inf->m, inf->base, inf->size, flags);
556: if (s != NULL) {
557: inf->result = xstrdup(s);
558: return (1);
559: }
560:
561: s = text_try_words(inf->base, inf->size, flags);
562: if (s != NULL) {
563: if (iflag)
564: inf->result = xstrdup(s);
1.18 chl 565: else
1.27 nicm 566: xasprintf(&inf->result, "%s %s text", type, s);
567: return (1);
1.18 chl 568: }
1.14 tedu 569:
1.27 nicm 570: if (iflag)
571: inf->result = xstrdup("text/plain");
1.14 tedu 572: else
1.27 nicm 573: xasprintf(&inf->result, "%s text", type);
574: return (1);
1.14 tedu 575: }
576:
1.27 nicm 577: static int
578: try_magic(struct input_file *inf)
1.1 deraadt 579: {
1.27 nicm 580: const char *s;
581: int flags;
1.1 deraadt 582:
1.27 nicm 583: flags = 0;
584: if (iflag)
585: flags |= MAGIC_TEST_MIME;
586:
587: s = magic_test(inf->m, inf->base, inf->size, flags);
588: if (s != NULL) {
589: inf->result = xstrdup(s);
590: return (1);
1.1 deraadt 591: }
1.27 nicm 592: return (0);
1.14 tedu 593: }
1.1 deraadt 594:
1.27 nicm 595: static int
596: try_unknown(struct input_file *inf)
1.14 tedu 597: {
1.27 nicm 598: if (iflag)
599: xasprintf(&inf->result, "application/x-not-regular-file");
600: else
601: xasprintf(&inf->result, "data");
602: return (1);
1.1 deraadt 603: }
604:
1.27 nicm 605: static void
1.35 nicm 606: test_file(struct input_file *inf, size_t width)
1.1 deraadt 607: {
1.35 nicm 608: char *label;
609: int stop;
1.27 nicm 610:
611: stop = 0;
612: if (!stop)
613: stop = try_stat(inf);
614: if (!stop)
615: stop = try_access(inf);
616: if (!stop)
617: stop = load_file(inf);
618: if (!stop)
619: stop = try_empty(inf);
620: if (!stop)
621: stop = try_magic(inf);
622: if (!stop)
623: stop = try_text(inf);
624: if (!stop)
625: stop = try_unknown(inf);
626:
627: if (bflag)
628: printf("%s\n", inf->result);
1.35 nicm 629: else {
1.45 nicm 630: if (strcmp(inf->path, "-") == 0)
631: xasprintf(&label, "/dev/stdin:");
632: else
633: xasprintf(&label, "%s:", inf->path);
1.35 nicm 634: printf("%-*s %s\n", (int)width, label, inf->result);
635: free(label);
636: }
1.30 nicm 637: free(inf->result);
1.27 nicm 638:
639: if (inf->mapped && inf->base != NULL)
640: munmap(inf->base, inf->size);
1.1 deraadt 641: }