1284194Sdelphij/* 2284194Sdelphij * Copyright (c) Ian F. Darwin 1986-1995. 3284194Sdelphij * Software written by Ian F. Darwin and others; 4284194Sdelphij * maintained 1995-present by Christos Zoulas and others. 5284194Sdelphij * 6284194Sdelphij * Redistribution and use in source and binary forms, with or without 7284194Sdelphij * modification, are permitted provided that the following conditions 8284194Sdelphij * are met: 9284194Sdelphij * 1. Redistributions of source code must retain the above copyright 10284194Sdelphij * notice immediately at the beginning of the file, without modification, 11284194Sdelphij * this list of conditions, and the following disclaimer. 12284194Sdelphij * 2. Redistributions in binary form must reproduce the above copyright 13284194Sdelphij * notice, this list of conditions and the following disclaimer in the 14284194Sdelphij * documentation and/or other materials provided with the distribution. 15284194Sdelphij * 16284194Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17284194Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18284194Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19284194Sdelphij * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20284194Sdelphij * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21284194Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22284194Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23284194Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24284194Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25284194Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26284194Sdelphij * SUCH DAMAGE. 27284194Sdelphij */ 28284194Sdelphij/* 29284194Sdelphij * fsmagic - magic based on filesystem info - directory, special files, etc. 30284194Sdelphij */ 31284194Sdelphij 32284194Sdelphij#include "file.h" 33284194Sdelphij 34284194Sdelphij#ifndef lint 35284194SdelphijFILE_RCSID("@(#)$File: fsmagic.c,v 1.75 2014/12/04 15:56:46 christos Exp $") 36284194Sdelphij#endif /* lint */ 37284194Sdelphij 38284194Sdelphij#include "magic.h" 39284194Sdelphij#include <string.h> 40284194Sdelphij#ifdef HAVE_UNISTD_H 41284194Sdelphij#include <unistd.h> 42284194Sdelphij#endif 43284194Sdelphij#include <stdlib.h> 44284194Sdelphij/* Since major is a function on SVR4, we cannot use `ifndef major'. */ 45284194Sdelphij#ifdef MAJOR_IN_MKDEV 46284194Sdelphij# include <sys/mkdev.h> 47284194Sdelphij# define HAVE_MAJOR 48284194Sdelphij#endif 49284194Sdelphij#ifdef MAJOR_IN_SYSMACROS 50284194Sdelphij# include <sys/sysmacros.h> 51284194Sdelphij# define HAVE_MAJOR 52284194Sdelphij#endif 53284194Sdelphij#ifdef major /* Might be defined in sys/types.h. */ 54284194Sdelphij# define HAVE_MAJOR 55284194Sdelphij#endif 56284194Sdelphij#ifdef WIN32 57284194Sdelphij# define WIN32_LEAN_AND_MEAN 58284194Sdelphij# include <windows.h> 59284194Sdelphij#endif 60284194Sdelphij 61284194Sdelphij#ifndef HAVE_MAJOR 62284194Sdelphij# define major(dev) (((dev) >> 8) & 0xff) 63284194Sdelphij# define minor(dev) ((dev) & 0xff) 64284194Sdelphij#endif 65284194Sdelphij#undef HAVE_MAJOR 66284194Sdelphij#ifdef S_IFLNK 67284194Sdelphijprivate int 68284194Sdelphijbad_link(struct magic_set *ms, int err, char *buf) 69284194Sdelphij{ 70284194Sdelphij int mime = ms->flags & MAGIC_MIME; 71284194Sdelphij if ((mime & MAGIC_MIME_TYPE) && 72284194Sdelphij file_printf(ms, "inode/symlink") 73284194Sdelphij == -1) 74284194Sdelphij return -1; 75284194Sdelphij else if (!mime) { 76284194Sdelphij if (ms->flags & MAGIC_ERROR) { 77284194Sdelphij file_error(ms, err, 78284194Sdelphij "broken symbolic link to %s", buf); 79284194Sdelphij return -1; 80284194Sdelphij } 81284194Sdelphij if (file_printf(ms, "broken symbolic link to %s", buf) == -1) 82284194Sdelphij return -1; 83284194Sdelphij } 84284194Sdelphij return 1; 85284194Sdelphij} 86284194Sdelphij#endif 87284194Sdelphijprivate int 88284194Sdelphijhandle_mime(struct magic_set *ms, int mime, const char *str) 89284194Sdelphij{ 90284194Sdelphij if ((mime & MAGIC_MIME_TYPE)) { 91284194Sdelphij if (file_printf(ms, "inode/%s", str) == -1) 92284194Sdelphij return -1; 93284194Sdelphij if ((mime & MAGIC_MIME_ENCODING) && file_printf(ms, 94284194Sdelphij "; charset=") == -1) 95284194Sdelphij return -1; 96284194Sdelphij } 97284194Sdelphij if ((mime & MAGIC_MIME_ENCODING) && file_printf(ms, "binary") == -1) 98284194Sdelphij return -1; 99284194Sdelphij return 0; 100284194Sdelphij} 101284194Sdelphij 102284194Sdelphijprotected int 103284194Sdelphijfile_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) 104284194Sdelphij{ 105284194Sdelphij int ret, did = 0; 106284194Sdelphij int mime = ms->flags & MAGIC_MIME; 107284194Sdelphij#ifdef S_IFLNK 108284194Sdelphij char buf[BUFSIZ+4]; 109284194Sdelphij ssize_t nch; 110284194Sdelphij struct stat tstatbuf; 111284194Sdelphij#endif 112284194Sdelphij 113284194Sdelphij if (ms->flags & MAGIC_APPLE) 114284194Sdelphij return 0; 115284194Sdelphij if (fn == NULL) 116284194Sdelphij return 0; 117284194Sdelphij 118284194Sdelphij#define COMMA (did++ ? ", " : "") 119284194Sdelphij /* 120284194Sdelphij * Fstat is cheaper but fails for files you don't have read perms on. 121284194Sdelphij * On 4.2BSD and similar systems, use lstat() to identify symlinks. 122284194Sdelphij */ 123284194Sdelphij#ifdef S_IFLNK 124284194Sdelphij if ((ms->flags & MAGIC_SYMLINK) == 0) 125284194Sdelphij ret = lstat(fn, sb); 126284194Sdelphij else 127284194Sdelphij#endif 128284194Sdelphij ret = stat(fn, sb); /* don't merge into if; see "ret =" above */ 129284194Sdelphij 130284194Sdelphij#ifdef WIN32 131284194Sdelphij { 132284194Sdelphij HANDLE hFile = CreateFile((LPCSTR)fn, 0, FILE_SHARE_DELETE | 133284194Sdelphij FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 134284194Sdelphij NULL); 135284194Sdelphij if (hFile != INVALID_HANDLE_VALUE) { 136284194Sdelphij /* 137284194Sdelphij * Stat failed, but we can still open it - assume it's 138284194Sdelphij * a block device, if nothing else. 139284194Sdelphij */ 140284194Sdelphij if (ret) { 141284194Sdelphij sb->st_mode = S_IFBLK; 142284194Sdelphij ret = 0; 143284194Sdelphij } 144284194Sdelphij switch (GetFileType(hFile)) { 145284194Sdelphij case FILE_TYPE_CHAR: 146284194Sdelphij sb->st_mode |= S_IFCHR; 147284194Sdelphij sb->st_mode &= ~S_IFREG; 148284194Sdelphij break; 149284194Sdelphij case FILE_TYPE_PIPE: 150284194Sdelphij sb->st_mode |= S_IFIFO; 151284194Sdelphij sb->st_mode &= ~S_IFREG; 152284194Sdelphij break; 153284194Sdelphij } 154284194Sdelphij CloseHandle(hFile); 155284194Sdelphij } 156284194Sdelphij } 157284194Sdelphij#endif 158284194Sdelphij 159284194Sdelphij if (ret) { 160284194Sdelphij if (ms->flags & MAGIC_ERROR) { 161284194Sdelphij file_error(ms, errno, "cannot stat `%s'", fn); 162284194Sdelphij return -1; 163284194Sdelphij } 164284194Sdelphij if (file_printf(ms, "cannot open `%s' (%s)", 165284194Sdelphij fn, strerror(errno)) == -1) 166284194Sdelphij return -1; 167284194Sdelphij return 0; 168284194Sdelphij } 169284194Sdelphij 170284194Sdelphij ret = 1; 171284194Sdelphij if (!mime) { 172284194Sdelphij#ifdef S_ISUID 173284194Sdelphij if (sb->st_mode & S_ISUID) 174284194Sdelphij if (file_printf(ms, "%ssetuid", COMMA) == -1) 175284194Sdelphij return -1; 176284194Sdelphij#endif 177284194Sdelphij#ifdef S_ISGID 178284194Sdelphij if (sb->st_mode & S_ISGID) 179284194Sdelphij if (file_printf(ms, "%ssetgid", COMMA) == -1) 180284194Sdelphij return -1; 181284194Sdelphij#endif 182284194Sdelphij#ifdef S_ISVTX 183284194Sdelphij if (sb->st_mode & S_ISVTX) 184284194Sdelphij if (file_printf(ms, "%ssticky", COMMA) == -1) 185284194Sdelphij return -1; 186284194Sdelphij#endif 187284194Sdelphij } 188284194Sdelphij 189284194Sdelphij switch (sb->st_mode & S_IFMT) { 190284194Sdelphij case S_IFDIR: 191284194Sdelphij if (mime) { 192284194Sdelphij if (handle_mime(ms, mime, "directory") == -1) 193284194Sdelphij return -1; 194284194Sdelphij } else if (file_printf(ms, "%sdirectory", COMMA) == -1) 195284194Sdelphij return -1; 196284194Sdelphij break; 197284194Sdelphij#ifdef S_IFCHR 198284194Sdelphij case S_IFCHR: 199284194Sdelphij /* 200284194Sdelphij * If -s has been specified, treat character special files 201284194Sdelphij * like ordinary files. Otherwise, just report that they 202284194Sdelphij * are block special files and go on to the next file. 203284194Sdelphij */ 204284194Sdelphij if ((ms->flags & MAGIC_DEVICES) != 0) { 205284194Sdelphij ret = 0; 206284194Sdelphij break; 207284194Sdelphij } 208284194Sdelphij if (mime) { 209284194Sdelphij if (handle_mime(ms, mime, "chardevice") == -1) 210284194Sdelphij return -1; 211284194Sdelphij } else { 212284194Sdelphij#ifdef HAVE_STRUCT_STAT_ST_RDEV 213284194Sdelphij# ifdef dv_unit 214284194Sdelphij if (file_printf(ms, "%scharacter special (%d/%d/%d)", 215284194Sdelphij COMMA, major(sb->st_rdev), dv_unit(sb->st_rdev), 216284194Sdelphij dv_subunit(sb->st_rdev)) == -1) 217284194Sdelphij return -1; 218284194Sdelphij# else 219284194Sdelphij if (file_printf(ms, "%scharacter special (%ld/%ld)", 220284194Sdelphij COMMA, (long)major(sb->st_rdev), 221284194Sdelphij (long)minor(sb->st_rdev)) == -1) 222284194Sdelphij return -1; 223284194Sdelphij# endif 224284194Sdelphij#else 225284194Sdelphij if (file_printf(ms, "%scharacter special", COMMA) == -1) 226284194Sdelphij return -1; 227284194Sdelphij#endif 228284194Sdelphij } 229284194Sdelphij break; 230284194Sdelphij#endif 231284194Sdelphij#ifdef S_IFBLK 232284194Sdelphij case S_IFBLK: 233284194Sdelphij /* 234284194Sdelphij * If -s has been specified, treat block special files 235284194Sdelphij * like ordinary files. Otherwise, just report that they 236284194Sdelphij * are block special files and go on to the next file. 237284194Sdelphij */ 238284194Sdelphij if ((ms->flags & MAGIC_DEVICES) != 0) { 239284194Sdelphij ret = 0; 240284194Sdelphij break; 241284194Sdelphij } 242284194Sdelphij if (mime) { 243284194Sdelphij if (handle_mime(ms, mime, "blockdevice") == -1) 244284194Sdelphij return -1; 245284194Sdelphij } else { 246284194Sdelphij#ifdef HAVE_STRUCT_STAT_ST_RDEV 247284194Sdelphij# ifdef dv_unit 248284194Sdelphij if (file_printf(ms, "%sblock special (%d/%d/%d)", 249284194Sdelphij COMMA, major(sb->st_rdev), dv_unit(sb->st_rdev), 250284194Sdelphij dv_subunit(sb->st_rdev)) == -1) 251284194Sdelphij return -1; 252284194Sdelphij# else 253284194Sdelphij if (file_printf(ms, "%sblock special (%ld/%ld)", 254284194Sdelphij COMMA, (long)major(sb->st_rdev), 255284194Sdelphij (long)minor(sb->st_rdev)) == -1) 256284194Sdelphij return -1; 257284194Sdelphij# endif 258284194Sdelphij#else 259284194Sdelphij if (file_printf(ms, "%sblock special", COMMA) == -1) 260284194Sdelphij return -1; 261284194Sdelphij#endif 262284194Sdelphij } 263284194Sdelphij break; 264284194Sdelphij#endif 265284194Sdelphij /* TODO add code to handle V7 MUX and Blit MUX files */ 266284194Sdelphij#ifdef S_IFIFO 267284194Sdelphij case S_IFIFO: 268284194Sdelphij if((ms->flags & MAGIC_DEVICES) != 0) 269284194Sdelphij break; 270284194Sdelphij if (mime) { 271284194Sdelphij if (handle_mime(ms, mime, "fifo") == -1) 272284194Sdelphij return -1; 273284194Sdelphij } else if (file_printf(ms, "%sfifo (named pipe)", COMMA) == -1) 274284194Sdelphij return -1; 275284194Sdelphij break; 276284194Sdelphij#endif 277284194Sdelphij#ifdef S_IFDOOR 278284194Sdelphij case S_IFDOOR: 279284194Sdelphij if (mime) { 280284194Sdelphij if (handle_mime(ms, mime, "door") == -1) 281284194Sdelphij return -1; 282284194Sdelphij } else if (file_printf(ms, "%sdoor", COMMA) == -1) 283284194Sdelphij return -1; 284284194Sdelphij break; 285284194Sdelphij#endif 286284194Sdelphij#ifdef S_IFLNK 287284194Sdelphij case S_IFLNK: 288284194Sdelphij if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) { 289284194Sdelphij if (ms->flags & MAGIC_ERROR) { 290284194Sdelphij file_error(ms, errno, "unreadable symlink `%s'", 291284194Sdelphij fn); 292284194Sdelphij return -1; 293284194Sdelphij } 294284194Sdelphij if (mime) { 295284194Sdelphij if (handle_mime(ms, mime, "symlink") == -1) 296284194Sdelphij return -1; 297284194Sdelphij } else if (file_printf(ms, 298284194Sdelphij "%sunreadable symlink `%s' (%s)", COMMA, fn, 299284194Sdelphij strerror(errno)) == -1) 300284194Sdelphij return -1; 301284194Sdelphij break; 302284194Sdelphij } 303284194Sdelphij buf[nch] = '\0'; /* readlink(2) does not do this */ 304284194Sdelphij 305284194Sdelphij /* If broken symlink, say so and quit early. */ 306284194Sdelphij if (*buf == '/') { 307284194Sdelphij if (stat(buf, &tstatbuf) < 0) 308284194Sdelphij return bad_link(ms, errno, buf); 309284194Sdelphij } else { 310284194Sdelphij char *tmp; 311284194Sdelphij char buf2[BUFSIZ+BUFSIZ+4]; 312284194Sdelphij 313284194Sdelphij if ((tmp = strrchr(fn, '/')) == NULL) { 314284194Sdelphij tmp = buf; /* in current directory anyway */ 315284194Sdelphij } else { 316284194Sdelphij if (tmp - fn + 1 > BUFSIZ) { 317284194Sdelphij if (ms->flags & MAGIC_ERROR) { 318284194Sdelphij file_error(ms, 0, 319284194Sdelphij "path too long: `%s'", buf); 320284194Sdelphij return -1; 321284194Sdelphij } 322284194Sdelphij if (mime) { 323284194Sdelphij if (handle_mime(ms, mime, 324284194Sdelphij "x-path-too-long") == -1) 325284194Sdelphij return -1; 326284194Sdelphij } else if (file_printf(ms, 327284194Sdelphij "%spath too long: `%s'", COMMA, 328284194Sdelphij fn) == -1) 329284194Sdelphij return -1; 330284194Sdelphij break; 331284194Sdelphij } 332284194Sdelphij /* take dir part */ 333284194Sdelphij (void)strlcpy(buf2, fn, sizeof buf2); 334284194Sdelphij buf2[tmp - fn + 1] = '\0'; 335284194Sdelphij /* plus (rel) link */ 336284194Sdelphij (void)strlcat(buf2, buf, sizeof buf2); 337284194Sdelphij tmp = buf2; 338284194Sdelphij } 339284194Sdelphij if (stat(tmp, &tstatbuf) < 0) 340284194Sdelphij return bad_link(ms, errno, buf); 341284194Sdelphij } 342284194Sdelphij 343284194Sdelphij /* Otherwise, handle it. */ 344284194Sdelphij if ((ms->flags & MAGIC_SYMLINK) != 0) { 345284194Sdelphij const char *p; 346284194Sdelphij ms->flags &= MAGIC_SYMLINK; 347284194Sdelphij p = magic_file(ms, buf); 348284194Sdelphij ms->flags |= MAGIC_SYMLINK; 349284194Sdelphij if (p == NULL) 350284194Sdelphij return -1; 351284194Sdelphij } else { /* just print what it points to */ 352284194Sdelphij if (mime) { 353284194Sdelphij if (handle_mime(ms, mime, "symlink") == -1) 354284194Sdelphij return -1; 355284194Sdelphij } else if (file_printf(ms, "%ssymbolic link to %s", 356284194Sdelphij COMMA, buf) == -1) 357284194Sdelphij return -1; 358284194Sdelphij } 359284194Sdelphij break; 360284194Sdelphij#endif 361284194Sdelphij#ifdef S_IFSOCK 362284194Sdelphij#ifndef __COHERENT__ 363284194Sdelphij case S_IFSOCK: 364284194Sdelphij if (mime) { 365284194Sdelphij if (handle_mime(ms, mime, "socket") == -1) 366284194Sdelphij return -1; 367284194Sdelphij } else if (file_printf(ms, "%ssocket", COMMA) == -1) 368284194Sdelphij return -1; 369284194Sdelphij break; 370284194Sdelphij#endif 371284194Sdelphij#endif 372284194Sdelphij case S_IFREG: 373284194Sdelphij /* 374284194Sdelphij * regular file, check next possibility 375284194Sdelphij * 376284194Sdelphij * If stat() tells us the file has zero length, report here that 377284194Sdelphij * the file is empty, so we can skip all the work of opening and 378284194Sdelphij * reading the file. 379284194Sdelphij * But if the -s option has been given, we skip this 380284194Sdelphij * optimization, since on some systems, stat() reports zero 381284194Sdelphij * size for raw disk partitions. (If the block special device 382284194Sdelphij * really has zero length, the fact that it is empty will be 383284194Sdelphij * detected and reported correctly when we read the file.) 384284194Sdelphij */ 385284194Sdelphij if ((ms->flags & MAGIC_DEVICES) == 0 && sb->st_size == 0) { 386284194Sdelphij if (mime) { 387284194Sdelphij if (handle_mime(ms, mime, "x-empty") == -1) 388284194Sdelphij return -1; 389284194Sdelphij } else if (file_printf(ms, "%sempty", COMMA) == -1) 390284194Sdelphij return -1; 391284194Sdelphij break; 392284194Sdelphij } 393284194Sdelphij ret = 0; 394284194Sdelphij break; 395284194Sdelphij 396284194Sdelphij default: 397284194Sdelphij file_error(ms, 0, "invalid mode 0%o", sb->st_mode); 398284194Sdelphij return -1; 399284194Sdelphij /*NOTREACHED*/ 400284194Sdelphij } 401284194Sdelphij 402284194Sdelphij if (!mime && did && ret == 0) { 403284194Sdelphij if (file_printf(ms, " ") == -1) 404284194Sdelphij return -1; 405284194Sdelphij } 406284194Sdelphij return ret; 407284194Sdelphij} 408