fsmagic.c revision 68349
168349Sobrien/*
268349Sobrien * fsmagic - magic based on filesystem info - directory, special files, etc.
368349Sobrien *
468349Sobrien * Copyright (c) Ian F. Darwin, 1987.
568349Sobrien * Written by Ian F. Darwin.
668349Sobrien *
768349Sobrien * This software is not subject to any license of the American Telephone
868349Sobrien * and Telegraph Company or of the Regents of the University of California.
968349Sobrien *
1068349Sobrien * Permission is granted to anyone to use this software for any purpose on
1168349Sobrien * any computer system, and to alter it and redistribute it freely, subject
1268349Sobrien * to the following restrictions:
1368349Sobrien *
1468349Sobrien * 1. The author is not responsible for the consequences of use of this
1568349Sobrien *    software, no matter how awful, even if they arise from flaws in it.
1668349Sobrien *
1768349Sobrien * 2. The origin of this software must not be misrepresented, either by
1868349Sobrien *    explicit claim or by omission.  Since few users ever read sources,
1968349Sobrien *    credits must appear in the documentation.
2068349Sobrien *
2168349Sobrien * 3. Altered versions must be plainly marked as such, and must not be
2268349Sobrien *    misrepresented as being the original software.  Since few users
2368349Sobrien *    ever read sources, credits must appear in the documentation.
2468349Sobrien *
2568349Sobrien * 4. This notice may not be removed or altered.
2668349Sobrien */
2768349Sobrien
2868349Sobrien#include "file.h"
2968349Sobrien#include <stdio.h>
3068349Sobrien#include <string.h>
3168349Sobrien#include <sys/types.h>
3268349Sobrien#include <sys/stat.h>
3368349Sobrien#ifdef HAVE_UNISTD_H
3468349Sobrien#include <unistd.h>
3568349Sobrien#endif
3668349Sobrien#include <stdlib.h>
3768349Sobrien/* Since major is a function on SVR4, we can't use `ifndef major'.  */
3868349Sobrien#ifdef MAJOR_IN_MKDEV
3968349Sobrien# include <sys/mkdev.h>
4068349Sobrien# define HAVE_MAJOR
4168349Sobrien#endif
4268349Sobrien#ifdef MAJOR_IN_SYSMACROS
4368349Sobrien# include <sys/sysmacros.h>
4468349Sobrien# define HAVE_MAJOR
4568349Sobrien#endif
4668349Sobrien#ifdef major			/* Might be defined in sys/types.h.  */
4768349Sobrien# define HAVE_MAJOR
4868349Sobrien#endif
4968349Sobrien
5068349Sobrien#ifndef HAVE_MAJOR
5168349Sobrien# define major(dev)  (((dev) >> 8) & 0xff)
5268349Sobrien# define minor(dev)  ((dev) & 0xff)
5368349Sobrien#endif
5468349Sobrien#undef HAVE_MAJOR
5568349Sobrien
5668349Sobrien#ifndef	lint
5768349SobrienFILE_RCSID("@(#)$Id: fsmagic.c,v 1.33 2000/08/05 17:36:48 christos Exp $")
5868349Sobrien#endif	/* lint */
5968349Sobrien
6068349Sobrienint
6168349Sobrienfsmagic(fn, sb)
6268349Sobrien	const char *fn;
6368349Sobrien	struct stat *sb;
6468349Sobrien{
6568349Sobrien	int ret = 0;
6668349Sobrien
6768349Sobrien	/*
6868349Sobrien	 * Fstat is cheaper but fails for files you don't have read perms on.
6968349Sobrien	 * On 4.2BSD and similar systems, use lstat() to identify symlinks.
7068349Sobrien	 */
7168349Sobrien#ifdef	S_IFLNK
7268349Sobrien	if (!lflag)
7368349Sobrien		ret = lstat(fn, sb);
7468349Sobrien	else
7568349Sobrien#endif
7668349Sobrien	ret = stat(fn, sb);	/* don't merge into if; see "ret =" above */
7768349Sobrien
7868349Sobrien	if (ret) {
7968349Sobrien		ckfprintf(stdout,
8068349Sobrien			/* Yes, I do mean stdout. */
8168349Sobrien			/* No \n, caller will provide. */
8268349Sobrien			"can't stat `%s' (%s).", fn, strerror(errno));
8368349Sobrien		return 1;
8468349Sobrien	}
8568349Sobrien
8668349Sobrien	if (iflag) {
8768349Sobrien		if ((sb->st_mode & S_IFMT) != S_IFREG) {
8868349Sobrien			ckfputs("application/x-not-regular-file", stdout);
8968349Sobrien			return 1;
9068349Sobrien		}
9168349Sobrien	}
9268349Sobrien	else {
9368349Sobrien#ifdef S_ISUID
9468349Sobrien		if (sb->st_mode & S_ISUID) ckfputs("setuid ", stdout);
9568349Sobrien#endif
9668349Sobrien#ifdef S_ISGID
9768349Sobrien		if (sb->st_mode & S_ISGID) ckfputs("setgid ", stdout);
9868349Sobrien#endif
9968349Sobrien#ifdef S_ISVTX
10068349Sobrien		if (sb->st_mode & S_ISVTX) ckfputs("sticky ", stdout);
10168349Sobrien#endif
10268349Sobrien	}
10368349Sobrien
10468349Sobrien	switch (sb->st_mode & S_IFMT) {
10568349Sobrien	case S_IFDIR:
10668349Sobrien		ckfputs("directory", stdout);
10768349Sobrien		return 1;
10868349Sobrien#ifdef S_IFCHR
10968349Sobrien	case S_IFCHR:
11068349Sobrien		/*
11168349Sobrien		 * If -s has been specified, treat character special files
11268349Sobrien		 * like ordinary files.  Otherwise, just report that they
11368349Sobrien		 * are block special files and go on to the next file.
11468349Sobrien		 */
11568349Sobrien		if (sflag)
11668349Sobrien			break;
11768349Sobrien#ifdef HAVE_ST_RDEV
11868349Sobrien# ifdef dv_unit
11968349Sobrien		(void) printf("character special (%d/%d/%d)",
12068349Sobrien			major(sb->st_rdev),
12168349Sobrien			dv_unit(sb->st_rdev),
12268349Sobrien			dv_subunit(sb->st_rdev));
12368349Sobrien# else
12468349Sobrien		(void) printf("character special (%ld/%ld)",
12568349Sobrien			(long) major(sb->st_rdev), (long) minor(sb->st_rdev));
12668349Sobrien# endif
12768349Sobrien#else
12868349Sobrien		(void) printf("character special");
12968349Sobrien#endif
13068349Sobrien		return 1;
13168349Sobrien#endif
13268349Sobrien#ifdef S_IFBLK
13368349Sobrien	case S_IFBLK:
13468349Sobrien		/*
13568349Sobrien		 * If -s has been specified, treat block special files
13668349Sobrien		 * like ordinary files.  Otherwise, just report that they
13768349Sobrien		 * are block special files and go on to the next file.
13868349Sobrien		 */
13968349Sobrien		if (sflag)
14068349Sobrien			break;
14168349Sobrien#ifdef HAVE_ST_RDEV
14268349Sobrien# ifdef dv_unit
14368349Sobrien		(void) printf("block special (%d/%d/%d)",
14468349Sobrien			major(sb->st_rdev),
14568349Sobrien			dv_unit(sb->st_rdev),
14668349Sobrien			dv_subunit(sb->st_rdev));
14768349Sobrien# else
14868349Sobrien		(void) printf("block special (%ld/%ld)",
14968349Sobrien			(long) major(sb->st_rdev), (long) minor(sb->st_rdev));
15068349Sobrien# endif
15168349Sobrien#else
15268349Sobrien		(void) printf("block special");
15368349Sobrien#endif
15468349Sobrien		return 1;
15568349Sobrien#endif
15668349Sobrien	/* TODO add code to handle V7 MUX and Blit MUX files */
15768349Sobrien#ifdef	S_IFIFO
15868349Sobrien	case S_IFIFO:
15968349Sobrien		ckfputs("fifo (named pipe)", stdout);
16068349Sobrien		return 1;
16168349Sobrien#endif
16268349Sobrien#ifdef	S_IFDOOR
16368349Sobrien	case S_IFDOOR:
16468349Sobrien		ckfputs("door", stdout);
16568349Sobrien		return 1;
16668349Sobrien#endif
16768349Sobrien#ifdef	S_IFLNK
16868349Sobrien	case S_IFLNK:
16968349Sobrien		{
17068349Sobrien			char buf[BUFSIZ+4];
17168349Sobrien			int nch;
17268349Sobrien			struct stat tstatbuf;
17368349Sobrien
17468349Sobrien			if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) {
17568349Sobrien				ckfprintf(stdout, "unreadable symlink (%s).",
17668349Sobrien				      strerror(errno));
17768349Sobrien				return 1;
17868349Sobrien			}
17968349Sobrien			buf[nch] = '\0';	/* readlink(2) forgets this */
18068349Sobrien
18168349Sobrien			/* If broken symlink, say so and quit early. */
18268349Sobrien			if (*buf == '/') {
18368349Sobrien			    if (stat(buf, &tstatbuf) < 0) {
18468349Sobrien				ckfprintf(stdout,
18568349Sobrien					"broken symbolic link to %s", buf);
18668349Sobrien				return 1;
18768349Sobrien			    }
18868349Sobrien			}
18968349Sobrien			else {
19068349Sobrien			    char *tmp;
19168349Sobrien			    char buf2[BUFSIZ+BUFSIZ+4];
19268349Sobrien
19368349Sobrien			    if ((tmp = strrchr(fn,  '/')) == NULL) {
19468349Sobrien				tmp = buf; /* in current directory anyway */
19568349Sobrien			    }
19668349Sobrien			    else {
19768349Sobrien				strcpy (buf2, fn);  /* take directory part */
19868349Sobrien				buf2[tmp-fn+1] = '\0';
19968349Sobrien				strcat (buf2, buf); /* plus (relative) symlink */
20068349Sobrien				tmp = buf2;
20168349Sobrien			    }
20268349Sobrien			    if (stat(tmp, &tstatbuf) < 0) {
20368349Sobrien				ckfprintf(stdout,
20468349Sobrien					"broken symbolic link to %s", buf);
20568349Sobrien				return 1;
20668349Sobrien			    }
20768349Sobrien                        }
20868349Sobrien
20968349Sobrien			/* Otherwise, handle it. */
21068349Sobrien			if (lflag) {
21168349Sobrien				process(buf, strlen(buf));
21268349Sobrien				return 1;
21368349Sobrien			} else { /* just print what it points to */
21468349Sobrien				ckfputs("symbolic link to ", stdout);
21568349Sobrien				ckfputs(buf, stdout);
21668349Sobrien			}
21768349Sobrien		}
21868349Sobrien		return 1;
21968349Sobrien#endif
22068349Sobrien#ifdef	S_IFSOCK
22168349Sobrien#ifndef __COHERENT__
22268349Sobrien	case S_IFSOCK:
22368349Sobrien		ckfputs("socket", stdout);
22468349Sobrien		return 1;
22568349Sobrien#endif
22668349Sobrien#endif
22768349Sobrien	case S_IFREG:
22868349Sobrien		break;
22968349Sobrien	default:
23068349Sobrien		error("invalid mode 0%o.\n", sb->st_mode);
23168349Sobrien		/*NOTREACHED*/
23268349Sobrien	}
23368349Sobrien
23468349Sobrien	/*
23568349Sobrien	 * regular file, check next possibility
23668349Sobrien	 *
23768349Sobrien	 * If stat() tells us the file has zero length, report here that
23868349Sobrien	 * the file is empty, so we can skip all the work of opening and
23968349Sobrien	 * reading the file.
24068349Sobrien	 * But if the -s option has been given, we skip this optimization,
24168349Sobrien	 * since on some systems, stat() reports zero size for raw disk
24268349Sobrien	 * partitions.  (If the block special device really has zero length,
24368349Sobrien	 * the fact that it is empty will be detected and reported correctly
24468349Sobrien	 * when we read the file.)
24568349Sobrien	 */
24668349Sobrien	if (!sflag && sb->st_size == 0) {
24768349Sobrien		ckfputs(iflag ? "application/x-empty" : "empty", stdout);
24868349Sobrien		return 1;
24968349Sobrien	}
25068349Sobrien	return 0;
25168349Sobrien}
252