fsmagic.c revision 139368
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 * 16133359Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18133359Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19133359Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20133359Sobrien * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21133359Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22133359Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23133359Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24133359Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25133359Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26133359Sobrien * SUCH DAMAGE. 27133359Sobrien */ 28133359Sobrien/* 2968349Sobrien * fsmagic - magic based on filesystem info - directory, special files, etc. 3068349Sobrien */ 3168349Sobrien 3268349Sobrien#include "file.h" 33133359Sobrien#include "magic.h" 3468349Sobrien#include <string.h> 3568349Sobrien#ifdef HAVE_UNISTD_H 3668349Sobrien#include <unistd.h> 3768349Sobrien#endif 3868349Sobrien#include <stdlib.h> 39133359Sobrien#include <sys/stat.h> 40133359Sobrien/* Since major is a function on SVR4, we cannot use `ifndef major'. */ 4168349Sobrien#ifdef MAJOR_IN_MKDEV 4268349Sobrien# include <sys/mkdev.h> 4368349Sobrien# define HAVE_MAJOR 4468349Sobrien#endif 4568349Sobrien#ifdef MAJOR_IN_SYSMACROS 4668349Sobrien# include <sys/sysmacros.h> 4768349Sobrien# define HAVE_MAJOR 4868349Sobrien#endif 4968349Sobrien#ifdef major /* Might be defined in sys/types.h. */ 5068349Sobrien# define HAVE_MAJOR 5168349Sobrien#endif 5268349Sobrien 5368349Sobrien#ifndef HAVE_MAJOR 5468349Sobrien# define major(dev) (((dev) >> 8) & 0xff) 5568349Sobrien# define minor(dev) ((dev) & 0xff) 5668349Sobrien#endif 5768349Sobrien#undef HAVE_MAJOR 5868349Sobrien 5968349Sobrien#ifndef lint 60139368SobrienFILE_RCSID("@(#)$Id: fsmagic.c,v 1.45 2004/11/13 10:19:48 christos Exp $") 6168349Sobrien#endif /* lint */ 6268349Sobrien 63133359Sobrienprotected int 64133359Sobrienfile_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) 6568349Sobrien{ 6668349Sobrien int ret = 0; 67133359Sobrien#ifdef S_IFLNK 68133359Sobrien char buf[BUFSIZ+4]; 69133359Sobrien int nch; 70133359Sobrien struct stat tstatbuf; 71133359Sobrien#endif 7268349Sobrien 73133359Sobrien if (fn == NULL) 74133359Sobrien return 0; 75133359Sobrien 7668349Sobrien /* 7768349Sobrien * Fstat is cheaper but fails for files you don't have read perms on. 7868349Sobrien * On 4.2BSD and similar systems, use lstat() to identify symlinks. 7968349Sobrien */ 8068349Sobrien#ifdef S_IFLNK 81133359Sobrien if ((ms->flags & MAGIC_SYMLINK) == 0) 8268349Sobrien ret = lstat(fn, sb); 8368349Sobrien else 8468349Sobrien#endif 8568349Sobrien ret = stat(fn, sb); /* don't merge into if; see "ret =" above */ 8668349Sobrien 8768349Sobrien if (ret) { 88133359Sobrien if (ms->flags & MAGIC_ERROR) { 89133359Sobrien file_error(ms, errno, "cannot stat `%s'", fn); 90133359Sobrien return -1; 91133359Sobrien } 92139368Sobrien if (file_printf(ms, "cannot open `%s' (%s)", 93133359Sobrien fn, strerror(errno)) == -1) 94133359Sobrien return -1; 9568349Sobrien return 1; 9668349Sobrien } 9768349Sobrien 98133359Sobrien if ((ms->flags & MAGIC_MIME) != 0) { 9968349Sobrien if ((sb->st_mode & S_IFMT) != S_IFREG) { 100133359Sobrien if (file_printf(ms, "application/x-not-regular-file") 101133359Sobrien == -1) 102133359Sobrien return -1; 10368349Sobrien return 1; 10468349Sobrien } 10568349Sobrien } 10668349Sobrien else { 10768349Sobrien#ifdef S_ISUID 108133359Sobrien if (sb->st_mode & S_ISUID) 109133359Sobrien if (file_printf(ms, "setuid ") == -1) 110133359Sobrien return -1; 11168349Sobrien#endif 11268349Sobrien#ifdef S_ISGID 113133359Sobrien if (sb->st_mode & S_ISGID) 114133359Sobrien if (file_printf(ms, "setgid ") == -1) 115133359Sobrien return -1; 11668349Sobrien#endif 11768349Sobrien#ifdef S_ISVTX 118133359Sobrien if (sb->st_mode & S_ISVTX) 119133359Sobrien if (file_printf(ms, "sticky ") == -1) 120133359Sobrien return -1; 12168349Sobrien#endif 12268349Sobrien } 12368349Sobrien 12468349Sobrien switch (sb->st_mode & S_IFMT) { 12568349Sobrien case S_IFDIR: 126133359Sobrien if (file_printf(ms, "directory") == -1) 127133359Sobrien return -1; 12868349Sobrien return 1; 12968349Sobrien#ifdef S_IFCHR 13068349Sobrien case S_IFCHR: 13168349Sobrien /* 13268349Sobrien * If -s has been specified, treat character special files 13368349Sobrien * like ordinary files. Otherwise, just report that they 13468349Sobrien * are block special files and go on to the next file. 13568349Sobrien */ 136133359Sobrien if ((ms->flags & MAGIC_DEVICES) != 0) 13768349Sobrien break; 13868349Sobrien#ifdef HAVE_ST_RDEV 13968349Sobrien# ifdef dv_unit 140133359Sobrien if (file_printf(ms, "character special (%d/%d/%d)", 141133359Sobrien major(sb->st_rdev), dv_unit(sb->st_rdev), 142133359Sobrien dv_subunit(sb->st_rdev)) == -1) 143133359Sobrien return -1; 14468349Sobrien# else 145133359Sobrien if (file_printf(ms, "character special (%ld/%ld)", 146133359Sobrien (long) major(sb->st_rdev), (long) minor(sb->st_rdev)) == -1) 147133359Sobrien return -1; 14868349Sobrien# endif 14968349Sobrien#else 150133359Sobrien if (file_printf(ms, "character special") == -1) 151133359Sobrien return -1; 15268349Sobrien#endif 15368349Sobrien return 1; 15468349Sobrien#endif 15568349Sobrien#ifdef S_IFBLK 15668349Sobrien case S_IFBLK: 15768349Sobrien /* 15868349Sobrien * If -s has been specified, treat block special files 15968349Sobrien * like ordinary files. Otherwise, just report that they 16068349Sobrien * are block special files and go on to the next file. 16168349Sobrien */ 162133359Sobrien if ((ms->flags & MAGIC_DEVICES) != 0) 16368349Sobrien break; 16468349Sobrien#ifdef HAVE_ST_RDEV 16568349Sobrien# ifdef dv_unit 166133359Sobrien if (file_printf(ms, "block special (%d/%d/%d)", 167133359Sobrien major(sb->st_rdev), dv_unit(sb->st_rdev), 168133359Sobrien dv_subunit(sb->st_rdev)) == -1) 169133359Sobrien return -1; 17068349Sobrien# else 171133359Sobrien if (file_printf(ms, "block special (%ld/%ld)", 172133359Sobrien (long)major(sb->st_rdev), (long)minor(sb->st_rdev)) == -1) 173133359Sobrien return -1; 17468349Sobrien# endif 17568349Sobrien#else 176133359Sobrien if (file_printf(ms, "block special") == -1) 177133359Sobrien return -1; 17868349Sobrien#endif 17968349Sobrien return 1; 18068349Sobrien#endif 18168349Sobrien /* TODO add code to handle V7 MUX and Blit MUX files */ 18268349Sobrien#ifdef S_IFIFO 18368349Sobrien case S_IFIFO: 184133359Sobrien if (file_printf(ms, "fifo (named pipe)") == -1) 185133359Sobrien return -1; 18668349Sobrien return 1; 18768349Sobrien#endif 18868349Sobrien#ifdef S_IFDOOR 18968349Sobrien case S_IFDOOR: 190133359Sobrien if (file_printf(ms, "door") == -1) 191133359Sobrien return -1; 19268349Sobrien return 1; 19368349Sobrien#endif 19468349Sobrien#ifdef S_IFLNK 19568349Sobrien case S_IFLNK: 196133359Sobrien if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) { 197133359Sobrien if (ms->flags & MAGIC_ERROR) { 198133359Sobrien file_error(ms, errno, "unreadable symlink `%s'", 199133359Sobrien fn); 200133359Sobrien return -1; 20168349Sobrien } 202133359Sobrien if (file_printf(ms, 203133359Sobrien "unreadable symlink `%s' (%s)", fn, 204133359Sobrien strerror(errno)) == -1) 205133359Sobrien return -1; 206133359Sobrien return 1; 207133359Sobrien } 208133359Sobrien buf[nch] = '\0'; /* readlink(2) forgets this */ 20968349Sobrien 210133359Sobrien /* If broken symlink, say so and quit early. */ 211133359Sobrien if (*buf == '/') { 212133359Sobrien if (stat(buf, &tstatbuf) < 0) { 213133359Sobrien if (ms->flags & MAGIC_ERROR) { 214133359Sobrien file_error(ms, errno, 215133359Sobrien "broken symbolic link to `%s'", buf); 216133359Sobrien return -1; 217133359Sobrien } 218133359Sobrien if (file_printf(ms, "broken symbolic link to `%s'", 219133359Sobrien buf) == -1) 220133359Sobrien return -1; 221133359Sobrien return 1; 222133359Sobrien } 223133359Sobrien } 224133359Sobrien else { 225133359Sobrien char *tmp; 226133359Sobrien char buf2[BUFSIZ+BUFSIZ+4]; 22768349Sobrien 228133359Sobrien if ((tmp = strrchr(fn, '/')) == NULL) { 22968349Sobrien tmp = buf; /* in current directory anyway */ 230133359Sobrien } else { 231133359Sobrien if (tmp - fn + 1 > BUFSIZ) { 232133359Sobrien if (ms->flags & MAGIC_ERROR) { 233133359Sobrien file_error(ms, 0, 234133359Sobrien "path too long: `%s'", buf); 235133359Sobrien return -1; 236133359Sobrien } 237133359Sobrien if (file_printf(ms, 238133359Sobrien "path too long: `%s'", fn) == -1) 239133359Sobrien return -1; 240133359Sobrien return 1; 241133359Sobrien } 242133359Sobrien (void)strcpy(buf2, fn); /* take dir part */ 243133359Sobrien buf2[tmp - fn + 1] = '\0'; 244133359Sobrien (void)strcat(buf2, buf); /* plus (rel) link */ 24568349Sobrien tmp = buf2; 246133359Sobrien } 247133359Sobrien if (stat(tmp, &tstatbuf) < 0) { 248133359Sobrien if (ms->flags & MAGIC_ERROR) { 249133359Sobrien file_error(ms, errno, 250133359Sobrien "broken symbolic link to `%s'", 251133359Sobrien buf); 252133359Sobrien return -1; 253133359Sobrien } 254133359Sobrien if (file_printf(ms, 255133359Sobrien "broken symbolic link to `%s'", buf) == -1) 256133359Sobrien return -1; 25768349Sobrien return 1; 25868349Sobrien } 25968349Sobrien } 260133359Sobrien 261133359Sobrien /* Otherwise, handle it. */ 262133359Sobrien if ((ms->flags & MAGIC_SYMLINK) != 0) { 263133359Sobrien const char *p; 264133359Sobrien ms->flags &= MAGIC_SYMLINK; 265133359Sobrien p = magic_file(ms, buf); 266133359Sobrien ms->flags |= MAGIC_SYMLINK; 267133359Sobrien return p != NULL ? 1 : -1; 268133359Sobrien } else { /* just print what it points to */ 269133359Sobrien if (file_printf(ms, "symbolic link to `%s'", 270133359Sobrien buf) == -1) 271133359Sobrien return -1; 272133359Sobrien } 273133359Sobrien return 1; 27468349Sobrien#endif 27568349Sobrien#ifdef S_IFSOCK 27668349Sobrien#ifndef __COHERENT__ 27768349Sobrien case S_IFSOCK: 278133359Sobrien if (file_printf(ms, "socket") == -1) 279133359Sobrien return -1; 28068349Sobrien return 1; 28168349Sobrien#endif 28268349Sobrien#endif 28368349Sobrien case S_IFREG: 28468349Sobrien break; 28568349Sobrien default: 286133359Sobrien file_error(ms, 0, "invalid mode 0%o", sb->st_mode); 287133359Sobrien return -1; 28868349Sobrien /*NOTREACHED*/ 28968349Sobrien } 29068349Sobrien 29168349Sobrien /* 29268349Sobrien * regular file, check next possibility 29368349Sobrien * 29468349Sobrien * If stat() tells us the file has zero length, report here that 29568349Sobrien * the file is empty, so we can skip all the work of opening and 29668349Sobrien * reading the file. 29768349Sobrien * But if the -s option has been given, we skip this optimization, 29868349Sobrien * since on some systems, stat() reports zero size for raw disk 29968349Sobrien * partitions. (If the block special device really has zero length, 30068349Sobrien * the fact that it is empty will be detected and reported correctly 30168349Sobrien * when we read the file.) 30268349Sobrien */ 303133359Sobrien if ((ms->flags & MAGIC_DEVICES) == 0 && sb->st_size == 0) { 304133359Sobrien if (file_printf(ms, (ms->flags & MAGIC_MIME) ? 305133359Sobrien "application/x-empty" : "empty") == -1) 306133359Sobrien return -1; 30768349Sobrien return 1; 30868349Sobrien } 30968349Sobrien return 0; 31068349Sobrien} 311