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

Diff for /src/usr.bin/file/file.c between version 1.59 and 1.60

version 1.59, 2017/04/18 14:16:48 version 1.60, 2017/06/28 13:37:56
Line 29 
Line 29 
 #include <errno.h>  #include <errno.h>
 #include <fcntl.h>  #include <fcntl.h>
 #include <getopt.h>  #include <getopt.h>
 #include <imsg.h>  
 #include <libgen.h>  #include <libgen.h>
 #include <limits.h>  #include <limits.h>
 #include <pwd.h>  #include <pwd.h>
 #include <stdlib.h>  #include <stdlib.h>
 #include <stdlib.h>  
 #include <string.h>  #include <string.h>
 #include <time.h>  #include <time.h>
 #include <unistd.h>  #include <unistd.h>
Line 43 
Line 41 
 #include "magic.h"  #include "magic.h"
 #include "xmalloc.h"  #include "xmalloc.h"
   
 struct input_msg {  struct input_file {
         int             idx;          struct magic    *m;
   
         struct stat     sb;          const char      *path;
         int             error;          struct stat      sb;
           int              fd;
           int              error;
   
         char            link_path[PATH_MAX];          char             link_path[PATH_MAX];
         int             link_error;          int              link_error;
         int             link_target;          int              link_target;
 };  
   
 struct input_ack {          void            *base;
         int             idx;          size_t           size;
           int              mapped;
           char            *result;
 };  };
   
 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;  extern char     *__progname;
   
 __dead void      usage(void);  __dead void      usage(void);
   
 static int       prepare_message(struct input_msg *, int, const char *);  static void      prepare_input(struct input_file *, const char *);
 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 void      read_link(struct input_file *, const char *);
   
 static __dead void child(int, pid_t, int, char **);  
   
 static void      test_file(struct input_file *, size_t);  static void      test_file(struct input_file *, size_t);
   
 static int       try_stat(struct input_file *);  static int       try_stat(struct input_file *);
