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#include "file.h"
29
30#ifndef	lint
31FILE_RCSID("@(#)$File: magic.c,v 1.65 2009/09/14 17:50:38 christos Exp $")
32#endif	/* lint */
33
34#include "magic.h"
35
36#include <stdlib.h>
37#include <unistd.h>
38#include <string.h>
39#ifdef QUICK
40#include <sys/mman.h>
41#endif
42#ifdef HAVE_LIMITS_H
43#include <limits.h>	/* for PIPE_BUF */
44#endif
45
46#if defined(HAVE_UTIMES)
47# include <sys/time.h>
48#elif defined(HAVE_UTIME)
49# if defined(HAVE_SYS_UTIME_H)
50#  include <sys/utime.h>
51# elif defined(HAVE_UTIME_H)
52#  include <utime.h>
53# endif
54#endif
55
56#ifdef HAVE_UNISTD_H
57#include <unistd.h>	/* for read() */
58#endif
59
60#include "patchlevel.h"
61
62#ifdef __APPLE__
63#include "get_compat.h"
64#else
65#define COMPAT_MODE(func, mode) 1
66#endif
67
68#ifndef PIPE_BUF
69/* Get the PIPE_BUF from pathconf */
70#ifdef _PC_PIPE_BUF
71#define PIPE_BUF pathconf(".", _PC_PIPE_BUF)
72#else
73#define PIPE_BUF 512
74#endif
75#endif
76
77private void free_mlist(struct mlist *);
78private void close_and_restore(const struct magic_set *, const char *, int,
79    const struct stat *);
80private int unreadable_info(struct magic_set *, mode_t, const char *);
81private const char* get_default_magic(void);
82#ifndef COMPILE_ONLY
83private const char *file_or_fd(struct magic_set *, const char *, int);
84#endif
85
86#ifndef	STDIN_FILENO
87#define	STDIN_FILENO	0
88#endif
89
90private const char *
91get_default_magic(void)
92{
93	return MAGIC;
94}
95
96public const char *
97magic_getpath(const char *magicfile, int action)
98{
99	if (magicfile != NULL)
100		return magicfile;
101
102	magicfile = getenv("MAGIC");
103	if (magicfile != NULL)
104		return magicfile;
105
106	return action == FILE_LOAD ? get_default_magic() : MAGIC;
107}
108
109public struct magic_set *
110magic_open(int flags)
111{
112	struct magic_set *ms;
113	size_t len;
114
115	if ((ms = CAST(struct magic_set *, calloc((size_t)1,
116	    sizeof(struct magic_set)))) == NULL)
117		return NULL;
118
119	if (magic_setflags(ms, flags) == -1) {
120		errno = EINVAL;
121		goto free;
122	}
123
124	ms->o.buf = ms->o.pbuf = NULL;
125	len = (ms->c.len = 10) * sizeof(*ms->c.li);
126
127	if ((ms->c.li = CAST(struct level_info *, malloc(len))) == NULL)
128		goto free;
129
130	ms->event_flags = 0;
131	ms->error = -1;
132	ms->mlist = NULL;
133	ms->file = "unknown";
134	ms->line = 0;
135	return ms;
136free:
137	free(ms);
138	return NULL;
139}
140
141private void
142free_mlist(struct mlist *mlist)
143{
144	struct mlist *ml;
145
146	if (mlist == NULL)
147		return;
148
149	for (ml = mlist->next; ml != mlist;) {
150		struct mlist *next = ml->next;
151		struct magic *mg = ml->magic;
152		file_delmagic(mg, ml->mapped, ml->nmagic);
153		free(ml);
154		ml = next;
155	}
156	free(ml);
157}
158
159private int
160unreadable_info(struct magic_set *ms, mode_t md, const char *file)
161{
162	/* We cannot open it, but we were able to stat it. */
163	if (COMPAT_MODE("bin/file", "unix2003")) {
164		if (file_printf(ms, "cannot open: %s", strerror(errno)) == -1)
165			return -1;
166		return 0;
167	}
168	if (access(file, W_OK) == 0)
169		if (file_printf(ms, "writable, ") == -1)
170			return -1;
171	if (access(file, X_OK) == 0)
172		if (file_printf(ms, "executable, ") == -1)
173			return -1;
174	if (S_ISREG(md))
175		if (file_printf(ms, "regular file, ") == -1)
176			return -1;
177	if (file_printf(ms, "no read permission") == -1)
178		return -1;
179	return 0;
180}
181
182public void
183magic_close(struct magic_set *ms)
184{
185	free_mlist(ms->mlist);
186	free(ms->o.pbuf);
187	free(ms->o.buf);
188	free(ms->c.li);
189	free(ms);
190}
191
192/*
193 * load a magic file
194 */
195public int
196magic_load(struct magic_set *ms, const char *magicfile)
197{
198	struct mlist *ml = file_apprentice(ms, magicfile, FILE_LOAD);
199	if (ml) {
200		free_mlist(ms->mlist);
201		ms->mlist = ml;
202		return 0;
203	}
204	return -1;
205}
206
207public int
208magic_compile(struct magic_set *ms, const char *magicfile)
209{
210	struct mlist *ml = file_apprentice(ms, magicfile, FILE_COMPILE);
211	free_mlist(ml);
212	return ml ? 0 : -1;
213}
214
215public int
216magic_check(struct magic_set *ms, const char *magicfile)
217{
218	struct mlist *ml = file_apprentice(ms, magicfile, FILE_CHECK);
219	free_mlist(ml);
220	return ml ? 0 : -1;
221}
222
223private void
224close_and_restore(const struct magic_set *ms, const char *name, int fd,
225    const struct stat *sb)
226{
227	if (fd == STDIN_FILENO)
228		return;
229	(void) close(fd);
230
231	if ((ms->flags & MAGIC_PRESERVE_ATIME) != 0) {
232		/*
233		 * Try to restore access, modification times if read it.
234		 * This is really *bad* because it will modify the status
235		 * time of the file... And of course this will affect
236		 * backup programs
237		 */
238#ifdef HAVE_UTIMES
239		struct timeval  utsbuf[2];
240		(void)memset(utsbuf, 0, sizeof(utsbuf));
241		utsbuf[0].tv_sec = sb->st_atime;
242		utsbuf[1].tv_sec = sb->st_mtime;
243
244		(void) utimes(name, utsbuf); /* don't care if loses */
245#elif defined(HAVE_UTIME_H) || defined(HAVE_SYS_UTIME_H)
246		struct utimbuf  utbuf;
247
248		(void)memset(&utbuf, 0, sizeof(utbuf));
249		utbuf.actime = sb->st_atime;
250		utbuf.modtime = sb->st_mtime;
251		(void) utime(name, &utbuf); /* don't care if loses */
252#endif
253	}
254}
255
256#ifndef COMPILE_ONLY
257
258/*
259 * find type of descriptor
260 */
261public const char *
262magic_descriptor(struct magic_set *ms, int fd)
263{
264	return file_or_fd(ms, NULL, fd);
265}
266
267/*
268 * find type of named file
269 */
270public const char *
271magic_file(struct magic_set *ms, const char *inname)
272{
273	return file_or_fd(ms, inname, STDIN_FILENO);
274}
275
276private const char *
277file_or_fd(struct magic_set *ms, const char *inname, int fd)
278{
279	int	rv = -1;
280	unsigned char *buf;
281	struct stat	sb;
282	ssize_t nbytes = 0;	/* number of bytes read from a datafile */
283	int	ispipe = 0;
284
285	/*
286	 * one extra for terminating '\0', and
287	 * some overlapping space for matches near EOF
288	 */
289#define SLOP (1 + sizeof(union VALUETYPE))
290	if ((buf = CAST(unsigned char *, malloc(HOWMANY + SLOP))) == NULL)
291		return NULL;
292
293	if (file_reset(ms) == -1)
294		goto done;
295
296	switch (file_fsmagic(ms, inname, &sb)) {
297	case -1:		/* error */
298		goto done;
299	case 0:			/* nothing found */
300		break;
301	default:		/* matched it and printed type */
302		rv = 0;
303		goto done;
304	}
305
306	if (inname == NULL) {
307		if (fstat(fd, &sb) == 0 && S_ISFIFO(sb.st_mode))
308			ispipe = 1;
309	} else {
310		int flags = O_RDONLY|O_BINARY;
311
312		if (stat(inname, &sb) == 0 && S_ISFIFO(sb.st_mode)) {
313			flags |= O_NONBLOCK;
314			ispipe = 1;
315		}
316
317		errno = 0;
318		if ((fd = open(inname, flags)) < 0) {
319			if (unreadable_info(ms, sb.st_mode, inname) == -1)
320				goto done;
321			rv = 0;
322			goto done;
323		}
324#ifdef O_NONBLOCK
325		if ((flags = fcntl(fd, F_GETFL)) != -1) {
326			flags &= ~O_NONBLOCK;
327			(void)fcntl(fd, F_SETFL, flags);
328		}
329#endif
330	}
331
332	/*
333	 * try looking at the first HOWMANY bytes
334	 */
335	if (ispipe) {
336		ssize_t r = 0;
337
338		while ((r = sread(fd, (void *)&buf[nbytes],
339		    (size_t)(HOWMANY - nbytes), 1)) > 0) {
340			nbytes += r;
341			if (r < PIPE_BUF) break;
342		}
343
344		if (nbytes == 0) {
345			/* We can not read it, but we were able to stat it. */
346			if (unreadable_info(ms, sb.st_mode, inname) == -1)
347				goto done;
348			rv = 0;
349			goto done;
350		}
351
352	} else {
353		if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) {
354			file_error(ms, errno, "cannot read `%s'", inname);
355			goto done;
356		}
357	}
358
359	(void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */
360	if (file_buffer(ms, fd, inname, buf, (size_t)nbytes) == -1)
361		goto done;
362	rv = 0;
363done:
364	free(buf);
365	close_and_restore(ms, inname, fd, &sb);
366	return rv == 0 ? file_getbuffer(ms) : NULL;
367}
368
369
370public const char *
371magic_buffer(struct magic_set *ms, const void *buf, size_t nb)
372{
373	if (file_reset(ms) == -1)
374		return NULL;
375	/*
376	 * The main work is done here!
377	 * We have the file name and/or the data buffer to be identified.
378	 */
379	if (file_buffer(ms, -1, NULL, buf, nb) == -1) {
380		return NULL;
381	}
382	return file_getbuffer(ms);
383}
384#endif
385
386public const char *
387magic_error(struct magic_set *ms)
388{
389	return (ms->event_flags & EVENT_HAD_ERR) ? ms->o.buf : NULL;
390}
391
392public int
393magic_errno(struct magic_set *ms)
394{
395	return (ms->event_flags & EVENT_HAD_ERR) ? ms->error : 0;
396}
397
398public int
399magic_setflags(struct magic_set *ms, int flags)
400{
401#if !defined(HAVE_UTIME) && !defined(HAVE_UTIMES)
402	if (flags & MAGIC_PRESERVE_ATIME)
403		return -1;
404#endif
405	ms->flags = flags;
406	return 0;
407}
408