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