fsmagic.c revision 169962
1/*
2 * Copyright (c) Ian F. Darwin 1986-1995.
3 * Software written by Ian F. Darwin and others;
4 * maintained 1995-present by Christos Zoulas and others.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice immediately at the beginning of the file, without modification,
11 *    this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28/*
29 * fsmagic - magic based on filesystem info - directory, special files, etc.
30 */
31
32#include "file.h"
33#include "magic.h"
34#include <string.h>
35#ifdef HAVE_UNISTD_H
36#include <unistd.h>
37#endif
38#include <stdlib.h>
39#include <sys/stat.h>
40/* Since major is a function on SVR4, we cannot use `ifndef major'.  */
41#ifdef MAJOR_IN_MKDEV
42# include <sys/mkdev.h>
43# define HAVE_MAJOR
44#endif
45#ifdef MAJOR_IN_SYSMACROS
46# include <sys/sysmacros.h>
47# define HAVE_MAJOR
48#endif
49#ifdef major			/* Might be defined in sys/types.h.  */
50# define HAVE_MAJOR
51#endif
52
53#ifndef HAVE_MAJOR
54# define major(dev)  (((dev) >> 8) & 0xff)
55# define minor(dev)  ((dev) & 0xff)
56#endif
57#undef HAVE_MAJOR
58
59#ifndef	lint
60FILE_RCSID("@(#)$File: fsmagic.c,v 1.47 2007/01/12 17:38:28 christos Exp $")
61#endif	/* lint */
62
63protected int
64file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb)
65{
66	int ret = 0;
67#ifdef	S_IFLNK
68	char buf[BUFSIZ+4];
69	int nch;
70	struct stat tstatbuf;
71#endif
72
73	if (fn == NULL)
74		return 0;
75
76	/*
77	 * Fstat is cheaper but fails for files you don't have read perms on.
78	 * On 4.2BSD and similar systems, use lstat() to identify symlinks.
79	 */
80#ifdef	S_IFLNK
81	if ((ms->flags & MAGIC_SYMLINK) == 0)
82		ret = lstat(fn, sb);
83	else
84#endif
85	ret = stat(fn, sb);	/* don't merge into if; see "ret =" above */
86
87	if (ret) {
88		if (ms->flags & MAGIC_ERROR) {
89			file_error(ms, errno, "cannot stat `%s'", fn);
90			return -1;
91		}
92		if (file_printf(ms, "cannot open `%s' (%s)",
93		    fn, strerror(errno)) == -1)
94			return -1;
95		return 1;
96	}
97
98	if ((ms->flags & MAGIC_MIME) != 0) {
99		if ((sb->st_mode & S_IFMT) != S_IFREG) {
100			if (file_printf(ms, "application/x-not-regular-file")
101			    == -1)
102				return -1;
103			return 1;
104		}
105	}
106	else {
107#ifdef S_ISUID
108		if (sb->st_mode & S_ISUID)
109			if (file_printf(ms, "setuid ") == -1)
110				return -1;
111#endif
112#ifdef S_ISGID
113		if (sb->st_mode & S_ISGID)
114			if (file_printf(ms, "setgid ") == -1)
115				return -1;
116#endif
117#ifdef S_ISVTX
118		if (sb->st_mode & S_ISVTX)
119			if (file_printf(ms, "sticky ") == -1)
120				return -1;
121#endif
122	}
123
124	switch (sb->st_mode & S_IFMT) {
125	case S_IFDIR:
126		if (file_printf(ms, "directory") == -1)
127			return -1;
128		return 1;
129#ifdef S_IFCHR
130	case S_IFCHR:
131		/*
132		 * If -s has been specified, treat character special files
133		 * like ordinary files.  Otherwise, just report that they
134		 * are block special files and go on to the next file.
135		 */
136		if ((ms->flags & MAGIC_DEVICES) != 0)
137			break;
138#ifdef HAVE_ST_RDEV
139# ifdef dv_unit
140		if (file_printf(ms, "character special (%d/%d/%d)",
141		    major(sb->st_rdev), dv_unit(sb->st_rdev),
142		    dv_subunit(sb->st_rdev)) == -1)
143			return -1;
144# else
145		if (file_printf(ms, "character special (%ld/%ld)",
146		    (long) major(sb->st_rdev), (long) minor(sb->st_rdev)) == -1)
147			return -1;
148# endif
149#else
150		if (file_printf(ms, "character special") == -1)
151			return -1;
152#endif
153		return 1;
154#endif
155#ifdef S_IFBLK
156	case S_IFBLK:
157		/*
158		 * If -s has been specified, treat block special files
159		 * like ordinary files.  Otherwise, just report that they
160		 * are block special files and go on to the next file.
161		 */
162		if ((ms->flags & MAGIC_DEVICES) != 0)
163			break;
164#ifdef HAVE_ST_RDEV
165# ifdef dv_unit
166		if (file_printf(ms, "block special (%d/%d/%d)",
167		    major(sb->st_rdev), dv_unit(sb->st_rdev),
168		    dv_subunit(sb->st_rdev)) == -1)
169			return -1;
170# else
171		if (file_printf(ms, "block special (%ld/%ld)",
172		    (long)major(sb->st_rdev), (long)minor(sb->st_rdev)) == -1)
173			return -1;
174# endif
175#else
176		if (file_printf(ms, "block special") == -1)
177			return -1;
178#endif
179		return 1;
180#endif
181	/* TODO add code to handle V7 MUX and Blit MUX files */
182#ifdef	S_IFIFO
183	case S_IFIFO:
184		if((ms->flags & MAGIC_DEVICES) != 0)
185			break;
186		if (file_printf(ms, "fifo (named pipe)") == -1)
187			return -1;
188		return 1;
189#endif
190#ifdef	S_IFDOOR
191	case S_IFDOOR:
192		if (file_printf(ms, "door") == -1)
193			return -1;
194		return 1;
195#endif
196#ifdef	S_IFLNK
197	case S_IFLNK:
198		if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) {
199			if (ms->flags & MAGIC_ERROR) {
200			    file_error(ms, errno, "unreadable symlink `%s'",
201				fn);
202			    return -1;
203			}
204			if (file_printf(ms,
205			    "unreadable symlink `%s' (%s)", fn,
206			    strerror(errno)) == -1)
207				return -1;
208			return 1;
209		}
210		buf[nch] = '\0';	/* readlink(2) forgets this */
211
212		/* If broken symlink, say so and quit early. */
213		if (*buf == '/') {
214		    if (stat(buf, &tstatbuf) < 0) {
215			    if (ms->flags & MAGIC_ERROR) {
216				    file_error(ms, errno,
217					"broken symbolic link to `%s'", buf);
218				    return -1;
219			    }
220			    if (file_printf(ms, "broken symbolic link to `%s'",
221				buf) == -1)
222				    return -1;
223			    return 1;
224		    }
225		}
226		else {
227			char *tmp;
228			char buf2[BUFSIZ+BUFSIZ+4];
229
230			if ((tmp = strrchr(fn,  '/')) == NULL) {
231				tmp = buf; /* in current directory anyway */
232			} else {
233				if (tmp - fn + 1 > BUFSIZ) {
234					if (ms->flags & MAGIC_ERROR) {
235						file_error(ms, 0,
236						    "path too long: `%s'", buf);
237						return -1;
238					}
239					if (file_printf(ms,
240					    "path too long: `%s'", fn) == -1)
241						return -1;
242					return 1;
243				}
244				(void)strcpy(buf2, fn);  /* take dir part */
245				buf2[tmp - fn + 1] = '\0';
246				(void)strcat(buf2, buf); /* plus (rel) link */
247				tmp = buf2;
248			}
249			if (stat(tmp, &tstatbuf) < 0) {
250				if (ms->flags & MAGIC_ERROR) {
251					file_error(ms, errno,
252					    "broken symbolic link to `%s'",
253					    buf);
254					return -1;
255				}
256				if (file_printf(ms,
257				    "broken symbolic link to `%s'", buf) == -1)
258					return -1;
259				return 1;
260			}
261		}
262
263		/* Otherwise, handle it. */
264		if ((ms->flags & MAGIC_SYMLINK) != 0) {
265			const char *p;
266			ms->flags &= MAGIC_SYMLINK;
267			p = magic_file(ms, buf);
268			ms->flags |= MAGIC_SYMLINK;
269			return p != NULL ? 1 : -1;
270		} else { /* just print what it points to */
271			if (file_printf(ms, "symbolic link to `%s'",
272			    buf) == -1)
273				return -1;
274		}
275	return 1;
276#endif
277#ifdef	S_IFSOCK
278#ifndef __COHERENT__
279	case S_IFSOCK:
280		if (file_printf(ms, "socket") == -1)
281			return -1;
282		return 1;
283#endif
284#endif
285	case S_IFREG:
286		break;
287	default:
288		file_error(ms, 0, "invalid mode 0%o", sb->st_mode);
289		return -1;
290		/*NOTREACHED*/
291	}
292
293	/*
294	 * regular file, check next possibility
295	 *
296	 * If stat() tells us the file has zero length, report here that
297	 * the file is empty, so we can skip all the work of opening and
298	 * reading the file.
299	 * But if the -s option has been given, we skip this optimization,
300	 * since on some systems, stat() reports zero size for raw disk
301	 * partitions.  (If the block special device really has zero length,
302	 * the fact that it is empty will be detected and reported correctly
303	 * when we read the file.)
304	 */
305	if ((ms->flags & MAGIC_DEVICES) == 0 && sb->st_size == 0) {
306		if (file_printf(ms, (ms->flags & MAGIC_MIME) ?
307		    "application/x-empty" : "empty") == -1)
308			return -1;
309		return 1;
310	}
311	return 0;
312}
313