xref: /netbsd-src/external/bsd/file/dist/src/magic.c (revision 6cf6fe02a981b55727c49c3d37b0d8191a98c0ee)
1 /*	$NetBSD: magic.c,v 1.8 2014/06/13 02:08:06 christos Exp $	*/
2 /*
3  * Copyright (c) Christos Zoulas 2003.
4  * All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice immediately at the beginning of the file, without modification,
11  *    this list of conditions, and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #ifdef WIN32
30 #include <windows.h>
31 #include <shlwapi.h>
32 #endif
33 
34 #include "file.h"
35 
36 #ifndef	lint
37 #if 0
38 FILE_RCSID("@(#)$File: magic.c,v 1.84 2014/05/14 23:15:42 christos Exp $")
39 #else
40 __RCSID("$NetBSD: magic.c,v 1.8 2014/06/13 02:08:06 christos Exp $");
41 #endif
42 #endif	/* lint */
43 
44 #include "magic.h"
45 
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <string.h>
49 #ifdef QUICK
50 #include <sys/mman.h>
51 #endif
52 #ifdef HAVE_LIMITS_H
53 #include <limits.h>	/* for PIPE_BUF */
54 #endif
55 
56 #if defined(HAVE_UTIMES)
57 # include <sys/time.h>
58 #elif defined(HAVE_UTIME)
59 # if defined(HAVE_SYS_UTIME_H)
60 #  include <sys/utime.h>
61 # elif defined(HAVE_UTIME_H)
62 #  include <utime.h>
63 # endif
64 #endif
65 
66 #ifdef HAVE_UNISTD_H
67 #include <unistd.h>	/* for read() */
68 #endif
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 close_and_restore(const struct magic_set *, const char *, int,
80     const struct stat *);
81 private int unreadable_info(struct magic_set *, mode_t, const char *);
82 private const char* get_default_magic(void);
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 private const char *
92 get_default_magic(void)
93 {
94 	static const char hmagic[] = "/.magic/magic.mgc";
95 	static char *default_magic;
96 	char *home, *hmagicpath;
97 
98 #ifndef WIN32
99 	struct stat st;
100 
101 	if (default_magic) {
102 		free(default_magic);
103 		default_magic = NULL;
104 	}
105 	if ((home = getenv("HOME")) == NULL)
106 		return MAGIC;
107 
108 	if (asprintf(&hmagicpath, "%s/.magic.mgc", home) < 0)
109 		return MAGIC;
110 	if (stat(hmagicpath, &st) == -1) {
111 		free(hmagicpath);
112 		if (asprintf(&hmagicpath, "%s/.magic", home) < 0)
113 			return MAGIC;
114 		if (stat(hmagicpath, &st) == -1)
115 			goto out;
116 		if (S_ISDIR(st.st_mode)) {
117 			free(hmagicpath);
118 			if (asprintf(&hmagicpath, "%s/%s", home, hmagic) < 0)
119 				return MAGIC;
120 			if (access(hmagicpath, R_OK) == -1)
121 				goto out;
122 		}
123 	}
124 
125 	if (asprintf(&default_magic, "%s:%s", hmagicpath, MAGIC) < 0)
126 		goto out;
127 	free(hmagicpath);
128 	return default_magic;
129 out:
130 	default_magic = NULL;
131 	free(hmagicpath);
132 	return MAGIC;
133 #else
134 	char *hmagicp;
135 	char *tmppath = NULL;
136 	hmagicpath = NULL;
137 
138 #define APPENDPATH() \
139 	do { \
140 		if (tmppath && access(tmppath, R_OK) != -1) { \
141 			if (hmagicpath == NULL) \
142 				hmagicpath = tmppath; \
143 			else { \
144 				if (asprintf(&hmagicp, "%s%c%s", hmagicpath, \
145 				    PATHSEP, tmppath) >= 0) { \
146 					free(hmagicpath); \
147 					hmagicpath = hmagicp; \
148 				} \
149 				free(tmppath); \
150 			} \
151 			tmppath = NULL; \
152 		} \
153 	} while (/*CONSTCOND*/0)
154 
155 	if (default_magic) {
156 		free(default_magic);
157 		default_magic = NULL;
158 	}
159 
160 	/* First, try to get user-specific magic file */
161 	if ((home = getenv("LOCALAPPDATA")) == NULL) {
162 		if ((home = getenv("USERPROFILE")) != NULL)
163 			if (asprintf(&tmppath,
164 			    "%s/Local Settings/Application Data%s", home,
165 			    hmagic) < 0)
166 				tmppath = NULL;
167 	} else {
168 		if (asprintf(&tmppath, "%s%s", home, hmagic) < 0)
169 			tmppath = NULL;
170 	}
171 
172 	APPENDPATH();
173 
174 	/* Second, try to get a magic file from Common Files */
175 	if ((home = getenv("COMMONPROGRAMFILES")) != NULL) {
176 		if (asprintf(&tmppath, "%s%s", home, hmagic) >= 0)
177 			APPENDPATH();
178 	}
179 
180 	/* Third, try to get magic file relative to dll location */
181 	LPTSTR dllpath = malloc(sizeof(*dllpath) * (MAX_PATH + 1));
182 	dllpath[MAX_PATH] = 0;	/* just in case long path gets truncated and not null terminated */
183 	if (GetModuleFileNameA(NULL, dllpath, MAX_PATH)){
184 		PathRemoveFileSpecA(dllpath);
185 		if (strlen(dllpath) > 3 &&
186 		    stricmp(&dllpath[strlen(dllpath) - 3], "bin") == 0) {
187 			if (asprintf(&tmppath,
188 			    "%s/../share/misc/magic.mgc", dllpath) >= 0)
189 				APPENDPATH();
190 		} else {
191 			if (asprintf(&tmppath,
192 			    "%s/share/misc/magic.mgc", dllpath) >= 0)
193 				APPENDPATH();
194 			else if (asprintf(&tmppath,
195 			    "%s/magic.mgc", dllpath) >= 0)
196 				APPENDPATH();
197 		}
198 	}
199 
200 	/* Don't put MAGIC constant - it likely points to a file within MSys
201 	tree */
202 	default_magic = hmagicpath;
203 	return default_magic;
204 #endif
205 }
206 
207 public const char *
208 magic_getpath(const char *magicfile, int action)
209 {
210 	if (magicfile != NULL)
211 		return magicfile;
212 
213 	magicfile = getenv("MAGIC");
214 	if (magicfile != NULL)
215 		return magicfile;
216 
217 	return action == FILE_LOAD ? get_default_magic() : MAGIC;
218 }
219 
220 public struct magic_set *
221 magic_open(int flags)
222 {
223 	return file_ms_alloc(flags);
224 }
225 
226 private int
227 unreadable_info(struct magic_set *ms, mode_t md, const char *file)
228 {
229 	if (file) {
230 		/* We cannot open it, but we were able to stat it. */
231 		if (access(file, W_OK) == 0)
232 			if (file_printf(ms, "writable, ") == -1)
233 				return -1;
234 		if (access(file, X_OK) == 0)
235 			if (file_printf(ms, "executable, ") == -1)
236 				return -1;
237 	}
238 	if (S_ISREG(md))
239 		if (file_printf(ms, "regular file, ") == -1)
240 			return -1;
241 	if (file_printf(ms, "no read permission") == -1)
242 		return -1;
243 	return 0;
244 }
245 
246 public void
247 magic_close(struct magic_set *ms)
248 {
249 	if (ms == NULL)
250 		return;
251 	file_ms_free(ms);
252 }
253 
254 /*
255  * load a magic file
256  */
257 public int
258 magic_load(struct magic_set *ms, const char *magicfile)
259 {
260 	if (ms == NULL)
261 		return -1;
262 	return file_apprentice(ms, magicfile, FILE_LOAD);
263 }
264 
265 public int
266 magic_compile(struct magic_set *ms, const char *magicfile)
267 {
268 	if (ms == NULL)
269 		return -1;
270 	return file_apprentice(ms, magicfile, FILE_COMPILE);
271 }
272 
273 public int
274 magic_check(struct magic_set *ms, const char *magicfile)
275 {
276 	if (ms == NULL)
277 		return -1;
278 	return file_apprentice(ms, magicfile, FILE_CHECK);
279 }
280 
281 public int
282 magic_list(struct magic_set *ms, const char *magicfile)
283 {
284 	if (ms == NULL)
285 		return -1;
286 	return file_apprentice(ms, magicfile, FILE_LIST);
287 }
288 
289 private void
290 close_and_restore(const struct magic_set *ms, const char *name, int fd,
291     const struct stat *sb)
292 {
293 	if (fd == STDIN_FILENO || name == NULL)
294 		return;
295 	(void) close(fd);
296 
297 	if ((ms->flags & MAGIC_PRESERVE_ATIME) != 0) {
298 		/*
299 		 * Try to restore access, modification times if read it.
300 		 * This is really *bad* because it will modify the status
301 		 * time of the file... And of course this will affect
302 		 * backup programs
303 		 */
304 #ifdef HAVE_UTIMES
305 		struct timeval  utsbuf[2];
306 		(void)memset(utsbuf, 0, sizeof(utsbuf));
307 		utsbuf[0].tv_sec = sb->st_atime;
308 		utsbuf[1].tv_sec = sb->st_mtime;
309 
310 		(void) utimes(name, utsbuf); /* don't care if loses */
311 #elif defined(HAVE_UTIME_H) || defined(HAVE_SYS_UTIME_H)
312 		struct utimbuf  utbuf;
313 
314 		(void)memset(&utbuf, 0, sizeof(utbuf));
315 		utbuf.actime = sb->st_atime;
316 		utbuf.modtime = sb->st_mtime;
317 		(void) utime(name, &utbuf); /* don't care if loses */
318 #endif
319 	}
320 }
321 
322 #ifndef COMPILE_ONLY
323 
324 /*
325  * find type of descriptor
326  */
327 public const char *
328 magic_descriptor(struct magic_set *ms, int fd)
329 {
330 	if (ms == NULL)
331 		return NULL;
332 	return file_or_fd(ms, NULL, fd);
333 }
334 
335 /*
336  * find type of named file
337  */
338 public const char *
339 magic_file(struct magic_set *ms, const char *inname)
340 {
341 	if (ms == NULL)
342 		return NULL;
343 	return file_or_fd(ms, inname, STDIN_FILENO);
344 }
345 
346 private const char *
347 file_or_fd(struct magic_set *ms, const char *inname, int fd)
348 {
349 	int	rv = -1;
350 	unsigned char *buf;
351 	struct stat	sb;
352 	ssize_t nbytes = 0;	/* number of bytes read from a datafile */
353 	int	ispipe = 0;
354 	off_t	pos = (off_t)-1;
355 
356 	if (file_reset(ms) == -1)
357 		goto out;
358 
359 	/*
360 	 * one extra for terminating '\0', and
361 	 * some overlapping space for matches near EOF
362 	 */
363 #define SLOP (1 + sizeof(union VALUETYPE))
364 	if ((buf = CAST(unsigned char *, malloc(HOWMANY + SLOP))) == NULL)
365 		return NULL;
366 
367 	switch (file_fsmagic(ms, inname, &sb)) {
368 	case -1:		/* error */
369 		goto done;
370 	case 0:			/* nothing found */
371 		break;
372 	default:		/* matched it and printed type */
373 		rv = 0;
374 		goto done;
375 	}
376 
377 #ifdef WIN32
378 	/* Place stdin in binary mode, so EOF (Ctrl+Z) doesn't stop early. */
379 	if (fd == STDIN_FILENO)
380 		_setmode(STDIN_FILENO, O_BINARY);
381 #endif
382 
383 	if (inname == NULL) {
384 		if (fstat(fd, &sb) == 0 && S_ISFIFO(sb.st_mode))
385 			ispipe = 1;
386 		else
387 			pos = lseek(fd, (off_t)0, SEEK_CUR);
388 	} else {
389 		int flags = O_RDONLY|O_BINARY;
390 		int okstat = stat(inname, &sb) == 0;
391 
392 		if (okstat && S_ISFIFO(sb.st_mode)) {
393 #ifdef O_NONBLOCK
394 			flags |= O_NONBLOCK;
395 #endif
396 			ispipe = 1;
397 		}
398 
399 		errno = 0;
400 		if ((fd = open(inname, flags)) < 0) {
401 #ifdef WIN32
402 			/*
403 			 * Can't stat, can't open.  It may have been opened in
404 			 * fsmagic, so if the user doesn't have read permission,
405 			 * allow it to say so; otherwise an error was probably
406 			 * displayed in fsmagic.
407 			 */
408 			if (!okstat && errno == EACCES) {
409 				sb.st_mode = S_IFBLK;
410 				okstat = 1;
411 			}
412 #endif
413 			if (okstat &&
414 			    unreadable_info(ms, sb.st_mode, inname) == -1)
415 				goto done;
416 			rv = 0;
417 			goto done;
418 		}
419 #ifdef O_NONBLOCK
420 		if ((flags = fcntl(fd, F_GETFL)) != -1) {
421 			flags &= ~O_NONBLOCK;
422 			(void)fcntl(fd, F_SETFL, flags);
423 		}
424 #endif
425 	}
426 
427 	/*
428 	 * try looking at the first HOWMANY bytes
429 	 */
430 	if (ispipe) {
431 		ssize_t r = 0;
432 
433 		while ((r = sread(fd, (void *)&buf[nbytes],
434 		    (size_t)(HOWMANY - nbytes), 1)) > 0) {
435 			nbytes += r;
436 			if (r < PIPE_BUF) break;
437 		}
438 
439 		if (nbytes == 0) {
440 			/* We can not read it, but we were able to stat it. */
441 			if (unreadable_info(ms, sb.st_mode, inname) == -1)
442 				goto done;
443 			rv = 0;
444 			goto done;
445 		}
446 
447 	} else {
448 		/* Windows refuses to read from a big console buffer. */
449 		size_t howmany =
450 #if defined(WIN32) && HOWMANY > 8 * 1024
451 				_isatty(fd) ? 8 * 1024 :
452 #endif
453 				HOWMANY;
454 		if ((nbytes = read(fd, (char *)buf, howmany)) == -1) {
455 			if (inname == NULL && fd != STDIN_FILENO)
456 				file_error(ms, errno, "cannot read fd %d", fd);
457 			else
458 				file_error(ms, errno, "cannot read `%s'",
459 				    inname == NULL ? "/dev/stdin" : inname);
460 			goto done;
461 		}
462 	}
463 
464 	(void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */
465 	if (file_buffer(ms, fd, inname, buf, (size_t)nbytes) == -1)
466 		goto done;
467 	rv = 0;
468 done:
469 	free(buf);
470 	if (pos != (off_t)-1)
471 		(void)lseek(fd, pos, SEEK_SET);
472 	close_and_restore(ms, inname, fd, &sb);
473 out:
474 	return rv == 0 ? file_getbuffer(ms) : NULL;
475 }
476 
477 
478 public const char *
479 magic_buffer(struct magic_set *ms, const void *buf, size_t nb)
480 {
481 	if (ms == NULL)
482 		return NULL;
483 	if (file_reset(ms) == -1)
484 		return NULL;
485 	/*
486 	 * The main work is done here!
487 	 * We have the file name and/or the data buffer to be identified.
488 	 */
489 	if (file_buffer(ms, -1, NULL, buf, nb) == -1) {
490 		return NULL;
491 	}
492 	return file_getbuffer(ms);
493 }
494 #endif
495 
496 public const char *
497 magic_error(struct magic_set *ms)
498 {
499 	if (ms == NULL)
500 		return "Magic database is not open";
501 	return (ms->event_flags & EVENT_HAD_ERR) ? ms->o.buf : NULL;
502 }
503 
504 public int
505 magic_errno(struct magic_set *ms)
506 {
507 	if (ms == NULL)
508 		return EINVAL;
509 	return (ms->event_flags & EVENT_HAD_ERR) ? ms->error : 0;
510 }
511 
512 public int
513 magic_setflags(struct magic_set *ms, int flags)
514 {
515 	if (ms == NULL)
516 		return -1;
517 #if !defined(HAVE_UTIME) && !defined(HAVE_UTIMES)
518 	if (flags & MAGIC_PRESERVE_ATIME)
519 		return -1;
520 #endif
521 	ms->flags = flags;
522 	return 0;
523 }
524 
525 public int
526 magic_version(void)
527 {
528 	return MAGIC_VERSION;
529 }
530