fsmagic.c revision 139368
1169689Skan/*
2169689Skan * Copyright (c) Ian F. Darwin 1986-1995.
3169689Skan * Software written by Ian F. Darwin and others;
4169689Skan * maintained 1995-present by Christos Zoulas and others.
5169689Skan *
6169689Skan * Redistribution and use in source and binary forms, with or without
7169689Skan * modification, are permitted provided that the following conditions
8169689Skan * are met:
9169689Skan * 1. Redistributions of source code must retain the above copyright
10169689Skan *    notice immediately at the beginning of the file, without modification,
11169689Skan *    this list of conditions, and the following disclaimer.
12169689Skan * 2. Redistributions in binary form must reproduce the above copyright
13169689Skan *    notice, this list of conditions and the following disclaimer in the
14169689Skan *    documentation and/or other materials provided with the distribution.
15169689Skan *
16169689Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19169689Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20169689Skan * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26169689Skan * SUCH DAMAGE.
27169689Skan */
28169689Skan/*
29169689Skan * fsmagic - magic based on filesystem info - directory, special files, etc.
30169689Skan */
31169689Skan
32169689Skan#include "file.h"
33169689Skan#include "magic.h"
34169689Skan#include <string.h>
35169689Skan#ifdef HAVE_UNISTD_H
36169689Skan#include <unistd.h>
37169689Skan#endif
38169689Skan#include <stdlib.h>
39169689Skan#include <sys/stat.h>
40169689Skan/* Since major is a function on SVR4, we cannot use `ifndef major'.  */
41169689Skan#ifdef MAJOR_IN_MKDEV
42169689Skan# include <sys/mkdev.h>
43169689Skan# define HAVE_MAJOR
44169689Skan#endif
45169689Skan#ifdef MAJOR_IN_SYSMACROS
46169689Skan# include <sys/sysmacros.h>
47169689Skan# define HAVE_MAJOR
48169689Skan#endif
49169689Skan#ifdef major			/* Might be defined in sys/types.h.  */
50169689Skan# define HAVE_MAJOR
51169689Skan#endif
52169689Skan
53169689Skan#ifndef HAVE_MAJOR
54169689Skan# define major(dev)  (((dev) >> 8) & 0xff)
55# define minor(dev)  ((dev) & 0xff)
56#endif
57#undef HAVE_MAJOR
58
59#ifndef	lint
60FILE_RCSID("@(#)$Id: fsmagic.c,v 1.45 2004/11/13 10:19:48 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 (file_printf(ms, "fifo (named pipe)") == -1)
185			return -1;
186		return 1;
187#endif
188#ifdef	S_IFDOOR
189	case S_IFDOOR:
190		if (file_printf(ms, "door") == -1)
191			return -1;
192		return 1;
193#endif
194#ifdef	S_IFLNK
195	case S_IFLNK:
196		if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) {
197			if (ms->flags & MAGIC_ERROR) {
198			    file_error(ms, errno, "unreadable symlink `%s'",
199				fn);
200			    return -1;
201			}
202			if (file_printf(ms,
203			    "unreadable symlink `%s' (%s)", fn,
204			    strerror(errno)) == -1)
205				return -1;
206			return 1;
207		}
208		buf[nch] = '\0';	/* readlink(2) forgets this */
209
210		/* If broken symlink, say so and quit early. */
211		if (*buf == '/') {
212		    if (stat(buf, &tstatbuf) < 0) {
213			    if (ms->flags & MAGIC_ERROR) {
214				    file_error(ms, errno,
215					"broken symbolic link to `%s'", buf);
216				    return -1;
217			    }
218			    if (file_printf(ms, "broken symbolic link to `%s'",
219				buf) == -1)
220				    return -1;
221			    return 1;
222		    }
223		}
224		else {
225			char *tmp;
226			char buf2[BUFSIZ+BUFSIZ+4];
227
228			if ((tmp = strrchr(fn,  '/')) == NULL) {
229				tmp = buf; /* in current directory anyway */
230			} else {
231				if (tmp - fn + 1 > BUFSIZ) {
232					if (ms->flags & MAGIC_ERROR) {
233						file_error(ms, 0,
234						    "path too long: `%s'", buf);
235						return -1;
236					}
237					if (file_printf(ms,
238					    "path too long: `%s'", fn) == -1)
239						return -1;
240					return 1;
241				}
242				(void)strcpy(buf2, fn);  /* take dir part */
243				buf2[tmp - fn + 1] = '\0';
244				(void)strcat(buf2, buf); /* plus (rel) link */
245				tmp = buf2;
246			}
247			if (stat(tmp, &tstatbuf) < 0) {
248				if (ms->flags & MAGIC_ERROR) {
249					file_error(ms, errno,
250					    "broken symbolic link to `%s'",
251					    buf);
252					return -1;
253				}
254				if (file_printf(ms,
255				    "broken symbolic link to `%s'", buf) == -1)
256					return -1;
257				return 1;
258			}
259		}
260
261		/* Otherwise, handle it. */
262		if ((ms->flags & MAGIC_SYMLINK) != 0) {
263			const char *p;
264			ms->flags &= MAGIC_SYMLINK;
265			p = magic_file(ms, buf);
266			ms->flags |= MAGIC_SYMLINK;
267			return p != NULL ? 1 : -1;
268		} else { /* just print what it points to */
269			if (file_printf(ms, "symbolic link to `%s'",
270			    buf) == -1)
271				return -1;
272		}
273	return 1;
274#endif
275#ifdef	S_IFSOCK
276#ifndef __COHERENT__
277	case S_IFSOCK:
278		if (file_printf(ms, "socket") == -1)
279			return -1;
280		return 1;
281#endif
282#endif
283	case S_IFREG:
284		break;
285	default:
286		file_error(ms, 0, "invalid mode 0%o", sb->st_mode);
287		return -1;
288		/*NOTREACHED*/
289	}
290
291	/*
292	 * regular file, check next possibility
293	 *
294	 * If stat() tells us the file has zero length, report here that
295	 * the file is empty, so we can skip all the work of opening and
296	 * reading the file.
297	 * But if the -s option has been given, we skip this optimization,
298	 * since on some systems, stat() reports zero size for raw disk
299	 * partitions.  (If the block special device really has zero length,
300	 * the fact that it is empty will be detected and reported correctly
301	 * when we read the file.)
302	 */
303	if ((ms->flags & MAGIC_DEVICES) == 0 && sb->st_size == 0) {
304		if (file_printf(ms, (ms->flags & MAGIC_MIME) ?
305		    "application/x-empty" : "empty") == -1)
306			return -1;
307		return 1;
308	}
309	return 0;
310}
311