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