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