magic.c revision 169962
1/* 2 * Copyright (c) Christos Zoulas 2003. 3 * All Rights Reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice immediately at the beginning of the file, without modification, 10 * this list of conditions, and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 19 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include "file.h" 29#include "magic.h" 30 31#include <stdio.h> 32#include <stdlib.h> 33#include <unistd.h> 34#include <string.h> 35#include <sys/types.h> 36#include <sys/param.h> /* for MAXPATHLEN */ 37#include <sys/stat.h> 38#ifdef QUICK 39#include <sys/mman.h> 40#endif 41#include <limits.h> /* for PIPE_BUF */ 42 43#if defined(HAVE_UTIMES) 44# include <sys/time.h> 45#elif defined(HAVE_UTIME) 46# if defined(HAVE_SYS_UTIME_H) 47# include <sys/utime.h> 48# elif defined(HAVE_UTIME_H) 49# include <utime.h> 50# endif 51#endif 52 53#ifdef HAVE_UNISTD_H 54#include <unistd.h> /* for read() */ 55#endif 56 57#ifdef HAVE_LOCALE_H 58#include <locale.h> 59#endif 60 61#include <netinet/in.h> /* for byte swapping */ 62 63#include "patchlevel.h" 64 65#ifndef lint 66FILE_RCSID("@(#)$File: magic.c,v 1.41 2007/03/26 17:59:50 christos Exp $") 67#endif /* lint */ 68 69#ifdef __EMX__ 70private char *apptypeName = NULL; 71protected int file_os2_apptype(struct magic_set *ms, const char *fn, 72 const void *buf, size_t nb); 73#endif /* __EMX__ */ 74 75private void free_mlist(struct mlist *); 76private void close_and_restore(const struct magic_set *, const char *, int, 77 const struct stat *); 78private int info_from_stat(struct magic_set *, mode_t); 79 80#ifndef STDIN_FILENO 81#define STDIN_FILENO 0 82#endif 83 84public struct magic_set * 85magic_open(int flags) 86{ 87 struct magic_set *ms; 88 89 if ((ms = calloc((size_t)1, sizeof(struct magic_set))) == NULL) 90 return NULL; 91 92 if (magic_setflags(ms, flags) == -1) { 93 errno = EINVAL; 94 goto free1; 95 } 96 97 ms->o.ptr = ms->o.buf = malloc(ms->o.left = ms->o.size = 1024); 98 if (ms->o.buf == NULL) 99 goto free1; 100 101 ms->o.pbuf = malloc(ms->o.psize = 1024); 102 if (ms->o.pbuf == NULL) 103 goto free2; 104 105 ms->c.li = malloc((ms->c.len = 10) * sizeof(*ms->c.li)); 106 if (ms->c.li == NULL) 107 goto free3; 108 109 ms->haderr = 0; 110 ms->error = -1; 111 ms->mlist = NULL; 112 ms->file = "unknown"; 113 ms->line = 0; 114 return ms; 115free3: 116 free(ms->o.pbuf); 117free2: 118 free(ms->o.buf); 119free1: 120 free(ms); 121 return NULL; 122} 123 124private void 125free_mlist(struct mlist *mlist) 126{ 127 struct mlist *ml; 128 129 if (mlist == NULL) 130 return; 131 132 for (ml = mlist->next; ml != mlist;) { 133 struct mlist *next = ml->next; 134 struct magic *mg = ml->magic; 135 file_delmagic(mg, ml->mapped, ml->nmagic); 136 free(ml); 137 ml = next; 138 } 139 free(ml); 140} 141 142private int 143info_from_stat(struct magic_set *ms, mode_t md) 144{ 145 /* We cannot open it, but we were able to stat it. */ 146 if (md & 0222) 147 if (file_printf(ms, "writable, ") == -1) 148 return -1; 149 if (md & 0111) 150 if (file_printf(ms, "executable, ") == -1) 151 return -1; 152 if (S_ISREG(md)) 153 if (file_printf(ms, "regular file, ") == -1) 154 return -1; 155 if (file_printf(ms, "no read permission") == -1) 156 return -1; 157 return 0; 158} 159 160public void 161magic_close(struct magic_set *ms) 162{ 163 free_mlist(ms->mlist); 164 free(ms->o.pbuf); 165 free(ms->o.buf); 166 free(ms->c.li); 167 free(ms); 168} 169 170/* 171 * load a magic file 172 */ 173public int 174magic_load(struct magic_set *ms, const char *magicfile) 175{ 176 struct mlist *ml = file_apprentice(ms, magicfile, FILE_LOAD); 177 if (ml) { 178 free_mlist(ms->mlist); 179 ms->mlist = ml; 180 return 0; 181 } 182 return -1; 183} 184 185public int 186magic_compile(struct magic_set *ms, const char *magicfile) 187{ 188 struct mlist *ml = file_apprentice(ms, magicfile, FILE_COMPILE); 189 free_mlist(ml); 190 return ml ? 0 : -1; 191} 192 193public int 194magic_check(struct magic_set *ms, const char *magicfile) 195{ 196 struct mlist *ml = file_apprentice(ms, magicfile, FILE_CHECK); 197 free_mlist(ml); 198 return ml ? 0 : -1; 199} 200 201private void 202close_and_restore(const struct magic_set *ms, const char *name, int fd, 203 const struct stat *sb) 204{ 205 if (fd == STDIN_FILENO) 206 return; 207 (void) close(fd); 208 209 if ((ms->flags & MAGIC_PRESERVE_ATIME) != 0) { 210 /* 211 * Try to restore access, modification times if read it. 212 * This is really *bad* because it will modify the status 213 * time of the file... And of course this will affect 214 * backup programs 215 */ 216#ifdef HAVE_UTIMES 217 struct timeval utsbuf[2]; 218 utsbuf[0].tv_sec = sb->st_atime; 219 utsbuf[1].tv_sec = sb->st_mtime; 220 221 (void) utimes(name, utsbuf); /* don't care if loses */ 222#elif defined(HAVE_UTIME_H) || defined(HAVE_SYS_UTIME_H) 223 struct utimbuf utbuf; 224 225 utbuf.actime = sb->st_atime; 226 utbuf.modtime = sb->st_mtime; 227 (void) utime(name, &utbuf); /* don't care if loses */ 228#endif 229 } 230} 231 232#ifndef COMPILE_ONLY 233/* 234 * find type of named file 235 */ 236public const char * 237magic_file(struct magic_set *ms, const char *inname) 238{ 239 int fd = 0; 240 int rv = -1; 241 unsigned char *buf; 242 struct stat sb; 243 ssize_t nbytes = 0; /* number of bytes read from a datafile */ 244 int ispipe = 0; 245 246 /* 247 * one extra for terminating '\0', and 248 * some overlapping space for matches near EOF 249 */ 250#define SLOP (1 + sizeof(union VALUETYPE)) 251 if ((buf = malloc(HOWMANY + SLOP)) == NULL) 252 return NULL; 253 254 if (file_reset(ms) == -1) 255 goto done; 256 257 switch (file_fsmagic(ms, inname, &sb)) { 258 case -1: /* error */ 259 goto done; 260 case 0: /* nothing found */ 261 break; 262 default: /* matched it and printed type */ 263 rv = 0; 264 goto done; 265 } 266 267 if (inname == NULL) { 268 fd = STDIN_FILENO; 269 if (fstat(fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) 270 ispipe = 1; 271 } else { 272 int flags = O_RDONLY|O_BINARY; 273 274 if (stat(inname, &sb) == 0 && S_ISFIFO(sb.st_mode)) { 275 flags |= O_NONBLOCK; 276 ispipe = 1; 277 } 278 279 errno = 0; 280 if ((fd = open(inname, flags)) < 0) { 281#ifdef __CYGWIN__ 282 char *tmp = alloca(strlen(inname) + 5); 283 (void)strcat(strcpy(tmp, inname), ".exe"); 284 if ((fd = open(tmp, flags)) < 0) { 285#endif 286 if (info_from_stat(ms, sb.st_mode) == -1) 287 goto done; 288 rv = 0; 289 goto done; 290#ifdef __CYGWIN__ 291 } 292#endif 293 } 294#ifdef O_NONBLOCK 295 if ((flags = fcntl(fd, F_GETFL)) != -1) { 296 flags &= ~O_NONBLOCK; 297 (void)fcntl(fd, F_SETFL, flags); 298 } 299#endif 300 } 301 302 /* 303 * try looking at the first HOWMANY bytes 304 */ 305 if (ispipe) { 306 ssize_t r = 0; 307 308 while ((r = sread(fd, (void *)&buf[nbytes], 309 (size_t)(HOWMANY - nbytes), 1)) > 0) { 310 nbytes += r; 311 if (r < PIPE_BUF) break; 312 } 313 314 if (nbytes == 0) { 315 /* We can not read it, but we were able to stat it. */ 316 if (info_from_stat(ms, sb.st_mode) == -1) 317 goto done; 318 rv = 0; 319 goto done; 320 } 321 322 } else { 323 if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) { 324 file_error(ms, errno, "cannot read `%s'", inname); 325 goto done; 326 } 327 } 328 329 if (nbytes == 0) { 330 if (file_printf(ms, (ms->flags & MAGIC_MIME) ? 331 "application/x-empty" : "empty") == -1) 332 goto done; 333 } else if (nbytes == 1) { 334 if (file_printf(ms, "very short file (no magic)") == -1) 335 goto done; 336 } else { 337 (void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */ 338 if (file_buffer(ms, fd, inname, buf, (size_t)nbytes) == -1) 339 goto done; 340 } 341 rv = 0; 342done: 343 free(buf); 344 close_and_restore(ms, inname, fd, &sb); 345 return rv == 0 ? file_getbuffer(ms) : NULL; 346} 347 348 349public const char * 350magic_buffer(struct magic_set *ms, const void *buf, size_t nb) 351{ 352 if (file_reset(ms) == -1) 353 return NULL; 354 /* 355 * The main work is done here! 356 * We have the file name and/or the data buffer to be identified. 357 */ 358 if (file_buffer(ms, -1, NULL, buf, nb) == -1) { 359 return NULL; 360 } 361 return file_getbuffer(ms); 362} 363#endif 364 365public const char * 366magic_error(struct magic_set *ms) 367{ 368 return ms->haderr ? ms->o.buf : NULL; 369} 370 371public int 372magic_errno(struct magic_set *ms) 373{ 374 return ms->haderr ? ms->error : 0; 375} 376 377public int 378magic_setflags(struct magic_set *ms, int flags) 379{ 380#if !defined(HAVE_UTIME) && !defined(HAVE_UTIMES) 381 if (flags & MAGIC_PRESERVE_ATIME) 382 return -1; 383#endif 384 ms->flags = flags; 385 return 0; 386} 387