]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/file/fsmagic.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / file / 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("@(#)$File: fsmagic.c,v 1.48 2007/10/17 19:33:31 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         int mime = ms->flags & MAGIC_MIME;
68 #ifdef  S_IFLNK
69         char buf[BUFSIZ+4];
70         int nch;
71         struct stat tstatbuf;
72 #endif
73
74         if (fn == NULL)
75                 return 0;
76
77         /*
78          * Fstat is cheaper but fails for files you don't have read perms on.
79          * On 4.2BSD and similar systems, use lstat() to identify symlinks.
80          */
81 #ifdef  S_IFLNK
82         if ((ms->flags & MAGIC_SYMLINK) == 0)
83                 ret = lstat(fn, sb);
84         else
85 #endif
86         ret = stat(fn, sb);     /* don't merge into if; see "ret =" above */
87
88         if (ret) {
89                 if (ms->flags & MAGIC_ERROR) {
90                         file_error(ms, errno, "cannot stat `%s'", fn);
91                         return -1;
92                 }
93                 if (file_printf(ms, "cannot open `%s' (%s)",
94                     fn, strerror(errno)) == -1)
95                         return -1;
96                 return 1;
97         }
98
99         if (mime) {
100                 if ((sb->st_mode & S_IFMT) != S_IFREG) {
101                         if ((mime & MAGIC_MIME_TYPE) &&
102                             file_printf(ms, "application/x-not-regular-file")
103                             == -1)
104                                     return -1;
105                         return 1;
106                 }
107         }
108         else {
109 #ifdef S_ISUID
110                 if (sb->st_mode & S_ISUID) 
111                         if (file_printf(ms, "setuid ") == -1)
112                                 return -1;
113 #endif
114 #ifdef S_ISGID
115                 if (sb->st_mode & S_ISGID) 
116                         if (file_printf(ms, "setgid ") == -1)
117                                 return -1;
118 #endif
119 #ifdef S_ISVTX
120                 if (sb->st_mode & S_ISVTX) 
121                         if (file_printf(ms, "sticky ") == -1)
122                                 return -1;
123 #endif
124         }
125         
126         switch (sb->st_mode & S_IFMT) {
127         case S_IFDIR:
128                 if (file_printf(ms, "directory") == -1)
129                         return -1;
130                 return 1;
131 #ifdef S_IFCHR
132         case S_IFCHR:
133                 /* 
134                  * If -s has been specified, treat character special files
135                  * like ordinary files.  Otherwise, just report that they
136                  * are block special files and go on to the next file.
137                  */
138                 if ((ms->flags & MAGIC_DEVICES) != 0)
139                         break;
140 #ifdef HAVE_ST_RDEV
141 # ifdef dv_unit
142                 if (file_printf(ms, "character special (%d/%d/%d)",
143                     major(sb->st_rdev), dv_unit(sb->st_rdev),
144                     dv_subunit(sb->st_rdev)) == -1)
145                         return -1;
146 # else
147                 if (file_printf(ms, "character special (%ld/%ld)",
148                     (long) major(sb->st_rdev), (long) minor(sb->st_rdev)) == -1)
149                         return -1;
150 # endif
151 #else
152                 if (file_printf(ms, "character special") == -1)
153                         return -1;
154 #endif
155                 return 1;
156 #endif
157 #ifdef S_IFBLK
158         case S_IFBLK:
159                 /* 
160                  * If -s has been specified, treat block special files
161                  * like ordinary files.  Otherwise, just report that they
162                  * are block special files and go on to the next file.
163                  */
164                 if ((ms->flags & MAGIC_DEVICES) != 0)
165                         break;
166 #ifdef HAVE_ST_RDEV
167 # ifdef dv_unit
168                 if (file_printf(ms, "block special (%d/%d/%d)",
169                     major(sb->st_rdev), dv_unit(sb->st_rdev),
170                     dv_subunit(sb->st_rdev)) == -1)
171                         return -1;
172 # else
173                 if (file_printf(ms, "block special (%ld/%ld)",
174                     (long)major(sb->st_rdev), (long)minor(sb->st_rdev)) == -1)
175                         return -1;
176 # endif
177 #else
178                 if (file_printf(ms, "block special") == -1)
179                         return -1;
180 #endif
181                 return 1;
182 #endif
183         /* TODO add code to handle V7 MUX and Blit MUX files */
184 #ifdef  S_IFIFO
185         case S_IFIFO:
186                 if((ms->flags & MAGIC_DEVICES) != 0)
187                         break;
188                 if (file_printf(ms, "fifo (named pipe)") == -1)
189                         return -1;
190                 return 1;
191 #endif
192 #ifdef  S_IFDOOR
193         case S_IFDOOR:
194                 if (file_printf(ms, "door") == -1)
195                         return -1;
196                 return 1;
197 #endif
198 #ifdef  S_IFLNK
199         case S_IFLNK:
200                 if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) {
201                         if (ms->flags & MAGIC_ERROR) {
202                             file_error(ms, errno, "unreadable symlink `%s'",
203                                 fn);
204                             return -1;
205                         }
206                         if (file_printf(ms,
207                             "unreadable symlink `%s' (%s)", fn,
208                             strerror(errno)) == -1)
209                                 return -1;
210                         return 1;
211                 }
212                 buf[nch] = '\0';        /* readlink(2) forgets this */
213
214                 /* If broken symlink, say so and quit early. */
215                 if (*buf == '/') {
216                     if (stat(buf, &tstatbuf) < 0) {
217                             if (ms->flags & MAGIC_ERROR) {
218                                     file_error(ms, errno, 
219                                         "broken symbolic link to `%s'", buf);
220                                     return -1;
221                             } 
222                             if (file_printf(ms, "broken symbolic link to `%s'",
223                                 buf) == -1)
224                                     return -1;
225                             return 1;
226                     }
227                 }
228                 else {
229                         char *tmp;
230                         char buf2[BUFSIZ+BUFSIZ+4];
231
232                         if ((tmp = strrchr(fn,  '/')) == NULL) {
233                                 tmp = buf; /* in current directory anyway */
234                         } else {
235                                 if (tmp - fn + 1 > BUFSIZ) {
236                                         if (ms->flags & MAGIC_ERROR) {
237                                                 file_error(ms, 0, 
238                                                     "path too long: `%s'", buf);
239                                                 return -1;
240                                         }
241                                         if (file_printf(ms,
242                                             "path too long: `%s'", fn) == -1)
243                                                 return -1;
244                                         return 1;
245                                 }
246                                 (void)strcpy(buf2, fn);  /* take dir part */
247                                 buf2[tmp - fn + 1] = '\0';
248                                 (void)strcat(buf2, buf); /* plus (rel) link */
249                                 tmp = buf2;
250                         }
251                         if (stat(tmp, &tstatbuf) < 0) {
252                                 if (ms->flags & MAGIC_ERROR) {
253                                         file_error(ms, errno, 
254                                             "broken symbolic link to `%s'",
255                                             buf);
256                                         return -1;
257                                 }
258                                 if (file_printf(ms,
259                                     "broken symbolic link to `%s'", buf) == -1)
260                                         return -1;
261                                 return 1;
262                         }
263                 }
264
265                 /* Otherwise, handle it. */
266                 if ((ms->flags & MAGIC_SYMLINK) != 0) {
267                         const char *p;
268                         ms->flags &= MAGIC_SYMLINK;
269                         p = magic_file(ms, buf);
270                         ms->flags |= MAGIC_SYMLINK;
271                         return p != NULL ? 1 : -1;
272                 } else { /* just print what it points to */
273                         if (file_printf(ms, "symbolic link to `%s'",
274                             buf) == -1)
275                                 return -1;
276                 }
277         return 1;
278 #endif
279 #ifdef  S_IFSOCK
280 #ifndef __COHERENT__
281         case S_IFSOCK:
282                 if (file_printf(ms, "socket") == -1)
283                         return -1;
284                 return 1;
285 #endif
286 #endif
287         case S_IFREG:
288                 break;
289         default:
290                 file_error(ms, 0, "invalid mode 0%o", sb->st_mode);
291                 return -1;
292                 /*NOTREACHED*/
293         }
294
295         /*
296          * regular file, check next possibility
297          *
298          * If stat() tells us the file has zero length, report here that
299          * the file is empty, so we can skip all the work of opening and 
300          * reading the file.
301          * But if the -s option has been given, we skip this optimization,
302          * since on some systems, stat() reports zero size for raw disk
303          * partitions.  (If the block special device really has zero length,
304          * the fact that it is empty will be detected and reported correctly
305          * when we read the file.)
306          */
307         if ((ms->flags & MAGIC_DEVICES) == 0 && sb->st_size == 0) {
308                 if ((!mime || (mime & MAGIC_MIME_TYPE)) &&
309                     file_printf(ms, mime ? "application/x-empty" :
310                     "empty") == -1)
311                         return -1;
312                 return 1;
313         }
314         return 0;
315 }