[BACK]Return to file.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / file

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: }