Annotation of src/usr.bin/file/file.c, Revision 1.29
1.29 ! nicm 1: /* $OpenBSD: file.c,v 1.28 2015/04/24 16:28:00 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.20 deraadt 22:
1.27 nicm 23: #include <errno.h>
24: #include <libgen.h>
25: #include <getopt.h>
26: #include <fcntl.h>
27: #include <pwd.h>
1.1 deraadt 28: #include <stdlib.h>
1.14 tedu 29: #include <unistd.h>
30:
1.27 nicm 31: #include "file.h"
32: #include "magic.h"
33: #include "xmalloc.h"
1.1 deraadt 34:
1.27 nicm 35: struct input_file
36: {
37: struct magic *m;
1.1 deraadt 38:
1.27 nicm 39: const char *path;
40: const char *label;
1.14 tedu 41:
1.27 nicm 42: int fd;
43: struct stat sb;
44: const char *error;
45:
46: void *base;
47: size_t size;
48: int mapped;
49: char *result;
50:
51: char link_path[PATH_MAX];
52: const char *link_error;
53: int link_target;
54: };
1.1 deraadt 55:
1.27 nicm 56: extern char *__progname;
1.1 deraadt 57:
1.27 nicm 58: __dead void usage(void);
1.1 deraadt 59:
1.27 nicm 60: static void open_file(struct input_file *, const char *, int *);
61: static void read_link(struct input_file *);
62: static void test_file(struct magic *, struct input_file *, int);
63:
64: static int try_stat(struct input_file *);
65: static int try_empty(struct input_file *);
66: static int try_access(struct input_file *);
67: static int try_text(struct input_file *);
68: static int try_magic(struct input_file *);
69: static int try_unknown(struct input_file *);
70:
71: static int bflag;
72: static int cflag;
73: static int iflag;
74: static int Lflag;
75: static int sflag;
76: static int Wflag;
77:
78: static struct option longopts[] = {
79: { "mime", no_argument, NULL, 'i' },
80: { "mime-type", no_argument, NULL, 'i' },
81: { NULL, 0, NULL, 0 }
82: };
1.14 tedu 83:
1.27 nicm 84: __dead void
85: usage(void)
86: {
87: fprintf(stderr, "usage: %s [-bchiLsW] [file ...]\n", __progname);
88: exit(1);
89: }
1.14 tedu 90:
1.1 deraadt 91: int
1.27 nicm 92: main(int argc, char **argv)
1.1 deraadt 93: {
1.27 nicm 94: struct input_file *files = NULL;
95: int nfiles, opt, i, width = 0;
96: FILE *f;
97: struct magic *m;
98: char *home, *path;
99: struct passwd *pw;
100:
101: for (;;) {
102: opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL);
103: if (opt == -1)
1.18 chl 104: break;
1.27 nicm 105: switch (opt) {
1.9 millert 106: case 'b':
1.27 nicm 107: bflag = 1;
1.9 millert 108: break;
1.1 deraadt 109: case 'c':
1.27 nicm 110: cflag = 1;
1.14 tedu 111: break;
1.27 nicm 112: case 'h':
113: Lflag = 0;
1.14 tedu 114: break;
1.19 chl 115: case 'i':
1.27 nicm 116: iflag = 1;
1.19 chl 117: break;
1.27 nicm 118: case 'L':
119: Lflag = 1;
1.14 tedu 120: break;
121: case 's':
1.27 nicm 122: sflag = 1;
1.14 tedu 123: break;
1.27 nicm 124: case 'W':
125: Wflag = 1;
1.18 chl 126: break;
1.1 deraadt 127: default:
1.27 nicm 128: usage();
1.1 deraadt 129: }
1.27 nicm 130: }
131: argc -= optind;
132: argv += optind;
133: if (cflag) {
134: if (argc != 0)
135: usage();
136: } else if (argc == 0)
137: usage();
1.1 deraadt 138:
1.27 nicm 139: nfiles = argc;
140: if (nfiles != 0) {
141: files = xcalloc(nfiles, sizeof *files);
142: for (i = 0; i < argc; i++)
143: open_file(&files[i], argv[i], &width);
1.1 deraadt 144: }
145:
1.27 nicm 146: home = getenv("HOME");
147: if (home == NULL || *home == '\0') {
148: pw = getpwuid(getuid());
149: if (pw != NULL)
150: home = pw->pw_dir;
151: else
152: home = NULL;
153: }
154: if (home != NULL) {
155: xasprintf(&path, "%s/.magic", home);
156: f = fopen(path, "r");
1.28 nicm 157: if (f == NULL && errno != ENOENT)
158: err(1, "%s", path);
159: if (f == NULL)
160: free(path);
1.27 nicm 161: } else
162: f = NULL;
163: if (f == NULL) {
164: path = xstrdup("/etc/magic");
165: f = fopen(path, "r");
1.1 deraadt 166: }
1.27 nicm 167: if (f == NULL)
168: err(1, "%s", path);
1.1 deraadt 169:
1.27 nicm 170: if (geteuid() == 0) {
171: pw = getpwnam(FILE_USER);
172: if (pw == NULL)
173: errx(1, "unknown user %s", FILE_USER);
174: if (setgroups(1, &pw->pw_gid) != 0)
175: err(1, "setgroups");
176: if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0)
177: err(1, "setresgid");
178: if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
179: err(1, "setresuid");
1.14 tedu 180: }
1.27 nicm 181:
182: m = magic_load(f, path, cflag || Wflag);
183: if (cflag) {
184: magic_dump(m);
185: exit(0);
1.1 deraadt 186: }
187:
1.27 nicm 188: for (i = 0; i < nfiles; i++)
189: test_file(m, &files[i], width);
190: exit(0);
1.1 deraadt 191: }
192:
1.27 nicm 193: static void
194: open_file(struct input_file *inf, const char *path, int *width)
195: {
196: char *label;
197: int n, retval;
1.1 deraadt 198:
1.27 nicm 199: inf->path = xstrdup(path);
200:
201: n = xasprintf(&label, "%s:", inf->path);
202: if (n > *width)
203: *width = n;
204: inf->label = label;
205:
206: retval = lstat(inf->path, &inf->sb);
207: if (retval == -1) {
208: inf->error = strerror(errno);
209: return;
210: }
211:
212: if (S_ISLNK(inf->sb.st_mode))
213: read_link(inf);
214: inf->fd = open(inf->path, O_RDONLY|O_NONBLOCK);
215: }
216:
217: static void
218: read_link(struct input_file *inf)
1.14 tedu 219: {
1.27 nicm 220: struct stat sb;
221: char path[PATH_MAX];
222: char *copy, *root;
223: int used;
224: ssize_t size;
225:
226: size = readlink(inf->path, path, sizeof path);
227: if (size == -1) {
228: inf->link_error = strerror(errno);
1.14 tedu 229: return;
1.27 nicm 230: }
231: path[size] = '\0';
232:
233: if (*path == '/')
234: strlcpy(inf->link_path, path, sizeof inf->link_path);
235: else {
236: copy = xstrdup(inf->path);
237:
238: root = dirname(copy);
239: if (*root == '\0' || strcmp(root, ".") == 0 ||
240: strcmp (root, "/") == 0)
241: strlcpy(inf->link_path, path, sizeof inf->link_path);
242: else {
243: used = snprintf(inf->link_path, sizeof inf->link_path,
244: "%s/%s", root, path);
245: if (used < 0 || (size_t)used >= sizeof inf->link_path) {
246: inf->link_error = strerror(ENAMETOOLONG);
247: return;
248: }
249: }
250:
251: free(copy);
252: }
253:
254: if (Lflag) {
255: if (stat(inf->path, &inf->sb) == -1)
256: inf->error = strerror(errno);
257: } else {
258: if (stat(inf->link_path, &sb) == -1)
259: inf->link_target = errno;
1.14 tedu 260: }
261: }
262:
1.27 nicm 263: static void *
264: fill_buffer(struct input_file *inf)
1.1 deraadt 265: {
1.27 nicm 266: static void *buffer;
267: ssize_t got;
268: size_t left;
269: void *next;
270:
271: if (buffer == NULL)
272: buffer = xmalloc(FILE_READ_SIZE);
273:
274: next = buffer;
275: left = inf->size;
276: while (left != 0) {
277: got = read(inf->fd, next, left);
278: if (got == -1) {
279: if (errno == EINTR)
280: continue;
281: return NULL;
1.5 millert 282: }
1.27 nicm 283: if (got == 0)
284: break;
285: next = (char*)next + got;
286: left -= got;
287: }
288:
289: return buffer;
290: }
291:
292: static int
293: load_file(struct input_file *inf)
294: {
295: int available;
296:
297: inf->size = inf->sb.st_size;
298: if (inf->size > FILE_READ_SIZE)
299: inf->size = FILE_READ_SIZE;
300: if (S_ISFIFO(inf->sb.st_mode)) {
301: if (ioctl(inf->fd, FIONREAD, &available) == -1) {
302: xasprintf(&inf->result, "cannot read '%s' (%s)",
303: inf->path, strerror(errno));
304: return (1);
305: }
306: inf->size = available;
307: } else if (!S_ISREG(inf->sb.st_mode) && inf->size == 0)
308: inf->size = FILE_READ_SIZE;
309: if (inf->size == 0)
310: return (0);
311:
312: inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0);
313: if (inf->base == MAP_FAILED) {
314: inf->base = fill_buffer(inf);
315: if (inf->base == NULL) {
316: xasprintf(&inf->result, "cannot read '%s' (%s)",
317: inf->path, strerror(errno));
318: return (1);
319: }
320: } else
321: inf->mapped = 1;
322: return (0);
323: }
1.5 millert 324:
1.27 nicm 325: static int
326: try_stat(struct input_file *inf)
327: {
328: if (inf->error != NULL) {
329: xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path,
330: inf->error);
331: return (1);
332: }
333: if (sflag) {
334: switch (inf->sb.st_mode & S_IFMT) {
335: case S_IFBLK:
336: case S_IFCHR:
337: case S_IFIFO:
338: case S_IFREG:
339: return (0);
1.5 millert 340: }
1.27 nicm 341: }
1.1 deraadt 342:
1.27 nicm 343: if (iflag && (inf->sb.st_mode & S_IFMT) != S_IFREG) {
344: xasprintf(&inf->result, "application/x-not-regular-file");
345: return (1);
1.1 deraadt 346: }
347:
1.27 nicm 348:
349: switch (inf->sb.st_mode & S_IFMT) {
350: case S_IFDIR:
351: xasprintf(&inf->result, "directory");
352: return (1);
353: case S_IFLNK:
354: if (inf->link_error != NULL) {
355: xasprintf(&inf->result, "unreadable symlink '%s' (%s)",
356: inf->path, inf->link_error);
357: return (1);
358: }
359: if (inf->link_target == ELOOP)
360: xasprintf(&inf->result, "symbolic link in a loop");
361: else if (inf->link_target != 0) {
362: xasprintf(&inf->result, "broken symbolic link to '%s'",
363: inf->link_path);
364: } else {
365: xasprintf(&inf->result, "symbolic link to '%s'",
366: inf->link_path);
367: }
368: return (1);
369: case S_IFSOCK:
370: xasprintf(&inf->result, "socket");
371: return (1);
372: case S_IFBLK:
373: xasprintf(&inf->result, "block special (%ld/%ld)",
374: (long)major(inf->sb.st_rdev), (long)minor(inf->sb.st_rdev));
375: return (1);
376: case S_IFCHR:
377: xasprintf(&inf->result, "character special (%ld/%ld)",
378: (long)major(inf->sb.st_rdev), (long)minor(inf->sb.st_rdev));
379: return (1);
380: case S_IFIFO:
381: xasprintf(&inf->result, "fifo (named pipe)");
382: return (1);
1.1 deraadt 383: }
1.27 nicm 384: return (0);
385: }
386:
387: static int
388: try_empty(struct input_file *inf)
389: {
390: if (inf->size != 0)
391: return (0);
392:
393: if (iflag)
394: xasprintf(&inf->result, "application/x-empty");
395: else
396: xasprintf(&inf->result, "empty");
397: return (1);
398: }
399:
400: static int
401: try_access(struct input_file *inf)
402: {
403: char tmp[256] = "";
404:
405: if (inf->fd != -1)
406: return (0);
1.1 deraadt 407:
1.29 ! nicm 408: if (inf->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
1.27 nicm 409: strlcat(tmp, "writable, ", sizeof tmp);
1.29 ! nicm 410: if (inf->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
1.27 nicm 411: strlcat(tmp, "executable, ", sizeof tmp);
412: if (S_ISREG(inf->sb.st_mode))
413: strlcat(tmp, "regular file, ", sizeof tmp);
414: strlcat(tmp, "no read permission", sizeof tmp);
415:
416: inf->result = xstrdup(tmp);
417: return (1);
1.1 deraadt 418: }
419:
1.27 nicm 420: static int
421: try_text(struct input_file *inf)
1.14 tedu 422: {
1.27 nicm 423: const char *type, *s;
424: int flags;
425:
426: flags = MAGIC_TEST_TEXT;
427: if (iflag)
428: flags |= MAGIC_TEST_MIME;
429:
430: type = text_get_type(inf->base, inf->size);
431: if (type == NULL)
432: return (0);
1.14 tedu 433:
1.27 nicm 434: s = magic_test(inf->m, inf->base, inf->size, flags);
435: if (s != NULL) {
436: inf->result = xstrdup(s);
437: return (1);
438: }
439:
440: s = text_try_words(inf->base, inf->size, flags);
441: if (s != NULL) {
442: if (iflag)
443: inf->result = xstrdup(s);
1.18 chl 444: else
1.27 nicm 445: xasprintf(&inf->result, "%s %s text", type, s);
446: return (1);
1.18 chl 447: }
1.14 tedu 448:
1.27 nicm 449: if (iflag)
450: inf->result = xstrdup("text/plain");
1.14 tedu 451: else
1.27 nicm 452: xasprintf(&inf->result, "%s text", type);
453: return (1);
1.14 tedu 454: }
455:
1.27 nicm 456: static int
457: try_magic(struct input_file *inf)
1.1 deraadt 458: {
1.27 nicm 459: const char *s;
460: int flags;
1.1 deraadt 461:
1.27 nicm 462: flags = 0;
463: if (iflag)
464: flags |= MAGIC_TEST_MIME;
465:
466: s = magic_test(inf->m, inf->base, inf->size, flags);
467: if (s != NULL) {
468: inf->result = xstrdup(s);
469: return (1);
1.1 deraadt 470: }
1.27 nicm 471: return (0);
1.14 tedu 472: }
1.1 deraadt 473:
1.27 nicm 474: static int
475: try_unknown(struct input_file *inf)
1.14 tedu 476: {
1.27 nicm 477: if (iflag)
478: xasprintf(&inf->result, "application/x-not-regular-file");
479: else
480: xasprintf(&inf->result, "data");
481: return (1);
1.1 deraadt 482: }
483:
1.27 nicm 484: static void
485: test_file(struct magic *m, struct input_file *inf, int width)
1.1 deraadt 486: {
1.27 nicm 487: int stop;
488:
489: inf->m = m;
490:
491: stop = 0;
492: if (!stop)
493: stop = try_stat(inf);
494: if (!stop)
495: stop = try_access(inf);
496: if (!stop)
497: stop = load_file(inf);
498: if (!stop)
499: stop = try_empty(inf);
500: if (!stop)
501: stop = try_magic(inf);
502: if (!stop)
503: stop = try_text(inf);
504: if (!stop)
505: stop = try_unknown(inf);
506:
507: if (bflag)
508: printf("%s\n", inf->result);
509: else
510: printf("%-*s %s\n", width, inf->label, inf->result);
511:
512: if (inf->mapped && inf->base != NULL)
513: munmap(inf->base, inf->size);
514: inf->base = NULL;
515:
516: free(inf->result);
1.1 deraadt 517: }