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#ifdef WIN32 29#include <windows.h> 30#include <shlwapi.h> 31#endif 32 33#include "file.h" 34 35#ifndef lint 36FILE_RCSID("@(#)$File: magic.c,v 1.74 2011/05/26 01:27:59 christos Exp $") 37#endif /* lint */ 38 39#include "magic.h" 40 41#include <stdlib.h> 42#include <unistd.h> 43#include <string.h> 44#ifdef QUICK 45#include <sys/mman.h> 46#endif 47#ifdef HAVE_LIMITS_H 48#include <limits.h> /* for PIPE_BUF */ 49#endif 50 51#if defined(HAVE_UTIMES) 52# include <sys/time.h> 53#elif defined(HAVE_UTIME) 54# if defined(HAVE_SYS_UTIME_H) 55# include <sys/utime.h> 56# elif defined(HAVE_UTIME_H) 57# include <utime.h> 58# endif 59#endif 60 61#ifdef HAVE_UNISTD_H 62#include <unistd.h> /* for read() */ 63#endif 64 65#ifndef PIPE_BUF 66/* Get the PIPE_BUF from pathconf */ 67#ifdef _PC_PIPE_BUF 68#define PIPE_BUF pathconf(".", _PC_PIPE_BUF) 69#else 70#define PIPE_BUF 512 71#endif 72#endif 73 74private void free_mlist(struct mlist *); 75#ifndef COMPILE_ONLY 76private void close_and_restore(const struct magic_set *, const char *, int, 77 const struct stat *); 78private int unreadable_info(struct magic_set *, mode_t, const char *); 79private const char* get_default_magic(void); 80private const char *file_or_fd(struct magic_set *, const char *, int); 81#endif 82 83#ifndef STDIN_FILENO 84#define STDIN_FILENO 0 85#endif 86 87private const char * 88get_default_magic(void) 89{ 90 static const char hmagic[] = "/.magic/magic.mgc"; 91 static char *default_magic; 92 char *home, *hmagicpath; 93 94#ifndef WIN32 95 struct stat st; 96 97 if (default_magic) { 98 free(default_magic); 99 default_magic = NULL; 100 } 101 if ((home = getenv("HOME")) == NULL) 102 return MAGIC; 103 104 if (asprintf(&hmagicpath, "%s/.magic", home) < 0) 105 return MAGIC; 106 if (stat(hmagicpath, &st) == -1) 107 goto out; 108 if (S_ISDIR(st.st_mode)) { 109 free(hmagicpath); 110 if (asprintf(&hmagicpath, "%s/%s", home, hmagic) < 0) 111 return MAGIC; 112 if (access(hmagicpath, R_OK) == -1) 113 goto out; 114 } 115 116 if (asprintf(&default_magic, "%s:%s", hmagicpath, MAGIC) < 0) 117 goto out; 118 free(hmagicpath); 119 return default_magic; 120out: 121 default_magic = NULL; 122 free(hmagicpath); 123 return MAGIC; 124#else 125 char *hmagicp = hmagicpath; 126 char *tmppath = NULL; 127 128#define APPENDPATH() \ 129 do { \ 130 if (tmppath && access(tmppath, R_OK) != -1) { \ 131 if (hmagicpath == NULL) \ 132 hmagicpath = tmppath; \ 133 else { \ 134 if (asprintf(&hmagicp, "%s%c%s", hmagicpath, \ 135 PATHSEP, tmppath) >= 0) { \ 136 free(hmagicpath); \ 137 hmagicpath = hmagicp; \ 138 } \ 139 free(tmppath); \ 140 } \ 141 tmppath = NULL; \ 142 } \ 143 } while (/*CONSTCOND*/0) 144 145 if (default_magic) { 146 free(default_magic); 147 default_magic = NULL; 148 } 149 150 /* First, try to get user-specific magic file */ 151 if ((home = getenv("LOCALAPPDATA")) == NULL) { 152 if ((home = getenv("USERPROFILE")) != NULL) 153 if (asprintf(&tmppath, 154 "%s/Local Settings/Application Data%s", home, 155 hmagic) < 0) 156 tmppath = NULL; 157 } else { 158 if (asprintf(&tmppath, "%s%s", home, hmagic) < 0) 159 tmppath = NULL; 160 } 161 162 APPENDPATH(); 163 164 /* Second, try to get a magic file from Common Files */ 165 if ((home = getenv("COMMONPROGRAMFILES")) != NULL) { 166 if (asprintf(&tmppath, "%s%s", home, hmagic) >= 0) 167 APPENDPATH(); 168 } 169 170 /* Third, try to get magic file relative to dll location */ 171 LPTSTR dllpath = malloc(sizeof(*dllpath) * (MAX_PATH + 1)); 172 dllpath[MAX_PATH] = 0; /* just in case long path gets truncated and not null terminated */ 173 if (GetModuleFileNameA(NULL, dllpath, MAX_PATH)){ 174 PathRemoveFileSpecA(dllpath); 175 if (strlen(dllpath) > 3 && 176 stricmp(&dllpath[strlen(dllpath) - 3], "bin") == 0) { 177 if (asprintf(&tmppath, 178 "%s/../share/misc/magic.mgc", dllpath) >= 0) 179 APPENDPATH(); 180 } else { 181 if (asprintf(&tmppath, 182 "%s/share/misc/magic.mgc", dllpath) >= 0) 183 APPENDPATH(); 184 else if (asprintf(&tmppath, 185 "%s/magic.mgc", dllpath) >= 0) 186 APPENDPATH(); 187 } 188 } 189 190 /* Don't put MAGIC constant - it likely points to a file within MSys 191 tree */ 192 default_magic = hmagicpath; 193 return default_magic; 194#endif 195} 196 197public const char * 198magic_getpath(const char *magicfile, int action) 199{ 200 if (magicfile != NULL) 201 return magicfile; 202 203 magicfile = getenv("MAGIC"); 204 if (magicfile != NULL) 205 return magicfile; 206 207 return action == FILE_LOAD ? get_default_magic() : MAGIC; 208} 209 210public struct magic_set * 211magic_open(int flags) 212{ 213 struct magic_set *ms; 214 size_t len; 215 216 if ((ms = CAST(struct magic_set *, calloc((size_t)1, 217 sizeof(struct magic_set)))) == NULL) 218 return NULL; 219 220 if (magic_setflags(ms, flags) == -1) { 221 errno = EINVAL; 222 goto free; 223 } 224 225 ms->o.buf = ms->o.pbuf = NULL; 226 len = (ms->c.len = 10) * sizeof(*ms->c.li); 227 228 if ((ms->c.li = CAST(struct level_info *, malloc(len))) == NULL) 229 goto free; 230 231 ms->event_flags = 0; 232 ms->error = -1; 233 ms->mlist = NULL; 234 ms->file = "unknown"; 235 ms->line = 0; 236 return ms; 237free: 238 free(ms); 239 return NULL; 240} 241 242private void 243free_mlist(struct mlist *mlist) 244{ 245 struct mlist *ml; 246 247 if (mlist == NULL) 248 return; 249 250 for (ml = mlist->next; ml != mlist;) { 251 struct mlist *next = ml->next; 252 struct magic *mg = ml->magic; 253 file_delmagic(mg, ml->mapped, ml->nmagic); 254 free(ml); 255 ml = next; 256 } 257 free(ml); 258} 259 260#ifndef COMPILE_ONLY 261private int 262unreadable_info(struct magic_set *ms, mode_t md, const char *file) 263{ 264 /* We cannot open it, but we were able to stat it. */ 265 if (access(file, W_OK) == 0) 266 if (file_printf(ms, "writable, ") == -1) 267 return -1; 268 if (access(file, X_OK) == 0) 269 if (file_printf(ms, "executable, ") == -1) 270 return -1; 271 if (S_ISREG(md)) 272 if (file_printf(ms, "regular file, ") == -1) 273 return -1; 274 if (file_printf(ms, "no read permission") == -1) 275 return -1; 276 return 0; 277} 278#endif 279 280public void 281magic_close(struct magic_set *ms) 282{ 283 free_mlist(ms->mlist); 284 free(ms->o.pbuf); 285 free(ms->o.buf); 286 free(ms->c.li); 287 free(ms); 288} 289 290/* 291 * load a magic file 292 */ 293public int 294magic_load(struct magic_set *ms, const char *magicfile) 295{ 296 struct mlist *ml = file_apprentice(ms, magicfile, FILE_LOAD); 297 if (ml) { 298 free_mlist(ms->mlist); 299 ms->mlist = ml; 300 return 0; 301 } 302 return -1; 303} 304 305public int 306magic_compile(struct magic_set *ms, const char *magicfile) 307{ 308 struct mlist *ml = file_apprentice(ms, magicfile, FILE_COMPILE); 309 free_mlist(ml); 310 return ml ? 0 : -1; 311} 312 313public int 314magic_check(struct magic_set *ms, const char *magicfile) 315{ 316 struct mlist *ml = file_apprentice(ms, magicfile, FILE_CHECK); 317 free_mlist(ml); 318 return ml ? 0 : -1; 319} 320 321public int 322magic_list(struct magic_set *ms, const char *magicfile) 323{ 324 struct mlist *ml = file_apprentice(ms, magicfile, FILE_LIST); 325 free_mlist(ml); 326 return ml ? 0 : -1; 327} 328 329#ifndef COMPILE_ONLY 330private void 331close_and_restore(const struct magic_set *ms, const char *name, int fd, 332 const struct stat *sb) 333{ 334 if (fd == STDIN_FILENO) 335 return; 336 (void) close(fd); 337 338 if ((ms->flags & MAGIC_PRESERVE_ATIME) != 0) { 339 /* 340 * Try to restore access, modification times if read it. 341 * This is really *bad* because it will modify the status 342 * time of the file... And of course this will affect 343 * backup programs 344 */ 345#ifdef HAVE_UTIMES 346 struct timeval utsbuf[2]; 347 (void)memset(utsbuf, 0, sizeof(utsbuf)); 348 utsbuf[0].tv_sec = sb->st_atime; 349 utsbuf[1].tv_sec = sb->st_mtime; 350 351 (void) utimes(name, utsbuf); /* don't care if loses */ 352#elif defined(HAVE_UTIME_H) || defined(HAVE_SYS_UTIME_H) 353 struct utimbuf utbuf; 354 355 (void)memset(&utbuf, 0, sizeof(utbuf)); 356 utbuf.actime = sb->st_atime; 357 utbuf.modtime = sb->st_mtime; 358 (void) utime(name, &utbuf); /* don't care if loses */ 359#endif 360 } 361} 362 363 364/* 365 * find type of descriptor 366 */ 367public const char * 368magic_descriptor(struct magic_set *ms, int fd) 369{ 370 return file_or_fd(ms, NULL, fd); 371} 372 373/* 374 * find type of named file 375 */ 376public const char * 377magic_file(struct magic_set *ms, const char *inname) 378{ 379 return file_or_fd(ms, inname, STDIN_FILENO); 380} 381 382private const char * 383file_or_fd(struct magic_set *ms, const char *inname, int fd) 384{ 385 int rv = -1; 386 unsigned char *buf; 387 struct stat sb; 388 ssize_t nbytes = 0; /* number of bytes read from a datafile */ 389 int ispipe = 0; 390 391 /* 392 * one extra for terminating '\0', and 393 * some overlapping space for matches near EOF 394 */ 395#define SLOP (1 + sizeof(union VALUETYPE)) 396 if ((buf = CAST(unsigned char *, malloc(HOWMANY + SLOP))) == NULL) 397 return NULL; 398 399 if (file_reset(ms) == -1) 400 goto done; 401 402 switch (file_fsmagic(ms, inname, &sb)) { 403 case -1: /* error */ 404 goto done; 405 case 0: /* nothing found */ 406 break; 407 default: /* matched it and printed type */ 408 rv = 0; 409 goto done; 410 } 411 412 if (inname == NULL) { 413 if (fstat(fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) 414 ispipe = 1; 415 } else { 416 int flags = O_RDONLY|O_BINARY; 417 418 if (stat(inname, &sb) == 0 && S_ISFIFO(sb.st_mode)) { 419#ifdef O_NONBLOCK 420 flags |= O_NONBLOCK; 421#endif 422 ispipe = 1; 423 } 424 425 errno = 0; 426 if ((fd = open(inname, flags)) < 0) { 427 if (unreadable_info(ms, sb.st_mode, inname) == -1) 428 goto done; 429 rv = 0; 430 goto done; 431 } 432#ifdef O_NONBLOCK 433 if ((flags = fcntl(fd, F_GETFL)) != -1) { 434 flags &= ~O_NONBLOCK; 435 (void)fcntl(fd, F_SETFL, flags); 436 } 437#endif 438 } 439 440 /* 441 * try looking at the first HOWMANY bytes 442 */ 443 if (ispipe) { 444 ssize_t r = 0; 445 446 while ((r = sread(fd, (void *)&buf[nbytes], 447 (size_t)(HOWMANY - nbytes), 1)) > 0) { 448 nbytes += r; 449 if (r < PIPE_BUF) break; 450 } 451 452 if (nbytes == 0) { 453 /* We can not read it, but we were able to stat it. */ 454 if (unreadable_info(ms, sb.st_mode, inname) == -1) 455 goto done; 456 rv = 0; 457 goto done; 458 } 459 460 } else { 461 if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) { 462 file_error(ms, errno, "cannot read `%s'", inname); 463 goto done; 464 } 465 } 466 467 (void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */ 468 if (file_buffer(ms, fd, inname, buf, (size_t)nbytes) == -1) 469 goto done; 470 rv = 0; 471done: 472 free(buf); 473 close_and_restore(ms, inname, fd, &sb); 474 return rv == 0 ? file_getbuffer(ms) : NULL; 475} 476 477 478public const char * 479magic_buffer(struct magic_set *ms, const void *buf, size_t nb) 480{ 481 if (file_reset(ms) == -1) 482 return NULL; 483 /* 484 * The main work is done here! 485 * We have the file name and/or the data buffer to be identified. 486 */ 487 if (file_buffer(ms, -1, NULL, buf, nb) == -1) { 488 return NULL; 489 } 490 return file_getbuffer(ms); 491} 492#endif /* COMPILE_ONLY */ 493 494public const char * 495magic_error(struct magic_set *ms) 496{ 497 return (ms->event_flags & EVENT_HAD_ERR) ? ms->o.buf : NULL; 498} 499 500public int 501magic_errno(struct magic_set *ms) 502{ 503 return (ms->event_flags & EVENT_HAD_ERR) ? ms->error : 0; 504} 505 506public int 507magic_setflags(struct magic_set *ms, int flags) 508{ 509#if !defined(HAVE_UTIME) && !defined(HAVE_UTIMES) 510 if (flags & MAGIC_PRESERVE_ATIME) 511 return -1; 512#endif 513 ms->flags = flags; 514 return 0; 515} 516