Update to file 4.13. Put the contrib files into contrib/file-4 instead
[dragonfly.git] / contrib / file-4 / src / fsmagic.c
1 /*
2  * Copyright (c) Ian F. Darwin 1986-1995.
3  * Software written by Ian F. Darwin and others;
4  * maintained 1995-present by Christos Zoulas and others.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice immediately at the beginning of the file, without modification,
11  *    this list of conditions, and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *  
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 /*
29  * fsmagic - magic based on filesystem info - directory, special files, etc.
30  */
31
32 #include "file.h"
33 #include "magic.h"
34 #include <string.h>
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #include <stdlib.h>
39 #include <sys/stat.h>
40 /* Since major is a function on SVR4, we cannot use `ifndef major'.  */
41 #ifdef MAJOR_IN_MKDEV
42 # include <sys/mkdev.h>
43 # define HAVE_MAJOR
44 #endif
45 #ifdef MAJOR_IN_SYSMACROS
46 # include <sys/sysmacros.h>
47 # define HAVE_MAJOR
48 #endif
49 #ifdef major                    /* Might be defined in sys/types.h.  */
50 # define HAVE_MAJOR
51 #endif
52   
53 #ifndef HAVE_MAJOR
54 # define major(dev)  (((dev) >> 8) & 0xff)
55 # define minor(dev)  ((dev) & 0xff)
56 #endif
57 #undef HAVE_MAJOR
58
59 #ifndef lint
60 FILE_RCSID("@(#)$Id: fsmagic.c,v 1.45 2004/11/13 10:19:48 christos Exp $")
61 #endif  /* lint */
62
63 protected int
64 file_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 }