Line 99 
Line 83 
 static int       sflag;  static int       sflag;
 static int       Wflag;  static int       Wflag;
   
 static char     *magicpath;  
 static FILE     *magicfp;  
   
 static struct option longopts[] = {  static struct option longopts[] = {
         { "brief",       no_argument, NULL, 'b' },          { "brief",       no_argument, NULL, 'b' },
         { "dereference", no_argument, NULL, 'L' },          { "dereference", no_argument, NULL, 'L' },
Line 120 
Line 101 
 int  int
 main(int argc, char **argv)  main(int argc, char **argv)
 {  {
         int                      opt, pair[2], fd, idx;          int                      opt, idx;
         char                    *home;          char                    *home, *magicpath;
         struct passwd           *pw;          struct passwd           *pw;
         struct imsgbuf           ibuf;          FILE                    *magicfp;
         struct imsg              imsg;          struct magic            *m;
         struct input_msg         msg;          struct input_file       *inf = NULL;
         struct input_ack        *ack;          size_t                   len, width = 0;
         pid_t                    pid, parent;  
   
         tzset();          tzset();
   
Line 193 
Line 173 
         if (magicfp == NULL)          if (magicfp == NULL)
                 err(1, "%s", magicpath);                  err(1, "%s", magicpath);
   
         parent = getpid();          if (!cflag) {
         if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)                  inf = xcalloc(argc, sizeof *inf);
                 err(1, "socketpair");                  for (idx = 0; idx < argc; idx++) {
         switch (pid = fork()) {                          len = strlen(argv[idx]) + 1;
         case -1:                          if (len > width)
                 err(1, "fork");                                  width = len;
         case 0:                          prepare_input(&inf[idx], argv[idx]);
                 close(pair[0]);                  }
                 child(pair[1], parent, argc, argv);  
         }          }
         close(pair[1]);  
   
         fclose(magicfp);          if (pledge("stdio getpw id", NULL) == -1)
         magicfp = NULL;                  err(1, "pledge");
   
         if (cflag)          if (geteuid() == 0) {
                 goto wait_for_child;                  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");
           }
   
         imsg_init(&ibuf, pair[0]);          if (pledge("stdio", NULL) == -1)
         for (idx = 0; idx < argc; idx++) {                  err(1, "pledge");
                 fd = prepare_message(&msg, idx, argv[idx]);  
                 send_message(&ibuf, &msg, sizeof msg, fd);  
   
                 if (read_message(&ibuf, &imsg, pid) == 0)          m = magic_load(magicfp, magicpath, cflag || Wflag);
                         break;          if (cflag) {
                 if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *ack)                  magic_dump(m);
                         errx(1, "message too small");                  exit(0);
                 ack = imsg.data;  
                 if (ack->idx != idx)  
                         errx(1, "index not expected");  
                 imsg_free(&imsg);  
         }          }
           fclose(magicfp);
   
 wait_for_child:          for (idx = 0; idx < argc; idx++) {
         close(pair[0]);                  inf[idx].m = m;
         while (wait(NULL) == -1 && errno != ECHILD) {                  test_file(&inf[idx], width);
                 if (errno != EINTR)  
                         err(1, "wait");  
         }          }
         _exit(0); /* let the child flush */          exit(0);
 }  }
   
 static int  static void
 prepare_message(struct input_msg *msg, int idx, const char *path)  prepare_input(struct input_file *inf, const char *path)
 {  {
         int     fd, mode, error;          int     fd, mode, error;
   
         memset(msg, 0, sizeof *msg);  
         msg->idx = idx;  
   
         if (strcmp(path, "-") == 0) {          if (strcmp(path, "-") == 0) {
                 if (fstat(STDIN_FILENO, &msg->sb) == -1) {                  if (fstat(STDIN_FILENO, &inf->sb) == -1) {
                         msg->error = errno;                          inf->error = errno;
                         return (-1);                          inf->fd = -1;
                 }                  }
                 return (STDIN_FILENO);                  inf->fd = STDIN_FILENO;
         }          }
   
         if (Lflag)          if (Lflag)
                 error = stat(path, &msg->sb);                  error = stat(path, &inf->sb);
         else          else
                 error = lstat(path, &msg->sb);                  error = lstat(path, &inf->sb);
         if (error == -1) {          if (error == -1) {
                 msg->error = errno;                  inf->error = errno;
                 return (-1);                  inf->fd = -1;
         }          }
   
         /*          /* We don't need them, so don't open directories or symlinks. */
          * pledge(2) doesn't let us pass directory file descriptors around -          mode = inf->sb.st_mode;
          * but in fact we don't need them, so just don't open directories or  
          * symlinks (which could be to directories).  
          */  
         mode = msg->sb.st_mode;  
         if (!S_ISDIR(mode) && !S_ISLNK(mode)) {          if (!S_ISDIR(mode) && !S_ISLNK(mode)) {
                 fd = open(path, O_RDONLY|O_NONBLOCK);                  fd = open(path, O_RDONLY|O_NONBLOCK);
                 if (fd == -1 && (errno == ENFILE || errno == EMFILE))                  if (fd == -1 && (errno == ENFILE || errno == EMFILE))
Line 273 
Line 246 
         } else          } else
                 fd = -1;                  fd = -1;
         if (S_ISLNK(mode))          if (S_ISLNK(mode))
                 read_link(msg, path);                  read_link(inf, path);
         return (fd);          inf->fd = fd;
           inf->path = path;
 }  }
   
 static void  static void
 send_message(struct imsgbuf *ibuf, void *msg, size_t msglen, int fd)  read_link(struct input_file *inf, const char *path)
 {  {
         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;  
   
         while ((n = imsg_read(ibuf)) == -1 && errno == EAGAIN)  
                 /* nothing */ ;  
         if (n == -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;          struct stat      sb;
         char             lpath[PATH_MAX];          char             lpath[PATH_MAX];
         char            *copy, *root;          char            *copy, *root;
Line 322 
Line 262 
   
         size = readlink(path, lpath, sizeof lpath - 1);          size = readlink(path, lpath, sizeof lpath - 1);
         if (size == -1) {          if (size == -1) {
                 msg->link_error = errno;                  inf->link_error = errno;
                 return;                  return;
         }          }
         lpath[size] = '\0';          lpath[size] = '\0';
   
         if (*lpath == '/')          if (*lpath == '/')
                 strlcpy(msg->link_path, lpath, sizeof msg->link_path);                  strlcpy(inf->link_path, lpath, sizeof inf->link_path);
         else {          else {
                 copy = xstrdup(path);                  copy = xstrdup(path);
   
                 root = dirname(copy);                  root = dirname(copy);
                 if (*root == '\0' || strcmp(root, ".") == 0 ||                  if (*root == '\0' || strcmp(root, ".") == 0 ||
                     strcmp (root, "/") == 0)                      strcmp (root, "/") == 0)
                         strlcpy(msg->link_path, lpath, sizeof msg->link_path);                          strlcpy(inf->link_path, lpath, sizeof inf->link_path);
                 else {                  else {
                         used = snprintf(msg->link_path, sizeof msg->link_path,                          used = snprintf(inf->link_path, sizeof inf->link_path,
                             "%s/%s", root, lpath);                              "%s/%s", root, lpath);
                         if (used < 0 || (size_t)used >= sizeof msg->link_path) {                          if (used < 0 || (size_t)used >= sizeof inf->link_path) {
                                 msg->link_error = ENAMETOOLONG;                                  inf->link_error = ENAMETOOLONG;
                                 free(copy);                                  free(copy);
                                 return;                                  return;
                         }                          }
Line 350 
Line 290 
         }          }
   
         if (!Lflag && stat(path, &sb) == -1)          if (!Lflag && stat(path, &sb) == -1)
                 msg->link_target = errno;                  inf->link_target = errno;
 }  }
   
 static __dead void  
 child(int fd, pid_t parent, int argc, char **argv)  
 {  
         struct passwd           *pw;  
         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;  
   
         if (pledge("stdio getpw recvfd id", NULL) == -1)  
                 err(1, "pledge");  
   
         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");  
         }  
   
         if (pledge("stdio recvfd", NULL) == -1)  
                 err(1, "pledge");  
   
         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 *  static void *
 fill_buffer(int fd, size_t size, size_t *used)  fill_buffer(int fd, size_t size, size_t *used)
 {  {
Line 461 
Line 327 
 {  {
         size_t  used;          size_t  used;
   
         if (inf->msg->sb.st_size == 0 && S_ISREG(inf->msg->sb.st_mode))          if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode))
                 return (0); /* empty file */                  return (0); /* empty file */
         if (inf->msg->sb.st_size == 0 || inf->msg->sb.st_size > FILE_READ_SIZE)          if (inf->sb.st_size == 0 || inf->sb.st_size > FILE_READ_SIZE)
                 inf->size = FILE_READ_SIZE;                  inf->size = FILE_READ_SIZE;
         else          else
                 inf->size = inf->msg->sb.st_size;                  inf->size = inf->sb.st_size;
   
         if (!S_ISREG(inf->msg->sb.st_mode))          if (!S_ISREG(inf->sb.st_mode))
                 goto try_read;                  goto try_read;
   
         inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0);          inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0);
Line 491 
Line 357 
 static int  static int
 try_stat(struct input_file *inf)  try_stat(struct input_file *inf)
 {  {
         if (inf->msg->error != 0) {          if (inf->error != 0) {
                 xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path,                  xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path,
                     strerror(inf->msg->error));                      strerror(inf->error));
                 return (1);                  return (1);
         }          }
         if (sflag || strcmp(inf->path, "-") == 0) {          if (sflag || strcmp(inf->path, "-") == 0) {
                 switch (inf->msg->sb.st_mode & S_IFMT) {                  switch (inf->sb.st_mode & S_IFMT) {
                 case S_IFIFO:                  case S_IFIFO:
                         if (strcmp(inf->path, "-") != 0)                          if (strcmp(inf->path, "-") != 0)
                                 break;                                  break;
Line 508 
Line 374 
                 }                  }
         }          }
   
         if (iflag && (inf->msg->sb.st_mode & S_IFMT) != S_IFREG) {          if (iflag && (inf->sb.st_mode & S_IFMT) != S_IFREG) {
                 xasprintf(&inf->result, "application/x-not-regular-file");                  xasprintf(&inf->result, "application/x-not-regular-file");
                 return (1);                  return (1);
         }          }
   
         switch (inf->msg->sb.st_mode & S_IFMT) {          switch (inf->sb.st_mode & S_IFMT) {
         case S_IFDIR:          case S_IFDIR:
                 xasprintf(&inf->result, "directory");                  xasprintf(&inf->result, "directory");
                 return (1);                  return (1);
         case S_IFLNK:          case S_IFLNK:
                 if (inf->msg->link_error != 0) {                  if (inf->link_error != 0) {
                         xasprintf(&inf->result, "unreadable symlink '%s' (%s)",                          xasprintf(&inf->result, "unreadable symlink '%s' (%s)",
                             inf->path, strerror(inf->msg->link_error));                              inf->path, strerror(inf->link_error));
                         return (1);                          return (1);
                 }                  }
                 if (inf->msg->link_target == ELOOP)                  if (inf->link_target == ELOOP)
                         xasprintf(&inf->result, "symbolic link in a loop");                          xasprintf(&inf->result, "symbolic link in a loop");
                 else if (inf->msg->link_target != 0) {                  else if (inf->link_target != 0) {
                         xasprintf(&inf->result, "broken symbolic link to '%s'",                          xasprintf(&inf->result, "broken symbolic link to '%s'",
                             inf->msg->link_path);                              inf->link_path);
                 } else {                  } else {
                         xasprintf(&inf->result, "symbolic link to '%s'",                          xasprintf(&inf->result, "symbolic link to '%s'",
                             inf->msg->link_path);                              inf->link_path);
                 }                  }
                 return (1);                  return (1);
         case S_IFSOCK:          case S_IFSOCK:
Line 538 
Line 404 
                 return (1);                  return (1);
         case S_IFBLK:          case S_IFBLK:
                 xasprintf(&inf->result, "block special (%ld/%ld)",                  xasprintf(&inf->result, "block special (%ld/%ld)",
                     (long)major(inf->msg->sb.st_rdev),                      (long)major(inf->sb.st_rdev),
                     (long)minor(inf->msg->sb.st_rdev));                      (long)minor(inf->sb.st_rdev));
                 return (1);                  return (1);
         case S_IFCHR:          case S_IFCHR:
                 xasprintf(&inf->result, "character special (%ld/%ld)",                  xasprintf(&inf->result, "character special (%ld/%ld)",
                     (long)major(inf->msg->sb.st_rdev),                      (long)major(inf->sb.st_rdev),
                     (long)minor(inf->msg->sb.st_rdev));                      (long)minor(inf->sb.st_rdev));
                 return (1);                  return (1);
         case S_IFIFO:          case S_IFIFO:
                 xasprintf(&inf->result, "fifo (named pipe)");                  xasprintf(&inf->result, "fifo (named pipe)");
Line 571 
Line 437 
 {  {
         char tmp[256] = "";          char tmp[256] = "";
   
         if (inf->msg->sb.st_size == 0 && S_ISREG(inf->msg->sb.st_mode))          if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode))
                 return (0); /* empty file */                  return (0); /* empty file */
         if (inf->fd != -1)          if (inf->fd != -1)
                 return (0);                  return (0);
   
         if (inf->msg->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))          if (inf->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
                 strlcat(tmp, "writable, ", sizeof tmp);                  strlcat(tmp, "writable, ", sizeof tmp);
         if (inf->msg->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))          if (inf->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
                 strlcat(tmp, "executable, ", sizeof tmp);                  strlcat(tmp, "executable, ", sizeof tmp);
         if (S_ISREG(inf->msg->sb.st_mode))          if (S_ISREG(inf->sb.st_mode))
                 strlcat(tmp, "regular file, ", sizeof tmp);                  strlcat(tmp, "regular file, ", sizeof tmp);
         strlcat(tmp, "no read permission", sizeof tmp);          strlcat(tmp, "no read permission", sizeof tmp);
   

Legend:
Removed from v.1.59  
changed lines
  Added in v.1.60