168349Sobrien/*
2133359Sobrien * Copyright (c) Ian F. Darwin 1986-1995.
3133359Sobrien * Software written by Ian F. Darwin and others;
4133359Sobrien * maintained 1995-present by Christos Zoulas and others.
5226048Sobrien *
6133359Sobrien * Redistribution and use in source and binary forms, with or without
7133359Sobrien * modification, are permitted provided that the following conditions
8133359Sobrien * are met:
9133359Sobrien * 1. Redistributions of source code must retain the above copyright
10133359Sobrien *    notice immediately at the beginning of the file, without modification,
11133359Sobrien *    this list of conditions, and the following disclaimer.
12133359Sobrien * 2. Redistributions in binary form must reproduce the above copyright
13133359Sobrien *    notice, this list of conditions and the following disclaimer in the
14133359Sobrien *    documentation and/or other materials provided with the distribution.
15226048Sobrien *
16133359Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18133359Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19133359Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20133359Sobrien * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21133359Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22133359Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23133359Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24133359Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25133359Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26133359Sobrien * SUCH DAMAGE.
27133359Sobrien */
28133359Sobrien/*
2968349Sobrien * is_tar() -- figure out whether file is a tar archive.
3068349Sobrien *
3168349Sobrien * Stolen (by the author!) from the public domain tar program:
3268349Sobrien * Public Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu).
3368349Sobrien *
3468349Sobrien * @(#)list.c 1.18 9/23/86 Public Domain - gnu
3568349Sobrien *
3668349Sobrien * Comments changed and some code/comments reformatted
3768349Sobrien * for file command by Ian Darwin.
3868349Sobrien */
3968349Sobrien
40103373Sobrien#include "file.h"
41191736Sobrien
42191736Sobrien#ifndef lint
43330569SgordonFILE_RCSID("@(#)$File: is_tar.c,v 1.39 2017/03/17 20:45:01 christos Exp $")
44191736Sobrien#endif
45191736Sobrien
46133359Sobrien#include "magic.h"
4768349Sobrien#include <string.h>
4868349Sobrien#include <ctype.h>
4968349Sobrien#include "tar.h"
5068349Sobrien
5168349Sobrien#define	isodigit(c)	( ((c) >= '0') && ((c) <= '7') )
5268349Sobrien
53133359Sobrienprivate int is_tar(const unsigned char *, size_t);
54330569Sgordonprivate int from_oct(const char *, size_t);	/* Decode octal number */
5568349Sobrien
56186690Sobrienstatic const char tartype[][32] = {
57175296Sobrien	"tar archive",
58175296Sobrien	"POSIX tar archive",
59175296Sobrien	"POSIX tar archive (GNU)",
60175296Sobrien};
61175296Sobrien
62133359Sobrienprotected int
63133359Sobrienfile_is_tar(struct magic_set *ms, const unsigned char *buf, size_t nbytes)
64133359Sobrien{
65133359Sobrien	/*
66133359Sobrien	 * Do the tar test first, because if the first file in the tar
67133359Sobrien	 * archive starts with a dot, we can confuse it with an nroff file.
68133359Sobrien	 */
69191736Sobrien	int tar;
70175296Sobrien	int mime = ms->flags & MAGIC_MIME;
71175296Sobrien
72284778Sdelphij	if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) != 0)
73133359Sobrien		return 0;
74175296Sobrien
75191736Sobrien	tar = is_tar(buf, nbytes);
76191736Sobrien	if (tar < 1 || tar > 3)
77175296Sobrien		return 0;
78175296Sobrien
79191736Sobrien	if (file_printf(ms, "%s", mime ? "application/x-tar" :
80175296Sobrien	    tartype[tar - 1]) == -1)
81175296Sobrien		return -1;
82175296Sobrien	return 1;
83133359Sobrien}
84133359Sobrien
8568349Sobrien/*
86226048Sobrien * Return
87226048Sobrien *	0 if the checksum is bad (i.e., probably not a tar archive),
8868349Sobrien *	1 for old UNIX tar file,
89186690Sobrien *	2 for Unix Std (POSIX) tar file,
90186690Sobrien *	3 for GNU tar file.
9168349Sobrien */
92133359Sobrienprivate int
93133359Sobrienis_tar(const unsigned char *buf, size_t nbytes)
9468349Sobrien{
95133359Sobrien	const union record *header = (const union record *)(const void *)buf;
96330569Sgordon	size_t i;
97330569Sgordon	int sum, recsum;
98330569Sgordon	const unsigned char *p, *ep;
9968349Sobrien
100330569Sgordon	if (nbytes < sizeof(*header))
10168349Sobrien		return 0;
10268349Sobrien
103330569Sgordon	recsum = from_oct(header->header.chksum, sizeof(header->header.chksum));
10468349Sobrien
10568349Sobrien	sum = 0;
10668349Sobrien	p = header->charptr;
107330569Sgordon	ep = header->charptr + sizeof(*header);
108330569Sgordon	while (p < ep)
109226048Sobrien		sum += *p++;
11068349Sobrien
11168349Sobrien	/* Adjust checksum to count the "chksum" field as blanks. */
112330569Sgordon	for (i = 0; i < sizeof(header->header.chksum); i++)
113226048Sobrien		sum -= header->header.chksum[i];
114330569Sgordon	sum += ' ' * sizeof(header->header.chksum);
11568349Sobrien
11668349Sobrien	if (sum != recsum)
11768349Sobrien		return 0;	/* Not a tar archive */
118226048Sobrien
119330569Sgordon	if (strncmp(header->header.magic, GNUTMAGIC,
120330569Sgordon	    sizeof(header->header.magic)) == 0)
121169942Sobrien		return 3;		/* GNU Unix Standard tar archive */
122330569Sgordon
123330569Sgordon	if (strncmp(header->header.magic, TMAGIC,
124330569Sgordon	    sizeof(header->header.magic)) == 0)
12568349Sobrien		return 2;		/* Unix Standard tar archive */
12668349Sobrien
12768349Sobrien	return 1;			/* Old fashioned tar archive */
12868349Sobrien}
12968349Sobrien
13068349Sobrien
13168349Sobrien/*
13268349Sobrien * Quick and dirty octal conversion.
13368349Sobrien *
134226048Sobrien * Result is -1 if the field is invalid (all blank, or non-octal).
13568349Sobrien */
136133359Sobrienprivate int
137330569Sgordonfrom_oct(const char *where, size_t digs)
13868349Sobrien{
13968349Sobrien	int	value;
14068349Sobrien
141330569Sgordon	if (digs == 0)
142330569Sgordon		return -1;
143330569Sgordon
14468349Sobrien	while (isspace((unsigned char)*where)) {	/* Skip spaces */
14568349Sobrien		where++;
146330569Sgordon		if (digs-- == 0)
14768349Sobrien			return -1;		/* All blank field */
14868349Sobrien	}
14968349Sobrien	value = 0;
150226048Sobrien	while (digs > 0 && isodigit(*where)) {	/* Scan til non-octal */
15168349Sobrien		value = (value << 3) | (*where++ - '0');
152330569Sgordon		digs--;
15368349Sobrien	}
15468349Sobrien
15568349Sobrien	if (digs > 0 && *where && !isspace((unsigned char)*where))
156226048Sobrien		return -1;			/* Ended on non-(space/NUL) */
15768349Sobrien
15868349Sobrien	return value;
15968349Sobrien}
160