[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.34

1.34    ! nicm        1: /* $OpenBSD: file.c,v 1.33 2015/04/26 19:53: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 {
1.33      nicm      253:                if (stat(inf->path, &sb) == -1)
1.27      nicm      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:        inf->size = inf->sb.st_size;
                    291:        if (inf->size > FILE_READ_SIZE)
                    292:                inf->size = FILE_READ_SIZE;
1.34    ! nicm      293:        if (inf->size == 0) {
        !           294:                if (!S_ISREG(inf->sb.st_mode))
        !           295:                        inf->size = FILE_READ_SIZE;
        !           296:                else
        !           297:                        return (0);
        !           298:        }
1.27      nicm      299:
                    300:        inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0);
                    301:        if (inf->base == MAP_FAILED) {
                    302:                inf->base = fill_buffer(inf);
                    303:                if (inf->base == NULL) {
                    304:                        xasprintf(&inf->result, "cannot read '%s' (%s)",
                    305:                            inf->path, strerror(errno));
                    306:                        return (1);
                    307:                }
                    308:        } else
                    309:                inf->mapped = 1;
                    310:        return (0);
                    311: }
1.5       millert   312:
1.27      nicm      313: static int
                    314: try_stat(struct input_file *inf)
                    315: {
                    316:        if (inf->error != NULL) {
                    317:                xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path,
                    318:                    inf->error);
                    319:                return (1);
                    320:        }
                    321:        if (sflag) {
                    322:                switch (inf->sb.st_mode & S_IFMT) {
                    323:                case S_IFBLK:
                    324:                case S_IFCHR:
                    325:                case S_IFREG:
                    326:                        return (0);
1.5       millert   327:                }
1.27      nicm      328:        }
1.1       deraadt   329:
1.27      nicm      330:        if (iflag && (inf->sb.st_mode & S_IFMT) != S_IFREG) {
                    331:                xasprintf(&inf->result, "application/x-not-regular-file");
                    332:                return (1);
1.1       deraadt   333:        }
                    334:
1.27      nicm      335:
                    336:        switch (inf->sb.st_mode & S_IFMT) {
                    337:        case S_IFDIR:
                    338:                xasprintf(&inf->result, "directory");
                    339:                return (1);
                    340:        case S_IFLNK:
                    341:                if (inf->link_error != NULL) {
                    342:                        xasprintf(&inf->result, "unreadable symlink '%s' (%s)",
                    343:                            inf->path, inf->link_error);
                    344:                        return (1);
                    345:                }
                    346:                if (inf->link_target == ELOOP)
                    347:                        xasprintf(&inf->result, "symbolic link in a loop");
                    348:                else if (inf->link_target != 0) {
                    349:                        xasprintf(&inf->result, "broken symbolic link to '%s'",
                    350:                            inf->link_path);
                    351:                } else {
                    352:                        xasprintf(&inf->result, "symbolic link to '%s'",
                    353:                            inf->link_path);
                    354:                }
                    355:                return (1);
                    356:        case S_IFSOCK:
                    357:                xasprintf(&inf->result, "socket");
                    358:                return (1);
                    359:        case S_IFBLK:
                    360:                xasprintf(&inf->result, "block special (%ld/%ld)",
                    361:                    (long)major(inf->sb.st_rdev), (long)minor(inf->sb.st_rdev));
                    362:                return (1);
                    363:        case S_IFCHR:
                    364:                xasprintf(&inf->result, "character special (%ld/%ld)",
                    365:                    (long)major(inf->sb.st_rdev), (long)minor(inf->sb.st_rdev));
                    366:                return (1);
                    367:        case S_IFIFO:
                    368:                xasprintf(&inf->result, "fifo (named pipe)");
                    369:                return (1);
1.1       deraadt   370:        }
1.27      nicm      371:        return (0);
                    372: }
                    373:
                    374: static int
                    375: try_empty(struct input_file *inf)
                    376: {
                    377:        if (inf->size != 0)
                    378:                return (0);
                    379:
                    380:        if (iflag)
                    381:                xasprintf(&inf->result, "application/x-empty");
                    382:        else
                    383:                xasprintf(&inf->result, "empty");
                    384:        return (1);
                    385: }
                    386:
                    387: static int
                    388: try_access(struct input_file *inf)
                    389: {
                    390:        char tmp[256] = "";
                    391:
                    392:        if (inf->fd != -1)
                    393:                return (0);
1.1       deraadt   394:
1.29      nicm      395:        if (inf->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
1.27      nicm      396:                strlcat(tmp, "writable, ", sizeof tmp);
1.29      nicm      397:        if (inf->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
1.27      nicm      398:                strlcat(tmp, "executable, ", sizeof tmp);
                    399:        if (S_ISREG(inf->sb.st_mode))
                    400:                strlcat(tmp, "regular file, ", sizeof tmp);
                    401:        strlcat(tmp, "no read permission", sizeof tmp);
                    402:
                    403:        inf->result = xstrdup(tmp);
                    404:        return (1);
1.1       deraadt   405: }
                    406:
1.27      nicm      407: static int
                    408: try_text(struct input_file *inf)
1.14      tedu      409: {
1.27      nicm      410:        const char      *type, *s;
                    411:        int              flags;
                    412:
                    413:        flags = MAGIC_TEST_TEXT;
                    414:        if (iflag)
                    415:                flags |= MAGIC_TEST_MIME;
                    416:
                    417:        type = text_get_type(inf->base, inf->size);
                    418:        if (type == NULL)
                    419:                return (0);
1.14      tedu      420:
1.27      nicm      421:        s = magic_test(inf->m, inf->base, inf->size, flags);
                    422:        if (s != NULL) {
                    423:                inf->result = xstrdup(s);
                    424:                return (1);
                    425:        }
                    426:
                    427:        s = text_try_words(inf->base, inf->size, flags);
                    428:        if (s != NULL) {
                    429:                if (iflag)
                    430:                        inf->result = xstrdup(s);
1.18      chl       431:                else
1.27      nicm      432:                        xasprintf(&inf->result, "%s %s text", type, s);
                    433:                return (1);
1.18      chl       434:        }
1.14      tedu      435:
1.27      nicm      436:        if (iflag)
                    437:                inf->result = xstrdup("text/plain");
1.14      tedu      438:        else
1.27      nicm      439:                xasprintf(&inf->result, "%s text", type);
                    440:        return (1);
1.14      tedu      441: }
                    442:
1.27      nicm      443: static int
                    444: try_magic(struct input_file *inf)
1.1       deraadt   445: {
1.27      nicm      446:        const char      *s;
                    447:        int              flags;
1.1       deraadt   448:
1.27      nicm      449:        flags = 0;
                    450:        if (iflag)
                    451:                flags |= MAGIC_TEST_MIME;
                    452:
                    453:        s = magic_test(inf->m, inf->base, inf->size, flags);
                    454:        if (s != NULL) {
                    455:                inf->result = xstrdup(s);
                    456:                return (1);
1.1       deraadt   457:        }
1.27      nicm      458:        return (0);
1.14      tedu      459: }
1.1       deraadt   460:
1.27      nicm      461: static int
                    462: try_unknown(struct input_file *inf)
1.14      tedu      463: {
1.27      nicm      464:        if (iflag)
                    465:                xasprintf(&inf->result, "application/x-not-regular-file");
                    466:        else
                    467:                xasprintf(&inf->result, "data");
                    468:        return (1);
1.1       deraadt   469: }
                    470:
1.27      nicm      471: static void
                    472: test_file(struct magic *m, struct input_file *inf, int width)
1.1       deraadt   473: {
1.27      nicm      474:        int     stop;
                    475:
                    476:        inf->m = m;
                    477:
                    478:        stop = 0;
                    479:        if (!stop)
                    480:                stop = try_stat(inf);
                    481:        if (!stop)
                    482:                stop = try_access(inf);
                    483:        if (!stop)
                    484:                stop = load_file(inf);
                    485:        if (!stop)
                    486:                stop = try_empty(inf);
                    487:        if (!stop)
                    488:                stop = try_magic(inf);
                    489:        if (!stop)
                    490:                stop = try_text(inf);
                    491:        if (!stop)
                    492:                stop = try_unknown(inf);
                    493:
                    494:        if (bflag)
                    495:                printf("%s\n", inf->result);
                    496:        else
                    497:                printf("%-*s %s\n", width, inf->label, inf->result);
1.30      nicm      498:        free(inf->result);
1.27      nicm      499:
                    500:        if (inf->mapped && inf->base != NULL)
                    501:                munmap(inf->base, inf->size);
                    502:        inf->base = NULL;
                    503:
1.30      nicm      504:        if (inf->fd != -1)
                    505:                close(inf->fd);
                    506:        free((void *)inf->label);
                    507:        free((void *)inf->path);
1.1       deraadt   508: }