fsmagic.c revision 133359
168349Sobrien/*
2133359Sobrien * Copyright (c) Ian F. Darwin 1986-1995.
3133359Sobrien * Software written by Ian F. Darwin and others;
4133359Sobrien * maintained 1995-present by Christos Zoulas and others.
5133359Sobrien *
6133359Sobrien * Redistribution and use in source and binary forms, with or without
7133359Sobrien * modification, are permitted provided that the following conditions
8133359Sobrien * are met:
9133359Sobrien * 1. Redistributions of source code must retain the above copyright
10133359Sobrien *    notice immediately at the beginning of the file, without modification,
11133359Sobrien *    this list of conditions, and the following disclaimer.
12133359Sobrien * 2. Redistributions in binary form must reproduce the above copyright
13133359Sobrien *    notice, this list of conditions and the following disclaimer in the
14133359Sobrien *    documentation and/or other materials provided with the distribution.
15133359Sobrien * 3. All advertising materials mentioning features or use of this software
16133359Sobrien *    must display the following acknowledgement:
17133359Sobrien *    This product includes software developed by Ian F. Darwin and others.
18133359Sobrien * 4. The name of the author may not be used to endorse or promote products
19133359Sobrien *    derived from this software without specific prior written permission.
20133359Sobrien *
21133359Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23133359Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24133359Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
25133359Sobrien * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26133359Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27133359Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28133359Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29133359Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30133359Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31133359Sobrien * SUCH DAMAGE.
32133359Sobrien */
33133359Sobrien/*
3468349Sobrien * fsmagic - magic based on filesystem info - directory, special files, etc.
3568349Sobrien */
3668349Sobrien
3768349Sobrien#include "file.h"
38133359Sobrien#include "magic.h"
3968349Sobrien#include <string.h>
4068349Sobrien#ifdef HAVE_UNISTD_H
4168349Sobrien#include <unistd.h>
4268349Sobrien#endif
4368349Sobrien#include <stdlib.h>
44133359Sobrien#include <sys/stat.h>
45133359Sobrien/* Since major is a function on SVR4, we cannot use `ifndef major'.  */
4668349Sobrien#ifdef MAJOR_IN_MKDEV
4768349Sobrien# include <sys/mkdev.h>
4868349Sobrien# define HAVE_MAJOR
4968349Sobrien#endif
5068349Sobrien#ifdef MAJOR_IN_SYSMACROS
5168349Sobrien# include <sys/sysmacros.h>
5268349Sobrien# define HAVE_MAJOR
5368349Sobrien#endif
5468349Sobrien#ifdef major			/* Might be defined in sys/types.h.  */
5568349Sobrien# define HAVE_MAJOR
5668349Sobrien#endif
5768349Sobrien
5868349Sobrien#ifndef HAVE_MAJOR
5968349Sobrien# define major(dev)  (((dev) >> 8) & 0xff)
6068349Sobrien# define minor(dev)  ((dev) & 0xff)
6168349Sobrien#endif
6268349Sobrien#undef HAVE_MAJOR
6368349Sobrien
6468349Sobrien#ifndef	lint
65133359SobrienFILE_RCSID("@(#)$Id: fsmagic.c,v 1.43 2003/10/14 19:29:55 christos Exp $")
6668349Sobrien#endif	/* lint */
6768349Sobrien
68133359Sobrienprotected int
69133359Sobrienfile_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb)
7068349Sobrien{
7168349Sobrien	int ret = 0;
72133359Sobrien#ifdef	S_IFLNK
73133359Sobrien	char buf[BUFSIZ+4];
74133359Sobrien	int nch;
75133359Sobrien	struct stat tstatbuf;
76133359Sobrien#endif
7768349Sobrien
78133359Sobrien	if (fn == NULL)
79133359Sobrien		return 0;
80133359Sobrien
8168349Sobrien	/*
8268349Sobrien	 * Fstat is cheaper but fails for files you don't have read perms on.
8368349Sobrien	 * On 4.2BSD and similar systems, use lstat() to identify symlinks.
8468349Sobrien	 */
8568349Sobrien#ifdef	S_IFLNK
86133359Sobrien	if ((ms->flags & MAGIC_SYMLINK) == 0)
8768349Sobrien		ret = lstat(fn, sb);
8868349Sobrien	else
8968349Sobrien#endif
9068349Sobrien	ret = stat(fn, sb);	/* don't merge into if; see "ret =" above */
9168349Sobrien
9268349Sobrien	if (ret) {
93133359Sobrien		if (ms->flags & MAGIC_ERROR) {
94133359Sobrien			file_error(ms, errno, "cannot stat `%s'", fn);
95133359Sobrien			return -1;
96133359Sobrien		}
97133359Sobrien		if (file_printf(ms, "cannot open (%s)",
98133359Sobrien		    fn, strerror(errno)) == -1)
99133359Sobrien			return -1;
10068349Sobrien		return 1;
10168349Sobrien	}
10268349Sobrien
103133359Sobrien	if ((ms->flags & MAGIC_MIME) != 0) {
10468349Sobrien		if ((sb->st_mode & S_IFMT) != S_IFREG) {
105133359Sobrien			if (file_printf(ms, "application/x-not-regular-file")
106133359Sobrien			    == -1)
107133359Sobrien				return -1;
10868349Sobrien			return 1;
10968349Sobrien		}
11068349Sobrien	}
11168349Sobrien	else {
11268349Sobrien#ifdef S_ISUID
113133359Sobrien		if (sb->st_mode & S_ISUID)
114133359Sobrien			if (file_printf(ms, "setuid ") == -1)
115133359Sobrien				return -1;
11668349Sobrien#endif
11768349Sobrien#ifdef S_ISGID
118133359Sobrien		if (sb->st_mode & S_ISGID)
119133359Sobrien			if (file_printf(ms, "setgid ") == -1)
120133359Sobrien				return -1;
12168349Sobrien#endif
12268349Sobrien#ifdef S_ISVTX
123133359Sobrien		if (sb->st_mode & S_ISVTX)
124133359Sobrien			if (file_printf(ms, "sticky ") == -1)
125133359Sobrien				return -1;
12668349Sobrien#endif
12768349Sobrien	}
12868349Sobrien
12968349Sobrien	switch (sb->st_mode & S_IFMT) {
13068349Sobrien	case S_IFDIR:
131133359Sobrien		if (file_printf(ms, "directory") == -1)
132133359Sobrien			return -1;
13368349Sobrien		return 1;
13468349Sobrien#ifdef S_IFCHR
13568349Sobrien	case S_IFCHR:
13668349Sobrien		/*
13768349Sobrien		 * If -s has been specified, treat character special files
13868349Sobrien		 * like ordinary files.  Otherwise, just report that they
13968349Sobrien		 * are block special files and go on to the next file.
14068349Sobrien		 */
141133359Sobrien		if ((ms->flags & MAGIC_DEVICES) != 0)
14268349Sobrien			break;
14368349Sobrien#ifdef HAVE_ST_RDEV
14468349Sobrien# ifdef dv_unit
145133359Sobrien		if (file_printf(ms, "character special (%d/%d/%d)",
146133359Sobrien		    major(sb->st_rdev), dv_unit(sb->st_rdev),
147133359Sobrien		    dv_subunit(sb->st_rdev)) == -1)
148133359Sobrien			return -1;
14968349Sobrien# else
150133359Sobrien		if (file_printf(ms, "character special (%ld/%ld)",
151133359Sobrien		    (long) major(sb->st_rdev), (long) minor(sb->st_rdev)) == -1)
152133359Sobrien			return -1;
15368349Sobrien# endif
15468349Sobrien#else
155133359Sobrien		if (file_printf(ms, "character special") == -1)
156133359Sobrien			return -1;
15768349Sobrien#endif
15868349Sobrien		return 1;
15968349Sobrien#endif
16068349Sobrien#ifdef S_IFBLK
16168349Sobrien	case S_IFBLK:
16268349Sobrien		/*
16368349Sobrien		 * If -s has been specified, treat block special files
16468349Sobrien		 * like ordinary files.  Otherwise, just report that they
16568349Sobrien		 * are block special files and go on to the next file.
16668349Sobrien		 */
167133359Sobrien		if ((ms->flags & MAGIC_DEVICES) != 0)
16868349Sobrien			break;
16968349Sobrien#ifdef HAVE_ST_RDEV
17068349Sobrien# ifdef dv_unit
171133359Sobrien		if (file_printf(ms, "block special (%d/%d/%d)",
172133359Sobrien		    major(sb->st_rdev), dv_unit(sb->st_rdev),
173133359Sobrien		    dv_subunit(sb->st_rdev)) == -1)
174133359Sobrien			return -1;
17568349Sobrien# else
176133359Sobrien		if (file_printf(ms, "block special (%ld/%ld)",
177133359Sobrien		    (long)major(sb->st_rdev), (long)minor(sb->st_rdev)) == -1)
178133359Sobrien			return -1;
17968349Sobrien# endif
18068349Sobrien#else
181133359Sobrien		if (file_printf(ms, "block special") == -1)
182133359Sobrien			return -1;
18368349Sobrien#endif
18468349Sobrien		return 1;
18568349Sobrien#endif
18668349Sobrien	/* TODO add code to handle V7 MUX and Blit MUX files */
18768349Sobrien#ifdef	S_IFIFO
18868349Sobrien	case S_IFIFO:
189133359Sobrien		if (file_printf(ms, "fifo (named pipe)") == -1)
190133359Sobrien			return -1;
19168349Sobrien		return 1;
19268349Sobrien#endif
19368349Sobrien#ifdef	S_IFDOOR
19468349Sobrien	case S_IFDOOR:
195133359Sobrien		if (file_printf(ms, "door") == -1)
196133359Sobrien			return -1;
19768349Sobrien		return 1;
19868349Sobrien#endif
19968349Sobrien#ifdef	S_IFLNK
20068349Sobrien	case S_IFLNK:
201133359Sobrien		if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) {
202133359Sobrien			if (ms->flags & MAGIC_ERROR) {
203133359Sobrien			    file_error(ms, errno, "unreadable symlink `%s'",
204133359Sobrien				fn);
205133359Sobrien			    return -1;
20668349Sobrien			}
207133359Sobrien			if (file_printf(ms,
208133359Sobrien			    "unreadable symlink `%s' (%s)", fn,
209133359Sobrien			    strerror(errno)) == -1)
210133359Sobrien				return -1;
211133359Sobrien			return 1;
212133359Sobrien		}
213133359Sobrien		buf[nch] = '\0';	/* readlink(2) forgets this */
21468349Sobrien
215133359Sobrien		/* If broken symlink, say so and quit early. */
216133359Sobrien		if (*buf == '/') {
217133359Sobrien		    if (stat(buf, &tstatbuf) < 0) {
218133359Sobrien			    if (ms->flags & MAGIC_ERROR) {
219133359Sobrien				    file_error(ms, errno,
220133359Sobrien					"broken symbolic link to `%s'", buf);
221133359Sobrien				    return -1;
222133359Sobrien			    }
223133359Sobrien			    if (file_printf(ms, "broken symbolic link to `%s'",
224133359Sobrien				buf) == -1)
225133359Sobrien				    return -1;
226133359Sobrien			    return 1;
227133359Sobrien		    }
228133359Sobrien		}
229133359Sobrien		else {
230133359Sobrien			char *tmp;
231133359Sobrien			char buf2[BUFSIZ+BUFSIZ+4];
23268349Sobrien
233133359Sobrien			if ((tmp = strrchr(fn,  '/')) == NULL) {
23468349Sobrien				tmp = buf; /* in current directory anyway */
235133359Sobrien			} else {
236133359Sobrien				if (tmp - fn + 1 > BUFSIZ) {
237133359Sobrien					if (ms->flags & MAGIC_ERROR) {
238133359Sobrien						file_error(ms, 0,
239133359Sobrien						    "path too long: `%s'", buf);
240133359Sobrien						return -1;
241133359Sobrien					}
242133359Sobrien					if (file_printf(ms,
243133359Sobrien					    "path too long: `%s'", fn) == -1)
244133359Sobrien						return -1;
245133359Sobrien					return 1;
246133359Sobrien				}
247133359Sobrien				(void)strcpy(buf2, fn);  /* take dir part */
248133359Sobrien				buf2[tmp - fn + 1] = '\0';
249133359Sobrien				(void)strcat(buf2, buf); /* plus (rel) link */
25068349Sobrien				tmp = buf2;
251133359Sobrien			}
252133359Sobrien			if (stat(tmp, &tstatbuf) < 0) {
253133359Sobrien				if (ms->flags & MAGIC_ERROR) {
254133359Sobrien					file_error(ms, errno,
255133359Sobrien					    "broken symbolic link to `%s'",
256133359Sobrien					    buf);
257133359Sobrien					return -1;
258133359Sobrien				}
259133359Sobrien				if (file_printf(ms,
260133359Sobrien				    "broken symbolic link to `%s'", buf) == -1)
261133359Sobrien					return -1;
26268349Sobrien				return 1;
26368349Sobrien			}
26468349Sobrien		}
265133359Sobrien
266133359Sobrien		/* Otherwise, handle it. */
267133359Sobrien		if ((ms->flags & MAGIC_SYMLINK) != 0) {
268133359Sobrien			const char *p;
269133359Sobrien			ms->flags &= MAGIC_SYMLINK;
270133359Sobrien			p = magic_file(ms, buf);
271133359Sobrien			ms->flags |= MAGIC_SYMLINK;
272133359Sobrien			return p != NULL ? 1 : -1;
273133359Sobrien		} else { /* just print what it points to */
274133359Sobrien			if (file_printf(ms, "symbolic link to `%s'",
275133359Sobrien			    buf) == -1)
276133359Sobrien				return -1;
277133359Sobrien		}
278133359Sobrien	return 1;
27968349Sobrien#endif
28068349Sobrien#ifdef	S_IFSOCK
28168349Sobrien#ifndef __COHERENT__
28268349Sobrien	case S_IFSOCK:
283133359Sobrien		if (file_printf(ms, "socket") == -1)
284133359Sobrien			return -1;
28568349Sobrien		return 1;
28668349Sobrien#endif
28768349Sobrien#endif
28868349Sobrien	case S_IFREG:
28968349Sobrien		break;
29068349Sobrien	default:
291133359Sobrien		file_error(ms, 0, "invalid mode 0%o", sb->st_mode);
292133359Sobrien		return -1;
29368349Sobrien		/*NOTREACHED*/
29468349Sobrien	}
29568349Sobrien
29668349Sobrien	/*
29768349Sobrien	 * regular file, check next possibility
29868349Sobrien	 *
29968349Sobrien	 * If stat() tells us the file has zero length, report here that
30068349Sobrien	 * the file is empty, so we can skip all the work of opening and
30168349Sobrien	 * reading the file.
30268349Sobrien	 * But if the -s option has been given, we skip this optimization,
30368349Sobrien	 * since on some systems, stat() reports zero size for raw disk
30468349Sobrien	 * partitions.  (If the block special device really has zero length,
30568349Sobrien	 * the fact that it is empty will be detected and reported correctly
30668349Sobrien	 * when we read the file.)
30768349Sobrien	 */
308133359Sobrien	if ((ms->flags & MAGIC_DEVICES) == 0 && sb->st_size == 0) {
309133359Sobrien		if (file_printf(ms, (ms->flags & MAGIC_MIME) ?
310133359Sobrien		    "application/x-empty" : "empty") == -1)
311133359Sobrien			return -1;
31268349Sobrien		return 1;
31368349Sobrien	}
31468349Sobrien	return 0;
31568349Sobrien}
316