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