xref: /freebsd/sys/fs/tarfs/tarfs_subr.c (revision ba8cc6d7271a50fec978a1d3a088aec7985fae48)
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.
569d94f4cSDag-Erling Smørgrav  * Copyright (c) 2022-2023 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/stat.h>
3369d94f4cSDag-Erling Smørgrav #include <sys/systm.h>
3469d94f4cSDag-Erling Smørgrav #include <sys/buf.h>
3569d94f4cSDag-Erling Smørgrav #include <sys/fcntl.h>
3669d94f4cSDag-Erling Smørgrav #include <sys/libkern.h>
3769d94f4cSDag-Erling Smørgrav #include <sys/lock.h>
3869d94f4cSDag-Erling Smørgrav #include <sys/malloc.h>
3969d94f4cSDag-Erling Smørgrav #include <sys/mount.h>
4069d94f4cSDag-Erling Smørgrav #include <sys/namei.h>
4169d94f4cSDag-Erling Smørgrav #include <sys/proc.h>
4269d94f4cSDag-Erling Smørgrav #include <sys/queue.h>
4369d94f4cSDag-Erling Smørgrav #include <sys/sysctl.h>
4469d94f4cSDag-Erling Smørgrav #include <sys/vnode.h>
4569d94f4cSDag-Erling Smørgrav 
4669d94f4cSDag-Erling Smørgrav #include <vm/vm_param.h>
4769d94f4cSDag-Erling Smørgrav 
4869d94f4cSDag-Erling Smørgrav #include <fs/tarfs/tarfs.h>
4969d94f4cSDag-Erling Smørgrav #include <fs/tarfs/tarfs_dbg.h>
5069d94f4cSDag-Erling Smørgrav 
5169d94f4cSDag-Erling Smørgrav MALLOC_DEFINE(M_TARFSNAME, "tarfs name", "tarfs file names");
5269d94f4cSDag-Erling Smørgrav MALLOC_DEFINE(M_TARFSBLK, "tarfs blk", "tarfs block maps");
5369d94f4cSDag-Erling Smørgrav 
5469d94f4cSDag-Erling Smørgrav SYSCTL_NODE(_vfs, OID_AUTO, tarfs, CTLFLAG_RW, 0, "Tar filesystem");
5569d94f4cSDag-Erling Smørgrav 
5669d94f4cSDag-Erling Smørgrav unsigned int tarfs_ioshift = TARFS_IOSHIFT_DEFAULT;
5769d94f4cSDag-Erling Smørgrav 
5869d94f4cSDag-Erling Smørgrav static int
tarfs_sysctl_handle_ioshift(SYSCTL_HANDLER_ARGS)5969d94f4cSDag-Erling Smørgrav tarfs_sysctl_handle_ioshift(SYSCTL_HANDLER_ARGS)
6069d94f4cSDag-Erling Smørgrav {
6169d94f4cSDag-Erling Smørgrav 	unsigned int tmp;
6269d94f4cSDag-Erling Smørgrav 	int error;
6369d94f4cSDag-Erling Smørgrav 
6469d94f4cSDag-Erling Smørgrav 	tmp = *(unsigned int *)arg1;
6569d94f4cSDag-Erling Smørgrav 	if ((error = SYSCTL_OUT(req, &tmp, sizeof(tmp))) != 0)
6669d94f4cSDag-Erling Smørgrav 		return (error);
6769d94f4cSDag-Erling Smørgrav 	if (req->newptr != NULL) {
6869d94f4cSDag-Erling Smørgrav 		if ((error = SYSCTL_IN(req, &tmp, sizeof(tmp))) != 0)
6969d94f4cSDag-Erling Smørgrav 			return (error);
7069d94f4cSDag-Erling Smørgrav 		if (tmp == 0)
7169d94f4cSDag-Erling Smørgrav 			tmp = TARFS_IOSHIFT_DEFAULT;
7269d94f4cSDag-Erling Smørgrav 		if (tmp < TARFS_IOSHIFT_MIN)
7369d94f4cSDag-Erling Smørgrav 			tmp = TARFS_IOSHIFT_MIN;
7469d94f4cSDag-Erling Smørgrav 		if (tmp > TARFS_IOSHIFT_MAX)
7569d94f4cSDag-Erling Smørgrav 			tmp = TARFS_IOSHIFT_MAX;
7669d94f4cSDag-Erling Smørgrav 		*(unsigned int *)arg1 = tmp;
7769d94f4cSDag-Erling Smørgrav 	}
7869d94f4cSDag-Erling Smørgrav 	return (0);
7969d94f4cSDag-Erling Smørgrav }
8069d94f4cSDag-Erling Smørgrav 
8169d94f4cSDag-Erling Smørgrav SYSCTL_PROC(_vfs_tarfs, OID_AUTO, ioshift,
82c3c5e6c3SZhenlei Huang     CTLTYPE_UINT | CTLFLAG_MPSAFE | CTLFLAG_RWTUN,
8369d94f4cSDag-Erling Smørgrav     &tarfs_ioshift, 0, tarfs_sysctl_handle_ioshift, "IU",
8469d94f4cSDag-Erling Smørgrav     "Tar filesystem preferred I/O size (log 2)");
8569d94f4cSDag-Erling Smørgrav 
8669d94f4cSDag-Erling Smørgrav #ifdef TARFS_DEBUG
8769d94f4cSDag-Erling Smørgrav int tarfs_debug;
88c3c5e6c3SZhenlei Huang SYSCTL_INT(_vfs_tarfs, OID_AUTO, debug, CTLFLAG_RWTUN,
8969d94f4cSDag-Erling Smørgrav     &tarfs_debug, 0, "Tar filesystem debug mask");
9069d94f4cSDag-Erling Smørgrav #endif	/* TARFS_DEBUG */
9169d94f4cSDag-Erling Smørgrav 
9269d94f4cSDag-Erling Smørgrav struct tarfs_node *
tarfs_lookup_node(struct tarfs_node * tnp,struct tarfs_node * f,struct componentname * cnp)9369d94f4cSDag-Erling Smørgrav tarfs_lookup_node(struct tarfs_node *tnp, struct tarfs_node *f,
9469d94f4cSDag-Erling Smørgrav     struct componentname *cnp)
9569d94f4cSDag-Erling Smørgrav {
9669d94f4cSDag-Erling Smørgrav 	boolean_t found;
9769d94f4cSDag-Erling Smørgrav 	struct tarfs_node *entry;
9869d94f4cSDag-Erling Smørgrav 
9969d94f4cSDag-Erling Smørgrav 	TARFS_DPF(LOOKUP, "%s: name: %.*s\n", __func__, (int)cnp->cn_namelen,
10069d94f4cSDag-Erling Smørgrav 	    cnp->cn_nameptr);
10169d94f4cSDag-Erling Smørgrav 
10269d94f4cSDag-Erling Smørgrav 	found = false;
10369d94f4cSDag-Erling Smørgrav 	TAILQ_FOREACH(entry, &tnp->dir.dirhead, dirents) {
10469d94f4cSDag-Erling Smørgrav 		if (f != NULL && entry != f)
10569d94f4cSDag-Erling Smørgrav 			continue;
10669d94f4cSDag-Erling Smørgrav 
10769d94f4cSDag-Erling Smørgrav 		if (entry->namelen == cnp->cn_namelen &&
10869d94f4cSDag-Erling Smørgrav 		    bcmp(entry->name, cnp->cn_nameptr,
10969d94f4cSDag-Erling Smørgrav 		    entry->namelen) == 0) {
11069d94f4cSDag-Erling Smørgrav 			found = 1;
11169d94f4cSDag-Erling Smørgrav 			break;
11269d94f4cSDag-Erling Smørgrav 		}
11369d94f4cSDag-Erling Smørgrav 	}
11469d94f4cSDag-Erling Smørgrav 
11569d94f4cSDag-Erling Smørgrav 	if (found) {
11669d94f4cSDag-Erling Smørgrav 		if (entry->type == VREG && entry->other != NULL) {
11769d94f4cSDag-Erling Smørgrav 			TARFS_DPF_IFF(LOOKUP, "%s: following hard link %p\n",
11869d94f4cSDag-Erling Smørgrav 			    __func__, entry);
11969d94f4cSDag-Erling Smørgrav 			entry = entry->other;
12069d94f4cSDag-Erling Smørgrav 		}
12169d94f4cSDag-Erling Smørgrav 		TARFS_DPF(LOOKUP, "%s: found tarfs_node %p\n", __func__,
12269d94f4cSDag-Erling Smørgrav 		    entry);
12369d94f4cSDag-Erling Smørgrav 		return (entry);
12469d94f4cSDag-Erling Smørgrav 	}
12569d94f4cSDag-Erling Smørgrav 
12669d94f4cSDag-Erling Smørgrav 	TARFS_DPF(LOOKUP, "%s: no match found\n", __func__);
12769d94f4cSDag-Erling Smørgrav 	return (NULL);
12869d94f4cSDag-Erling Smørgrav }
12969d94f4cSDag-Erling Smørgrav 
13069d94f4cSDag-Erling Smørgrav struct tarfs_node *
tarfs_lookup_dir(struct tarfs_node * tnp,off_t cookie)13169d94f4cSDag-Erling Smørgrav tarfs_lookup_dir(struct tarfs_node *tnp, off_t cookie)
13269d94f4cSDag-Erling Smørgrav {
13369d94f4cSDag-Erling Smørgrav 	struct tarfs_node *current;
13469d94f4cSDag-Erling Smørgrav 
13569d94f4cSDag-Erling Smørgrav 	TARFS_DPF(LOOKUP, "%s: tarfs_node %p, cookie %jd\n", __func__, tnp,
13669d94f4cSDag-Erling Smørgrav 	    cookie);
13769d94f4cSDag-Erling Smørgrav 	TARFS_DPF(LOOKUP, "%s: name: %s\n", __func__,
13869d94f4cSDag-Erling Smørgrav 	    (tnp->name == NULL) ? "<<root>>" : tnp->name);
13969d94f4cSDag-Erling Smørgrav 
14069d94f4cSDag-Erling Smørgrav 	if (cookie == tnp->dir.lastcookie &&
14169d94f4cSDag-Erling Smørgrav 	    tnp->dir.lastnode != NULL) {
14269d94f4cSDag-Erling Smørgrav 		TARFS_DPF(LOOKUP, "%s: Using cached entry: tarfs_node %p, "
14369d94f4cSDag-Erling Smørgrav 		    "cookie %jd\n", __func__, tnp->dir.lastnode,
14469d94f4cSDag-Erling Smørgrav 		    tnp->dir.lastcookie);
14569d94f4cSDag-Erling Smørgrav 		return (tnp->dir.lastnode);
14669d94f4cSDag-Erling Smørgrav 	}
14769d94f4cSDag-Erling Smørgrav 
14869d94f4cSDag-Erling Smørgrav 	TAILQ_FOREACH(current, &tnp->dir.dirhead, dirents) {
14969d94f4cSDag-Erling Smørgrav 		TARFS_DPF(LOOKUP, "%s: tarfs_node %p, current %p, ino %lu\n",
15069d94f4cSDag-Erling Smørgrav 		    __func__, tnp, current, current->ino);
15169d94f4cSDag-Erling Smørgrav 		TARFS_DPF_IFF(LOOKUP, current->name != NULL,
15269d94f4cSDag-Erling Smørgrav 		    "%s: name: %s\n", __func__, current->name);
15369d94f4cSDag-Erling Smørgrav 		if (current->ino == cookie) {
15469d94f4cSDag-Erling Smørgrav 			TARFS_DPF(LOOKUP, "%s: Found entry: tarfs_node %p, "
15569d94f4cSDag-Erling Smørgrav 			    "cookie %lu\n", __func__, current,
15669d94f4cSDag-Erling Smørgrav 			    current->ino);
15769d94f4cSDag-Erling Smørgrav 			break;
15869d94f4cSDag-Erling Smørgrav 		}
15969d94f4cSDag-Erling Smørgrav 	}
16069d94f4cSDag-Erling Smørgrav 
16169d94f4cSDag-Erling Smørgrav 	return (current);
16269d94f4cSDag-Erling Smørgrav }
16369d94f4cSDag-Erling Smørgrav 
16469d94f4cSDag-Erling Smørgrav int
tarfs_alloc_node(struct tarfs_mount * tmp,const char * name,size_t namelen,__enum_uint8 (vtype)type,off_t off,size_t sz,time_t mtime,uid_t uid,gid_t gid,mode_t mode,unsigned int flags,const char * linkname,dev_t rdev,struct tarfs_node * parent,struct tarfs_node ** retnode)16569d94f4cSDag-Erling Smørgrav tarfs_alloc_node(struct tarfs_mount *tmp, const char *name, size_t namelen,
166*ba8cc6d7SMateusz Guzik     __enum_uint8(vtype) type, off_t off, size_t sz, time_t mtime, uid_t uid, gid_t gid,
16769d94f4cSDag-Erling Smørgrav     mode_t mode, unsigned int flags, const char *linkname, dev_t rdev,
16869d94f4cSDag-Erling Smørgrav     struct tarfs_node *parent, struct tarfs_node **retnode)
16969d94f4cSDag-Erling Smørgrav {
17069d94f4cSDag-Erling Smørgrav 	struct tarfs_node *tnp;
17169d94f4cSDag-Erling Smørgrav 
17269d94f4cSDag-Erling Smørgrav 	TARFS_DPF(ALLOC, "%s(%.*s)\n", __func__, (int)namelen, name);
17369d94f4cSDag-Erling Smørgrav 
174ae6cff89SDag-Erling Smørgrav 	if (parent != NULL && parent->type != VDIR)
175ae6cff89SDag-Erling Smørgrav 		return (ENOTDIR);
17669d94f4cSDag-Erling Smørgrav 	tnp = malloc(sizeof(struct tarfs_node), M_TARFSNODE, M_WAITOK | M_ZERO);
17769d94f4cSDag-Erling Smørgrav 	mtx_init(&tnp->lock, "tarfs node lock", NULL, MTX_DEF);
17869d94f4cSDag-Erling Smørgrav 	tnp->gen = arc4random();
17969d94f4cSDag-Erling Smørgrav 	tnp->tmp = tmp;
18069d94f4cSDag-Erling Smørgrav 	if (namelen > 0) {
18169d94f4cSDag-Erling Smørgrav 		tnp->name = malloc(namelen + 1, M_TARFSNAME, M_WAITOK);
18269d94f4cSDag-Erling Smørgrav 		tnp->namelen = namelen;
18369d94f4cSDag-Erling Smørgrav 		memcpy(tnp->name, name, namelen);
18469d94f4cSDag-Erling Smørgrav 		tnp->name[namelen] = '\0';
18569d94f4cSDag-Erling Smørgrav 	}
18669d94f4cSDag-Erling Smørgrav 	tnp->type = type;
18769d94f4cSDag-Erling Smørgrav 	tnp->uid = uid;
18869d94f4cSDag-Erling Smørgrav 	tnp->gid = gid;
18969d94f4cSDag-Erling Smørgrav 	tnp->mode = mode;
19069d94f4cSDag-Erling Smørgrav 	tnp->nlink = 1;
19169d94f4cSDag-Erling Smørgrav 	vfs_timestamp(&tnp->atime);
19269d94f4cSDag-Erling Smørgrav 	tnp->mtime.tv_sec = mtime;
19369d94f4cSDag-Erling Smørgrav 	tnp->birthtime = tnp->atime;
19469d94f4cSDag-Erling Smørgrav 	tnp->ctime = tnp->mtime;
19569d94f4cSDag-Erling Smørgrav 	if (parent != NULL) {
19669d94f4cSDag-Erling Smørgrav 		tnp->ino = alloc_unr(tmp->ino_unr);
19769d94f4cSDag-Erling Smørgrav 	}
19869d94f4cSDag-Erling Smørgrav 	tnp->offset = off;
19969d94f4cSDag-Erling Smørgrav 	tnp->size = tnp->physize = sz;
20069d94f4cSDag-Erling Smørgrav 	switch (type) {
20169d94f4cSDag-Erling Smørgrav 	case VDIR:
20269d94f4cSDag-Erling Smørgrav 		MPASS(parent != tnp);
20369d94f4cSDag-Erling Smørgrav 		MPASS(parent != NULL || tmp->root == NULL);
20469d94f4cSDag-Erling Smørgrav 		TAILQ_INIT(&tnp->dir.dirhead);
20569d94f4cSDag-Erling Smørgrav 		tnp->nlink++;
20669d94f4cSDag-Erling Smørgrav 		if (parent == NULL) {
20769d94f4cSDag-Erling Smørgrav 			tnp->ino = TARFS_ROOTINO;
20869d94f4cSDag-Erling Smørgrav 		}
20969d94f4cSDag-Erling Smørgrav 		tnp->physize = 0;
21069d94f4cSDag-Erling Smørgrav 		break;
21169d94f4cSDag-Erling Smørgrav 	case VLNK:
21269d94f4cSDag-Erling Smørgrav 		tnp->link.name = malloc(sz + 1, M_TARFSNAME,
21369d94f4cSDag-Erling Smørgrav 		    M_WAITOK);
21469d94f4cSDag-Erling Smørgrav 		tnp->link.namelen = sz;
21569d94f4cSDag-Erling Smørgrav 		memcpy(tnp->link.name, linkname, sz);
21669d94f4cSDag-Erling Smørgrav 		tnp->link.name[sz] = '\0';
21769d94f4cSDag-Erling Smørgrav 		break;
21869d94f4cSDag-Erling Smørgrav 	case VREG:
21969d94f4cSDag-Erling Smørgrav 		/* create dummy block map */
22069d94f4cSDag-Erling Smørgrav 		tnp->nblk = 1;
22169d94f4cSDag-Erling Smørgrav 		tnp->blk = malloc(sizeof(*tnp->blk), M_TARFSBLK, M_WAITOK);
22269d94f4cSDag-Erling Smørgrav 		tnp->blk[0].i = 0;
22369d94f4cSDag-Erling Smørgrav 		tnp->blk[0].o = 0;
22469d94f4cSDag-Erling Smørgrav 		tnp->blk[0].l = tnp->physize;
22569d94f4cSDag-Erling Smørgrav 		break;
22669d94f4cSDag-Erling Smørgrav 	case VFIFO:
22769d94f4cSDag-Erling Smørgrav 		/* Nothing extra to do */
22869d94f4cSDag-Erling Smørgrav 		break;
22969d94f4cSDag-Erling Smørgrav 	case VBLK:
23069d94f4cSDag-Erling Smørgrav 	case VCHR:
23169d94f4cSDag-Erling Smørgrav 		tnp->rdev = rdev;
23269d94f4cSDag-Erling Smørgrav 		tnp->physize = 0;
23369d94f4cSDag-Erling Smørgrav 		break;
23469d94f4cSDag-Erling Smørgrav 	default:
23569d94f4cSDag-Erling Smørgrav 		panic("%s: type %d not allowed", __func__, type);
23669d94f4cSDag-Erling Smørgrav 	}
23769d94f4cSDag-Erling Smørgrav 	if (parent != NULL) {
23869d94f4cSDag-Erling Smørgrav 		TARFS_NODE_LOCK(parent);
23969d94f4cSDag-Erling Smørgrav 		TAILQ_INSERT_TAIL(&parent->dir.dirhead, tnp, dirents);
24069d94f4cSDag-Erling Smørgrav 		parent->size += sizeof(struct tarfs_node);
24169d94f4cSDag-Erling Smørgrav 		tnp->parent = parent;
24269d94f4cSDag-Erling Smørgrav 		if (type == VDIR) {
24369d94f4cSDag-Erling Smørgrav 			parent->nlink++;
24469d94f4cSDag-Erling Smørgrav 		}
24569d94f4cSDag-Erling Smørgrav 		TARFS_NODE_UNLOCK(parent);
24669d94f4cSDag-Erling Smørgrav 	} else {
24769d94f4cSDag-Erling Smørgrav 		tnp->parent = tnp;
24869d94f4cSDag-Erling Smørgrav 	}
24969d94f4cSDag-Erling Smørgrav 	MPASS(tnp->ino != 0);
25069d94f4cSDag-Erling Smørgrav 
25169d94f4cSDag-Erling Smørgrav 	TARFS_ALLNODES_LOCK(tmp);
25269d94f4cSDag-Erling Smørgrav 	TAILQ_INSERT_TAIL(&tmp->allnodes, tnp, entries);
25369d94f4cSDag-Erling Smørgrav 	TARFS_ALLNODES_UNLOCK(tmp);
25469d94f4cSDag-Erling Smørgrav 
25569d94f4cSDag-Erling Smørgrav 	*retnode = tnp;
25669d94f4cSDag-Erling Smørgrav 	tmp->nfiles++;
25769d94f4cSDag-Erling Smørgrav 	return (0);
25869d94f4cSDag-Erling Smørgrav }
25969d94f4cSDag-Erling Smørgrav 
26069d94f4cSDag-Erling Smørgrav #define is09(ch) ((ch) >= '0' && (ch) <= '9')
26169d94f4cSDag-Erling Smørgrav 
26269d94f4cSDag-Erling Smørgrav int
tarfs_load_blockmap(struct tarfs_node * tnp,size_t realsize)26369d94f4cSDag-Erling Smørgrav tarfs_load_blockmap(struct tarfs_node *tnp, size_t realsize)
26469d94f4cSDag-Erling Smørgrav {
26569d94f4cSDag-Erling Smørgrav 	struct tarfs_blk *blk = NULL;
26669d94f4cSDag-Erling Smørgrav 	char *map = NULL;
26769d94f4cSDag-Erling Smørgrav 	size_t nmap = 0, nblk = 0;
26869d94f4cSDag-Erling Smørgrav 	char *p, *q;
26969d94f4cSDag-Erling Smørgrav 	ssize_t res;
27069d94f4cSDag-Erling Smørgrav 	unsigned int i;
27169d94f4cSDag-Erling Smørgrav 	long n;
27269d94f4cSDag-Erling Smørgrav 
27369d94f4cSDag-Erling Smørgrav 	/*
27469d94f4cSDag-Erling Smørgrav 	 * Load the entire map into memory.  We don't know how big it is,
27569d94f4cSDag-Erling Smørgrav 	 * but as soon as we start reading it we will know how many
27669d94f4cSDag-Erling Smørgrav 	 * entries it contains, and then we can count newlines.
27769d94f4cSDag-Erling Smørgrav 	 */
27869d94f4cSDag-Erling Smørgrav 	do {
27969d94f4cSDag-Erling Smørgrav 		nmap++;
28069d94f4cSDag-Erling Smørgrav 		if (tnp->size < nmap * TARFS_BLOCKSIZE) {
28169d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s: map too large\n", __func__);
28269d94f4cSDag-Erling Smørgrav 			goto bad;
28369d94f4cSDag-Erling Smørgrav 		}
28469d94f4cSDag-Erling Smørgrav 		/* grow the map */
28569d94f4cSDag-Erling Smørgrav 		map = realloc(map, nmap * TARFS_BLOCKSIZE + 1, M_TARFSBLK,
28669d94f4cSDag-Erling Smørgrav 		    M_ZERO | M_WAITOK);
28769d94f4cSDag-Erling Smørgrav 		/* read an additional block */
28869d94f4cSDag-Erling Smørgrav 		res = tarfs_io_read_buf(tnp->tmp, false,
28969d94f4cSDag-Erling Smørgrav 		    map + (nmap - 1) * TARFS_BLOCKSIZE,
29069d94f4cSDag-Erling Smørgrav 		    tnp->offset + (nmap - 1) * TARFS_BLOCKSIZE,
29169d94f4cSDag-Erling Smørgrav 		    TARFS_BLOCKSIZE);
29269d94f4cSDag-Erling Smørgrav 		if (res < 0)
29369d94f4cSDag-Erling Smørgrav 			return (-res);
29469d94f4cSDag-Erling Smørgrav 		else if (res < TARFS_BLOCKSIZE)
29569d94f4cSDag-Erling Smørgrav 			return (EIO);
29669d94f4cSDag-Erling Smørgrav 		map[nmap * TARFS_BLOCKSIZE] = '\0'; /* sentinel */
29769d94f4cSDag-Erling Smørgrav 		if (nblk == 0) {
29869d94f4cSDag-Erling Smørgrav 			n = strtol(p = map, &q, 10);
29969d94f4cSDag-Erling Smørgrav 			if (q == p || *q != '\n' || n < 1)
30069d94f4cSDag-Erling Smørgrav 				goto syntax;
30169d94f4cSDag-Erling Smørgrav 			nblk = n;
30269d94f4cSDag-Erling Smørgrav 		}
30369d94f4cSDag-Erling Smørgrav 		for (n = 0, p = map; *p != '\0'; ++p) {
30469d94f4cSDag-Erling Smørgrav 			if (*p == '\n') {
30569d94f4cSDag-Erling Smørgrav 				++n;
30669d94f4cSDag-Erling Smørgrav 			}
30769d94f4cSDag-Erling Smørgrav 		}
30869d94f4cSDag-Erling Smørgrav 		TARFS_DPF(MAP, "%s: %ld newlines in map\n", __func__, n);
30969d94f4cSDag-Erling Smørgrav 	} while (n < nblk * 2 + 1);
31069d94f4cSDag-Erling Smørgrav 	TARFS_DPF(MAP, "%s: block map length %zu\n", __func__, nblk);
31169d94f4cSDag-Erling Smørgrav 	blk = malloc(sizeof(*blk) * nblk, M_TARFSBLK, M_WAITOK | M_ZERO);
31269d94f4cSDag-Erling Smørgrav 	p = strchr(map, '\n') + 1;
31369d94f4cSDag-Erling Smørgrav 	for (i = 0; i < nblk; i++) {
31469d94f4cSDag-Erling Smørgrav 		if (i == 0)
31569d94f4cSDag-Erling Smørgrav 			blk[i].i = nmap * TARFS_BLOCKSIZE;
31669d94f4cSDag-Erling Smørgrav 		else
31769d94f4cSDag-Erling Smørgrav 			blk[i].i = blk[i - 1].i + blk[i - 1].l;
31869d94f4cSDag-Erling Smørgrav 		n = strtol(p, &q, 10);
31969d94f4cSDag-Erling Smørgrav 		if (q == p || *q != '\n' || n < 0)
32069d94f4cSDag-Erling Smørgrav 			goto syntax;
32169d94f4cSDag-Erling Smørgrav 		p = q + 1;
32269d94f4cSDag-Erling Smørgrav 		blk[i].o = n;
32369d94f4cSDag-Erling Smørgrav 		n = strtol(p, &q, 10);
32469d94f4cSDag-Erling Smørgrav 		if (q == p || *q != '\n' || n < 0)
32569d94f4cSDag-Erling Smørgrav 			goto syntax;
32669d94f4cSDag-Erling Smørgrav 		p = q + 1;
32769d94f4cSDag-Erling Smørgrav 		blk[i].l = n;
32869d94f4cSDag-Erling Smørgrav 		TARFS_DPF(MAP, "%s: %3d %12zu %12zu %12zu\n", __func__,
32969d94f4cSDag-Erling Smørgrav 		    i, blk[i].i, blk[i].o, blk[i].l);
33069d94f4cSDag-Erling Smørgrav 		/*
33169d94f4cSDag-Erling Smørgrav 		 * Check block alignment if the block is of non-zero
33269d94f4cSDag-Erling Smørgrav 		 * length (a zero-length block indicates the end of a
33369d94f4cSDag-Erling Smørgrav 		 * trailing hole).  Checking i indirectly checks the
33469d94f4cSDag-Erling Smørgrav 		 * previous block's l.  It's ok for the final block to
33569d94f4cSDag-Erling Smørgrav 		 * have an uneven length.
33669d94f4cSDag-Erling Smørgrav 		 */
33769d94f4cSDag-Erling Smørgrav 		if (blk[i].l == 0) {
33869d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s: zero-length block\n", __func__);
33969d94f4cSDag-Erling Smørgrav 		} else if (blk[i].i % TARFS_BLOCKSIZE != 0 ||
34069d94f4cSDag-Erling Smørgrav 		    blk[i].o % TARFS_BLOCKSIZE != 0) {
34169d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s: misaligned map entry\n", __func__);
34269d94f4cSDag-Erling Smørgrav 			goto bad;
34369d94f4cSDag-Erling Smørgrav 		}
34469d94f4cSDag-Erling Smørgrav 		/*
34569d94f4cSDag-Erling Smørgrav 		 * Check that this block starts after the end of the
34669d94f4cSDag-Erling Smørgrav 		 * previous one.
34769d94f4cSDag-Erling Smørgrav 		 */
34869d94f4cSDag-Erling Smørgrav 		if (i > 0 && blk[i].o < blk[i - 1].o + blk[i - 1].l) {
34969d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s: overlapping map entries\n", __func__);
35069d94f4cSDag-Erling Smørgrav 			goto bad;
35169d94f4cSDag-Erling Smørgrav 		}
35269d94f4cSDag-Erling Smørgrav 		/*
35369d94f4cSDag-Erling Smørgrav 		 * Check that the block is within the file, both
35469d94f4cSDag-Erling Smørgrav 		 * physically and logically.
35569d94f4cSDag-Erling Smørgrav 		 */
35669d94f4cSDag-Erling Smørgrav 		if (blk[i].i + blk[i].l > tnp->physize ||
35769d94f4cSDag-Erling Smørgrav 		    blk[i].o + blk[i].l > realsize) {
35869d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s: map overflow\n", __func__);
35969d94f4cSDag-Erling Smørgrav 			goto bad;
36069d94f4cSDag-Erling Smørgrav 		}
36169d94f4cSDag-Erling Smørgrav 	}
36269d94f4cSDag-Erling Smørgrav 	free(map, M_TARFSBLK);
36369d94f4cSDag-Erling Smørgrav 
36469d94f4cSDag-Erling Smørgrav 	/* store in node */
36569d94f4cSDag-Erling Smørgrav 	free(tnp->blk, M_TARFSBLK);
36669d94f4cSDag-Erling Smørgrav 	tnp->nblk = nblk;
36769d94f4cSDag-Erling Smørgrav 	tnp->blk = blk;
36869d94f4cSDag-Erling Smørgrav 	tnp->size = realsize;
36969d94f4cSDag-Erling Smørgrav 	return (0);
37069d94f4cSDag-Erling Smørgrav syntax:
37169d94f4cSDag-Erling Smørgrav 	TARFS_DPF(MAP, "%s: syntax error in block map\n", __func__);
37269d94f4cSDag-Erling Smørgrav bad:
37369d94f4cSDag-Erling Smørgrav 	free(map, M_TARFSBLK);
37469d94f4cSDag-Erling Smørgrav 	free(blk, M_TARFSBLK);
37569d94f4cSDag-Erling Smørgrav 	return (EINVAL);
37669d94f4cSDag-Erling Smørgrav }
37769d94f4cSDag-Erling Smørgrav 
37869d94f4cSDag-Erling Smørgrav void
tarfs_free_node(struct tarfs_node * tnp)37969d94f4cSDag-Erling Smørgrav tarfs_free_node(struct tarfs_node *tnp)
38069d94f4cSDag-Erling Smørgrav {
38169d94f4cSDag-Erling Smørgrav 	struct tarfs_mount *tmp;
38269d94f4cSDag-Erling Smørgrav 
38369d94f4cSDag-Erling Smørgrav 	MPASS(tnp != NULL);
38469d94f4cSDag-Erling Smørgrav 	tmp = tnp->tmp;
38569d94f4cSDag-Erling Smørgrav 
38669d94f4cSDag-Erling Smørgrav 	switch (tnp->type) {
387fd8c98a5SDag-Erling Smørgrav 	case VREG:
388fd8c98a5SDag-Erling Smørgrav 		if (tnp->nlink-- > 1)
389fd8c98a5SDag-Erling Smørgrav 			return;
390fd8c98a5SDag-Erling Smørgrav 		if (tnp->other != NULL)
391fd8c98a5SDag-Erling Smørgrav 			tarfs_free_node(tnp->other);
392fd8c98a5SDag-Erling Smørgrav 		break;
393fd8c98a5SDag-Erling Smørgrav 	case VDIR:
394fd8c98a5SDag-Erling Smørgrav 		if (tnp->nlink-- > 2)
395fd8c98a5SDag-Erling Smørgrav 			return;
396fd8c98a5SDag-Erling Smørgrav 		if (tnp->parent != NULL && tnp->parent != tnp)
397fd8c98a5SDag-Erling Smørgrav 			tarfs_free_node(tnp->parent);
398fd8c98a5SDag-Erling Smørgrav 		break;
39969d94f4cSDag-Erling Smørgrav 	case VLNK:
40069d94f4cSDag-Erling Smørgrav 		if (tnp->link.name)
40169d94f4cSDag-Erling Smørgrav 			free(tnp->link.name, M_TARFSNAME);
40269d94f4cSDag-Erling Smørgrav 		break;
40369d94f4cSDag-Erling Smørgrav 	default:
40469d94f4cSDag-Erling Smørgrav 		break;
40569d94f4cSDag-Erling Smørgrav 	}
40669d94f4cSDag-Erling Smørgrav 	if (tnp->name != NULL)
40769d94f4cSDag-Erling Smørgrav 		free(tnp->name, M_TARFSNAME);
40869d94f4cSDag-Erling Smørgrav 	if (tnp->blk != NULL)
40969d94f4cSDag-Erling Smørgrav 		free(tnp->blk, M_TARFSBLK);
41069d94f4cSDag-Erling Smørgrav 	if (tnp->ino >= TARFS_MININO)
41169d94f4cSDag-Erling Smørgrav 		free_unr(tmp->ino_unr, tnp->ino);
412fd8c98a5SDag-Erling Smørgrav 	TAILQ_REMOVE(&tmp->allnodes, tnp, entries);
41369d94f4cSDag-Erling Smørgrav 	free(tnp, M_TARFSNODE);
41469d94f4cSDag-Erling Smørgrav 	tmp->nfiles--;
41569d94f4cSDag-Erling Smørgrav }
41669d94f4cSDag-Erling Smørgrav 
41769d94f4cSDag-Erling Smørgrav int
tarfs_read_file(struct tarfs_node * tnp,size_t len,struct uio * uiop)41869d94f4cSDag-Erling Smørgrav tarfs_read_file(struct tarfs_node *tnp, size_t len, struct uio *uiop)
41969d94f4cSDag-Erling Smørgrav {
42069d94f4cSDag-Erling Smørgrav 	struct uio auio;
42169d94f4cSDag-Erling Smørgrav 	size_t resid = len;
42269d94f4cSDag-Erling Smørgrav 	size_t copylen;
42369d94f4cSDag-Erling Smørgrav 	unsigned int i;
42469d94f4cSDag-Erling Smørgrav 	int error;
42569d94f4cSDag-Erling Smørgrav 
42669d94f4cSDag-Erling Smørgrav 	TARFS_DPF(VNODE, "%s(%s, %zu, %zu)\n", __func__,
42769d94f4cSDag-Erling Smørgrav 	    tnp->name, uiop->uio_offset, resid);
42869d94f4cSDag-Erling Smørgrav 	for (i = 0; i < tnp->nblk && resid > 0; ++i) {
42969d94f4cSDag-Erling Smørgrav 		if (uiop->uio_offset > tnp->blk[i].o + tnp->blk[i].l) {
43069d94f4cSDag-Erling Smørgrav 			/* skip this block */
43169d94f4cSDag-Erling Smørgrav 			continue;
43269d94f4cSDag-Erling Smørgrav 		}
43369d94f4cSDag-Erling Smørgrav 		while (resid > 0 &&
43469d94f4cSDag-Erling Smørgrav 		    uiop->uio_offset < tnp->blk[i].o) {
43569d94f4cSDag-Erling Smørgrav 			/* move out some zeroes... */
43669d94f4cSDag-Erling Smørgrav 			copylen = tnp->blk[i].o - uiop->uio_offset;
43769d94f4cSDag-Erling Smørgrav 			if (copylen > resid)
43869d94f4cSDag-Erling Smørgrav 				copylen = resid;
43969d94f4cSDag-Erling Smørgrav 			if (copylen > ZERO_REGION_SIZE)
44069d94f4cSDag-Erling Smørgrav 				copylen = ZERO_REGION_SIZE;
44169d94f4cSDag-Erling Smørgrav 			auio = *uiop;
44269d94f4cSDag-Erling Smørgrav 			auio.uio_offset = 0;
44369d94f4cSDag-Erling Smørgrav 			auio.uio_resid = copylen;
44469d94f4cSDag-Erling Smørgrav 			error = uiomove(__DECONST(void *, zero_region),
44569d94f4cSDag-Erling Smørgrav 			    copylen, &auio);
44669d94f4cSDag-Erling Smørgrav 			if (error != 0)
44769d94f4cSDag-Erling Smørgrav 				return (error);
44869d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s(%s) = zero %zu\n", __func__,
44969d94f4cSDag-Erling Smørgrav 			    tnp->name, copylen - auio.uio_resid);
45069d94f4cSDag-Erling Smørgrav 			uiop->uio_offset += copylen - auio.uio_resid;
45169d94f4cSDag-Erling Smørgrav 			uiop->uio_resid -= copylen - auio.uio_resid;
45269d94f4cSDag-Erling Smørgrav 			resid -= copylen - auio.uio_resid;
45369d94f4cSDag-Erling Smørgrav 		}
45469d94f4cSDag-Erling Smørgrav 		while (resid > 0 &&
45569d94f4cSDag-Erling Smørgrav 		    uiop->uio_offset < tnp->blk[i].o + tnp->blk[i].l) {
45669d94f4cSDag-Erling Smørgrav 			/* now actual data */
45769d94f4cSDag-Erling Smørgrav 			copylen = tnp->blk[i].l;
45869d94f4cSDag-Erling Smørgrav 			if (copylen > resid)
45969d94f4cSDag-Erling Smørgrav 				copylen = resid;
46069d94f4cSDag-Erling Smørgrav 			auio = *uiop;
46169d94f4cSDag-Erling Smørgrav 			auio.uio_offset = tnp->offset + tnp->blk[i].i +
46269d94f4cSDag-Erling Smørgrav 			    uiop->uio_offset - tnp->blk[i].o;
46369d94f4cSDag-Erling Smørgrav 			auio.uio_resid = copylen;
46469d94f4cSDag-Erling Smørgrav 			error = tarfs_io_read(tnp->tmp, false, &auio);
46569d94f4cSDag-Erling Smørgrav 			if (error != 0)
46669d94f4cSDag-Erling Smørgrav 				return (error);
46769d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s(%s) = data %zu\n", __func__,
46869d94f4cSDag-Erling Smørgrav 			    tnp->name, copylen - auio.uio_resid);
46969d94f4cSDag-Erling Smørgrav 			uiop->uio_offset += copylen - auio.uio_resid;
47069d94f4cSDag-Erling Smørgrav 			uiop->uio_resid -= copylen - auio.uio_resid;
47169d94f4cSDag-Erling Smørgrav 			resid -= copylen - auio.uio_resid;
47269d94f4cSDag-Erling Smørgrav 		}
47369d94f4cSDag-Erling Smørgrav 	}
47469d94f4cSDag-Erling Smørgrav 	TARFS_DPF(VNODE, "%s(%s) = %zu\n", __func__,
47569d94f4cSDag-Erling Smørgrav 	    tnp->name, len - resid);
47669d94f4cSDag-Erling Smørgrav 	return (0);
47769d94f4cSDag-Erling Smørgrav }
47869d94f4cSDag-Erling Smørgrav 
47969d94f4cSDag-Erling Smørgrav /*
48069d94f4cSDag-Erling Smørgrav  * XXX ugly file flag parser which could easily be a finite state machine
48169d94f4cSDag-Erling Smørgrav  * driven by a small precomputed table.
48269d94f4cSDag-Erling Smørgrav  *
48369d94f4cSDag-Erling Smørgrav  * Note that unlike strtofflags(3), we make no attempt to handle negated
48469d94f4cSDag-Erling Smørgrav  * flags, since they shouldn't appear in tar files.
48569d94f4cSDag-Erling Smørgrav  */
48669d94f4cSDag-Erling Smørgrav static const struct tarfs_flag {
48769d94f4cSDag-Erling Smørgrav 	const char *name;
48869d94f4cSDag-Erling Smørgrav 	unsigned int flag;
48969d94f4cSDag-Erling Smørgrav } tarfs_flags[] = {
49069d94f4cSDag-Erling Smørgrav 	{ "nodump",	UF_NODUMP },
49169d94f4cSDag-Erling Smørgrav 	{ "uchg",	UF_IMMUTABLE },
49269d94f4cSDag-Erling Smørgrav 	{ "uappnd",	UF_APPEND },
49369d94f4cSDag-Erling Smørgrav 	{ "opaque",	UF_OPAQUE },
49469d94f4cSDag-Erling Smørgrav 	{ "uunlnk",	UF_NOUNLINK },
49569d94f4cSDag-Erling Smørgrav 	{ "arch",	SF_ARCHIVED },
49669d94f4cSDag-Erling Smørgrav 	{ "schg",	SF_IMMUTABLE },
49769d94f4cSDag-Erling Smørgrav 	{ "sappnd",	SF_APPEND },
49869d94f4cSDag-Erling Smørgrav 	{ "sunlnk",	SF_NOUNLINK },
49969d94f4cSDag-Erling Smørgrav 	{ NULL, 0 },
50069d94f4cSDag-Erling Smørgrav };
50169d94f4cSDag-Erling Smørgrav 
50269d94f4cSDag-Erling Smørgrav unsigned int
tarfs_strtofflags(const char * str,char ** end)50369d94f4cSDag-Erling Smørgrav tarfs_strtofflags(const char *str, char **end)
50469d94f4cSDag-Erling Smørgrav {
50569d94f4cSDag-Erling Smørgrav 	const struct tarfs_flag *tf;
50669d94f4cSDag-Erling Smørgrav 	const char *p, *q;
50769d94f4cSDag-Erling Smørgrav 	unsigned int ret;
50869d94f4cSDag-Erling Smørgrav 
50969d94f4cSDag-Erling Smørgrav 	ret = 0;
51069d94f4cSDag-Erling Smørgrav 	for (p = q = str; *q != '\0'; p = q + 1) {
51169d94f4cSDag-Erling Smørgrav 		for (q = p; *q != '\0' && *q != ','; ++q) {
51269d94f4cSDag-Erling Smørgrav 			if (*q < 'a' || *q > 'z') {
51369d94f4cSDag-Erling Smørgrav 				goto end;
51469d94f4cSDag-Erling Smørgrav 			}
51569d94f4cSDag-Erling Smørgrav 			/* nothing */
51669d94f4cSDag-Erling Smørgrav 		}
51769d94f4cSDag-Erling Smørgrav 		for (tf = tarfs_flags; tf->name != NULL; tf++) {
51869d94f4cSDag-Erling Smørgrav 			if (strncmp(tf->name, p, q - p) == 0 &&
51969d94f4cSDag-Erling Smørgrav 			    tf->name[q - p] == '\0') {
52069d94f4cSDag-Erling Smørgrav 				TARFS_DPF(ALLOC, "%s: %.*s = 0x%06x\n", __func__,
52169d94f4cSDag-Erling Smørgrav 				    (int)(q - p), p, tf->flag);
52269d94f4cSDag-Erling Smørgrav 				ret |= tf->flag;
52369d94f4cSDag-Erling Smørgrav 				break;
52469d94f4cSDag-Erling Smørgrav 			}
52569d94f4cSDag-Erling Smørgrav 		}
52669d94f4cSDag-Erling Smørgrav 		if (tf->name == NULL) {
52769d94f4cSDag-Erling Smørgrav 			TARFS_DPF(ALLOC, "%s: %.*s = 0x??????\n",
52869d94f4cSDag-Erling Smørgrav 			    __func__, (int)(q - p), p);
52969d94f4cSDag-Erling Smørgrav 			goto end;
53069d94f4cSDag-Erling Smørgrav 		}
53169d94f4cSDag-Erling Smørgrav 	}
53269d94f4cSDag-Erling Smørgrav end:
53369d94f4cSDag-Erling Smørgrav 	if (*end != NULL) {
53469d94f4cSDag-Erling Smørgrav 		*end = __DECONST(char *, q);
53569d94f4cSDag-Erling Smørgrav 	}
53669d94f4cSDag-Erling Smørgrav 	return (ret);
53769d94f4cSDag-Erling Smørgrav }
538