=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/file/file.c,v retrieving revision 1.34 retrieving revision 1.35 diff -c -r1.34 -r1.35 *** src/usr.bin/file/file.c 2015/04/26 22:51:32 1.34 --- src/usr.bin/file/file.c 2015/04/27 13:41:45 1.35 *************** *** 1,4 **** ! /* $OpenBSD: file.c,v 1.34 2015/04/26 22:51:32 nicm Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott --- 1,4 ---- ! /* $OpenBSD: file.c,v 1.35 2015/04/27 13:41:45 nicm Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott *************** *** 19,26 **** --- 19,31 ---- #include #include #include + #include + #include + #include + #include #include + #include #include #include #include *************** *** 32,67 **** #include "magic.h" #include "xmalloc.h" ! struct input_file { ! struct magic *m; ! const char *path; ! const char *label; ! int fd; ! struct stat sb; ! const char *error; ! void *base; ! size_t size; ! int mapped; ! char *result; ! char link_path[PATH_MAX]; ! const char *link_error; ! int link_target; }; extern char *__progname; __dead void usage(void); ! static void prepare_file(struct input_file *, const char *, int *); ! static void open_file(struct input_file *); ! static void read_link(struct input_file *); ! static void test_file(struct magic *, struct input_file *, int); static int try_stat(struct input_file *); static int try_empty(struct input_file *); static int try_access(struct input_file *); --- 37,86 ---- #include "magic.h" #include "xmalloc.h" ! struct input_msg { ! int idx; ! struct stat sb; ! int error; ! char link_path[PATH_MAX]; ! int link_error; ! int link_target; ! }; ! struct input_ack ! { ! int idx; ! }; ! struct input_file ! { ! struct magic *m; ! struct input_msg *msg; ! ! const char *path; ! int fd; ! ! void *base; ! size_t size; ! int mapped; ! char *result; }; extern char *__progname; __dead void usage(void); ! static void send_message(struct imsgbuf *, void *, size_t, int); ! static int read_message(struct imsgbuf *, struct imsg *, pid_t); + static void read_link(struct input_msg *, const char *); + + static __dead void child(int, pid_t, int, char **); + + static void test_file(struct input_file *, size_t); + static int try_stat(struct input_file *); static int try_empty(struct input_file *); static int try_access(struct input_file *); *************** *** 76,81 **** --- 95,103 ---- static int sflag; static int Wflag; + static char *magicpath; + static FILE *magicfp; + static struct option longopts[] = { { "mime", no_argument, NULL, 'i' }, { "mime-type", no_argument, NULL, 'i' }, *************** *** 92,103 **** int main(int argc, char **argv) { ! struct input_file *files = NULL; ! int opt, i, width = 0; ! FILE *f; ! struct magic *m; ! char *home, *path; struct passwd *pw; for (;;) { opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL); --- 114,127 ---- int main(int argc, char **argv) { ! int opt, pair[2], fd, idx; ! char *home; struct passwd *pw; + struct imsgbuf ibuf; + struct imsg imsg; + struct input_msg msg; + struct input_ack *ack; + pid_t pid, parent; for (;;) { opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL); *************** *** 137,143 **** } else if (argc == 0) usage(); ! f = NULL; if (geteuid() != 0 && !issetugid()) { home = getenv("HOME"); if (home == NULL || *home == '\0') { --- 161,167 ---- } else if (argc == 0) usage(); ! magicfp = NULL; if (geteuid() != 0 && !issetugid()) { home = getenv("HOME"); if (home == NULL || *home == '\0') { *************** *** 148,244 **** home = NULL; } if (home != NULL) { ! xasprintf(&path, "%s/.magic", home); ! f = fopen(path, "r"); ! if (f == NULL && errno != ENOENT) ! err(1, "%s", path); ! if (f == NULL) ! free(path); } } ! if (f == NULL) { ! path = xstrdup("/etc/magic"); ! f = fopen(path, "r"); } ! if (f == NULL) ! err(1, "%s", path); ! m = magic_load(f, path, cflag || Wflag); ! if (cflag) { ! magic_dump(m); ! exit(0); } ! files = xcalloc(argc, sizeof *files); ! for (i = 0; i < argc; i++) ! prepare_file(&files[i], argv[i], &width); ! for (i = 0; i < argc; i++) { ! open_file(&files[i]); ! test_file(m, &files[i], width); } ! exit(0); } static void ! prepare_file(struct input_file *inf, const char *path, int *width) { ! char *label; ! int n; ! ! inf->path = xstrdup(path); ! ! n = xasprintf(&label, "%s:", inf->path); ! if (n > *width) ! *width = n; ! inf->label = label; } ! static void ! open_file(struct input_file *inf) { ! int retval; ! retval = lstat(inf->path, &inf->sb); ! if (retval == -1) { ! inf->error = strerror(errno); ! return; ! } ! if (S_ISLNK(inf->sb.st_mode)) ! read_link(inf); ! inf->fd = open(inf->path, O_RDONLY|O_NONBLOCK); } static void ! read_link(struct input_file *inf) { struct stat sb; ! char path[PATH_MAX]; char *copy, *root; int used; ssize_t size; ! size = readlink(inf->path, path, sizeof path); if (size == -1) { ! inf->link_error = strerror(errno); return; } ! path[size] = '\0'; ! if (*path == '/') ! strlcpy(inf->link_path, path, sizeof inf->link_path); else { ! copy = xstrdup(inf->path); root = dirname(copy); if (*root == '\0' || strcmp(root, ".") == 0 || strcmp (root, "/") == 0) ! strlcpy(inf->link_path, path, sizeof inf->link_path); else { ! used = snprintf(inf->link_path, sizeof inf->link_path, ! "%s/%s", root, path); ! if (used < 0 || (size_t)used >= sizeof inf->link_path) { ! inf->link_error = strerror(ENAMETOOLONG); return; } } --- 172,307 ---- home = NULL; } if (home != NULL) { ! xasprintf(&magicpath, "%s/.magic", home); ! magicfp = fopen(magicpath, "r"); ! if (magicfp == NULL && errno != ENOENT) ! err(1, "%s", magicpath); ! if (magicfp == NULL) ! free(magicpath); } } ! if (magicfp == NULL) { ! magicpath = xstrdup("/etc/magic"); ! magicfp = fopen(magicpath, "r"); } ! if (magicfp == NULL) ! err(1, "%s", magicpath); ! parent = getpid(); ! if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) ! err(1, "socketpair"); ! switch (pid = fork()) { ! case 0: ! close(pair[0]); ! child(pair[1], parent, argc, argv); ! case -1: ! err(1, "fork"); } + close(pair[1]); ! fclose(magicfp); ! magicfp = NULL; ! ! if (cflag) ! goto wait_for_child; ! ! imsg_init(&ibuf, pair[0]); ! for (idx = 0; idx < argc; idx++) { ! memset(&msg, 0, sizeof msg); ! msg.idx = idx; ! ! if (lstat(argv[idx], &msg.sb) == -1) { ! fd = -1; ! msg.error = errno; ! } else { ! fd = open(argv[idx], O_RDONLY|O_NONBLOCK); ! if (fd == -1 && (errno == ENFILE || errno == EMFILE)) ! err(1, "open"); ! if (S_ISLNK(msg.sb.st_mode)) ! read_link(&msg, argv[idx]); ! } ! send_message(&ibuf, &msg, sizeof msg, fd); ! ! if (read_message(&ibuf, &imsg, pid) == 0) ! break; ! if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *ack) ! errx(1, "message too small"); ! ack = imsg.data; ! if (ack->idx != idx) ! errx(1, "index not expected"); ! imsg_free(&imsg); } ! ! wait_for_child: ! close(pair[0]); ! while (wait(NULL) == -1 && errno != ECHILD) { ! if (errno != EINTR) ! err(1, "wait"); ! } ! _exit(0); /* let the child flush */ } static void ! send_message(struct imsgbuf *ibuf, void *msg, size_t msglen, int fd) { ! if (imsg_compose(ibuf, -1, -1, 0, fd, msg, msglen) != 1) ! err(1, "imsg_compose"); ! if (imsg_flush(ibuf) != 0) ! err(1, "imsg_flush"); } ! static int ! read_message(struct imsgbuf *ibuf, struct imsg *imsg, pid_t from) { ! int n; ! if ((n = imsg_read(ibuf)) == -1) ! err(1, "imsg_read"); ! if (n == 0) ! return (0); ! if ((n = imsg_get(ibuf, imsg)) == -1) ! err(1, "imsg_get"); ! if (n == 0) ! return (0); ! ! if ((pid_t)imsg->hdr.pid != from) ! errx(1, "PIDs don't match"); ! ! return (n); ! } static void ! read_link(struct input_msg *msg, const char *path) { struct stat sb; ! char lpath[PATH_MAX]; char *copy, *root; int used; ssize_t size; ! size = readlink(path, lpath, sizeof lpath); if (size == -1) { ! msg->link_error = errno; return; } ! lpath[size] = '\0'; ! if (*lpath == '/') ! strlcpy(msg->link_path, lpath, sizeof msg->link_path); else { ! copy = xstrdup(path); root = dirname(copy); if (*root == '\0' || strcmp(root, ".") == 0 || strcmp (root, "/") == 0) ! strlcpy(msg->link_path, lpath, sizeof msg->link_path); else { ! used = snprintf(msg->link_path, sizeof msg->link_path, ! "%s/%s", root, lpath); ! if (used < 0 || (size_t)used >= sizeof msg->link_path) { ! msg->link_error = ENAMETOOLONG; return; } } *************** *** 247,260 **** } if (Lflag) { ! if (stat(inf->path, &inf->sb) == -1) ! inf->error = strerror(errno); } else { ! if (stat(inf->path, &sb) == -1) ! inf->link_target = errno; } } static void * fill_buffer(struct input_file *inf) { --- 310,391 ---- } if (Lflag) { ! if (stat(path, &msg->sb) == -1) ! msg->error = errno; } else { ! if (stat(path, &sb) == -1) ! msg->link_target = errno; } } + static __dead void + child(int fd, pid_t parent, int argc, char **argv) + { + struct magic *m; + struct imsgbuf ibuf; + struct imsg imsg; + struct input_msg *msg; + struct input_ack ack; + struct input_file inf; + int i, idx; + size_t len, width = 0; + struct passwd *pw; + + if (geteuid() == 0) { + pw = getpwnam(FILE_USER); + if (pw == NULL) + errx(1, "unknown user %s", FILE_USER); + if (setgroups(1, &pw->pw_gid) != 0) + err(1, "setgroups"); + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) + err(1, "setresgid"); + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) + err(1, "setresuid"); + } + + m = magic_load(magicfp, magicpath, cflag || Wflag); + if (cflag) { + magic_dump(m); + exit(0); + } + + for (i = 0; i < argc; i++) { + len = strlen(argv[i]) + 1; + if (len > width) + width = len; + } + + imsg_init(&ibuf, fd); + for (;;) { + if (read_message(&ibuf, &imsg, parent) == 0) + break; + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *msg) + errx(1, "message too small"); + msg = imsg.data; + + idx = msg->idx; + if (idx < 0 || idx >= argc) + errx(1, "index out of range"); + + memset(&inf, 0, sizeof inf); + inf.m = m; + inf.msg = msg; + + inf.path = argv[idx]; + inf.fd = imsg.fd; + + test_file(&inf, width); + + if (imsg.fd != -1) + close(imsg.fd); + imsg_free(&imsg); + + ack.idx = idx; + send_message(&ibuf, &ack, sizeof ack, -1); + } + exit(0); + } + static void * fill_buffer(struct input_file *inf) { *************** *** 287,297 **** static int load_file(struct input_file *inf) { ! inf->size = inf->sb.st_size; if (inf->size > FILE_READ_SIZE) inf->size = FILE_READ_SIZE; if (inf->size == 0) { ! if (!S_ISREG(inf->sb.st_mode)) inf->size = FILE_READ_SIZE; else return (0); --- 418,428 ---- static int load_file(struct input_file *inf) { ! inf->size = inf->msg->sb.st_size; if (inf->size > FILE_READ_SIZE) inf->size = FILE_READ_SIZE; if (inf->size == 0) { ! if (!S_ISREG(inf->msg->sb.st_mode)) inf->size = FILE_READ_SIZE; else return (0); *************** *** 313,325 **** static int try_stat(struct input_file *inf) { ! if (inf->error != NULL) { xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path, ! inf->error); return (1); } if (sflag) { ! switch (inf->sb.st_mode & S_IFMT) { case S_IFBLK: case S_IFCHR: case S_IFREG: --- 444,456 ---- static int try_stat(struct input_file *inf) { ! if (inf->msg->error != 0) { xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path, ! strerror(inf->msg->error)); return (1); } if (sflag) { ! switch (inf->msg->sb.st_mode & S_IFMT) { case S_IFBLK: case S_IFCHR: case S_IFREG: *************** *** 327,356 **** } } ! if (iflag && (inf->sb.st_mode & S_IFMT) != S_IFREG) { xasprintf(&inf->result, "application/x-not-regular-file"); return (1); } ! ! switch (inf->sb.st_mode & S_IFMT) { case S_IFDIR: xasprintf(&inf->result, "directory"); return (1); case S_IFLNK: ! if (inf->link_error != NULL) { xasprintf(&inf->result, "unreadable symlink '%s' (%s)", ! inf->path, inf->link_error); return (1); } ! if (inf->link_target == ELOOP) xasprintf(&inf->result, "symbolic link in a loop"); ! else if (inf->link_target != 0) { xasprintf(&inf->result, "broken symbolic link to '%s'", ! inf->link_path); } else { xasprintf(&inf->result, "symbolic link to '%s'", ! inf->link_path); } return (1); case S_IFSOCK: --- 458,486 ---- } } ! if (iflag && (inf->msg->sb.st_mode & S_IFMT) != S_IFREG) { xasprintf(&inf->result, "application/x-not-regular-file"); return (1); } ! switch (inf->msg->sb.st_mode & S_IFMT) { case S_IFDIR: xasprintf(&inf->result, "directory"); return (1); case S_IFLNK: ! if (inf->msg->link_error != 0) { xasprintf(&inf->result, "unreadable symlink '%s' (%s)", ! inf->path, strerror(inf->msg->link_error)); return (1); } ! if (inf->msg->link_target == ELOOP) xasprintf(&inf->result, "symbolic link in a loop"); ! else if (inf->msg->link_target != 0) { xasprintf(&inf->result, "broken symbolic link to '%s'", ! inf->msg->link_path); } else { xasprintf(&inf->result, "symbolic link to '%s'", ! inf->msg->link_path); } return (1); case S_IFSOCK: *************** *** 358,368 **** return (1); case S_IFBLK: xasprintf(&inf->result, "block special (%ld/%ld)", ! (long)major(inf->sb.st_rdev), (long)minor(inf->sb.st_rdev)); return (1); case S_IFCHR: xasprintf(&inf->result, "character special (%ld/%ld)", ! (long)major(inf->sb.st_rdev), (long)minor(inf->sb.st_rdev)); return (1); case S_IFIFO: xasprintf(&inf->result, "fifo (named pipe)"); --- 488,500 ---- return (1); case S_IFBLK: xasprintf(&inf->result, "block special (%ld/%ld)", ! (long)major(inf->msg->sb.st_rdev), ! (long)minor(inf->msg->sb.st_rdev)); return (1); case S_IFCHR: xasprintf(&inf->result, "character special (%ld/%ld)", ! (long)major(inf->msg->sb.st_rdev), ! (long)minor(inf->msg->sb.st_rdev)); return (1); case S_IFIFO: xasprintf(&inf->result, "fifo (named pipe)"); *************** *** 392,402 **** if (inf->fd != -1) return (0); ! if (inf->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) strlcat(tmp, "writable, ", sizeof tmp); ! if (inf->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) strlcat(tmp, "executable, ", sizeof tmp); ! if (S_ISREG(inf->sb.st_mode)) strlcat(tmp, "regular file, ", sizeof tmp); strlcat(tmp, "no read permission", sizeof tmp); --- 524,534 ---- if (inf->fd != -1) return (0); ! if (inf->msg->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) strlcat(tmp, "writable, ", sizeof tmp); ! if (inf->msg->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) strlcat(tmp, "executable, ", sizeof tmp); ! if (S_ISREG(inf->msg->sb.st_mode)) strlcat(tmp, "regular file, ", sizeof tmp); strlcat(tmp, "no read permission", sizeof tmp); *************** *** 469,480 **** } static void ! test_file(struct magic *m, struct input_file *inf, int width) { ! int stop; - inf->m = m; - stop = 0; if (!stop) stop = try_stat(inf); --- 601,611 ---- } static void ! test_file(struct input_file *inf, size_t width) { ! char *label; ! int stop; stop = 0; if (!stop) stop = try_stat(inf); *************** *** 493,508 **** if (bflag) printf("%s\n", inf->result); ! else ! printf("%-*s %s\n", width, inf->label, inf->result); free(inf->result); if (inf->mapped && inf->base != NULL) munmap(inf->base, inf->size); - inf->base = NULL; - - if (inf->fd != -1) - close(inf->fd); - free((void *)inf->label); - free((void *)inf->path); } --- 624,636 ---- if (bflag) printf("%s\n", inf->result); ! else { ! xasprintf(&label, "%s:", inf->path); ! printf("%-*s %s\n", (int)width, label, inf->result); ! free(label); ! } free(inf->result); if (inf->mapped && inf->base != NULL) munmap(inf->base, inf->size); }