xref: /netbsd-src/external/bsd/file/dist/src/magic.c (revision b8ecfcfef0e343ad71faea7a54fb5fcb42ad4e27)
1 /*	$NetBSD: magic.c,v 1.9 2014/10/10 20:15:02 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.85 2014/08/04 06:19:44 christos Exp $")
39 #else
40 __RCSID("$NetBSD: magic.c,v 1.9 2014/10/10 20:15:02 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 #ifndef COMPILE_ONLY
266 /*
267  * Install a set of compiled magic buffers.
268  */
269 public int
270 magic_load_buffers(struct magic_set *ms, void **bufs, size_t *sizes,
271     size_t nbufs)
272 {
273 	if (ms == NULL)
274 		return -1;
275 	return buffer_apprentice(ms, (struct magic **)bufs, sizes, nbufs);
276 }
277 #endif
278 
279 public int
280 magic_compile(struct magic_set *ms, const char *magicfile)
281 {
282 	if (ms == NULL)
283 		return -1;
284 	return file_apprentice(ms, magicfile, FILE_COMPILE);
285 }
286 
287 public int
288 magic_check(struct magic_set *ms, const char *magicfile)
289 {
290 	if (ms == NULL)
291 		return -1;
292 	return file_apprentice(ms, magicfile, FILE_CHECK);
293 }
294 
295 public int
296 magic_list(struct magic_set *ms, const char *magicfile)
297 {
298 	if (ms == NULL)
299 		return -1;
300 	return file_apprentice(ms, magicfile, FILE_LIST);
301 }
302 
303 private void
304 close_and_restore(const struct magic_set *ms, const char *name, int fd,
305     const struct stat *sb)
306 {
307 	if (fd == STDIN_FILENO || name == NULL)
308 		return;
309 	(void) close(fd);
310 
311 	if ((ms->flags & MAGIC_PRESERVE_ATIME) != 0) {
312 		/*
313 		 * Try to restore access, modification times if read it.
314 		 * This is really *bad* because it will modify the status
315 		 * time of the file... And of course this will affect
316 		 * backup programs
317 		 */
318 #ifdef HAVE_UTIMES
319 		struct timeval  utsbuf[2];
320 		(void)memset(utsbuf, 0, sizeof(utsbuf));
321 		utsbuf[0].tv_sec = sb->st_atime;
322 		utsbuf[1].tv_sec = sb->st_mtime;
323 
324 		(void) utimes(name, utsbuf); /* don't care if loses */
325 #elif defined(HAVE_UTIME_H) || defined(HAVE_SYS_UTIME_H)
326 		struct utimbuf  utbuf;
327 
328 		(void)memset(&utbuf, 0, sizeof(utbuf));
329 		utbuf.actime = sb->st_atime;
330 		utbuf.modtime = sb->st_mtime;
331 		(void) utime(name, &utbuf); /* don't care if loses */
332 #endif
333 	}
334 }
335 
336 #ifndef COMPILE_ONLY
337 
338 /*
339  * find type of descriptor
340  */
341 public const char *
342 magic_descriptor(struct magic_set *ms, int fd)
343 {
344 	if (ms == NULL)
345 		return NULL;
346 	return file_or_fd(ms, NULL, fd);
347 }
348 
349 /*
350  * find type of named file
351  */
352 public const char *
353 magic_file(struct magic_set *ms, const char *inname)
354 {
355 	if (ms == NULL)
356 		return NULL;
357 	return file_or_fd(ms, inname, STDIN_FILENO);
358 }
359 
360 private const char *
361 file_or_fd(struct magic_set *ms, const char *inname, int fd)
362 {
363 	int	rv = -1;
364 	unsigned char *buf;
365 	struct stat	sb;
366 	ssize_t nbytes = 0;	/* number of bytes read from a datafile */
367 	int	ispipe = 0;
368 	off_t	pos = (off_t)-1;
369 
370 	if (file_reset(ms) == -1)
371 		goto out;
372 
373 	/*
374 	 * one extra for terminating '\0', and
375 	 * some overlapping space for matches near EOF
376 	 */
377 #define SLOP (1 + sizeof(union VALUETYPE))
378 	if ((buf = CAST(unsigned char *, malloc(HOWMANY + SLOP))) == NULL)
379 		return NULL;
380 
381 	switch (file_fsmagic(ms, inname, &sb)) {
382 	case -1:		/* error */
383 		goto done;
384 	case 0:			/* nothing found */
385 		break;
386 	default:		/* matched it and printed type */
387 		rv = 0;
388 		goto done;
389 	}
390 
391 #ifdef WIN32
392 	/* Place stdin in binary mode, so EOF (Ctrl+Z) doesn't stop early. */
393 	if (fd == STDIN_FILENO)
394 		_setmode(STDIN_FILENO, O_BINARY);
395 #endif
396 
397 	if (inname == NULL) {
398 		if (fstat(fd, &sb) == 0 && S_ISFIFO(sb.st_mode))
399 			ispipe = 1;
400 		else
401 			pos = lseek(fd, (off_t)0, SEEK_CUR);
402 	} else {
403 		int flags = O_RDONLY|O_BINARY;
404 		int okstat = stat(inname, &sb) == 0;
405 
406 		if (okstat && S_ISFIFO(sb.st_mode)) {
407 #ifdef O_NONBLOCK
408 			flags |= O_NONBLOCK;
409 #endif
410 			ispipe = 1;
411 		}
412 
413 		errno = 0;
414 		if ((fd = open(inname, flags)) < 0) {
415 #ifdef WIN32
416 			/*
417 			 * Can't stat, can't open.  It may have been opened in
418 			 * fsmagic, so if the user doesn't have read permission,
419 			 * allow it to say so; otherwise an error was probably
420 			 * displayed in fsmagic.
421 			 */
422 			if (!okstat && errno == EACCES) {
423 				sb.st_mode = S_IFBLK;
424 				okstat = 1;
425 			}
426 #endif
427 			if (okstat &&
428 			    unreadable_info(ms, sb.st_mode, inname) == -1)
429 				goto done;
430 			rv = 0;
431 			goto done;
432 		}
433 #ifdef O_NONBLOCK
434 		if ((flags = fcntl(fd, F_GETFL)) != -1) {
435 			flags &= ~O_NONBLOCK;
436 			(void)fcntl(fd, F_SETFL, flags);
437 		}
438 #endif
439 	}
440 
441 	/*
442 	 * try looking at the first HOWMANY bytes
443 	 */
444 	if (ispipe) {
445 		ssize_t r = 0;
446 
447 		while ((r = sread(fd, (void *)&buf[nbytes],
448 		    (size_t)(HOWMANY - nbytes), 1)) > 0) {
449 			nbytes += r;
450 			if (r < PIPE_BUF) break;
451 		}
452 
453 		if (nbytes == 0) {
454 			/* We can not read it, but we were able to stat it. */
455 			if (unreadable_info(ms, sb.st_mode, inname) == -1)
456 				goto done;
457 			rv = 0;
458 			goto done;
459 		}
460 
461 	} else {
462 		/* Windows refuses to read from a big console buffer. */
463 		size_t howmany =
464 #if defined(WIN32) && HOWMANY > 8 * 1024
465 				_isatty(fd) ? 8 * 1024 :
466 #endif
467 				HOWMANY;
468 		if ((nbytes = read(fd, (char *)buf, howmany)) == -1) {
469 			if (inname == NULL && fd != STDIN_FILENO)
470 				file_error(ms, errno, "cannot read fd %d", fd);
471 			else
472 				file_error(ms, errno, "cannot read `%s'",
473 				    inname == NULL ? "/dev/stdin" : inname);
474 			goto done;
475 		}
476 	}
477 
478 	(void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */
479 	if (file_buffer(ms, fd, inname, buf, (size_t)nbytes) == -1)
480 		goto done;
481 	rv = 0;
482 done:
483 	free(buf);
484 	if (pos != (off_t)-1)
485 		(void)lseek(fd, pos, SEEK_SET);
486 	close_and_restore(ms, inname, fd, &sb);
487 out:
488 	return rv == 0 ? file_getbuffer(ms) : NULL;
489 }
490 
491 
492 public const char *
493 magic_buffer(struct magic_set *ms, const void *buf, size_t nb)
494 {
495 	if (ms == NULL)
496 		return NULL;
497 	if (file_reset(ms) == -1)
498 		return NULL;
499 	/*
500 	 * The main work is done here!
501 	 * We have the file name and/or the data buffer to be identified.
502 	 */
503 	if (file_buffer(ms, -1, NULL, buf, nb) == -1) {
504 		return NULL;
505 	}
506 	return file_getbuffer(ms);
507 }
508 #endif
509 
510 public const char *
511 magic_error(struct magic_set *ms)
512 {
513 	if (ms == NULL)
514 		return "Magic database is not open";
515 	return (ms->event_flags & EVENT_HAD_ERR) ? ms->o.buf : NULL;
516 }
517 
518 public int
519 magic_errno(struct magic_set *ms)
520 {
521 	if (ms == NULL)
522 		return EINVAL;
523 	return (ms->event_flags & EVENT_HAD_ERR) ? ms->error : 0;
524 }
525 
526 public int
527 magic_setflags(struct magic_set *ms, int flags)
528 {
529 	if (ms == NULL)
530 		return -1;
531 #if !defined(HAVE_UTIME) && !defined(HAVE_UTIMES)
532 	if (flags & MAGIC_PRESERVE_ATIME)
533 		return -1;
534 #endif
535 	ms->flags = flags;
536 	return 0;
537 }
538 
539 public int
540 magic_version(void)
541 {
542 	return MAGIC_VERSION;
543 }
544