xref: /freebsd-src/sys/fs/tarfs/tarfs_vfsops.c (revision 91eca18551554b7aca80fcfd3c648f524b321252)
169d94f4cSDag-Erling Smørgrav /*-
269d94f4cSDag-Erling Smørgrav  * SPDX-License-Identifier: BSD-2-Clause
369d94f4cSDag-Erling Smørgrav  *
469d94f4cSDag-Erling Smørgrav  * Copyright (c) 2013 Juniper Networks, Inc.
538b36835SDag-Erling Smørgrav  * Copyright (c) 2022-2024 Klara, Inc.
669d94f4cSDag-Erling Smørgrav  *
769d94f4cSDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
869d94f4cSDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
969d94f4cSDag-Erling Smørgrav  * are met:
1069d94f4cSDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
1169d94f4cSDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer.
1269d94f4cSDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
1369d94f4cSDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
1469d94f4cSDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
1569d94f4cSDag-Erling Smørgrav  *
1669d94f4cSDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1769d94f4cSDag-Erling Smørgrav  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1869d94f4cSDag-Erling Smørgrav  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1969d94f4cSDag-Erling Smørgrav  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2069d94f4cSDag-Erling Smørgrav  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2169d94f4cSDag-Erling Smørgrav  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2269d94f4cSDag-Erling Smørgrav  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2369d94f4cSDag-Erling Smørgrav  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2469d94f4cSDag-Erling Smørgrav  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2569d94f4cSDag-Erling Smørgrav  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2669d94f4cSDag-Erling Smørgrav  * SUCH DAMAGE.
2769d94f4cSDag-Erling Smørgrav  */
2869d94f4cSDag-Erling Smørgrav 
2969d94f4cSDag-Erling Smørgrav #include "opt_tarfs.h"
3069d94f4cSDag-Erling Smørgrav 
3169d94f4cSDag-Erling Smørgrav #include <sys/param.h>
3269d94f4cSDag-Erling Smørgrav #include <sys/systm.h>
3369d94f4cSDag-Erling Smørgrav #include <sys/buf.h>
3469d94f4cSDag-Erling Smørgrav #include <sys/conf.h>
3569d94f4cSDag-Erling Smørgrav #include <sys/fcntl.h>
3669d94f4cSDag-Erling Smørgrav #include <sys/libkern.h>
3769d94f4cSDag-Erling Smørgrav #include <sys/limits.h>
3869d94f4cSDag-Erling Smørgrav #include <sys/lock.h>
3969d94f4cSDag-Erling Smørgrav #include <sys/malloc.h>
4069d94f4cSDag-Erling Smørgrav #include <sys/mount.h>
4169d94f4cSDag-Erling Smørgrav #include <sys/mutex.h>
4269d94f4cSDag-Erling Smørgrav #include <sys/namei.h>
4369d94f4cSDag-Erling Smørgrav #include <sys/priv.h>
4469d94f4cSDag-Erling Smørgrav #include <sys/proc.h>
4569d94f4cSDag-Erling Smørgrav #include <sys/queue.h>
4669d94f4cSDag-Erling Smørgrav #include <sys/sbuf.h>
4769d94f4cSDag-Erling Smørgrav #include <sys/stat.h>
4869d94f4cSDag-Erling Smørgrav #include <sys/uio.h>
4969d94f4cSDag-Erling Smørgrav #include <sys/vnode.h>
5069d94f4cSDag-Erling Smørgrav 
5169d94f4cSDag-Erling Smørgrav #include <vm/vm_param.h>
5269d94f4cSDag-Erling Smørgrav 
5369d94f4cSDag-Erling Smørgrav #include <geom/geom.h>
5469d94f4cSDag-Erling Smørgrav #include <geom/geom_vfs.h>
5569d94f4cSDag-Erling Smørgrav 
5669d94f4cSDag-Erling Smørgrav #include <fs/tarfs/tarfs.h>
5769d94f4cSDag-Erling Smørgrav #include <fs/tarfs/tarfs_dbg.h>
5869d94f4cSDag-Erling Smørgrav 
590118b0c8SDag-Erling Smørgrav CTASSERT(ZERO_REGION_SIZE >= TARFS_BLOCKSIZE);
6069d94f4cSDag-Erling Smørgrav 
6169d94f4cSDag-Erling Smørgrav struct ustar_header {
6269d94f4cSDag-Erling Smørgrav 	char	name[100];		/* File name */
6369d94f4cSDag-Erling Smørgrav 	char	mode[8];		/* Mode flags */
6469d94f4cSDag-Erling Smørgrav 	char	uid[8];			/* User id */
6569d94f4cSDag-Erling Smørgrav 	char	gid[8];			/* Group id */
6669d94f4cSDag-Erling Smørgrav 	char	size[12];		/* Size */
6769d94f4cSDag-Erling Smørgrav 	char	mtime[12];		/* Modified time */
6869d94f4cSDag-Erling Smørgrav 	char	checksum[8];		/* Checksum */
6969d94f4cSDag-Erling Smørgrav 	char	typeflag[1];		/* Type */
7069d94f4cSDag-Erling Smørgrav 	char	linkname[100];		/* "old format" stops here */
7169d94f4cSDag-Erling Smørgrav 	char	magic[6];		/* POSIX UStar "ustar\0" indicator */
7269d94f4cSDag-Erling Smørgrav 	char	version[2];		/* POSIX UStar version "00" */
7369d94f4cSDag-Erling Smørgrav 	char	uname[32];		/* User name */
7469d94f4cSDag-Erling Smørgrav 	char	gname[32];		/* Group name */
7569d94f4cSDag-Erling Smørgrav 	char	major[8];		/* Device major number */
7669d94f4cSDag-Erling Smørgrav 	char	minor[8];		/* Device minor number */
7769d94f4cSDag-Erling Smørgrav 	char	prefix[155];		/* Path prefix */
780118b0c8SDag-Erling Smørgrav 	char	_pad[12];
7969d94f4cSDag-Erling Smørgrav };
8069d94f4cSDag-Erling Smørgrav 
810118b0c8SDag-Erling Smørgrav CTASSERT(sizeof(struct ustar_header) == TARFS_BLOCKSIZE);
820118b0c8SDag-Erling Smørgrav 
830238d371SDag-Erling Smørgrav #define	TAR_EOF			((size_t)-1)
8469d94f4cSDag-Erling Smørgrav 
8569d94f4cSDag-Erling Smørgrav #define	TAR_TYPE_FILE		'0'
8669d94f4cSDag-Erling Smørgrav #define	TAR_TYPE_HARDLINK	'1'
8769d94f4cSDag-Erling Smørgrav #define	TAR_TYPE_SYMLINK	'2'
8869d94f4cSDag-Erling Smørgrav #define	TAR_TYPE_CHAR		'3'
8969d94f4cSDag-Erling Smørgrav #define	TAR_TYPE_BLOCK		'4'
9069d94f4cSDag-Erling Smørgrav #define	TAR_TYPE_DIRECTORY	'5'
9169d94f4cSDag-Erling Smørgrav #define	TAR_TYPE_FIFO		'6'
9269d94f4cSDag-Erling Smørgrav #define	TAR_TYPE_CONTIG		'7'
9369d94f4cSDag-Erling Smørgrav #define	TAR_TYPE_GLOBAL_EXTHDR	'g'
9469d94f4cSDag-Erling Smørgrav #define	TAR_TYPE_EXTHDR		'x'
9569d94f4cSDag-Erling Smørgrav #define	TAR_TYPE_GNU_SPARSE	'S'
9669d94f4cSDag-Erling Smørgrav 
9769d94f4cSDag-Erling Smørgrav #define	USTAR_MAGIC		(uint8_t []){ 'u', 's', 't', 'a', 'r', 0 }
9869d94f4cSDag-Erling Smørgrav #define	USTAR_VERSION		(uint8_t []){ '0', '0' }
9969d94f4cSDag-Erling Smørgrav #define	GNUTAR_MAGIC		(uint8_t []){ 'u', 's', 't', 'a', 'r', ' ' }
10069d94f4cSDag-Erling Smørgrav #define	GNUTAR_VERSION		(uint8_t []){ ' ', '\x0' }
10169d94f4cSDag-Erling Smørgrav 
10269d94f4cSDag-Erling Smørgrav #define	DEFDIRMODE	(S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
10369d94f4cSDag-Erling Smørgrav 
10469d94f4cSDag-Erling Smørgrav MALLOC_DEFINE(M_TARFSMNT, "tarfs mount", "tarfs mount structures");
10569d94f4cSDag-Erling Smørgrav MALLOC_DEFINE(M_TARFSNODE, "tarfs node", "tarfs node structures");
10669d94f4cSDag-Erling Smørgrav 
10769d94f4cSDag-Erling Smørgrav static vfs_mount_t	tarfs_mount;
10869d94f4cSDag-Erling Smørgrav static vfs_unmount_t	tarfs_unmount;
10969d94f4cSDag-Erling Smørgrav static vfs_root_t	tarfs_root;
11069d94f4cSDag-Erling Smørgrav static vfs_statfs_t	tarfs_statfs;
11169d94f4cSDag-Erling Smørgrav static vfs_fhtovp_t	tarfs_fhtovp;
11269d94f4cSDag-Erling Smørgrav 
11369d94f4cSDag-Erling Smørgrav static const char *tarfs_opts[] = {
114a02d9cadSSimon J. Gerraty 	"as", "from", "gid", "mode", "uid", "verify",
11569d94f4cSDag-Erling Smørgrav 	NULL
11669d94f4cSDag-Erling Smørgrav };
11769d94f4cSDag-Erling Smørgrav 
11869d94f4cSDag-Erling Smørgrav /*
1198427d94cSDag-Erling Smørgrav  * Reads a len-width signed octal number from strp.  Returns 0 on success
1208427d94cSDag-Erling Smørgrav  * and non-zero on error.
12169d94f4cSDag-Erling Smørgrav  */
1228427d94cSDag-Erling Smørgrav static int
tarfs_str2octal(const char * strp,size_t len,int64_t * num)1238427d94cSDag-Erling Smørgrav tarfs_str2octal(const char *strp, size_t len, int64_t *num)
12469d94f4cSDag-Erling Smørgrav {
12569d94f4cSDag-Erling Smørgrav 	int64_t val;
12669d94f4cSDag-Erling Smørgrav 	size_t idx;
12769d94f4cSDag-Erling Smørgrav 	int sign;
12869d94f4cSDag-Erling Smørgrav 
1298427d94cSDag-Erling Smørgrav 	idx = 0;
13069d94f4cSDag-Erling Smørgrav 	if (strp[idx] == '-') {
13169d94f4cSDag-Erling Smørgrav 		sign = -1;
13269d94f4cSDag-Erling Smørgrav 		idx++;
1338427d94cSDag-Erling Smørgrav 	} else {
13469d94f4cSDag-Erling Smørgrav 		sign = 1;
1358427d94cSDag-Erling Smørgrav 	}
13669d94f4cSDag-Erling Smørgrav 
13769d94f4cSDag-Erling Smørgrav 	val = 0;
1388427d94cSDag-Erling Smørgrav 	for (; idx < len && strp[idx] != '\0' && strp[idx] != ' '; idx++) {
13969d94f4cSDag-Erling Smørgrav 		if (strp[idx] < '0' || strp[idx] > '7')
1408427d94cSDag-Erling Smørgrav 			return (EINVAL);
14169d94f4cSDag-Erling Smørgrav 		val <<= 3;
1428427d94cSDag-Erling Smørgrav 		val += strp[idx] - '0';
1438427d94cSDag-Erling Smørgrav 		if (val > INT64_MAX / 8)
1448427d94cSDag-Erling Smørgrav 			return (ERANGE);
14569d94f4cSDag-Erling Smørgrav 	}
14669d94f4cSDag-Erling Smørgrav 
1478427d94cSDag-Erling Smørgrav 	*num = val * sign;
1488427d94cSDag-Erling Smørgrav 	return (0);
14969d94f4cSDag-Erling Smørgrav }
15069d94f4cSDag-Erling Smørgrav 
15169d94f4cSDag-Erling Smørgrav /*
15269d94f4cSDag-Erling Smørgrav  * Reads a len-byte extended numeric value from strp.  The first byte has
15369d94f4cSDag-Erling Smørgrav  * bit 7 set to indicate the format; the remaining 7 bits + the (len - 1)
15469d94f4cSDag-Erling Smørgrav  * bytes that follow form a big-endian signed two's complement binary
1558427d94cSDag-Erling Smørgrav  * number.  Returns 0 on success and non-zero on error;
15669d94f4cSDag-Erling Smørgrav  */
1578427d94cSDag-Erling Smørgrav static int
tarfs_str2base256(const char * strp,size_t len,int64_t * num)1588427d94cSDag-Erling Smørgrav tarfs_str2base256(const char *strp, size_t len, int64_t *num)
15969d94f4cSDag-Erling Smørgrav {
16069d94f4cSDag-Erling Smørgrav 	int64_t val;
16169d94f4cSDag-Erling Smørgrav 	size_t idx;
16269d94f4cSDag-Erling Smørgrav 
16369d94f4cSDag-Erling Smørgrav 	KASSERT(strp[0] & 0x80, ("not an extended numeric value"));
16469d94f4cSDag-Erling Smørgrav 
16569d94f4cSDag-Erling Smørgrav 	/* Sign-extend the first byte */
16669d94f4cSDag-Erling Smørgrav 	if ((strp[0] & 0x40) != 0)
16769d94f4cSDag-Erling Smørgrav 		val = (int64_t)-1;
16869d94f4cSDag-Erling Smørgrav 	else
16969d94f4cSDag-Erling Smørgrav 		val = 0;
17069d94f4cSDag-Erling Smørgrav 	val <<= 6;
17169d94f4cSDag-Erling Smørgrav 	val |= (strp[0] & 0x3f);
17269d94f4cSDag-Erling Smørgrav 
17369d94f4cSDag-Erling Smørgrav 	/* Read subsequent bytes */
17469d94f4cSDag-Erling Smørgrav 	for (idx = 1; idx < len; idx++) {
17569d94f4cSDag-Erling Smørgrav 		val <<= 8;
17669d94f4cSDag-Erling Smørgrav 		val |= (0xff & (int64_t)strp[idx]);
1778427d94cSDag-Erling Smørgrav 		if (val > INT64_MAX / 256 || val < INT64_MIN / 256)
1788427d94cSDag-Erling Smørgrav 			return (ERANGE);
17969d94f4cSDag-Erling Smørgrav 	}
18069d94f4cSDag-Erling Smørgrav 
1818427d94cSDag-Erling Smørgrav 	*num = val;
1828427d94cSDag-Erling Smørgrav 	return (0);
18369d94f4cSDag-Erling Smørgrav }
18469d94f4cSDag-Erling Smørgrav 
18569d94f4cSDag-Erling Smørgrav /*
18669d94f4cSDag-Erling Smørgrav  * Read a len-byte numeric field from strp.  If bit 7 of the first byte it
18769d94f4cSDag-Erling Smørgrav  * set, assume an extended numeric value (signed two's complement);
18869d94f4cSDag-Erling Smørgrav  * otherwise, assume a signed octal value.
18969d94f4cSDag-Erling Smørgrav  */
1908427d94cSDag-Erling Smørgrav static int
tarfs_str2int64(const char * strp,size_t len,int64_t * num)1918427d94cSDag-Erling Smørgrav tarfs_str2int64(const char *strp, size_t len, int64_t *num)
19269d94f4cSDag-Erling Smørgrav {
19369d94f4cSDag-Erling Smørgrav 	if (len < 1)
1948427d94cSDag-Erling Smørgrav 		return (EINVAL);
19569d94f4cSDag-Erling Smørgrav 	if ((strp[0] & 0x80) != 0)
1968427d94cSDag-Erling Smørgrav 		return (tarfs_str2base256(strp, len, num));
1978427d94cSDag-Erling Smørgrav 	return (tarfs_str2octal(strp, len, num));
19869d94f4cSDag-Erling Smørgrav }
19969d94f4cSDag-Erling Smørgrav 
20069d94f4cSDag-Erling Smørgrav /*
20169d94f4cSDag-Erling Smørgrav  * Verifies the checksum of a header.  Returns true if the checksum is
20269d94f4cSDag-Erling Smørgrav  * valid, false otherwise.
20369d94f4cSDag-Erling Smørgrav  */
20469d94f4cSDag-Erling Smørgrav static boolean_t
tarfs_checksum(struct ustar_header * hdrp)20569d94f4cSDag-Erling Smørgrav tarfs_checksum(struct ustar_header *hdrp)
20669d94f4cSDag-Erling Smørgrav {
20769d94f4cSDag-Erling Smørgrav 	const unsigned char *ptr;
208cbddb2f0SDag-Erling Smørgrav 	int64_t checksum, hdrsum;
20969d94f4cSDag-Erling Smørgrav 
2100118b0c8SDag-Erling Smørgrav 	if (tarfs_str2int64(hdrp->checksum, sizeof(hdrp->checksum), &hdrsum) != 0) {
2110118b0c8SDag-Erling Smørgrav 		TARFS_DPF(CHECKSUM, "%s: invalid header checksum \"%.*s\"\n",
2120118b0c8SDag-Erling Smørgrav 		    __func__, (int)sizeof(hdrp->checksum), hdrp->checksum);
2130118b0c8SDag-Erling Smørgrav 		return (false);
2140118b0c8SDag-Erling Smørgrav 	}
2150118b0c8SDag-Erling Smørgrav 	TARFS_DPF(CHECKSUM, "%s: header checksum \"%.*s\" = %#lo\n", __func__,
2160118b0c8SDag-Erling Smørgrav 	    (int)sizeof(hdrp->checksum), hdrp->checksum, hdrsum);
21769d94f4cSDag-Erling Smørgrav 
21869d94f4cSDag-Erling Smørgrav 	checksum = 0;
21969d94f4cSDag-Erling Smørgrav 	for (ptr = (const unsigned char *)hdrp;
22069d94f4cSDag-Erling Smørgrav 	     ptr < (const unsigned char *)hdrp->checksum; ptr++)
22169d94f4cSDag-Erling Smørgrav 		checksum += *ptr;
2220118b0c8SDag-Erling Smørgrav 	for (;
2230118b0c8SDag-Erling Smørgrav 	     ptr < (const unsigned char *)hdrp->typeflag; ptr++)
22469d94f4cSDag-Erling Smørgrav 		checksum += 0x20;
2250118b0c8SDag-Erling Smørgrav 	for (;
22669d94f4cSDag-Erling Smørgrav 	     ptr < (const unsigned char *)(hdrp + 1); ptr++)
22769d94f4cSDag-Erling Smørgrav 		checksum += *ptr;
2280118b0c8SDag-Erling Smørgrav 	TARFS_DPF(CHECKSUM, "%s: calc unsigned checksum %#lo\n", __func__,
22969d94f4cSDag-Erling Smørgrav 	    checksum);
23069d94f4cSDag-Erling Smørgrav 	if (hdrsum == checksum)
23169d94f4cSDag-Erling Smørgrav 		return (true);
23269d94f4cSDag-Erling Smørgrav 
23369d94f4cSDag-Erling Smørgrav 	/*
23469d94f4cSDag-Erling Smørgrav 	 * Repeat test with signed bytes, some older formats use a broken
23569d94f4cSDag-Erling Smørgrav 	 * form of the calculation
23669d94f4cSDag-Erling Smørgrav 	 */
23769d94f4cSDag-Erling Smørgrav 	checksum = 0;
23869d94f4cSDag-Erling Smørgrav 	for (ptr = (const unsigned char *)hdrp;
23969d94f4cSDag-Erling Smørgrav 	     ptr < (const unsigned char *)&hdrp->checksum; ptr++)
24069d94f4cSDag-Erling Smørgrav 		checksum += *((const signed char *)ptr);
2410118b0c8SDag-Erling Smørgrav 	for (;
2420118b0c8SDag-Erling Smørgrav 	     ptr < (const unsigned char *)&hdrp->typeflag; ptr++)
24369d94f4cSDag-Erling Smørgrav 		checksum += 0x20;
2440118b0c8SDag-Erling Smørgrav 	for (;
24569d94f4cSDag-Erling Smørgrav 	     ptr < (const unsigned char *)(hdrp + 1); ptr++)
24669d94f4cSDag-Erling Smørgrav 		checksum += *((const signed char *)ptr);
2470118b0c8SDag-Erling Smørgrav 	TARFS_DPF(CHECKSUM, "%s: calc signed checksum %#lo\n", __func__,
24869d94f4cSDag-Erling Smørgrav 	    checksum);
24969d94f4cSDag-Erling Smørgrav 	if (hdrsum == checksum)
25069d94f4cSDag-Erling Smørgrav 		return (true);
25169d94f4cSDag-Erling Smørgrav 
25269d94f4cSDag-Erling Smørgrav 	return (false);
25369d94f4cSDag-Erling Smørgrav }
25469d94f4cSDag-Erling Smørgrav 
25569d94f4cSDag-Erling Smørgrav 
25669d94f4cSDag-Erling Smørgrav /*
25769d94f4cSDag-Erling Smørgrav  * Looks up a path in the tarfs node tree.
25869d94f4cSDag-Erling Smørgrav  *
25969d94f4cSDag-Erling Smørgrav  * - If the path exists, stores a pointer to the corresponding tarfs_node
26069d94f4cSDag-Erling Smørgrav  *   in retnode and a pointer to its parent in retparent.
26169d94f4cSDag-Erling Smørgrav  *
26269d94f4cSDag-Erling Smørgrav  * - If the path does not exist, but create_dirs is true, creates ancestor
26369d94f4cSDag-Erling Smørgrav  *   directories and returns NULL in retnode and the parent in retparent.
26469d94f4cSDag-Erling Smørgrav  *
26569d94f4cSDag-Erling Smørgrav  * - If the path does not exist and create_dirs is false, stops at the
26669d94f4cSDag-Erling Smørgrav  *   first missing path name component.
26769d94f4cSDag-Erling Smørgrav  *
26869d94f4cSDag-Erling Smørgrav  * - In all cases, on return, endp and sepp point to the beginning and
26969d94f4cSDag-Erling Smørgrav  *   end, respectively, of the last-processed path name component.
27069d94f4cSDag-Erling Smørgrav  *
27169d94f4cSDag-Erling Smørgrav  * - Returns 0 if the node was found, ENOENT if it was not, and some other
27269d94f4cSDag-Erling Smørgrav  *   positive errno value on failure.
27369d94f4cSDag-Erling Smørgrav  */
27469d94f4cSDag-Erling Smørgrav static int
tarfs_lookup_path(struct tarfs_mount * tmp,char * name,size_t namelen,char ** endp,char ** sepp,struct tarfs_node ** retparent,struct tarfs_node ** retnode,boolean_t create_dirs)27569d94f4cSDag-Erling Smørgrav tarfs_lookup_path(struct tarfs_mount *tmp, char *name, size_t namelen,
27669d94f4cSDag-Erling Smørgrav     char **endp, char **sepp, struct tarfs_node **retparent,
27769d94f4cSDag-Erling Smørgrav     struct tarfs_node **retnode, boolean_t create_dirs)
27869d94f4cSDag-Erling Smørgrav {
279ce6a0c77SDag-Erling Smørgrav 	struct componentname cn = { };
28069d94f4cSDag-Erling Smørgrav 	struct tarfs_node *parent, *tnp;
28169d94f4cSDag-Erling Smørgrav 	char *sep;
28269d94f4cSDag-Erling Smørgrav 	size_t len;
28369d94f4cSDag-Erling Smørgrav 	int error;
28469d94f4cSDag-Erling Smørgrav 	boolean_t do_lookup;
28569d94f4cSDag-Erling Smørgrav 
28669d94f4cSDag-Erling Smørgrav 	MPASS(name != NULL && namelen != 0);
28769d94f4cSDag-Erling Smørgrav 
28869d94f4cSDag-Erling Smørgrav 	do_lookup = true;
28969d94f4cSDag-Erling Smørgrav 	error = 0;
29069d94f4cSDag-Erling Smørgrav 	parent = tnp = tmp->root;
29169d94f4cSDag-Erling Smørgrav 	if (tnp == NULL)
29269d94f4cSDag-Erling Smørgrav 		panic("%s: root node not yet created", __func__);
29369d94f4cSDag-Erling Smørgrav 
294d481dceeSDag-Erling Smørgrav 	TARFS_DPF(LOOKUP, "%s: full path: %.*s\n", __func__,
295d481dceeSDag-Erling Smørgrav 	    (int)namelen, name);
29669d94f4cSDag-Erling Smørgrav 
29769d94f4cSDag-Erling Smørgrav 	sep = NULL;
29869d94f4cSDag-Erling Smørgrav 	for (;;) {
29969d94f4cSDag-Erling Smørgrav 		/* skip leading slash(es) */
30069d94f4cSDag-Erling Smørgrav 		while (name[0] == '/' && namelen > 0)
30169d94f4cSDag-Erling Smørgrav 			name++, namelen--;
30269d94f4cSDag-Erling Smørgrav 
30369d94f4cSDag-Erling Smørgrav 		/* did we reach the end? */
30469d94f4cSDag-Erling Smørgrav 		if (namelen == 0 || name[0] == '\0') {
30569d94f4cSDag-Erling Smørgrav 			name = do_lookup ? NULL : cn.cn_nameptr;
30669d94f4cSDag-Erling Smørgrav 			namelen = do_lookup ? 0 : cn.cn_namelen;
30769d94f4cSDag-Erling Smørgrav 			break;
30869d94f4cSDag-Erling Smørgrav 		}
30969d94f4cSDag-Erling Smørgrav 
310d481dceeSDag-Erling Smørgrav 		/* we're not at the end, so we must be in a directory */
311d481dceeSDag-Erling Smørgrav 		if (tnp != NULL && tnp->type != VDIR) {
312d481dceeSDag-Erling Smørgrav 			TARFS_DPF(LOOKUP, "%s: %.*s is not a directory\n", __func__,
313d481dceeSDag-Erling Smørgrav 			    (int)tnp->namelen, tnp->name);
314ae6cff89SDag-Erling Smørgrav 			error = ENOTDIR;
315ae6cff89SDag-Erling Smørgrav 			break;
316ae6cff89SDag-Erling Smørgrav 		}
317ae6cff89SDag-Erling Smørgrav 
31869d94f4cSDag-Erling Smørgrav 		/* locate the next separator */
31969d94f4cSDag-Erling Smørgrav 		for (sep = name, len = 0;
32069d94f4cSDag-Erling Smørgrav 		     *sep != '\0' && *sep != '/' && len < namelen;
32169d94f4cSDag-Erling Smørgrav 		     sep++, len++)
32269d94f4cSDag-Erling Smørgrav 			/* nothing */ ;
32369d94f4cSDag-Erling Smørgrav 
32469d94f4cSDag-Erling Smørgrav 		/* check for . and .. */
325ce6a0c77SDag-Erling Smørgrav 		if (name[0] == '.' && len == 1) {
32669d94f4cSDag-Erling Smørgrav 			name += len;
32769d94f4cSDag-Erling Smørgrav 			namelen -= len;
32869d94f4cSDag-Erling Smørgrav 			continue;
329ce6a0c77SDag-Erling Smørgrav 		}
330ce6a0c77SDag-Erling Smørgrav 		if (name[0] == '.' && name[1] == '.' && len == 2) {
33169d94f4cSDag-Erling Smørgrav 			if (tnp == tmp->root) {
33269d94f4cSDag-Erling Smørgrav 				error = EINVAL;
33369d94f4cSDag-Erling Smørgrav 				break;
33469d94f4cSDag-Erling Smørgrav 			}
335ce6a0c77SDag-Erling Smørgrav 			tnp = parent;
33669d94f4cSDag-Erling Smørgrav 			parent = tnp->parent;
337ef184e98SDag-Erling Smørgrav 			cn.cn_nameptr = tnp->name;
338ef184e98SDag-Erling Smørgrav 			cn.cn_namelen = tnp->namelen;
339ef184e98SDag-Erling Smørgrav 			do_lookup = true;
340ef184e98SDag-Erling Smørgrav 			TARFS_DPF(LOOKUP, "%s: back to %.*s/\n", __func__,
341ef184e98SDag-Erling Smørgrav 			    (int)tnp->namelen, tnp->name);
34269d94f4cSDag-Erling Smørgrav 			name += len;
34369d94f4cSDag-Erling Smørgrav 			namelen -= len;
34469d94f4cSDag-Erling Smørgrav 			continue;
34569d94f4cSDag-Erling Smørgrav 		}
34669d94f4cSDag-Erling Smørgrav 
34769d94f4cSDag-Erling Smørgrav 		/* create parent if necessary */
34869d94f4cSDag-Erling Smørgrav 		if (!do_lookup) {
34969d94f4cSDag-Erling Smørgrav 			TARFS_DPF(ALLOC, "%s: creating %.*s\n", __func__,
35069d94f4cSDag-Erling Smørgrav 			    (int)cn.cn_namelen, cn.cn_nameptr);
35169d94f4cSDag-Erling Smørgrav 			error = tarfs_alloc_node(tmp, cn.cn_nameptr,
35269d94f4cSDag-Erling Smørgrav 			    cn.cn_namelen, VDIR, -1, 0, tmp->mtime, 0, 0,
35369d94f4cSDag-Erling Smørgrav 			    DEFDIRMODE, 0, NULL, NODEV, parent, &tnp);
35469d94f4cSDag-Erling Smørgrav 			if (error != 0)
35569d94f4cSDag-Erling Smørgrav 				break;
35669d94f4cSDag-Erling Smørgrav 		}
35769d94f4cSDag-Erling Smørgrav 
35869d94f4cSDag-Erling Smørgrav 		parent = tnp;
35969d94f4cSDag-Erling Smørgrav 		tnp = NULL;
36069d94f4cSDag-Erling Smørgrav 		cn.cn_nameptr = name;
36169d94f4cSDag-Erling Smørgrav 		cn.cn_namelen = len;
362d481dceeSDag-Erling Smørgrav 		TARFS_DPF(LOOKUP, "%s: looking up %.*s in %.*s/\n", __func__,
363d481dceeSDag-Erling Smørgrav 		    (int)cn.cn_namelen, cn.cn_nameptr,
364d481dceeSDag-Erling Smørgrav 		    (int)parent->namelen, parent->name);
36569d94f4cSDag-Erling Smørgrav 		if (do_lookup) {
36669d94f4cSDag-Erling Smørgrav 			tnp = tarfs_lookup_node(parent, NULL, &cn);
36769d94f4cSDag-Erling Smørgrav 			if (tnp == NULL) {
36869d94f4cSDag-Erling Smørgrav 				do_lookup = false;
36938b36835SDag-Erling Smørgrav 				if (!create_dirs) {
37038b36835SDag-Erling Smørgrav 					error = ENOENT;
37169d94f4cSDag-Erling Smørgrav 					break;
37269d94f4cSDag-Erling Smørgrav 				}
37369d94f4cSDag-Erling Smørgrav 			}
37438b36835SDag-Erling Smørgrav 		}
37569d94f4cSDag-Erling Smørgrav 		name += cn.cn_namelen;
37669d94f4cSDag-Erling Smørgrav 		namelen -= cn.cn_namelen;
37769d94f4cSDag-Erling Smørgrav 	}
37869d94f4cSDag-Erling Smørgrav 
379d481dceeSDag-Erling Smørgrav 	TARFS_DPF(LOOKUP, "%s: parent %p node %p\n", __func__, parent, tnp);
38069d94f4cSDag-Erling Smørgrav 
38169d94f4cSDag-Erling Smørgrav 	if (retparent)
38269d94f4cSDag-Erling Smørgrav 		*retparent = parent;
38369d94f4cSDag-Erling Smørgrav 	if (retnode)
38469d94f4cSDag-Erling Smørgrav 		*retnode = tnp;
38569d94f4cSDag-Erling Smørgrav 	if (endp) {
38669d94f4cSDag-Erling Smørgrav 		if (namelen > 0)
38769d94f4cSDag-Erling Smørgrav 			*endp = name;
38869d94f4cSDag-Erling Smørgrav 		else
38969d94f4cSDag-Erling Smørgrav 			*endp = NULL;
39069d94f4cSDag-Erling Smørgrav 	}
39169d94f4cSDag-Erling Smørgrav 	if (sepp)
39269d94f4cSDag-Erling Smørgrav 		*sepp = sep;
39369d94f4cSDag-Erling Smørgrav 	return (error);
39469d94f4cSDag-Erling Smørgrav }
39569d94f4cSDag-Erling Smørgrav 
39669d94f4cSDag-Erling Smørgrav /*
39769d94f4cSDag-Erling Smørgrav  * Frees a tarfs_mount structure and everything it references.
39869d94f4cSDag-Erling Smørgrav  */
39969d94f4cSDag-Erling Smørgrav static void
tarfs_free_mount(struct tarfs_mount * tmp)40069d94f4cSDag-Erling Smørgrav tarfs_free_mount(struct tarfs_mount *tmp)
40169d94f4cSDag-Erling Smørgrav {
40269d94f4cSDag-Erling Smørgrav 	struct mount *mp;
403fd8c98a5SDag-Erling Smørgrav 	struct tarfs_node *tnp, *tnp_next;
40469d94f4cSDag-Erling Smørgrav 
40569d94f4cSDag-Erling Smørgrav 	MPASS(tmp != NULL);
40669d94f4cSDag-Erling Smørgrav 
40769d94f4cSDag-Erling Smørgrav 	TARFS_DPF(ALLOC, "%s: Freeing mount structure %p\n", __func__, tmp);
40869d94f4cSDag-Erling Smørgrav 
40969d94f4cSDag-Erling Smørgrav 	TARFS_DPF(ALLOC, "%s: freeing tarfs_node structures\n", __func__);
410fd8c98a5SDag-Erling Smørgrav 	TAILQ_FOREACH_SAFE(tnp, &tmp->allnodes, entries, tnp_next) {
41169d94f4cSDag-Erling Smørgrav 		tarfs_free_node(tnp);
41269d94f4cSDag-Erling Smørgrav 	}
41369d94f4cSDag-Erling Smørgrav 
41469d94f4cSDag-Erling Smørgrav 	(void)tarfs_io_fini(tmp);
41569d94f4cSDag-Erling Smørgrav 
41669d94f4cSDag-Erling Smørgrav 	TARFS_DPF(ALLOC, "%s: deleting unr header\n", __func__);
41769d94f4cSDag-Erling Smørgrav 	delete_unrhdr(tmp->ino_unr);
41869d94f4cSDag-Erling Smørgrav 	mp = tmp->vfs;
41969d94f4cSDag-Erling Smørgrav 	mp->mnt_data = NULL;
42069d94f4cSDag-Erling Smørgrav 
42169d94f4cSDag-Erling Smørgrav 	TARFS_DPF(ALLOC, "%s: freeing structure\n", __func__);
42269d94f4cSDag-Erling Smørgrav 	free(tmp, M_TARFSMNT);
42369d94f4cSDag-Erling Smørgrav }
42469d94f4cSDag-Erling Smørgrav 
42569d94f4cSDag-Erling Smørgrav /*
42669d94f4cSDag-Erling Smørgrav  * Processes the tar file header at block offset blknump and allocates and
42769d94f4cSDag-Erling Smørgrav  * populates a tarfs_node structure for the file it describes.  Updated
42869d94f4cSDag-Erling Smørgrav  * blknump to point to the next unread tar file block, or TAR_EOF if EOF
42969d94f4cSDag-Erling Smørgrav  * is reached.  Returns 0 on success or EOF and a positive errno value on
43069d94f4cSDag-Erling Smørgrav  * failure.
43169d94f4cSDag-Erling Smørgrav  */
43269d94f4cSDag-Erling Smørgrav static int
tarfs_alloc_one(struct tarfs_mount * tmp,size_t * blknump)4330238d371SDag-Erling Smørgrav tarfs_alloc_one(struct tarfs_mount *tmp, size_t *blknump)
43469d94f4cSDag-Erling Smørgrav {
43569d94f4cSDag-Erling Smørgrav 	char block[TARFS_BLOCKSIZE];
43669d94f4cSDag-Erling Smørgrav 	struct ustar_header *hdrp = (struct ustar_header *)block;
43769d94f4cSDag-Erling Smørgrav 	struct sbuf *namebuf = NULL;
43869d94f4cSDag-Erling Smørgrav 	char *exthdr = NULL, *name = NULL, *link = NULL;
4390238d371SDag-Erling Smørgrav 	size_t blknum = *blknump;
440ce6a0c77SDag-Erling Smørgrav 	int64_t num;
44169d94f4cSDag-Erling Smørgrav 	int endmarker = 0;
44269d94f4cSDag-Erling Smørgrav 	char *namep, *sep;
44338b36835SDag-Erling Smørgrav 	struct tarfs_node *parent, *tnp, *other;
44469d94f4cSDag-Erling Smørgrav 	size_t namelen = 0, linklen = 0, realsize = 0, sz;
44569d94f4cSDag-Erling Smørgrav 	ssize_t res;
44669d94f4cSDag-Erling Smørgrav 	dev_t rdev;
44769d94f4cSDag-Erling Smørgrav 	gid_t gid;
44869d94f4cSDag-Erling Smørgrav 	mode_t mode;
44969d94f4cSDag-Erling Smørgrav 	time_t mtime;
45069d94f4cSDag-Erling Smørgrav 	uid_t uid;
45169d94f4cSDag-Erling Smørgrav 	long major = -1, minor = -1;
45269d94f4cSDag-Erling Smørgrav 	unsigned int flags = 0;
45369d94f4cSDag-Erling Smørgrav 	int error;
45469d94f4cSDag-Erling Smørgrav 	boolean_t sparse = false;
45569d94f4cSDag-Erling Smørgrav 
45669d94f4cSDag-Erling Smørgrav again:
45769d94f4cSDag-Erling Smørgrav 	/* read next header */
45869d94f4cSDag-Erling Smørgrav 	res = tarfs_io_read_buf(tmp, false, block,
45969d94f4cSDag-Erling Smørgrav 	    TARFS_BLOCKSIZE * blknum, TARFS_BLOCKSIZE);
46069d94f4cSDag-Erling Smørgrav 	if (res < 0) {
46169d94f4cSDag-Erling Smørgrav 		error = -res;
46269d94f4cSDag-Erling Smørgrav 		goto bad;
46369d94f4cSDag-Erling Smørgrav 	} else if (res < TARFS_BLOCKSIZE) {
46469d94f4cSDag-Erling Smørgrav 		goto eof;
46569d94f4cSDag-Erling Smørgrav 	}
46669d94f4cSDag-Erling Smørgrav 	blknum++;
46769d94f4cSDag-Erling Smørgrav 
46869d94f4cSDag-Erling Smørgrav 	/* check for end marker */
46969d94f4cSDag-Erling Smørgrav 	if (memcmp(block, zero_region, TARFS_BLOCKSIZE) == 0) {
47069d94f4cSDag-Erling Smørgrav 		if (endmarker++) {
47169d94f4cSDag-Erling Smørgrav 			if (exthdr != NULL) {
47269d94f4cSDag-Erling Smørgrav 				TARFS_DPF(IO, "%s: orphaned extended header at %zu\n",
47369d94f4cSDag-Erling Smørgrav 				    __func__, TARFS_BLOCKSIZE * (blknum - 1));
47469d94f4cSDag-Erling Smørgrav 				free(exthdr, M_TEMP);
47569d94f4cSDag-Erling Smørgrav 			}
47669d94f4cSDag-Erling Smørgrav 			TARFS_DPF(IO, "%s: end of archive at %zu\n", __func__,
47769d94f4cSDag-Erling Smørgrav 			    TARFS_BLOCKSIZE * blknum);
47869d94f4cSDag-Erling Smørgrav 			tmp->nblocks = blknum;
47969d94f4cSDag-Erling Smørgrav 			*blknump = TAR_EOF;
48069d94f4cSDag-Erling Smørgrav 			return (0);
48169d94f4cSDag-Erling Smørgrav 		}
48269d94f4cSDag-Erling Smørgrav 		goto again;
48369d94f4cSDag-Erling Smørgrav 	}
48469d94f4cSDag-Erling Smørgrav 
48569d94f4cSDag-Erling Smørgrav 	/* verify magic */
48669d94f4cSDag-Erling Smørgrav 	if (memcmp(hdrp->magic, USTAR_MAGIC, sizeof(USTAR_MAGIC)) == 0 &&
48769d94f4cSDag-Erling Smørgrav 	    memcmp(hdrp->version, USTAR_VERSION, sizeof(USTAR_VERSION)) == 0) {
48869d94f4cSDag-Erling Smørgrav 		/* POSIX */
48969d94f4cSDag-Erling Smørgrav 	} else if (memcmp(hdrp->magic, GNUTAR_MAGIC, sizeof(GNUTAR_MAGIC)) == 0 &&
49069d94f4cSDag-Erling Smørgrav 	    memcmp(hdrp->magic, GNUTAR_MAGIC, sizeof(GNUTAR_MAGIC)) == 0) {
49169d94f4cSDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: GNU tar format at %zu\n", __func__,
49269d94f4cSDag-Erling Smørgrav 		    TARFS_BLOCKSIZE * (blknum - 1));
49369d94f4cSDag-Erling Smørgrav 		error = EFTYPE;
49469d94f4cSDag-Erling Smørgrav 		goto bad;
49569d94f4cSDag-Erling Smørgrav 	} else {
49669d94f4cSDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: unsupported TAR format at %zu\n",
49769d94f4cSDag-Erling Smørgrav 		    __func__, TARFS_BLOCKSIZE * (blknum - 1));
49869d94f4cSDag-Erling Smørgrav 		error = EINVAL;
49969d94f4cSDag-Erling Smørgrav 		goto bad;
50069d94f4cSDag-Erling Smørgrav 	}
50169d94f4cSDag-Erling Smørgrav 
50269d94f4cSDag-Erling Smørgrav 	/* verify checksum */
50369d94f4cSDag-Erling Smørgrav 	if (!tarfs_checksum(hdrp)) {
50469d94f4cSDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: header checksum failed at %zu\n",
50569d94f4cSDag-Erling Smørgrav 		    __func__, TARFS_BLOCKSIZE * (blknum - 1));
50669d94f4cSDag-Erling Smørgrav 		error = EINVAL;
50769d94f4cSDag-Erling Smørgrav 		goto bad;
50869d94f4cSDag-Erling Smørgrav 	}
50969d94f4cSDag-Erling Smørgrav 
51069d94f4cSDag-Erling Smørgrav 	/* get standard attributes */
5118427d94cSDag-Erling Smørgrav 	if (tarfs_str2int64(hdrp->mode, sizeof(hdrp->mode), &num) != 0 ||
5128427d94cSDag-Erling Smørgrav 	    num < 0 || num > (S_IFMT|ALLPERMS)) {
513ce6a0c77SDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: invalid file mode at %zu\n",
514ce6a0c77SDag-Erling Smørgrav 		    __func__, TARFS_BLOCKSIZE * (blknum - 1));
515ce6a0c77SDag-Erling Smørgrav 		mode = S_IRUSR;
516ce6a0c77SDag-Erling Smørgrav 	} else {
517e81d55b4SDag-Erling Smørgrav 		mode = num & ALLPERMS;
518ce6a0c77SDag-Erling Smørgrav 	}
5198427d94cSDag-Erling Smørgrav 	if (tarfs_str2int64(hdrp->uid, sizeof(hdrp->uid), &num) != 0 ||
5208427d94cSDag-Erling Smørgrav 	    num < 0 || num > UID_MAX) {
5218427d94cSDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: invalid UID at %zu\n",
522ce6a0c77SDag-Erling Smørgrav 		    __func__, TARFS_BLOCKSIZE * (blknum - 1));
523ce6a0c77SDag-Erling Smørgrav 		uid = tmp->root->uid;
524ce6a0c77SDag-Erling Smørgrav 		mode &= ~S_ISUID;
525ce6a0c77SDag-Erling Smørgrav 	} else {
526ce6a0c77SDag-Erling Smørgrav 		uid = num;
527ce6a0c77SDag-Erling Smørgrav 	}
5288427d94cSDag-Erling Smørgrav 	if (tarfs_str2int64(hdrp->gid, sizeof(hdrp->gid), &num) != 0 ||
5298427d94cSDag-Erling Smørgrav 	    num < 0 || num > GID_MAX) {
5308427d94cSDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: invalid GID at %zu\n",
531ce6a0c77SDag-Erling Smørgrav 		    __func__, TARFS_BLOCKSIZE * (blknum - 1));
532ce6a0c77SDag-Erling Smørgrav 		gid = tmp->root->gid;
533ce6a0c77SDag-Erling Smørgrav 		mode &= ~S_ISGID;
534ce6a0c77SDag-Erling Smørgrav 	} else {
535ce6a0c77SDag-Erling Smørgrav 		gid = num;
536ce6a0c77SDag-Erling Smørgrav 	}
5378427d94cSDag-Erling Smørgrav 	if (tarfs_str2int64(hdrp->size, sizeof(hdrp->size), &num) != 0 ||
5388427d94cSDag-Erling Smørgrav 	    num < 0) {
5398427d94cSDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: invalid size at %zu\n",
540ce6a0c77SDag-Erling Smørgrav 		    __func__, TARFS_BLOCKSIZE * (blknum - 1));
541ce6a0c77SDag-Erling Smørgrav 		error = EINVAL;
542ce6a0c77SDag-Erling Smørgrav 		goto bad;
543ce6a0c77SDag-Erling Smørgrav 	}
5448427d94cSDag-Erling Smørgrav 	sz = num;
5458427d94cSDag-Erling Smørgrav 	if (tarfs_str2int64(hdrp->mtime, sizeof(hdrp->mtime), &num) != 0) {
5468427d94cSDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: invalid modification time at %zu\n",
5478427d94cSDag-Erling Smørgrav 		    __func__, TARFS_BLOCKSIZE * (blknum - 1));
5488427d94cSDag-Erling Smørgrav 		error = EINVAL;
5498427d94cSDag-Erling Smørgrav 		goto bad;
5508427d94cSDag-Erling Smørgrav 	}
5518427d94cSDag-Erling Smørgrav 	mtime = num;
55269d94f4cSDag-Erling Smørgrav 	rdev = NODEV;
55369d94f4cSDag-Erling Smørgrav 	TARFS_DPF(ALLOC, "%s: [%c] %zu @%jd %o %d:%d\n", __func__,
55469d94f4cSDag-Erling Smørgrav 	    hdrp->typeflag[0], sz, (intmax_t)mtime, mode, uid, gid);
55569d94f4cSDag-Erling Smørgrav 
556584e1c35SDag-Erling Smørgrav 	/* global extended header? */
55769d94f4cSDag-Erling Smørgrav 	if (hdrp->typeflag[0] == TAR_TYPE_GLOBAL_EXTHDR) {
558584e1c35SDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: %zu-byte global extended header at %zu\n",
559584e1c35SDag-Erling Smørgrav 		    __func__, sz, TARFS_BLOCKSIZE * (blknum - 1));
560584e1c35SDag-Erling Smørgrav 		goto skip;
56169d94f4cSDag-Erling Smørgrav 	}
562584e1c35SDag-Erling Smørgrav 
563584e1c35SDag-Erling Smørgrav 	/* extended header? */
56469d94f4cSDag-Erling Smørgrav 	if (hdrp->typeflag[0] == TAR_TYPE_EXTHDR) {
56569d94f4cSDag-Erling Smørgrav 		if (exthdr != NULL) {
56669d94f4cSDag-Erling Smørgrav 			TARFS_DPF(IO, "%s: multiple extended headers at %zu\n",
56769d94f4cSDag-Erling Smørgrav 			    __func__, TARFS_BLOCKSIZE * (blknum - 1));
56869d94f4cSDag-Erling Smørgrav 			error = EFTYPE;
56969d94f4cSDag-Erling Smørgrav 			goto bad;
57069d94f4cSDag-Erling Smørgrav 		}
57169d94f4cSDag-Erling Smørgrav 		/* read the contents of the exthdr */
572584e1c35SDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: %zu-byte extended header at %zu\n",
57369d94f4cSDag-Erling Smørgrav 		    __func__, sz, TARFS_BLOCKSIZE * (blknum - 1));
57469d94f4cSDag-Erling Smørgrav 		exthdr = malloc(sz, M_TEMP, M_WAITOK);
57569d94f4cSDag-Erling Smørgrav 		res = tarfs_io_read_buf(tmp, false, exthdr,
57669d94f4cSDag-Erling Smørgrav 		    TARFS_BLOCKSIZE * blknum, sz);
57769d94f4cSDag-Erling Smørgrav 		if (res < 0) {
57869d94f4cSDag-Erling Smørgrav 			error = -res;
57969d94f4cSDag-Erling Smørgrav 			goto bad;
58069d94f4cSDag-Erling Smørgrav 		}
58169d94f4cSDag-Erling Smørgrav 		if (res < sz) {
58269d94f4cSDag-Erling Smørgrav 			goto eof;
58369d94f4cSDag-Erling Smørgrav 		}
58469d94f4cSDag-Erling Smørgrav 		blknum += TARFS_SZ2BLKS(res);
58569d94f4cSDag-Erling Smørgrav 		/* XXX TODO: refactor this parser */
58669d94f4cSDag-Erling Smørgrav 		char *line = exthdr;
58769d94f4cSDag-Erling Smørgrav 		while (line < exthdr + sz) {
58869d94f4cSDag-Erling Smørgrav 			char *eol, *key, *value, *sep;
58969d94f4cSDag-Erling Smørgrav 			size_t len = strtoul(line, &sep, 10);
59069d94f4cSDag-Erling Smørgrav 			if (len == 0 || sep == line || *sep != ' ') {
59169d94f4cSDag-Erling Smørgrav 				TARFS_DPF(ALLOC, "%s: exthdr syntax error\n",
59269d94f4cSDag-Erling Smørgrav 				    __func__);
59369d94f4cSDag-Erling Smørgrav 				error = EINVAL;
59469d94f4cSDag-Erling Smørgrav 				goto bad;
59569d94f4cSDag-Erling Smørgrav 			}
596c291b791SDag-Erling Smørgrav 			if ((uintptr_t)line + len < (uintptr_t)line ||
597c291b791SDag-Erling Smørgrav 			    line + len > exthdr + sz) {
59869d94f4cSDag-Erling Smørgrav 				TARFS_DPF(ALLOC, "%s: exthdr overflow\n",
59969d94f4cSDag-Erling Smørgrav 				    __func__);
60069d94f4cSDag-Erling Smørgrav 				error = EINVAL;
60169d94f4cSDag-Erling Smørgrav 				goto bad;
60269d94f4cSDag-Erling Smørgrav 			}
60369d94f4cSDag-Erling Smørgrav 			eol = line + len - 1;
60469d94f4cSDag-Erling Smørgrav 			*eol = '\0';
60569d94f4cSDag-Erling Smørgrav 			line += len;
60669d94f4cSDag-Erling Smørgrav 			key = sep + 1;
60769d94f4cSDag-Erling Smørgrav 			sep = strchr(key, '=');
60869d94f4cSDag-Erling Smørgrav 			if (sep == NULL) {
60969d94f4cSDag-Erling Smørgrav 				TARFS_DPF(ALLOC, "%s: exthdr syntax error\n",
61069d94f4cSDag-Erling Smørgrav 				    __func__);
61169d94f4cSDag-Erling Smørgrav 				error = EINVAL;
61269d94f4cSDag-Erling Smørgrav 				goto bad;
61369d94f4cSDag-Erling Smørgrav 			}
61469d94f4cSDag-Erling Smørgrav 			*sep = '\0';
61569d94f4cSDag-Erling Smørgrav 			value = sep + 1;
61669d94f4cSDag-Erling Smørgrav 			TARFS_DPF(ALLOC, "%s: exthdr %s=%s\n", __func__,
61769d94f4cSDag-Erling Smørgrav 			    key, value);
618b1fd95c9SDag-Erling Smørgrav 			if (strcmp(key, "path") == 0) {
619b1fd95c9SDag-Erling Smørgrav 				name = value;
620b1fd95c9SDag-Erling Smørgrav 				namelen = eol - value;
621b1fd95c9SDag-Erling Smørgrav 			} else if (strcmp(key, "linkpath") == 0) {
62269d94f4cSDag-Erling Smørgrav 				link = value;
62369d94f4cSDag-Erling Smørgrav 				linklen = eol - value;
62469d94f4cSDag-Erling Smørgrav 			} else if (strcmp(key, "GNU.sparse.major") == 0) {
62569d94f4cSDag-Erling Smørgrav 				sparse = true;
62669d94f4cSDag-Erling Smørgrav 				major = strtol(value, &sep, 10);
62769d94f4cSDag-Erling Smørgrav 				if (sep != eol) {
62869d94f4cSDag-Erling Smørgrav 					printf("exthdr syntax error\n");
62969d94f4cSDag-Erling Smørgrav 					error = EINVAL;
63069d94f4cSDag-Erling Smørgrav 					goto bad;
63169d94f4cSDag-Erling Smørgrav 				}
63269d94f4cSDag-Erling Smørgrav 			} else if (strcmp(key, "GNU.sparse.minor") == 0) {
63369d94f4cSDag-Erling Smørgrav 				sparse = true;
63469d94f4cSDag-Erling Smørgrav 				minor = strtol(value, &sep, 10);
63569d94f4cSDag-Erling Smørgrav 				if (sep != eol) {
63669d94f4cSDag-Erling Smørgrav 					printf("exthdr syntax error\n");
63769d94f4cSDag-Erling Smørgrav 					error = EINVAL;
63869d94f4cSDag-Erling Smørgrav 					goto bad;
63969d94f4cSDag-Erling Smørgrav 				}
64069d94f4cSDag-Erling Smørgrav 			} else if (strcmp(key, "GNU.sparse.name") == 0) {
64169d94f4cSDag-Erling Smørgrav 				sparse = true;
64269d94f4cSDag-Erling Smørgrav 				name = value;
64369d94f4cSDag-Erling Smørgrav 				namelen = eol - value;
64469d94f4cSDag-Erling Smørgrav 				if (namelen == 0) {
64569d94f4cSDag-Erling Smørgrav 					printf("exthdr syntax error\n");
64669d94f4cSDag-Erling Smørgrav 					error = EINVAL;
64769d94f4cSDag-Erling Smørgrav 					goto bad;
64869d94f4cSDag-Erling Smørgrav 				}
64969d94f4cSDag-Erling Smørgrav 			} else if (strcmp(key, "GNU.sparse.realsize") == 0) {
65069d94f4cSDag-Erling Smørgrav 				sparse = true;
65169d94f4cSDag-Erling Smørgrav 				realsize = strtoul(value, &sep, 10);
65269d94f4cSDag-Erling Smørgrav 				if (sep != eol) {
65369d94f4cSDag-Erling Smørgrav 					printf("exthdr syntax error\n");
65469d94f4cSDag-Erling Smørgrav 					error = EINVAL;
65569d94f4cSDag-Erling Smørgrav 					goto bad;
65669d94f4cSDag-Erling Smørgrav 				}
65769d94f4cSDag-Erling Smørgrav 			} else if (strcmp(key, "SCHILY.fflags") == 0) {
65869d94f4cSDag-Erling Smørgrav 				flags |= tarfs_strtofflags(value, &sep);
65969d94f4cSDag-Erling Smørgrav 				if (sep != eol) {
66069d94f4cSDag-Erling Smørgrav 					printf("exthdr syntax error\n");
66169d94f4cSDag-Erling Smørgrav 					error = EINVAL;
66269d94f4cSDag-Erling Smørgrav 					goto bad;
66369d94f4cSDag-Erling Smørgrav 				}
66469d94f4cSDag-Erling Smørgrav 			}
66569d94f4cSDag-Erling Smørgrav 		}
66669d94f4cSDag-Erling Smørgrav 		goto again;
66769d94f4cSDag-Erling Smørgrav 	}
66869d94f4cSDag-Erling Smørgrav 
66969d94f4cSDag-Erling Smørgrav 	/* sparse file consistency checks */
67069d94f4cSDag-Erling Smørgrav 	if (sparse) {
67169d94f4cSDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: %s: sparse %ld.%ld (%zu bytes)\n", __func__,
67269d94f4cSDag-Erling Smørgrav 		    name, major, minor, realsize);
67369d94f4cSDag-Erling Smørgrav 		if (major != 1 || minor != 0 || name == NULL || realsize == 0 ||
67469d94f4cSDag-Erling Smørgrav 		    hdrp->typeflag[0] != TAR_TYPE_FILE) {
67569d94f4cSDag-Erling Smørgrav 			TARFS_DPF(ALLOC, "%s: invalid sparse format\n", __func__);
67669d94f4cSDag-Erling Smørgrav 			error = EINVAL;
67769d94f4cSDag-Erling Smørgrav 			goto bad;
67869d94f4cSDag-Erling Smørgrav 		}
67969d94f4cSDag-Erling Smørgrav 	}
68069d94f4cSDag-Erling Smørgrav 
68169d94f4cSDag-Erling Smørgrav 	/* file name */
68269d94f4cSDag-Erling Smørgrav 	if (name == NULL) {
68369d94f4cSDag-Erling Smørgrav 		if (hdrp->prefix[0] != '\0') {
68469d94f4cSDag-Erling Smørgrav 			namebuf = sbuf_new_auto();
68569d94f4cSDag-Erling Smørgrav 			sbuf_printf(namebuf, "%.*s/%.*s",
68669d94f4cSDag-Erling Smørgrav 			    (int)sizeof(hdrp->prefix), hdrp->prefix,
68769d94f4cSDag-Erling Smørgrav 			    (int)sizeof(hdrp->name), hdrp->name);
68869d94f4cSDag-Erling Smørgrav 			sbuf_finish(namebuf);
68969d94f4cSDag-Erling Smørgrav 			name = sbuf_data(namebuf);
69069d94f4cSDag-Erling Smørgrav 			namelen = sbuf_len(namebuf);
69169d94f4cSDag-Erling Smørgrav 		} else {
69269d94f4cSDag-Erling Smørgrav 			name = hdrp->name;
69369d94f4cSDag-Erling Smørgrav 			namelen = strnlen(hdrp->name, sizeof(hdrp->name));
69469d94f4cSDag-Erling Smørgrav 		}
69569d94f4cSDag-Erling Smørgrav 	}
69669d94f4cSDag-Erling Smørgrav 
69769d94f4cSDag-Erling Smørgrav 	error = tarfs_lookup_path(tmp, name, namelen, &namep,
69869d94f4cSDag-Erling Smørgrav 	    &sep, &parent, &tnp, true);
699ae6cff89SDag-Erling Smørgrav 	if (error != 0) {
700ae6cff89SDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: failed to look up %.*s\n", __func__,
701ae6cff89SDag-Erling Smørgrav 		    (int)namelen, name);
702ae6cff89SDag-Erling Smørgrav 		error = EINVAL;
70369d94f4cSDag-Erling Smørgrav 		goto bad;
704ae6cff89SDag-Erling Smørgrav 	}
70569d94f4cSDag-Erling Smørgrav 	if (tnp != NULL) {
70669d94f4cSDag-Erling Smørgrav 		if (hdrp->typeflag[0] == TAR_TYPE_DIRECTORY) {
70769d94f4cSDag-Erling Smørgrav 			/* XXX set attributes? */
70869d94f4cSDag-Erling Smørgrav 			goto skip;
70969d94f4cSDag-Erling Smørgrav 		}
71069d94f4cSDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: duplicate file %.*s\n", __func__,
71169d94f4cSDag-Erling Smørgrav 		    (int)namelen, name);
71269d94f4cSDag-Erling Smørgrav 		error = EINVAL;
71369d94f4cSDag-Erling Smørgrav 		goto bad;
71469d94f4cSDag-Erling Smørgrav 	}
71569d94f4cSDag-Erling Smørgrav 	switch (hdrp->typeflag[0]) {
71669d94f4cSDag-Erling Smørgrav 	case TAR_TYPE_DIRECTORY:
71769d94f4cSDag-Erling Smørgrav 		error = tarfs_alloc_node(tmp, namep, sep - namep, VDIR,
71869d94f4cSDag-Erling Smørgrav 		    0, 0, mtime, uid, gid, mode, flags, NULL, 0,
71969d94f4cSDag-Erling Smørgrav 		    parent, &tnp);
72069d94f4cSDag-Erling Smørgrav 		break;
72169d94f4cSDag-Erling Smørgrav 	case TAR_TYPE_FILE:
72269d94f4cSDag-Erling Smørgrav 		error = tarfs_alloc_node(tmp, namep, sep - namep, VREG,
72369d94f4cSDag-Erling Smørgrav 		    blknum * TARFS_BLOCKSIZE, sz, mtime, uid, gid, mode,
72469d94f4cSDag-Erling Smørgrav 		    flags, NULL, 0, parent, &tnp);
72569d94f4cSDag-Erling Smørgrav 		if (error == 0 && sparse) {
72669d94f4cSDag-Erling Smørgrav 			error = tarfs_load_blockmap(tnp, realsize);
72769d94f4cSDag-Erling Smørgrav 		}
72869d94f4cSDag-Erling Smørgrav 		break;
72969d94f4cSDag-Erling Smørgrav 	case TAR_TYPE_HARDLINK:
73069d94f4cSDag-Erling Smørgrav 		if (link == NULL) {
73169d94f4cSDag-Erling Smørgrav 			link = hdrp->linkname;
73269d94f4cSDag-Erling Smørgrav 			linklen = strnlen(link, sizeof(hdrp->linkname));
73369d94f4cSDag-Erling Smørgrav 		}
73438b36835SDag-Erling Smørgrav 		if (linklen == 0) {
73538b36835SDag-Erling Smørgrav 			TARFS_DPF(ALLOC, "%s: %.*s: link without target\n",
73638b36835SDag-Erling Smørgrav 			    __func__, (int)namelen, name);
73738b36835SDag-Erling Smørgrav 			error = EINVAL;
73869d94f4cSDag-Erling Smørgrav 			goto bad;
73969d94f4cSDag-Erling Smørgrav 		}
74069d94f4cSDag-Erling Smørgrav 		error = tarfs_lookup_path(tmp, link, linklen, NULL,
74138b36835SDag-Erling Smørgrav 		    NULL, NULL, &other, false);
74238b36835SDag-Erling Smørgrav 		if (error != 0 || other == NULL ||
74338b36835SDag-Erling Smørgrav 		    other->type != VREG || other->other != NULL) {
74438b36835SDag-Erling Smørgrav 			TARFS_DPF(ALLOC, "%s: %.*s: invalid link to %.*s\n",
74569d94f4cSDag-Erling Smørgrav 			    __func__, (int)namelen, name, (int)linklen, link);
74669d94f4cSDag-Erling Smørgrav 			error = EINVAL;
74769d94f4cSDag-Erling Smørgrav 			goto bad;
74869d94f4cSDag-Erling Smørgrav 		}
74938b36835SDag-Erling Smørgrav 		error = tarfs_alloc_node(tmp, namep, sep - namep, VREG,
75038b36835SDag-Erling Smørgrav 		    0, 0, 0, 0, 0, 0, 0, NULL, 0, parent, &tnp);
75138b36835SDag-Erling Smørgrav 		if (error == 0) {
75238b36835SDag-Erling Smørgrav 			tnp->other = other;
753fd8c98a5SDag-Erling Smørgrav 			tnp->other->nlink++;
75438b36835SDag-Erling Smørgrav 		}
75569d94f4cSDag-Erling Smørgrav 		break;
75669d94f4cSDag-Erling Smørgrav 	case TAR_TYPE_SYMLINK:
75769d94f4cSDag-Erling Smørgrav 		if (link == NULL) {
75869d94f4cSDag-Erling Smørgrav 			link = hdrp->linkname;
75969d94f4cSDag-Erling Smørgrav 			linklen = strnlen(link, sizeof(hdrp->linkname));
76069d94f4cSDag-Erling Smørgrav 		}
76138b36835SDag-Erling Smørgrav 		if (linklen == 0) {
76238b36835SDag-Erling Smørgrav 			TARFS_DPF(ALLOC, "%s: %.*s: link without target\n",
76338b36835SDag-Erling Smørgrav 			    __func__, (int)namelen, name);
76438b36835SDag-Erling Smørgrav 			error = EINVAL;
76538b36835SDag-Erling Smørgrav 			goto bad;
76638b36835SDag-Erling Smørgrav 		}
76769d94f4cSDag-Erling Smørgrav 		error = tarfs_alloc_node(tmp, namep, sep - namep, VLNK,
76869d94f4cSDag-Erling Smørgrav 		    0, linklen, mtime, uid, gid, mode, flags, link, 0,
76969d94f4cSDag-Erling Smørgrav 		    parent, &tnp);
77069d94f4cSDag-Erling Smørgrav 		break;
77169d94f4cSDag-Erling Smørgrav 	case TAR_TYPE_BLOCK:
7728427d94cSDag-Erling Smørgrav 		if (tarfs_str2int64(hdrp->major, sizeof(hdrp->major), &num) != 0 ||
7738427d94cSDag-Erling Smørgrav 		    num < 0 || num > INT_MAX) {
7748427d94cSDag-Erling Smørgrav 			TARFS_DPF(ALLOC, "%s: %.*s: invalid device major\n",
7758427d94cSDag-Erling Smørgrav 			    __func__, (int)namelen, name);
7768427d94cSDag-Erling Smørgrav 			error = EINVAL;
7778427d94cSDag-Erling Smørgrav 			goto bad;
7788427d94cSDag-Erling Smørgrav 		}
7798427d94cSDag-Erling Smørgrav 		major = num;
7808427d94cSDag-Erling Smørgrav 		if (tarfs_str2int64(hdrp->minor, sizeof(hdrp->minor), &num) != 0 ||
7818427d94cSDag-Erling Smørgrav 		    num < 0 || num > INT_MAX) {
7828427d94cSDag-Erling Smørgrav 			TARFS_DPF(ALLOC, "%s: %.*s: invalid device minor\n",
7838427d94cSDag-Erling Smørgrav 			    __func__, (int)namelen, name);
7848427d94cSDag-Erling Smørgrav 			error = EINVAL;
7858427d94cSDag-Erling Smørgrav 			goto bad;
7868427d94cSDag-Erling Smørgrav 		}
7878427d94cSDag-Erling Smørgrav 		minor = num;
78869d94f4cSDag-Erling Smørgrav 		rdev = makedev(major, minor);
78969d94f4cSDag-Erling Smørgrav 		error = tarfs_alloc_node(tmp, namep, sep - namep, VBLK,
79069d94f4cSDag-Erling Smørgrav 		    0, 0, mtime, uid, gid, mode, flags, NULL, rdev,
79169d94f4cSDag-Erling Smørgrav 		    parent, &tnp);
79269d94f4cSDag-Erling Smørgrav 		break;
79369d94f4cSDag-Erling Smørgrav 	case TAR_TYPE_CHAR:
7948427d94cSDag-Erling Smørgrav 		if (tarfs_str2int64(hdrp->major, sizeof(hdrp->major), &num) != 0 ||
7958427d94cSDag-Erling Smørgrav 		    num < 0 || num > INT_MAX) {
7968427d94cSDag-Erling Smørgrav 			TARFS_DPF(ALLOC, "%s: %.*s: invalid device major\n",
7978427d94cSDag-Erling Smørgrav 			    __func__, (int)namelen, name);
7988427d94cSDag-Erling Smørgrav 			error = EINVAL;
7998427d94cSDag-Erling Smørgrav 			goto bad;
8008427d94cSDag-Erling Smørgrav 		}
8018427d94cSDag-Erling Smørgrav 		major = num;
8028427d94cSDag-Erling Smørgrav 		if (tarfs_str2int64(hdrp->minor, sizeof(hdrp->minor), &num) != 0 ||
8038427d94cSDag-Erling Smørgrav 		    num < 0 || num > INT_MAX) {
8048427d94cSDag-Erling Smørgrav 			TARFS_DPF(ALLOC, "%s: %.*s: invalid device minor\n",
8058427d94cSDag-Erling Smørgrav 			    __func__, (int)namelen, name);
8068427d94cSDag-Erling Smørgrav 			error = EINVAL;
8078427d94cSDag-Erling Smørgrav 			goto bad;
8088427d94cSDag-Erling Smørgrav 		}
8098427d94cSDag-Erling Smørgrav 		minor = num;
81069d94f4cSDag-Erling Smørgrav 		rdev = makedev(major, minor);
81169d94f4cSDag-Erling Smørgrav 		error = tarfs_alloc_node(tmp, namep, sep - namep, VCHR,
81269d94f4cSDag-Erling Smørgrav 		    0, 0, mtime, uid, gid, mode, flags, NULL, rdev,
81369d94f4cSDag-Erling Smørgrav 		    parent, &tnp);
81469d94f4cSDag-Erling Smørgrav 		break;
81569d94f4cSDag-Erling Smørgrav 	default:
81669d94f4cSDag-Erling Smørgrav 		TARFS_DPF(ALLOC, "%s: unsupported type %c for %.*s\n",
81769d94f4cSDag-Erling Smørgrav 		    __func__, hdrp->typeflag[0], (int)namelen, name);
81869d94f4cSDag-Erling Smørgrav 		error = EINVAL;
81969d94f4cSDag-Erling Smørgrav 		break;
82069d94f4cSDag-Erling Smørgrav 	}
82169d94f4cSDag-Erling Smørgrav 	if (error != 0)
82269d94f4cSDag-Erling Smørgrav 		goto bad;
82369d94f4cSDag-Erling Smørgrav 
82469d94f4cSDag-Erling Smørgrav skip:
82569d94f4cSDag-Erling Smørgrav 	blknum += TARFS_SZ2BLKS(sz);
82669d94f4cSDag-Erling Smørgrav 	tmp->nblocks = blknum;
82769d94f4cSDag-Erling Smørgrav 	*blknump = blknum;
82869d94f4cSDag-Erling Smørgrav 	if (exthdr != NULL) {
82969d94f4cSDag-Erling Smørgrav 		free(exthdr, M_TEMP);
83069d94f4cSDag-Erling Smørgrav 	}
83169d94f4cSDag-Erling Smørgrav 	if (namebuf != NULL) {
83269d94f4cSDag-Erling Smørgrav 		sbuf_delete(namebuf);
83369d94f4cSDag-Erling Smørgrav 	}
83469d94f4cSDag-Erling Smørgrav 	return (0);
83569d94f4cSDag-Erling Smørgrav eof:
83669d94f4cSDag-Erling Smørgrav 	TARFS_DPF(IO, "%s: premature end of file\n", __func__);
83769d94f4cSDag-Erling Smørgrav 	error = EIO;
83869d94f4cSDag-Erling Smørgrav 	goto bad;
83969d94f4cSDag-Erling Smørgrav bad:
84069d94f4cSDag-Erling Smørgrav 	if (exthdr != NULL) {
84169d94f4cSDag-Erling Smørgrav 		free(exthdr, M_TEMP);
84269d94f4cSDag-Erling Smørgrav 	}
84369d94f4cSDag-Erling Smørgrav 	if (namebuf != NULL) {
84469d94f4cSDag-Erling Smørgrav 		sbuf_delete(namebuf);
84569d94f4cSDag-Erling Smørgrav 	}
84669d94f4cSDag-Erling Smørgrav 	return (error);
84769d94f4cSDag-Erling Smørgrav }
84869d94f4cSDag-Erling Smørgrav 
84969d94f4cSDag-Erling Smørgrav /*
85069d94f4cSDag-Erling Smørgrav  * Allocates and populates the metadata structures for the tar file
85169d94f4cSDag-Erling Smørgrav  * referenced by vp.  On success, a pointer to the tarfs_mount structure
85269d94f4cSDag-Erling Smørgrav  * is stored in tmpp.  Returns 0 on success or a positive errno value on
85369d94f4cSDag-Erling Smørgrav  * failure.
85469d94f4cSDag-Erling Smørgrav  */
85569d94f4cSDag-Erling Smørgrav static int
tarfs_alloc_mount(struct mount * mp,struct vnode * vp,uid_t root_uid,gid_t root_gid,mode_t root_mode,struct tarfs_mount ** tmpp)85669d94f4cSDag-Erling Smørgrav tarfs_alloc_mount(struct mount *mp, struct vnode *vp,
85769d94f4cSDag-Erling Smørgrav     uid_t root_uid, gid_t root_gid, mode_t root_mode,
85869d94f4cSDag-Erling Smørgrav     struct tarfs_mount **tmpp)
85969d94f4cSDag-Erling Smørgrav {
86069d94f4cSDag-Erling Smørgrav 	struct vattr va;
86169d94f4cSDag-Erling Smørgrav 	struct thread *td = curthread;
86269d94f4cSDag-Erling Smørgrav 	struct tarfs_mount *tmp;
86369d94f4cSDag-Erling Smørgrav 	struct tarfs_node *root;
8640238d371SDag-Erling Smørgrav 	size_t blknum;
86569d94f4cSDag-Erling Smørgrav 	time_t mtime;
86669d94f4cSDag-Erling Smørgrav 	int error;
86769d94f4cSDag-Erling Smørgrav 
86869d94f4cSDag-Erling Smørgrav 	KASSERT(tmpp != NULL, ("tarfs mount return is NULL"));
86969d94f4cSDag-Erling Smørgrav 	ASSERT_VOP_LOCKED(vp, __func__);
87069d94f4cSDag-Erling Smørgrav 
87169d94f4cSDag-Erling Smørgrav 	tmp = NULL;
87269d94f4cSDag-Erling Smørgrav 
87369d94f4cSDag-Erling Smørgrav 	TARFS_DPF(ALLOC, "%s: Allocating tarfs mount structure for vp %p\n",
87469d94f4cSDag-Erling Smørgrav 	    __func__, vp);
87569d94f4cSDag-Erling Smørgrav 
87669d94f4cSDag-Erling Smørgrav 	/* Get source metadata */
87769d94f4cSDag-Erling Smørgrav 	error = VOP_GETATTR(vp, &va, td->td_ucred);
87869d94f4cSDag-Erling Smørgrav 	if (error != 0) {
87969d94f4cSDag-Erling Smørgrav 		return (error);
88069d94f4cSDag-Erling Smørgrav 	}
88169d94f4cSDag-Erling Smørgrav 	VOP_UNLOCK(vp);
88269d94f4cSDag-Erling Smørgrav 	mtime = va.va_mtime.tv_sec;
88369d94f4cSDag-Erling Smørgrav 
884*91eca185SMark Johnston 	mp->mnt_iosize_max = vp->v_mount->mnt_iosize_max;
885*91eca185SMark Johnston 
88669d94f4cSDag-Erling Smørgrav 	/* Allocate and initialize tarfs mount structure */
887ce6a0c77SDag-Erling Smørgrav 	tmp = malloc(sizeof(*tmp), M_TARFSMNT, M_WAITOK | M_ZERO);
88869d94f4cSDag-Erling Smørgrav 	TARFS_DPF(ALLOC, "%s: Allocated mount structure\n", __func__);
88969d94f4cSDag-Erling Smørgrav 	mp->mnt_data = tmp;
89069d94f4cSDag-Erling Smørgrav 
89169d94f4cSDag-Erling Smørgrav 	mtx_init(&tmp->allnode_lock, "tarfs allnode lock", NULL,
89269d94f4cSDag-Erling Smørgrav 	    MTX_DEF);
89369d94f4cSDag-Erling Smørgrav 	TAILQ_INIT(&tmp->allnodes);
89469d94f4cSDag-Erling Smørgrav 	tmp->ino_unr = new_unrhdr(TARFS_MININO, INT_MAX, &tmp->allnode_lock);
89569d94f4cSDag-Erling Smørgrav 	tmp->vp = vp;
89669d94f4cSDag-Erling Smørgrav 	tmp->vfs = mp;
89769d94f4cSDag-Erling Smørgrav 	tmp->mtime = mtime;
89869d94f4cSDag-Erling Smørgrav 
899e212f0c0SDag-Erling Smørgrav 	/* Initialize I/O layer */
90069d94f4cSDag-Erling Smørgrav 	tmp->iosize = 1U << tarfs_ioshift;
90169d94f4cSDag-Erling Smørgrav 	error = tarfs_io_init(tmp);
90269d94f4cSDag-Erling Smørgrav 	if (error != 0)
90369d94f4cSDag-Erling Smørgrav 		goto bad;
90469d94f4cSDag-Erling Smørgrav 
90569d94f4cSDag-Erling Smørgrav 	error = tarfs_alloc_node(tmp, NULL, 0, VDIR, 0, 0, mtime, root_uid,
90669d94f4cSDag-Erling Smørgrav 	    root_gid, root_mode & ALLPERMS, 0, NULL, NODEV, NULL, &root);
90769d94f4cSDag-Erling Smørgrav 	if (error != 0 || root == NULL)
90869d94f4cSDag-Erling Smørgrav 		goto bad;
90969d94f4cSDag-Erling Smørgrav 	tmp->root = root;
91069d94f4cSDag-Erling Smørgrav 
91169d94f4cSDag-Erling Smørgrav 	blknum = 0;
91269d94f4cSDag-Erling Smørgrav 	do {
91369d94f4cSDag-Erling Smørgrav 		if ((error = tarfs_alloc_one(tmp, &blknum)) != 0) {
914b1fd95c9SDag-Erling Smørgrav 			printf("unsupported or corrupt tar file at %zu\n",
915b1fd95c9SDag-Erling Smørgrav 			    TARFS_BLOCKSIZE * blknum);
91669d94f4cSDag-Erling Smørgrav 			goto bad;
91769d94f4cSDag-Erling Smørgrav 		}
91869d94f4cSDag-Erling Smørgrav 	} while (blknum != TAR_EOF);
91969d94f4cSDag-Erling Smørgrav 
92069d94f4cSDag-Erling Smørgrav 	*tmpp = tmp;
92169d94f4cSDag-Erling Smørgrav 
92269d94f4cSDag-Erling Smørgrav 	TARFS_DPF(ALLOC, "%s: pfsmnt_root %p\n", __func__, tmp->root);
92369d94f4cSDag-Erling Smørgrav 	return (0);
92469d94f4cSDag-Erling Smørgrav 
92569d94f4cSDag-Erling Smørgrav bad:
92669d94f4cSDag-Erling Smørgrav 	tarfs_free_mount(tmp);
92769d94f4cSDag-Erling Smørgrav 	return (error);
92869d94f4cSDag-Erling Smørgrav }
92969d94f4cSDag-Erling Smørgrav 
93069d94f4cSDag-Erling Smørgrav /*
93169d94f4cSDag-Erling Smørgrav  * VFS Operations.
93269d94f4cSDag-Erling Smørgrav  */
93369d94f4cSDag-Erling Smørgrav 
93469d94f4cSDag-Erling Smørgrav static int
tarfs_mount(struct mount * mp)93569d94f4cSDag-Erling Smørgrav tarfs_mount(struct mount *mp)
93669d94f4cSDag-Erling Smørgrav {
93769d94f4cSDag-Erling Smørgrav 	struct nameidata nd;
93869d94f4cSDag-Erling Smørgrav 	struct vattr va;
93969d94f4cSDag-Erling Smørgrav 	struct tarfs_mount *tmp = NULL;
94069d94f4cSDag-Erling Smørgrav 	struct thread *td = curthread;
94169d94f4cSDag-Erling Smørgrav 	struct vnode *vp;
942a02d9cadSSimon J. Gerraty 	char *as, *from;
94369d94f4cSDag-Erling Smørgrav 	uid_t root_uid;
94469d94f4cSDag-Erling Smørgrav 	gid_t root_gid;
94569d94f4cSDag-Erling Smørgrav 	mode_t root_mode;
946a02d9cadSSimon J. Gerraty 	int error, flags, aslen, len;
94769d94f4cSDag-Erling Smørgrav 
94869d94f4cSDag-Erling Smørgrav 	if (mp->mnt_flag & MNT_UPDATE)
94969d94f4cSDag-Erling Smørgrav 		return (EOPNOTSUPP);
95069d94f4cSDag-Erling Smørgrav 
95169d94f4cSDag-Erling Smørgrav 	if (vfs_filteropt(mp->mnt_optnew, tarfs_opts))
95269d94f4cSDag-Erling Smørgrav 		return (EINVAL);
95369d94f4cSDag-Erling Smørgrav 
95469d94f4cSDag-Erling Smørgrav 	vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY);
95569d94f4cSDag-Erling Smørgrav 	error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred);
95669d94f4cSDag-Erling Smørgrav 	VOP_UNLOCK(mp->mnt_vnodecovered);
95769d94f4cSDag-Erling Smørgrav 	if (error)
95869d94f4cSDag-Erling Smørgrav 		return (error);
95969d94f4cSDag-Erling Smørgrav 
96069d94f4cSDag-Erling Smørgrav 	if (mp->mnt_cred->cr_ruid != 0 ||
96169d94f4cSDag-Erling Smørgrav 	    vfs_scanopt(mp->mnt_optnew, "gid", "%d", &root_gid) != 1)
96269d94f4cSDag-Erling Smørgrav 		root_gid = va.va_gid;
96369d94f4cSDag-Erling Smørgrav 	if (mp->mnt_cred->cr_ruid != 0 ||
96469d94f4cSDag-Erling Smørgrav 	    vfs_scanopt(mp->mnt_optnew, "uid", "%d", &root_uid) != 1)
96569d94f4cSDag-Erling Smørgrav 		root_uid = va.va_uid;
96669d94f4cSDag-Erling Smørgrav 	if (mp->mnt_cred->cr_ruid != 0 ||
96769d94f4cSDag-Erling Smørgrav 	    vfs_scanopt(mp->mnt_optnew, "mode", "%ho", &root_mode) != 1)
96869d94f4cSDag-Erling Smørgrav 		root_mode = va.va_mode;
96969d94f4cSDag-Erling Smørgrav 
97069d94f4cSDag-Erling Smørgrav 	error = vfs_getopt(mp->mnt_optnew, "from", (void **)&from, &len);
97169d94f4cSDag-Erling Smørgrav 	if (error != 0 || from[len - 1] != '\0')
97269d94f4cSDag-Erling Smørgrav 		return (EINVAL);
973a02d9cadSSimon J. Gerraty 	error = vfs_getopt(mp->mnt_optnew, "as", (void **)&as, &aslen);
974a02d9cadSSimon J. Gerraty 	if (error != 0 || as[aslen - 1] != '\0')
975a02d9cadSSimon J. Gerraty 		as = from;
97669d94f4cSDag-Erling Smørgrav 
97769d94f4cSDag-Erling Smørgrav 	/* Find the source tarball */
978a02d9cadSSimon J. Gerraty 	TARFS_DPF(FS, "%s(%s%s%s, uid=%u, gid=%u, mode=%o)\n", __func__,
979a02d9cadSSimon J. Gerraty 	    from, (as != from) ? " as " : "", (as != from) ? as : "",
980a02d9cadSSimon J. Gerraty 	    root_uid, root_gid, root_mode);
98169d94f4cSDag-Erling Smørgrav 	flags = FREAD;
98269d94f4cSDag-Erling Smørgrav 	if (vfs_flagopt(mp->mnt_optnew, "verify", NULL, 0)) {
98369d94f4cSDag-Erling Smørgrav 	    flags |= O_VERIFY;
98469d94f4cSDag-Erling Smørgrav 	}
98569d94f4cSDag-Erling Smørgrav 	NDINIT(&nd, LOOKUP, ISOPEN | FOLLOW | LOCKLEAF, UIO_SYSSPACE, from);
98669d94f4cSDag-Erling Smørgrav 	error = namei(&nd);
98769d94f4cSDag-Erling Smørgrav 	if (error != 0)
98869d94f4cSDag-Erling Smørgrav 		return (error);
98969d94f4cSDag-Erling Smørgrav 	NDFREE_PNBUF(&nd);
99069d94f4cSDag-Erling Smørgrav 	vp = nd.ni_vp;
99169d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: N: hold %u use %u lock 0x%x\n", __func__,
99269d94f4cSDag-Erling Smørgrav 	    vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
99369d94f4cSDag-Erling Smørgrav 	/* vp is now held and locked */
99469d94f4cSDag-Erling Smørgrav 
99569d94f4cSDag-Erling Smørgrav 	/* Open the source tarball */
99669d94f4cSDag-Erling Smørgrav 	error = vn_open_vnode(vp, flags, td->td_ucred, td, NULL);
99769d94f4cSDag-Erling Smørgrav 	if (error != 0) {
99869d94f4cSDag-Erling Smørgrav 		TARFS_DPF(FS, "%s: failed to open %s: %d\n", __func__,
99969d94f4cSDag-Erling Smørgrav 		    from, error);
100069d94f4cSDag-Erling Smørgrav 		vput(vp);
100169d94f4cSDag-Erling Smørgrav 		goto bad;
100269d94f4cSDag-Erling Smørgrav 	}
100369d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: O: hold %u use %u lock 0x%x\n", __func__,
100469d94f4cSDag-Erling Smørgrav 	    vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
100569d94f4cSDag-Erling Smørgrav 	if (vp->v_type != VREG) {
100669d94f4cSDag-Erling Smørgrav 		TARFS_DPF(FS, "%s: not a regular file\n", __func__);
100769d94f4cSDag-Erling Smørgrav 		error = EOPNOTSUPP;
100869d94f4cSDag-Erling Smørgrav 		goto bad_open_locked;
100969d94f4cSDag-Erling Smørgrav 	}
101069d94f4cSDag-Erling Smørgrav 	error = priv_check(td, PRIV_VFS_MOUNT_PERM);
101169d94f4cSDag-Erling Smørgrav 	if (error != 0) {
101269d94f4cSDag-Erling Smørgrav 		TARFS_DPF(FS, "%s: not permitted to mount\n", __func__);
101369d94f4cSDag-Erling Smørgrav 		goto bad_open_locked;
101469d94f4cSDag-Erling Smørgrav 	}
101569d94f4cSDag-Erling Smørgrav 	if (flags & O_VERIFY) {
101669d94f4cSDag-Erling Smørgrav 		mp->mnt_flag |= MNT_VERIFIED;
101769d94f4cSDag-Erling Smørgrav 	}
101869d94f4cSDag-Erling Smørgrav 
101969d94f4cSDag-Erling Smørgrav 	/* Allocate the tarfs mount */
102069d94f4cSDag-Erling Smørgrav 	error = tarfs_alloc_mount(mp, vp, root_uid, root_gid, root_mode, &tmp);
102169d94f4cSDag-Erling Smørgrav 	/* vp is now held but unlocked */
102269d94f4cSDag-Erling Smørgrav 	if (error != 0) {
102369d94f4cSDag-Erling Smørgrav 		TARFS_DPF(FS, "%s: failed to mount %s: %d\n", __func__,
102469d94f4cSDag-Erling Smørgrav 		    from, error);
102569d94f4cSDag-Erling Smørgrav 		goto bad_open_unlocked;
102669d94f4cSDag-Erling Smørgrav 	}
102769d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: M: hold %u use %u lock 0x%x\n", __func__,
102869d94f4cSDag-Erling Smørgrav 	    vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
102969d94f4cSDag-Erling Smørgrav 
103069d94f4cSDag-Erling Smørgrav 	/* Unconditionally mount as read-only */
103169d94f4cSDag-Erling Smørgrav 	MNT_ILOCK(mp);
103269d94f4cSDag-Erling Smørgrav 	mp->mnt_flag |= (MNT_LOCAL | MNT_RDONLY);
103369d94f4cSDag-Erling Smørgrav 	MNT_IUNLOCK(mp);
103469d94f4cSDag-Erling Smørgrav 
103569d94f4cSDag-Erling Smørgrav 	vfs_getnewfsid(mp);
1036a02d9cadSSimon J. Gerraty 	vfs_mountedfrom(mp, as);
103769d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: success\n", __func__);
103869d94f4cSDag-Erling Smørgrav 
103969d94f4cSDag-Erling Smørgrav 	return (0);
104069d94f4cSDag-Erling Smørgrav 
104169d94f4cSDag-Erling Smørgrav bad_open_locked:
104269d94f4cSDag-Erling Smørgrav 	/* vp must be held and locked */
104369d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: L: hold %u use %u lock 0x%x\n", __func__,
104469d94f4cSDag-Erling Smørgrav 	    vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
104569d94f4cSDag-Erling Smørgrav 	VOP_UNLOCK(vp);
104669d94f4cSDag-Erling Smørgrav bad_open_unlocked:
104769d94f4cSDag-Erling Smørgrav 	/* vp must be held and unlocked */
104869d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: E: hold %u use %u lock 0x%x\n", __func__,
104969d94f4cSDag-Erling Smørgrav 	    vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
105069d94f4cSDag-Erling Smørgrav 	(void)vn_close(vp, flags, td->td_ucred, td);
105169d94f4cSDag-Erling Smørgrav bad:
105269d94f4cSDag-Erling Smørgrav 	/* vp must be released and unlocked */
105369d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: X: hold %u use %u lock 0x%x\n", __func__,
105469d94f4cSDag-Erling Smørgrav 	    vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
105569d94f4cSDag-Erling Smørgrav 	return (error);
105669d94f4cSDag-Erling Smørgrav }
105769d94f4cSDag-Erling Smørgrav 
105869d94f4cSDag-Erling Smørgrav /*
105969d94f4cSDag-Erling Smørgrav  * Unmounts a tarfs filesystem.
106069d94f4cSDag-Erling Smørgrav  */
106169d94f4cSDag-Erling Smørgrav static int
tarfs_unmount(struct mount * mp,int mntflags)106269d94f4cSDag-Erling Smørgrav tarfs_unmount(struct mount *mp, int mntflags)
106369d94f4cSDag-Erling Smørgrav {
106469d94f4cSDag-Erling Smørgrav 	struct thread *td = curthread;
106569d94f4cSDag-Erling Smørgrav 	struct tarfs_mount *tmp;
106669d94f4cSDag-Erling Smørgrav 	struct vnode *vp;
106769d94f4cSDag-Erling Smørgrav 	int error;
106869d94f4cSDag-Erling Smørgrav 	int flags = 0;
106969d94f4cSDag-Erling Smørgrav 
107069d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: Unmounting %p\n", __func__, mp);
107169d94f4cSDag-Erling Smørgrav 
107269d94f4cSDag-Erling Smørgrav 	/* Handle forced unmounts */
107369d94f4cSDag-Erling Smørgrav 	if (mntflags & MNT_FORCE)
107469d94f4cSDag-Erling Smørgrav 		flags |= FORCECLOSE;
107569d94f4cSDag-Erling Smørgrav 
107669d94f4cSDag-Erling Smørgrav 	/* Finalize all pending I/O */
107769d94f4cSDag-Erling Smørgrav 	error = vflush(mp, 0, flags, curthread);
107869d94f4cSDag-Erling Smørgrav 	if (error != 0)
107969d94f4cSDag-Erling Smørgrav 		return (error);
108069d94f4cSDag-Erling Smørgrav 	tmp = MP_TO_TARFS_MOUNT(mp);
108169d94f4cSDag-Erling Smørgrav 	vp = tmp->vp;
108269d94f4cSDag-Erling Smørgrav 
108369d94f4cSDag-Erling Smørgrav 	MPASS(vp != NULL);
108469d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: U: hold %u use %u lock 0x%x\n", __func__,
108569d94f4cSDag-Erling Smørgrav 	    vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
108669d94f4cSDag-Erling Smørgrav 	vn_close(vp, FREAD, td->td_ucred, td);
108769d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: C: hold %u use %u lock 0x%x\n", __func__,
108869d94f4cSDag-Erling Smørgrav 	    vp->v_holdcnt, vp->v_usecount, VOP_ISLOCKED(vp));
108969d94f4cSDag-Erling Smørgrav 	tarfs_free_mount(tmp);
109069d94f4cSDag-Erling Smørgrav 
109169d94f4cSDag-Erling Smørgrav 	return (0);
109269d94f4cSDag-Erling Smørgrav }
109369d94f4cSDag-Erling Smørgrav 
109469d94f4cSDag-Erling Smørgrav /*
109569d94f4cSDag-Erling Smørgrav  * Gets the root of a tarfs filesystem.  Returns 0 on success or a
109669d94f4cSDag-Erling Smørgrav  * positive errno value on failure.
109769d94f4cSDag-Erling Smørgrav  */
109869d94f4cSDag-Erling Smørgrav static int
tarfs_root(struct mount * mp,int flags,struct vnode ** vpp)109969d94f4cSDag-Erling Smørgrav tarfs_root(struct mount *mp, int flags, struct vnode **vpp)
110069d94f4cSDag-Erling Smørgrav {
110169d94f4cSDag-Erling Smørgrav 	struct vnode *nvp;
110269d94f4cSDag-Erling Smørgrav 	int error;
110369d94f4cSDag-Erling Smørgrav 
110469d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: Getting root vnode\n", __func__);
110569d94f4cSDag-Erling Smørgrav 
110669d94f4cSDag-Erling Smørgrav 	error = VFS_VGET(mp, TARFS_ROOTINO, LK_EXCLUSIVE, &nvp);
110769d94f4cSDag-Erling Smørgrav 	if (error != 0)
110869d94f4cSDag-Erling Smørgrav 		return (error);
110969d94f4cSDag-Erling Smørgrav 
111069d94f4cSDag-Erling Smørgrav 	nvp->v_vflag |= VV_ROOT;
111169d94f4cSDag-Erling Smørgrav 	*vpp = nvp;
111269d94f4cSDag-Erling Smørgrav 	return (0);
111369d94f4cSDag-Erling Smørgrav }
111469d94f4cSDag-Erling Smørgrav 
111569d94f4cSDag-Erling Smørgrav /*
111669d94f4cSDag-Erling Smørgrav  * Gets statistics for a tarfs filesystem.  Returns 0.
111769d94f4cSDag-Erling Smørgrav  */
111869d94f4cSDag-Erling Smørgrav static int
tarfs_statfs(struct mount * mp,struct statfs * sbp)111969d94f4cSDag-Erling Smørgrav tarfs_statfs(struct mount *mp, struct statfs *sbp)
112069d94f4cSDag-Erling Smørgrav {
112169d94f4cSDag-Erling Smørgrav 	struct tarfs_mount *tmp;
112269d94f4cSDag-Erling Smørgrav 
112369d94f4cSDag-Erling Smørgrav 	tmp = MP_TO_TARFS_MOUNT(mp);
112469d94f4cSDag-Erling Smørgrav 
112569d94f4cSDag-Erling Smørgrav 	sbp->f_bsize = TARFS_BLOCKSIZE;
112669d94f4cSDag-Erling Smørgrav 	sbp->f_iosize = tmp->iosize;
112769d94f4cSDag-Erling Smørgrav 	sbp->f_blocks = tmp->nblocks;
112869d94f4cSDag-Erling Smørgrav 	sbp->f_bfree = 0;
112969d94f4cSDag-Erling Smørgrav 	sbp->f_bavail = 0;
113069d94f4cSDag-Erling Smørgrav 	sbp->f_files = tmp->nfiles;
113169d94f4cSDag-Erling Smørgrav 	sbp->f_ffree = 0;
113269d94f4cSDag-Erling Smørgrav 
113369d94f4cSDag-Erling Smørgrav 	return (0);
113469d94f4cSDag-Erling Smørgrav }
113569d94f4cSDag-Erling Smørgrav 
113669d94f4cSDag-Erling Smørgrav /*
113769d94f4cSDag-Erling Smørgrav  * Gets a vnode for the given inode.  On success, a pointer to the vnode
113869d94f4cSDag-Erling Smørgrav  * is stored in vpp.  Returns 0 on success or a positive errno value on
113969d94f4cSDag-Erling Smørgrav  * failure.
114069d94f4cSDag-Erling Smørgrav  */
114169d94f4cSDag-Erling Smørgrav static int
tarfs_vget(struct mount * mp,ino_t ino,int lkflags,struct vnode ** vpp)114269d94f4cSDag-Erling Smørgrav tarfs_vget(struct mount *mp, ino_t ino, int lkflags, struct vnode **vpp)
114369d94f4cSDag-Erling Smørgrav {
114469d94f4cSDag-Erling Smørgrav 	struct tarfs_mount *tmp;
114569d94f4cSDag-Erling Smørgrav 	struct tarfs_node *tnp;
114669d94f4cSDag-Erling Smørgrav 	struct thread *td;
114769d94f4cSDag-Erling Smørgrav 	struct vnode *vp;
114869d94f4cSDag-Erling Smørgrav 	int error;
114969d94f4cSDag-Erling Smørgrav 
115069d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: mp %p, ino %lu, lkflags %d\n", __func__, mp, ino,
115169d94f4cSDag-Erling Smørgrav 	    lkflags);
115269d94f4cSDag-Erling Smørgrav 
115369d94f4cSDag-Erling Smørgrav 	td = curthread;
115469d94f4cSDag-Erling Smørgrav 	error = vfs_hash_get(mp, ino, lkflags, td, vpp, NULL, NULL);
115569d94f4cSDag-Erling Smørgrav 	if (error != 0)
115669d94f4cSDag-Erling Smørgrav 		return (error);
115769d94f4cSDag-Erling Smørgrav 
115869d94f4cSDag-Erling Smørgrav 	if (*vpp != NULL) {
115969d94f4cSDag-Erling Smørgrav 		TARFS_DPF(FS, "%s: found hashed vnode %p\n", __func__, *vpp);
116069d94f4cSDag-Erling Smørgrav 		return (error);
116169d94f4cSDag-Erling Smørgrav 	}
116269d94f4cSDag-Erling Smørgrav 
116369d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: no hashed vnode for inode %lu\n", __func__, ino);
116469d94f4cSDag-Erling Smørgrav 
116569d94f4cSDag-Erling Smørgrav 	tmp = MP_TO_TARFS_MOUNT(mp);
116669d94f4cSDag-Erling Smørgrav 
116769d94f4cSDag-Erling Smørgrav 	if (ino == TARFS_ZIOINO) {
116869d94f4cSDag-Erling Smørgrav 		error = vget(tmp->znode, lkflags);
116969d94f4cSDag-Erling Smørgrav 		if (error != 0)
117069d94f4cSDag-Erling Smørgrav 			return (error);
117169d94f4cSDag-Erling Smørgrav 		*vpp = tmp->znode;
117269d94f4cSDag-Erling Smørgrav 		return (0);
117369d94f4cSDag-Erling Smørgrav 	}
117469d94f4cSDag-Erling Smørgrav 
117569d94f4cSDag-Erling Smørgrav 	/* XXX Should use hash instead? */
117669d94f4cSDag-Erling Smørgrav 	TAILQ_FOREACH(tnp, &tmp->allnodes, entries) {
117769d94f4cSDag-Erling Smørgrav 		if (tnp->ino == ino)
117869d94f4cSDag-Erling Smørgrav 			break;
117969d94f4cSDag-Erling Smørgrav 	}
118069d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: search of all nodes found %p\n", __func__, tnp);
118169d94f4cSDag-Erling Smørgrav 	if (tnp == NULL)
118269d94f4cSDag-Erling Smørgrav 		return (ENOENT);
118369d94f4cSDag-Erling Smørgrav 
1184ce6a0c77SDag-Erling Smørgrav 	(void)getnewvnode("tarfs", mp, &tarfs_vnodeops, &vp);
118569d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: allocated vnode\n", __func__);
118669d94f4cSDag-Erling Smørgrav 	vp->v_data = tnp;
118769d94f4cSDag-Erling Smørgrav 	vp->v_type = tnp->type;
118869d94f4cSDag-Erling Smørgrav 	tnp->vnode = vp;
118969d94f4cSDag-Erling Smørgrav 
119069d94f4cSDag-Erling Smørgrav 	lockmgr(vp->v_vnlock, lkflags, NULL);
119169d94f4cSDag-Erling Smørgrav 	error = insmntque(vp, mp);
119269d94f4cSDag-Erling Smørgrav 	if (error != 0)
119369d94f4cSDag-Erling Smørgrav 		goto bad;
119469d94f4cSDag-Erling Smørgrav 	TARFS_DPF(FS, "%s: inserting entry into VFS hash\n", __func__);
119569d94f4cSDag-Erling Smørgrav 	error = vfs_hash_insert(vp, ino, lkflags, td, vpp, NULL, NULL);
119669d94f4cSDag-Erling Smørgrav 	if (error != 0 || *vpp != NULL)
119769d94f4cSDag-Erling Smørgrav 		return (error);
119869d94f4cSDag-Erling Smørgrav 
119969d94f4cSDag-Erling Smørgrav 	vn_set_state(vp, VSTATE_CONSTRUCTED);
120069d94f4cSDag-Erling Smørgrav 	*vpp = vp;
120169d94f4cSDag-Erling Smørgrav 	return (0);
120269d94f4cSDag-Erling Smørgrav 
120369d94f4cSDag-Erling Smørgrav bad:
120469d94f4cSDag-Erling Smørgrav 	*vpp = NULLVP;
120569d94f4cSDag-Erling Smørgrav 	return (error);
120669d94f4cSDag-Erling Smørgrav }
120769d94f4cSDag-Erling Smørgrav 
120869d94f4cSDag-Erling Smørgrav static int
tarfs_fhtovp(struct mount * mp,struct fid * fhp,int flags,struct vnode ** vpp)120969d94f4cSDag-Erling Smørgrav tarfs_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp)
121069d94f4cSDag-Erling Smørgrav {
121169d94f4cSDag-Erling Smørgrav 	struct tarfs_node *tnp;
121269d94f4cSDag-Erling Smørgrav 	struct tarfs_fid *tfp;
121369d94f4cSDag-Erling Smørgrav 	struct vnode *nvp;
121469d94f4cSDag-Erling Smørgrav 	int error;
121569d94f4cSDag-Erling Smørgrav 
121669d94f4cSDag-Erling Smørgrav 	tfp = (struct tarfs_fid *)fhp;
121769d94f4cSDag-Erling Smørgrav 	MP_TO_TARFS_MOUNT(mp);
121869d94f4cSDag-Erling Smørgrav 	if (tfp->ino < TARFS_ROOTINO || tfp->ino > INT_MAX)
121969d94f4cSDag-Erling Smørgrav 		return (ESTALE);
122069d94f4cSDag-Erling Smørgrav 
122169d94f4cSDag-Erling Smørgrav 	error = VFS_VGET(mp, tfp->ino, LK_EXCLUSIVE, &nvp);
122269d94f4cSDag-Erling Smørgrav 	if (error != 0) {
122369d94f4cSDag-Erling Smørgrav 		*vpp = NULLVP;
122469d94f4cSDag-Erling Smørgrav 		return (error);
122569d94f4cSDag-Erling Smørgrav 	}
122669d94f4cSDag-Erling Smørgrav 	tnp = VP_TO_TARFS_NODE(nvp);
122769d94f4cSDag-Erling Smørgrav 	if (tnp->mode == 0 ||
122869d94f4cSDag-Erling Smørgrav 	    tnp->gen != tfp->gen ||
122969d94f4cSDag-Erling Smørgrav 	    tnp->nlink <= 0) {
123069d94f4cSDag-Erling Smørgrav 		vput(nvp);
123169d94f4cSDag-Erling Smørgrav 		*vpp = NULLVP;
123269d94f4cSDag-Erling Smørgrav 		return (ESTALE);
123369d94f4cSDag-Erling Smørgrav 	}
123469d94f4cSDag-Erling Smørgrav 	*vpp = nvp;
123569d94f4cSDag-Erling Smørgrav 	return (0);
123669d94f4cSDag-Erling Smørgrav }
123769d94f4cSDag-Erling Smørgrav 
123869d94f4cSDag-Erling Smørgrav static struct vfsops tarfs_vfsops = {
123969d94f4cSDag-Erling Smørgrav 	.vfs_fhtovp =	tarfs_fhtovp,
124069d94f4cSDag-Erling Smørgrav 	.vfs_mount =	tarfs_mount,
124169d94f4cSDag-Erling Smørgrav 	.vfs_root =	tarfs_root,
124269d94f4cSDag-Erling Smørgrav 	.vfs_statfs =	tarfs_statfs,
124369d94f4cSDag-Erling Smørgrav 	.vfs_unmount =	tarfs_unmount,
124469d94f4cSDag-Erling Smørgrav 	.vfs_vget =	tarfs_vget,
124569d94f4cSDag-Erling Smørgrav };
124669d94f4cSDag-Erling Smørgrav VFS_SET(tarfs_vfsops, tarfs, VFCF_READONLY);
124769d94f4cSDag-Erling Smørgrav MODULE_VERSION(tarfs, 1);
124869d94f4cSDag-Erling Smørgrav MODULE_DEPEND(tarfs, xz, 1, 1, 1);
1249