Coverage Report

Created: 2024-10-01 06:58

/src/file/src/fsmagic.c
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
}