Coverage Report

Created: 2025-07-12 06:42

/src/file/src/is_tar.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
 * is_tar() -- figure out whether file is a tar archive.
30
 *
31
 * Stolen (by the author!) from the file_public domain tar program:
32
 * Public Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu).
33
 *
34
 * @(#)list.c 1.18 9/23/86 Public Domain - gnu
35
 *
36
 * Comments changed and some code/comments reformatted
37
 * for file command by Ian Darwin.
38
 */
39
40
#include "file.h"
41
42
#ifndef lint
43
FILE_RCSID("@(#)$File: is_tar.c,v 1.50 2022/12/26 17:31:14 christos Exp $")
44
#endif
45
46
#include "magic.h"
47
#include <string.h>
48
#include <ctype.h>
49
#include "tar.h"
50
51
0
#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
52
53
file_private int is_tar(const unsigned char *, size_t);
54
file_private int from_oct(const char *, size_t);  /* Decode octal number */
55
56
static const char tartype[][32] = { /* should be equal to messages */
57
  "tar archive",      /* found in ../magic/Magdir/archive */
58
  "POSIX tar archive",
59
  "POSIX tar archive (GNU)",  /*  */
60
};
61
62
file_protected int
63
file_is_tar(struct magic_set *ms, const struct buffer *b)
64
0
{
65
0
  const unsigned char *buf = CAST(const unsigned char *, b->fbuf);
66
0
  size_t nbytes = b->flen;
67
  /*
68
   * Do the tar test first, because if the first file in the tar
69
   * archive starts with a dot, we can confuse it with an nroff file.
70
   */
71
0
  int tar;
72
0
  int mime = ms->flags & MAGIC_MIME;
73
74
0
  if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) != 0)
75
0
    return 0;
76
77
0
  tar = is_tar(buf, nbytes);
78
0
  if (tar < 1 || tar > 3)
79
0
    return 0;
80
81
0
  if (mime == MAGIC_MIME_ENCODING)
82
0
    return 1;
83
84
0
  if (file_printf(ms, "%s", mime ? "application/x-tar" :
85
0
      tartype[tar - 1]) == -1)
86
0
    return -1;
87
88
0
  return 1;
89
0
}
90
91
/*
92
 * Return
93
 *  0 if the checksum is bad (i.e., probably not a tar archive),
94
 *  1 for old UNIX tar file,
95
 *  2 for Unix Std (POSIX) tar file,
96
 *  3 for GNU tar file.
97
 */
98
file_private int
99
is_tar(const unsigned char *buf, size_t nbytes)
100
0
{
101
0
  static const char gpkg_match[] = "/gpkg-1";
102
103
0
  const union record *header = RCAST(const union record *,
104
0
      RCAST(const void *, buf));
105
0
  size_t i;
106
0
  int sum, recsum;
107
0
  const unsigned char *p, *ep;
108
0
  const char *nulp;
109
110
0
  if (nbytes < sizeof(*header))
111
0
    return 0;
112
113
  /* If the file looks like Gentoo GLEP 78 binary package (GPKG),
114
   * don't waste time on further checks and fall back to magic rules.
115
   */
116
0
  nulp = CAST(const char *,
117
0
      memchr(header->header.name, 0, sizeof(header->header.name)));
118
0
  if (nulp != NULL && nulp >= header->header.name + sizeof(gpkg_match) &&
119
0
      memcmp(nulp - sizeof(gpkg_match) + 1, gpkg_match,
120
0
      sizeof(gpkg_match)) == 0)
121
0
      return 0;
122
123
0
  recsum = from_oct(header->header.chksum, sizeof(header->header.chksum));
124
125
0
  sum = 0;
126
0
  p = header->charptr;
127
0
  ep = header->charptr + sizeof(*header);
128
0
  while (p < ep)
129
0
    sum += *p++;
130
131
  /* Adjust checksum to count the "chksum" field as blanks. */
132
0
  for (i = 0; i < sizeof(header->header.chksum); i++)
133
0
    sum -= header->header.chksum[i];
134
0
  sum += ' ' * sizeof(header->header.chksum);
135
136
0
  if (sum != recsum)
137
0
    return 0; /* Not a tar archive */
138
139
0
  if (strncmp(header->header.magic, GNUTMAGIC,
140
0
      sizeof(header->header.magic)) == 0)
141
0
    return 3;   /* GNU Unix Standard tar archive */
142
143
0
  if (strncmp(header->header.magic, TMAGIC,
144
0
      sizeof(header->header.magic)) == 0)
145
0
    return 2;   /* Unix Standard tar archive */
146
147
0
  return 1;     /* Old fashioned tar archive */
148
0
}
149
150
151
/*
152
 * Quick and dirty octal conversion.
153
 *
154
 * Result is -1 if the field is invalid (all blank, or non-octal).
155
 */
156
file_private int
157
from_oct(const char *where, size_t digs)
158
0
{
159
0
  int value;
160
161
0
  if (digs == 0)
162
0
    return -1;
163
164
0
  while (isspace(CAST(unsigned char, *where))) { /* Skip spaces */
165
0
    where++;
166
0
    if (digs-- == 0)
167
0
      return -1;   /* All blank field */
168
0
  }
169
0
  value = 0;
170
0
  while (digs > 0 && isodigit(*where)) { /* Scan til non-octal */
171
0
    value = (value << 3) | (*where++ - '0');
172
0
    digs--;
173
0
  }
174
175
0
  if (digs > 0 && *where && !isspace(CAST(unsigned char, *where)))
176
0
    return -1;     /* Ended on non-(space/NUL) */
177
178
0
  return value;
179
0
}