xref: /dragonfly/contrib/file/src/magic.c (revision 1b722dce)
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
31 FILE_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 
73 private void free_mlist(struct mlist *);
74 private void close_and_restore(const struct magic_set *, const char *, int,
75     const struct stat *);
76 private int unreadable_info(struct magic_set *, mode_t, const char *);
77 #ifndef COMPILE_ONLY
78 private 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 
85 public struct magic_set *
86 magic_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;
112 free:
113 	free(ms);
114 	return NULL;
115 }
116 
117 private void
118 free_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 
135 private int
136 unreadable_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 
153 public void
154 magic_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  */
166 public int
167 magic_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 
178 public int
179 magic_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 
186 public int
187 magic_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 
194 private void
195 close_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  */
232 public const char *
233 magic_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  */
241 public const char *
242 magic_file(struct magic_set *ms, const char *inname)
243 {
244 	return file_or_fd(ms, inname, STDIN_FILENO);
245 }
246 
247 private const char *
248 file_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;
334 done:
335 	free(buf);
336 	close_and_restore(ms, inname, fd, &sb);
337 	return rv == 0 ? file_getbuffer(ms) : NULL;
338 }
339 
340 
341 public const char *
342 magic_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 
357 public const char *
358 magic_error(struct magic_set *ms)
359 {
360 	return (ms->event_flags & EVENT_HAD_ERR) ? ms->o.buf : NULL;
361 }
362 
363 public int
364 magic_errno(struct magic_set *ms)
365 {
366 	return (ms->event_flags & EVENT_HAD_ERR) ? ms->error : 0;
367 }
368 
369 public int
370 magic_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