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