Update to file-4.19.
[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.46 2005/06/25 15:52:14 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((ms->flags & MAGIC_DEVICES) != 0)
185                         break;
186                 if (file_printf(ms, "fifo (named pipe)") == -1)
187                         return -1;
188                 return 1;
189 #endif
190 #ifdef  S_IFDOOR
191         case S_IFDOOR:
192                 if (file_printf(ms, "door") == -1)
193                         return -1;
194                 return 1;
195 #endif
196 #ifdef  S_IFLNK
197         case S_IFLNK:
198                 if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) {
199                         if (ms->flags & MAGIC_ERROR) {
200                             file_error(ms, errno, "unreadable symlink `%s'",
201                                 fn);
202                             return -1;
203                         }
204                         if (file_printf(ms,
205                             "unreadable symlink `%s' (%s)", fn,
206                             strerror(errno)) == -1)
207                                 return -1;
208                         return 1;
209                 }
210                 buf[nch] = '\0';        /* readlink(2) forgets this */
211
212                 /* If broken symlink, say so and quit early. */
213                 if (*buf == '/') {
214                     if (stat(buf, &tstatbuf) < 0) {
215                             if (ms->flags & MAGIC_ERROR) {
216                                     file_error(ms, errno, 
217                                         "broken symbolic link to `%s'", buf);
218                                     return -1;
219                             } 
220                             if (file_printf(ms, "broken symbolic link to `%s'",
221                                 buf) == -1)
222                                     return -1;
223                             return 1;
224                     }
225                 }
226                 else {
227                         char *tmp;
228                         char buf2[BUFSIZ+BUFSIZ+4];
229
230                         if ((tmp = strrchr(fn,  '/')) == NULL) {
231                                 tmp = buf; /* in current directory anyway */
232                         } else {
233                                 if (tmp - fn + 1 > BUFSIZ) {
234                                         if (ms->flags & MAGIC_ERROR) {
235                                                 file_error(ms, 0, 
236                                                     "path too long: `%s'", buf);
237                                                 return -1;
238                                         }
239                                         if (file_printf(ms,
240                                             "path too long: `%s'", fn) == -1)
241                                                 return -1;
242                                         return 1;
243                                 }
244                                 (void)strcpy(buf2, fn);  /* take dir part */
245                                 buf2[tmp - fn + 1] = '\0';
246                                 (void)strcat(buf2, buf); /* plus (rel) link */
247                                 tmp = buf2;
248                         }
249                         if (stat(tmp, &tstatbuf) < 0) {
250                                 if (ms->flags & MAGIC_ERROR) {
251                                         file_error(ms, errno, 
252                                             "broken symbolic link to `%s'",
253                                             buf);
254                                         return -1;
255                                 }
256                                 if (file_printf(ms,
257                                     "broken symbolic link to `%s'", buf) == -1)
258                                         return -1;
259                                 return 1;
260                         }
261                 }
262
263                 /* Otherwise, handle it. */
264                 if ((ms->flags & MAGIC_SYMLINK) != 0) {
265                         const char *p;
266                         ms->flags &= MAGIC_SYMLINK;
267                         p = magic_file(ms, buf);
268                         ms->flags |= MAGIC_SYMLINK;
269                         return p != NULL ? 1 : -1;
270                 } else { /* just print what it points to */
271                         if (file_printf(ms, "symbolic link to `%s'",
272                             buf) == -1)
273                                 return -1;
274                 }
275         return 1;
276 #endif
277 #ifdef  S_IFSOCK
278 #ifndef __COHERENT__
279         case S_IFSOCK:
280                 if (file_printf(ms, "socket") == -1)
281                         return -1;
282                 return 1;
283 #endif
284 #endif
285         case S_IFREG:
286                 break;
287         default:
288                 file_error(ms, 0, "invalid mode 0%o", sb->st_mode);
289                 return -1;
290                 /*NOTREACHED*/
291         }
292
293         /*
294          * regular file, check next possibility
295          *
296          * If stat() tells us the file has zero length, report here that
297          * the file is empty, so we can skip all the work of opening and 
298          * reading the file.
299          * But if the -s option has been given, we skip this optimization,
300          * since on some systems, stat() reports zero size for raw disk
301          * partitions.  (If the block special device really has zero length,
302          * the fact that it is empty will be detected and reported correctly
303          * when we read the file.)
304          */
305         if ((ms->flags & MAGIC_DEVICES) == 0 && sb->st_size == 0) {
306                 if (file_printf(ms, (ms->flags & MAGIC_MIME) ?
307                     "application/x-empty" : "empty") == -1)
308                         return -1;
309                 return 1;
310         }
311         return 0;
312 }