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