]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/file/src/magic.c
MFC r267897: MFV r267843:
[FreeBSD/stable/10.git] / contrib / file / src / 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 #ifdef WIN32
29 #include <windows.h>
30 #include <shlwapi.h>
31 #endif
32
33 #include "file.h"
34
35 #ifndef lint
36 FILE_RCSID("@(#)$File: magic.c,v 1.84 2014/05/14 23:15:42 christos Exp $")
37 #endif  /* lint */
38
39 #include "magic.h"
40
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <string.h>
44 #ifdef QUICK
45 #include <sys/mman.h>
46 #endif
47 #ifdef HAVE_LIMITS_H
48 #include <limits.h>     /* for PIPE_BUF */
49 #endif
50
51 #if defined(HAVE_UTIMES)
52 # include <sys/time.h>
53 #elif defined(HAVE_UTIME)
54 # if defined(HAVE_SYS_UTIME_H)
55 #  include <sys/utime.h>
56 # elif defined(HAVE_UTIME_H)
57 #  include <utime.h>
58 # endif
59 #endif
60
61 #ifdef HAVE_UNISTD_H
62 #include <unistd.h>     /* for read() */
63 #endif
64
65 #ifndef PIPE_BUF
66 /* Get the PIPE_BUF from pathconf */
67 #ifdef _PC_PIPE_BUF
68 #define PIPE_BUF pathconf(".", _PC_PIPE_BUF)
69 #else
70 #define PIPE_BUF 512
71 #endif
72 #endif
73
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 private const char* get_default_magic(void);
78 #ifndef COMPILE_ONLY
79 private const char *file_or_fd(struct magic_set *, const char *, int);
80 #endif
81
82 #ifndef STDIN_FILENO
83 #define STDIN_FILENO    0
84 #endif
85
86 private const char *
87 get_default_magic(void)
88 {
89         static const char hmagic[] = "/.magic/magic.mgc";
90         static char *default_magic;
91         char *home, *hmagicpath;
92
93 #ifndef WIN32
94         struct stat st;
95
96         if (default_magic) {
97                 free(default_magic);
98                 default_magic = NULL;
99         }
100         if ((home = getenv("HOME")) == NULL)
101                 return MAGIC;
102
103         if (asprintf(&hmagicpath, "%s/.magic.mgc", home) < 0)
104                 return MAGIC;
105         if (stat(hmagicpath, &st) == -1) {
106                 free(hmagicpath);
107                 if (asprintf(&hmagicpath, "%s/.magic", home) < 0)
108                         return MAGIC;
109                 if (stat(hmagicpath, &st) == -1)
110                         goto out;
111                 if (S_ISDIR(st.st_mode)) {
112                         free(hmagicpath);
113                         if (asprintf(&hmagicpath, "%s/%s", home, hmagic) < 0)
114                                 return MAGIC;
115                         if (access(hmagicpath, R_OK) == -1)
116                                 goto out;
117                 }
118         }
119
120         if (asprintf(&default_magic, "%s:%s", hmagicpath, MAGIC) < 0)
121                 goto out;
122         free(hmagicpath);
123         return default_magic;
124 out:
125         default_magic = NULL;
126         free(hmagicpath);
127         return MAGIC;
128 #else
129         char *hmagicp;
130         char *tmppath = NULL;
131         hmagicpath = NULL;
132
133 #define APPENDPATH() \
134         do { \
135                 if (tmppath && access(tmppath, R_OK) != -1) { \
136                         if (hmagicpath == NULL) \
137                                 hmagicpath = tmppath; \
138                         else { \
139                                 if (asprintf(&hmagicp, "%s%c%s", hmagicpath, \
140                                     PATHSEP, tmppath) >= 0) { \
141                                         free(hmagicpath); \
142                                         hmagicpath = hmagicp; \
143                                 } \
144                                 free(tmppath); \
145                         } \
146                         tmppath = NULL; \
147                 } \
148         } while (/*CONSTCOND*/0)
149                                 
150         if (default_magic) {
151                 free(default_magic);
152                 default_magic = NULL;
153         }
154
155         /* First, try to get user-specific magic file */
156         if ((home = getenv("LOCALAPPDATA")) == NULL) {
157                 if ((home = getenv("USERPROFILE")) != NULL)
158                         if (asprintf(&tmppath,
159                             "%s/Local Settings/Application Data%s", home,
160                             hmagic) < 0)
161                                 tmppath = NULL;
162         } else {
163                 if (asprintf(&tmppath, "%s%s", home, hmagic) < 0)
164                         tmppath = NULL;
165         }
166
167         APPENDPATH();
168
169         /* Second, try to get a magic file from Common Files */
170         if ((home = getenv("COMMONPROGRAMFILES")) != NULL) {
171                 if (asprintf(&tmppath, "%s%s", home, hmagic) >= 0)
172                         APPENDPATH();
173         }
174
175         /* Third, try to get magic file relative to dll location */
176         LPTSTR dllpath = malloc(sizeof(*dllpath) * (MAX_PATH + 1));
177         dllpath[MAX_PATH] = 0;  /* just in case long path gets truncated and not null terminated */
178         if (GetModuleFileNameA(NULL, dllpath, MAX_PATH)){
179                 PathRemoveFileSpecA(dllpath);
180                 if (strlen(dllpath) > 3 &&
181                     stricmp(&dllpath[strlen(dllpath) - 3], "bin") == 0) {
182                         if (asprintf(&tmppath,
183                             "%s/../share/misc/magic.mgc", dllpath) >= 0)
184                                 APPENDPATH();
185                 } else {
186                         if (asprintf(&tmppath,
187                             "%s/share/misc/magic.mgc", dllpath) >= 0)
188                                 APPENDPATH();
189                         else if (asprintf(&tmppath,
190                             "%s/magic.mgc", dllpath) >= 0)
191                                 APPENDPATH();
192                 }
193         }
194
195         /* Don't put MAGIC constant - it likely points to a file within MSys
196         tree */
197         default_magic = hmagicpath;
198         return default_magic;
199 #endif
200 }
201
202 public const char *
203 magic_getpath(const char *magicfile, int action)
204 {
205         if (magicfile != NULL)
206                 return magicfile;
207
208         magicfile = getenv("MAGIC");
209         if (magicfile != NULL)
210                 return magicfile;
211
212         return action == FILE_LOAD ? get_default_magic() : MAGIC;
213 }
214
215 public struct magic_set *
216 magic_open(int flags)
217 {
218         return file_ms_alloc(flags);
219 }
220
221 private int
222 unreadable_info(struct magic_set *ms, mode_t md, const char *file)
223 {
224         if (file) {
225                 /* We cannot open it, but we were able to stat it. */
226                 if (access(file, W_OK) == 0)
227                         if (file_printf(ms, "writable, ") == -1)
228                                 return -1;
229                 if (access(file, X_OK) == 0)
230                         if (file_printf(ms, "executable, ") == -1)
231                                 return -1;
232         }
233         if (S_ISREG(md))
234                 if (file_printf(ms, "regular file, ") == -1)
235                         return -1;
236         if (file_printf(ms, "no read permission") == -1)
237                 return -1;
238         return 0;
239 }
240
241 public void
242 magic_close(struct magic_set *ms)
243 {
244         if (ms == NULL)
245                 return;
246         file_ms_free(ms);
247 }
248
249 /*
250  * load a magic file
251  */
252 public int
253 magic_load(struct magic_set *ms, const char *magicfile)
254 {
255         if (ms == NULL)
256                 return -1;
257         return file_apprentice(ms, magicfile, FILE_LOAD);
258 }
259
260 public int
261 magic_compile(struct magic_set *ms, const char *magicfile)
262 {
263         if (ms == NULL)
264                 return -1;
265         return file_apprentice(ms, magicfile, FILE_COMPILE);
266 }
267
268 public int
269 magic_check(struct magic_set *ms, const char *magicfile)
270 {
271         if (ms == NULL)
272                 return -1;
273         return file_apprentice(ms, magicfile, FILE_CHECK);
274 }
275
276 public int
277 magic_list(struct magic_set *ms, const char *magicfile)
278 {
279         if (ms == NULL)
280                 return -1;
281         return file_apprentice(ms, magicfile, FILE_LIST);
282 }
283
284 private void
285 close_and_restore(const struct magic_set *ms, const char *name, int fd,
286     const struct stat *sb)
287 {
288         if (fd == STDIN_FILENO || name == NULL)
289                 return;
290         (void) close(fd);
291
292         if ((ms->flags & MAGIC_PRESERVE_ATIME) != 0) {
293                 /*
294                  * Try to restore access, modification times if read it.
295                  * This is really *bad* because it will modify the status
296                  * time of the file... And of course this will affect
297                  * backup programs
298                  */
299 #ifdef HAVE_UTIMES
300                 struct timeval  utsbuf[2];
301                 (void)memset(utsbuf, 0, sizeof(utsbuf));
302                 utsbuf[0].tv_sec = sb->st_atime;
303                 utsbuf[1].tv_sec = sb->st_mtime;
304
305                 (void) utimes(name, utsbuf); /* don't care if loses */
306 #elif defined(HAVE_UTIME_H) || defined(HAVE_SYS_UTIME_H)
307                 struct utimbuf  utbuf;
308
309                 (void)memset(&utbuf, 0, sizeof(utbuf));
310                 utbuf.actime = sb->st_atime;
311                 utbuf.modtime = sb->st_mtime;
312                 (void) utime(name, &utbuf); /* don't care if loses */
313 #endif
314         }
315 }
316
317 #ifndef COMPILE_ONLY
318
319 /*
320  * find type of descriptor
321  */
322 public const char *
323 magic_descriptor(struct magic_set *ms, int fd)
324 {
325         if (ms == NULL)
326                 return NULL;
327         return file_or_fd(ms, NULL, fd);
328 }
329
330 /*
331  * find type of named file
332  */
333 public const char *
334 magic_file(struct magic_set *ms, const char *inname)
335 {
336         if (ms == NULL)
337                 return NULL;
338         return file_or_fd(ms, inname, STDIN_FILENO);
339 }
340
341 private const char *
342 file_or_fd(struct magic_set *ms, const char *inname, int fd)
343 {
344         int     rv = -1;
345         unsigned char *buf;
346         struct stat     sb;
347         ssize_t nbytes = 0;     /* number of bytes read from a datafile */
348         int     ispipe = 0;
349         off_t   pos = (off_t)-1;
350
351         if (file_reset(ms) == -1)
352                 goto out;
353
354         /*
355          * one extra for terminating '\0', and
356          * some overlapping space for matches near EOF
357          */
358 #define SLOP (1 + sizeof(union VALUETYPE))
359         if ((buf = CAST(unsigned char *, malloc(HOWMANY + SLOP))) == NULL)
360                 return NULL;
361
362         switch (file_fsmagic(ms, inname, &sb)) {
363         case -1:                /* error */
364                 goto done;
365         case 0:                 /* nothing found */
366                 break;
367         default:                /* matched it and printed type */
368                 rv = 0;
369                 goto done;
370         }
371
372 #ifdef WIN32
373         /* Place stdin in binary mode, so EOF (Ctrl+Z) doesn't stop early. */
374         if (fd == STDIN_FILENO)
375                 _setmode(STDIN_FILENO, O_BINARY);
376 #endif
377
378         if (inname == NULL) {
379                 if (fstat(fd, &sb) == 0 && S_ISFIFO(sb.st_mode))
380                         ispipe = 1;
381                 else
382                         pos = lseek(fd, (off_t)0, SEEK_CUR);
383         } else {
384                 int flags = O_RDONLY|O_BINARY;
385                 int okstat = stat(inname, &sb) == 0;
386
387                 if (okstat && S_ISFIFO(sb.st_mode)) {
388 #ifdef O_NONBLOCK
389                         flags |= O_NONBLOCK;
390 #endif
391                         ispipe = 1;
392                 }
393
394                 errno = 0;
395                 if ((fd = open(inname, flags)) < 0) {
396 #ifdef WIN32
397                         /*
398                          * Can't stat, can't open.  It may have been opened in
399                          * fsmagic, so if the user doesn't have read permission,
400                          * allow it to say so; otherwise an error was probably
401                          * displayed in fsmagic.
402                          */
403                         if (!okstat && errno == EACCES) {
404                                 sb.st_mode = S_IFBLK;
405                                 okstat = 1;
406                         }
407 #endif
408                         if (okstat &&
409                             unreadable_info(ms, sb.st_mode, inname) == -1)
410                                 goto done;
411                         rv = 0;
412                         goto done;
413                 }
414 #ifdef O_NONBLOCK
415                 if ((flags = fcntl(fd, F_GETFL)) != -1) {
416                         flags &= ~O_NONBLOCK;
417                         (void)fcntl(fd, F_SETFL, flags);
418                 }
419 #endif
420         }
421
422         /*
423          * try looking at the first HOWMANY bytes
424          */
425         if (ispipe) {
426                 ssize_t r = 0;
427
428                 while ((r = sread(fd, (void *)&buf[nbytes],
429                     (size_t)(HOWMANY - nbytes), 1)) > 0) {
430                         nbytes += r;
431                         if (r < PIPE_BUF) break;
432                 }
433
434                 if (nbytes == 0) {
435                         /* We can not read it, but we were able to stat it. */
436                         if (unreadable_info(ms, sb.st_mode, inname) == -1)
437                                 goto done;
438                         rv = 0;
439                         goto done;
440                 }
441
442         } else {
443                 /* Windows refuses to read from a big console buffer. */
444                 size_t howmany =
445 #if defined(WIN32) && HOWMANY > 8 * 1024
446                                 _isatty(fd) ? 8 * 1024 :
447 #endif
448                                 HOWMANY;
449                 if ((nbytes = read(fd, (char *)buf, howmany)) == -1) {
450                         if (inname == NULL && fd != STDIN_FILENO)
451                                 file_error(ms, errno, "cannot read fd %d", fd);
452                         else
453                                 file_error(ms, errno, "cannot read `%s'",
454                                     inname == NULL ? "/dev/stdin" : inname);
455                         goto done;
456                 }
457         }
458
459         (void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */
460         if (file_buffer(ms, fd, inname, buf, (size_t)nbytes) == -1)
461                 goto done;
462         rv = 0;
463 done:
464         free(buf);
465         if (pos != (off_t)-1)
466                 (void)lseek(fd, pos, SEEK_SET);
467         close_and_restore(ms, inname, fd, &sb);
468 out:
469         return rv == 0 ? file_getbuffer(ms) : NULL;
470 }
471
472
473 public const char *
474 magic_buffer(struct magic_set *ms, const void *buf, size_t nb)
475 {
476         if (ms == NULL)
477                 return NULL;
478         if (file_reset(ms) == -1)
479                 return NULL;
480         /*
481          * The main work is done here!
482          * We have the file name and/or the data buffer to be identified.
483          */
484         if (file_buffer(ms, -1, NULL, buf, nb) == -1) {
485                 return NULL;
486         }
487         return file_getbuffer(ms);
488 }
489 #endif
490
491 public const char *
492 magic_error(struct magic_set *ms)
493 {
494         if (ms == NULL)
495                 return "Magic database is not open";
496         return (ms->event_flags & EVENT_HAD_ERR) ? ms->o.buf : NULL;
497 }
498
499 public int
500 magic_errno(struct magic_set *ms)
501 {
502         if (ms == NULL)
503                 return EINVAL;
504         return (ms->event_flags & EVENT_HAD_ERR) ? ms->error : 0;
505 }
506
507 public int
508 magic_setflags(struct magic_set *ms, int flags)
509 {
510         if (ms == NULL)
511                 return -1;
512 #if !defined(HAVE_UTIME) && !defined(HAVE_UTIMES)
513         if (flags & MAGIC_PRESERVE_ATIME)
514                 return -1;
515 #endif
516         ms->flags = flags;
517         return 0;
518 }
519
520 public int
521 magic_version(void)
522 {
523         return MAGIC_VERSION;
524 }