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