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