Line | Count | Source (jump to first uncovered line) |
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 | | |
34 | | #ifndef lint |
35 | | FILE_RCSID("@(#)$File: fsmagic.c,v 1.85 2022/12/26 17:31:14 christos Exp $") |
36 | | #endif /* lint */ |
37 | | |
38 | | #include "magic.h" |
39 | | #include <string.h> |
40 | | #ifdef HAVE_UNISTD_H |
41 | | #include <unistd.h> |
42 | | #endif |
43 | | #include <stdlib.h> |
44 | | /* Since major is a function on SVR4, we cannot use `ifndef major'. */ |
45 | | #ifdef MAJOR_IN_MKDEV |
46 | | # include <sys/mkdev.h> |
47 | | # define HAVE_MAJOR |
48 | | #endif |
49 | | #ifdef HAVE_SYS_SYSMACROS_H |
50 | | # include <sys/sysmacros.h> |
51 | | #endif |
52 | | #ifdef MAJOR_IN_SYSMACROS |
53 | | # define HAVE_MAJOR |
54 | | #endif |
55 | | #if defined(major) && !defined(HAVE_MAJOR) |
56 | | /* Might be defined in sys/types.h. */ |
57 | | # define HAVE_MAJOR |
58 | | #endif |
59 | | #ifdef WIN32 |
60 | | # define WIN32_LEAN_AND_MEAN |
61 | | # include <windows.h> |
62 | | #endif |
63 | | |
64 | | #ifndef HAVE_MAJOR |
65 | | # define major(dev) (((dev) >> 8) & 0xff) |
66 | | # define minor(dev) ((dev) & 0xff) |
67 | | #endif |
68 | | #undef HAVE_MAJOR |
69 | | #ifdef S_IFLNK |
70 | | file_private int |
71 | | bad_link(struct magic_set *ms, int err, char *buf) |
72 | 0 | { |
73 | 0 | int mime = ms->flags & MAGIC_MIME; |
74 | 0 | if ((mime & MAGIC_MIME_TYPE) && |
75 | 0 | file_printf(ms, "inode/symlink") |
76 | 0 | == -1) |
77 | 0 | return -1; |
78 | 0 | else if (!mime) { |
79 | 0 | if (ms->flags & MAGIC_ERROR) { |
80 | 0 | file_error(ms, err, |
81 | 0 | "broken symbolic link to %s", buf); |
82 | 0 | return -1; |
83 | 0 | } |
84 | 0 | if (file_printf(ms, "broken symbolic link to %s", buf) == -1) |
85 | 0 | return -1; |
86 | 0 | } |
87 | 0 | return 1; |
88 | 0 | } |
89 | | #endif |
90 | | file_private int |
91 | | handle_mime(struct magic_set *ms, int mime, const char *str) |
92 | 0 | { |
93 | 0 | if ((mime & MAGIC_MIME_TYPE)) { |
94 | 0 | if (file_printf(ms, "inode/%s", str) == -1) |
95 | 0 | return -1; |
96 | 0 | if ((mime & MAGIC_MIME_ENCODING) && file_printf(ms, |
97 | 0 | "; charset=") == -1) |
98 | 0 | return -1; |
99 | 0 | } |
100 | 0 | if ((mime & MAGIC_MIME_ENCODING) && file_printf(ms, "binary") == -1) |
101 | 0 | return -1; |
102 | 0 | return 0; |
103 | 0 | } |
104 | | |
105 | | file_protected int |
106 | | file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) |
107 | 8.67k | { |
108 | 8.67k | int ret, did = 0; |
109 | 8.67k | int mime = ms->flags & MAGIC_MIME; |
110 | 8.67k | int silent = ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION); |
111 | 8.67k | #ifdef S_IFLNK |
112 | 8.67k | char buf[BUFSIZ+4]; |
113 | 8.67k | ssize_t nch; |
114 | 8.67k | struct stat tstatbuf; |
115 | 8.67k | #endif |
116 | | |
117 | 8.67k | if (fn == NULL) |
118 | 8.67k | return 0; |
119 | | |
120 | 0 | #define COMMA (did++ ? ", " : "") |
121 | | /* |
122 | | * Fstat is cheaper but fails for files you don't have read perms on. |
123 | | * On 4.2BSD and similar systems, use lstat() to identify symlinks. |
124 | | */ |
125 | 0 | #ifdef S_IFLNK |
126 | 0 | if ((ms->flags & MAGIC_SYMLINK) == 0) |
127 | 0 | ret = lstat(fn, sb); |
128 | 0 | else |
129 | 0 | #endif |
130 | 0 | ret = stat(fn, sb); /* don't merge into if; see "ret =" above */ |
131 | |
|
132 | | #ifdef WIN32 |
133 | | { |
134 | | HANDLE hFile = CreateFile((LPCSTR)fn, 0, FILE_SHARE_DELETE | |
135 | | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, |
136 | | NULL); |
137 | | if (hFile != INVALID_HANDLE_VALUE) { |
138 | | /* |
139 | | * Stat failed, but we can still open it - assume it's |
140 | | * a block device, if nothing else. |
141 | | */ |
142 | | if (ret) { |
143 | | sb->st_mode = S_IFBLK; |
144 | | ret = 0; |
145 | | } |
146 | | switch (GetFileType(hFile)) { |
147 | | case FILE_TYPE_CHAR: |
148 | | sb->st_mode |= S_IFCHR; |
149 | | sb->st_mode &= ~S_IFREG; |
150 | | break; |
151 | | case FILE_TYPE_PIPE: |
152 | | sb->st_mode |= S_IFIFO; |
153 | | sb->st_mode &= ~S_IFREG; |
154 | | break; |
155 | | } |
156 | | CloseHandle(hFile); |
157 | | } |
158 | | } |
159 | | #endif |
160 | |
|
161 | 0 | if (ret) { |
162 | 0 | if (ms->flags & MAGIC_ERROR) { |
163 | 0 | file_error(ms, errno, "cannot stat `%s'", fn); |
164 | 0 | return -1; |
165 | 0 | } |
166 | 0 | if (file_printf(ms, "cannot open `%s' (%s)", |
167 | 0 | fn, strerror(errno)) == -1) |
168 | 0 | return -1; |
169 | 0 | return 0; |
170 | 0 | } |
171 | | |
172 | 0 | ret = 1; |
173 | 0 | if (!mime && !silent) { |
174 | 0 | #ifdef S_ISUID |
175 | 0 | if (sb->st_mode & S_ISUID) |
176 | 0 | if (file_printf(ms, "%ssetuid", COMMA) == -1) |
177 | 0 | return -1; |
178 | 0 | #endif |
179 | 0 | #ifdef S_ISGID |
180 | 0 | if (sb->st_mode & S_ISGID) |
181 | 0 | if (file_printf(ms, "%ssetgid", COMMA) == -1) |
182 | 0 | return -1; |
183 | 0 | #endif |
184 | 0 | #ifdef S_ISVTX |
185 | 0 | if (sb->st_mode & S_ISVTX) |
186 | 0 | if (file_printf(ms, "%ssticky", COMMA) == -1) |
187 | 0 | return -1; |
188 | 0 | #endif |
189 | 0 | } |
190 | | |
191 | 0 | switch (sb->st_mode & S_IFMT) { |
192 | 0 | case S_IFDIR: |
193 | 0 | if (mime) { |
194 | 0 | if (handle_mime(ms, mime, "directory") == -1) |
195 | 0 | return -1; |
196 | 0 | } else if (silent) { |
197 | 0 | } else if (file_printf(ms, "%sdirectory", COMMA) == -1) |
198 | 0 | return -1; |
199 | 0 | break; |
200 | 0 | #ifdef S_IFCHR |
201 | 0 | case S_IFCHR: |
202 | | /* |
203 | | * If -s has been specified, treat character special files |
204 | | * like ordinary files. Otherwise, just report that they |
205 | | * are block special files and go on to the next file. |
206 | | */ |
207 | 0 | if ((ms->flags & MAGIC_DEVICES) != 0) { |
208 | 0 | ret = 0; |
209 | 0 | break; |
210 | 0 | } |
211 | 0 | if (mime) { |
212 | 0 | if (handle_mime(ms, mime, "chardevice") == -1) |
213 | 0 | return -1; |
214 | 0 | } else if (silent) { |
215 | 0 | } else { |
216 | 0 | #ifdef HAVE_STRUCT_STAT_ST_RDEV |
217 | | # ifdef dv_unit |
218 | | if (file_printf(ms, "%scharacter special (%d/%d/%d)", |
219 | | COMMA, major(sb->st_rdev), dv_unit(sb->st_rdev), |
220 | | dv_subunit(sb->st_rdev)) == -1) |
221 | | return -1; |
222 | | # else |
223 | 0 | if (file_printf(ms, "%scharacter special (%ld/%ld)", |
224 | 0 | COMMA, (long)major(sb->st_rdev), |
225 | 0 | (long)minor(sb->st_rdev)) == -1) |
226 | 0 | return -1; |
227 | 0 | # endif |
228 | | #else |
229 | | if (file_printf(ms, "%scharacter special", COMMA) == -1) |
230 | | return -1; |
231 | | #endif |
232 | 0 | } |
233 | 0 | break; |
234 | 0 | #endif |
235 | 0 | #ifdef S_IFBLK |
236 | 0 | case S_IFBLK: |
237 | | /* |
238 | | * If -s has been specified, treat block special files |
239 | | * like ordinary files. Otherwise, just report that they |
240 | | * are block special files and go on to the next file. |
241 | | */ |
242 | 0 | if ((ms->flags & MAGIC_DEVICES) != 0) { |
243 | 0 | ret = 0; |
244 | 0 | break; |
245 | 0 | } |
246 | 0 | if (mime) { |
247 | 0 | if (handle_mime(ms, mime, "blockdevice") == -1) |
248 | 0 | return -1; |
249 | 0 | } else if (silent) { |
250 | 0 | } else { |
251 | 0 | #ifdef HAVE_STRUCT_STAT_ST_RDEV |
252 | | # ifdef dv_unit |
253 | | if (file_printf(ms, "%sblock special (%d/%d/%d)", |
254 | | COMMA, major(sb->st_rdev), dv_unit(sb->st_rdev), |
255 | | dv_subunit(sb->st_rdev)) == -1) |
256 | | return -1; |
257 | | # else |
258 | 0 | if (file_printf(ms, "%sblock special (%ld/%ld)", |
259 | 0 | COMMA, (long)major(sb->st_rdev), |
260 | 0 | (long)minor(sb->st_rdev)) == -1) |
261 | 0 | return -1; |
262 | 0 | # endif |
263 | | #else |
264 | | if (file_printf(ms, "%sblock special", COMMA) == -1) |
265 | | return -1; |
266 | | #endif |
267 | 0 | } |
268 | 0 | break; |
269 | 0 | #endif |
270 | | /* TODO add code to handle V7 MUX and Blit MUX files */ |
271 | 0 | #ifdef S_IFIFO |
272 | 0 | case S_IFIFO: |
273 | 0 | if((ms->flags & MAGIC_DEVICES) != 0) |
274 | 0 | break; |
275 | 0 | if (mime) { |
276 | 0 | if (handle_mime(ms, mime, "fifo") == -1) |
277 | 0 | return -1; |
278 | 0 | } else if (silent) { |
279 | 0 | } else if (file_printf(ms, "%sfifo (named pipe)", COMMA) == -1) |
280 | 0 | return -1; |
281 | 0 | break; |
282 | 0 | #endif |
283 | | #ifdef S_IFDOOR |
284 | | case S_IFDOOR: |
285 | | if (mime) { |
286 | | if (handle_mime(ms, mime, "door") == -1) |
287 | | return -1; |
288 | | } else if (silent) { |
289 | | } else if (file_printf(ms, "%sdoor", COMMA) == -1) |
290 | | return -1; |
291 | | break; |
292 | | #endif |
293 | 0 | #ifdef S_IFLNK |
294 | 0 | case S_IFLNK: |
295 | 0 | if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) { |
296 | 0 | if (ms->flags & MAGIC_ERROR) { |
297 | 0 | file_error(ms, errno, "unreadable symlink `%s'", |
298 | 0 | fn); |
299 | 0 | return -1; |
300 | 0 | } |
301 | 0 | if (mime) { |
302 | 0 | if (handle_mime(ms, mime, "symlink") == -1) |
303 | 0 | return -1; |
304 | 0 | } else if (silent) { |
305 | 0 | } else if (file_printf(ms, |
306 | 0 | "%sunreadable symlink `%s' (%s)", COMMA, fn, |
307 | 0 | strerror(errno)) == -1) |
308 | 0 | return -1; |
309 | 0 | break; |
310 | 0 | } |
311 | 0 | buf[nch] = '\0'; /* readlink(2) does not do this */ |
312 | | |
313 | | /* If broken symlink, say so and quit early. */ |
314 | 0 | #ifdef __linux__ |
315 | | /* |
316 | | * linux procfs/devfs makes symlinks like pipe:[3515864880] |
317 | | * that we can't stat their readlink output, so stat the |
318 | | * original filename instead. |
319 | | */ |
320 | 0 | if (stat(fn, &tstatbuf) < 0) |
321 | 0 | return bad_link(ms, errno, buf); |
322 | | #else |
323 | | if (*buf == '/') { |
324 | | if (stat(buf, &tstatbuf) < 0) |
325 | | return bad_link(ms, errno, buf); |
326 | | } else { |
327 | | char *tmp; |
328 | | char buf2[BUFSIZ+BUFSIZ+4]; |
329 | | |
330 | | if ((tmp = CCAST(char *, strrchr(fn, '/'))) == NULL) { |
331 | | tmp = buf; /* in current directory anyway */ |
332 | | } else { |
333 | | if (tmp - fn + 1 > BUFSIZ) { |
334 | | if (ms->flags & MAGIC_ERROR) { |
335 | | file_error(ms, 0, |
336 | | "path too long: `%s'", buf); |
337 | | return -1; |
338 | | } |
339 | | if (mime) { |
340 | | if (handle_mime(ms, mime, |
341 | | "x-path-too-long") == -1) |
342 | | return -1; |
343 | | } else if (silent) { |
344 | | } else if (file_printf(ms, |
345 | | "%spath too long: `%s'", COMMA, |
346 | | fn) == -1) |
347 | | return -1; |
348 | | break; |
349 | | } |
350 | | /* take dir part */ |
351 | | (void)strlcpy(buf2, fn, sizeof buf2); |
352 | | buf2[tmp - fn + 1] = '\0'; |
353 | | /* plus (rel) link */ |
354 | | (void)strlcat(buf2, buf, sizeof buf2); |
355 | | tmp = buf2; |
356 | | } |
357 | | if (stat(tmp, &tstatbuf) < 0) |
358 | | return bad_link(ms, errno, buf); |
359 | | } |
360 | | #endif |
361 | | |
362 | | /* Otherwise, handle it. */ |
363 | 0 | if ((ms->flags & MAGIC_SYMLINK) != 0) { |
364 | 0 | const char *p; |
365 | 0 | ms->flags &= MAGIC_SYMLINK; |
366 | 0 | p = magic_file(ms, buf); |
367 | 0 | ms->flags |= MAGIC_SYMLINK; |
368 | 0 | if (p == NULL) |
369 | 0 | return -1; |
370 | 0 | } else { /* just print what it points to */ |
371 | 0 | if (mime) { |
372 | 0 | if (handle_mime(ms, mime, "symlink") == -1) |
373 | 0 | return -1; |
374 | 0 | } else if (silent) { |
375 | 0 | } else if (file_printf(ms, "%ssymbolic link to %s", |
376 | 0 | COMMA, buf) == -1) |
377 | 0 | return -1; |
378 | 0 | } |
379 | 0 | break; |
380 | 0 | #endif |
381 | 0 | #ifdef S_IFSOCK |
382 | 0 | #ifndef __COHERENT__ |
383 | 0 | case S_IFSOCK: |
384 | 0 | if (mime) { |
385 | 0 | if (handle_mime(ms, mime, "socket") == -1) |
386 | 0 | return -1; |
387 | 0 | } else if (silent) { |
388 | 0 | } else if (file_printf(ms, "%ssocket", COMMA) == -1) |
389 | 0 | return -1; |
390 | 0 | break; |
391 | 0 | #endif |
392 | 0 | #endif |
393 | 0 | case S_IFREG: |
394 | | /* |
395 | | * regular file, check next possibility |
396 | | * |
397 | | * If stat() tells us the file has zero length, report here that |
398 | | * the file is empty, so we can skip all the work of opening and |
399 | | * reading the file. |
400 | | * But if the -s option has been given, we skip this |
401 | | * optimization, since on some systems, stat() reports zero |
402 | | * size for raw disk partitions. (If the block special device |
403 | | * really has zero length, the fact that it is empty will be |
404 | | * detected and reported correctly when we read the file.) |
405 | | */ |
406 | 0 | if ((ms->flags & MAGIC_DEVICES) == 0 && sb->st_size == 0) { |
407 | 0 | if (mime) { |
408 | 0 | if (handle_mime(ms, mime, "x-empty") == -1) |
409 | 0 | return -1; |
410 | 0 | } else if (silent) { |
411 | 0 | } else if (file_printf(ms, "%sempty", COMMA) == -1) |
412 | 0 | return -1; |
413 | 0 | break; |
414 | 0 | } |
415 | 0 | ret = 0; |
416 | 0 | break; |
417 | | |
418 | 0 | default: |
419 | 0 | file_error(ms, 0, "invalid mode 0%o", sb->st_mode); |
420 | 0 | return -1; |
421 | | /*NOTREACHED*/ |
422 | 0 | } |
423 | | |
424 | 0 | if (!silent && !mime && did && ret == 0) { |
425 | 0 | if (file_printf(ms, " ") == -1) |
426 | 0 | return -1; |
427 | 0 | } |
428 | | /* |
429 | | * If we were looking for extensions or apple (silent) it is not our |
430 | | * job to print here, so don't count this as a match. |
431 | | */ |
432 | 0 | if (ret == 1 && silent) |
433 | 0 | return 0; |
434 | 0 | return ret; |
435 | 0 | } |