xref: /freebsd/sys/fs/tarfs/tarfs_subr.c (revision 69d94f4c7608e41505996559367450706e91fbb8)
1*69d94f4cSDag-Erling Smørgrav /*-
2*69d94f4cSDag-Erling Smørgrav  * SPDX-License-Identifier: BSD-2-Clause
3*69d94f4cSDag-Erling Smørgrav  *
4*69d94f4cSDag-Erling Smørgrav  * Copyright (c) 2013 Juniper Networks, Inc.
5*69d94f4cSDag-Erling Smørgrav  * Copyright (c) 2022-2023 Klara, Inc.
6*69d94f4cSDag-Erling Smørgrav  *
7*69d94f4cSDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
8*69d94f4cSDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
9*69d94f4cSDag-Erling Smørgrav  * are met:
10*69d94f4cSDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
11*69d94f4cSDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer.
12*69d94f4cSDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
13*69d94f4cSDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
14*69d94f4cSDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
15*69d94f4cSDag-Erling Smørgrav  *
16*69d94f4cSDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*69d94f4cSDag-Erling Smørgrav  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*69d94f4cSDag-Erling Smørgrav  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*69d94f4cSDag-Erling Smørgrav  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*69d94f4cSDag-Erling Smørgrav  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*69d94f4cSDag-Erling Smørgrav  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*69d94f4cSDag-Erling Smørgrav  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*69d94f4cSDag-Erling Smørgrav  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*69d94f4cSDag-Erling Smørgrav  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*69d94f4cSDag-Erling Smørgrav  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*69d94f4cSDag-Erling Smørgrav  * SUCH DAMAGE.
27*69d94f4cSDag-Erling Smørgrav  */
28*69d94f4cSDag-Erling Smørgrav 
29*69d94f4cSDag-Erling Smørgrav #include "opt_tarfs.h"
30*69d94f4cSDag-Erling Smørgrav 
31*69d94f4cSDag-Erling Smørgrav #include <sys/param.h>
32*69d94f4cSDag-Erling Smørgrav #include <sys/stat.h>
33*69d94f4cSDag-Erling Smørgrav #include <sys/systm.h>
34*69d94f4cSDag-Erling Smørgrav #include <sys/buf.h>
35*69d94f4cSDag-Erling Smørgrav #include <sys/fcntl.h>
36*69d94f4cSDag-Erling Smørgrav #include <sys/libkern.h>
37*69d94f4cSDag-Erling Smørgrav #include <sys/lock.h>
38*69d94f4cSDag-Erling Smørgrav #include <sys/malloc.h>
39*69d94f4cSDag-Erling Smørgrav #include <sys/mount.h>
40*69d94f4cSDag-Erling Smørgrav #include <sys/namei.h>
41*69d94f4cSDag-Erling Smørgrav #include <sys/proc.h>
42*69d94f4cSDag-Erling Smørgrav #include <sys/queue.h>
43*69d94f4cSDag-Erling Smørgrav #include <sys/sysctl.h>
44*69d94f4cSDag-Erling Smørgrav #include <sys/vnode.h>
45*69d94f4cSDag-Erling Smørgrav 
46*69d94f4cSDag-Erling Smørgrav #include <vm/vm_param.h>
47*69d94f4cSDag-Erling Smørgrav 
48*69d94f4cSDag-Erling Smørgrav #include <fs/tarfs/tarfs.h>
49*69d94f4cSDag-Erling Smørgrav #include <fs/tarfs/tarfs_dbg.h>
50*69d94f4cSDag-Erling Smørgrav 
51*69d94f4cSDag-Erling Smørgrav MALLOC_DEFINE(M_TARFSNAME, "tarfs name", "tarfs file names");
52*69d94f4cSDag-Erling Smørgrav MALLOC_DEFINE(M_TARFSBLK, "tarfs blk", "tarfs block maps");
53*69d94f4cSDag-Erling Smørgrav 
54*69d94f4cSDag-Erling Smørgrav SYSCTL_NODE(_vfs, OID_AUTO, tarfs, CTLFLAG_RW, 0, "Tar filesystem");
55*69d94f4cSDag-Erling Smørgrav 
56*69d94f4cSDag-Erling Smørgrav unsigned int tarfs_ioshift = TARFS_IOSHIFT_DEFAULT;
57*69d94f4cSDag-Erling Smørgrav 
58*69d94f4cSDag-Erling Smørgrav static int
59*69d94f4cSDag-Erling Smørgrav tarfs_sysctl_handle_ioshift(SYSCTL_HANDLER_ARGS)
60*69d94f4cSDag-Erling Smørgrav {
61*69d94f4cSDag-Erling Smørgrav 	unsigned int tmp;
62*69d94f4cSDag-Erling Smørgrav 	int error;
63*69d94f4cSDag-Erling Smørgrav 
64*69d94f4cSDag-Erling Smørgrav 	tmp = *(unsigned int *)arg1;
65*69d94f4cSDag-Erling Smørgrav 	if ((error = SYSCTL_OUT(req, &tmp, sizeof(tmp))) != 0)
66*69d94f4cSDag-Erling Smørgrav 		return (error);
67*69d94f4cSDag-Erling Smørgrav 	if (req->newptr != NULL) {
68*69d94f4cSDag-Erling Smørgrav 		if ((error = SYSCTL_IN(req, &tmp, sizeof(tmp))) != 0)
69*69d94f4cSDag-Erling Smørgrav 			return (error);
70*69d94f4cSDag-Erling Smørgrav 		if (tmp == 0)
71*69d94f4cSDag-Erling Smørgrav 			tmp = TARFS_IOSHIFT_DEFAULT;
72*69d94f4cSDag-Erling Smørgrav 		if (tmp < TARFS_IOSHIFT_MIN)
73*69d94f4cSDag-Erling Smørgrav 			tmp = TARFS_IOSHIFT_MIN;
74*69d94f4cSDag-Erling Smørgrav 		if (tmp > TARFS_IOSHIFT_MAX)
75*69d94f4cSDag-Erling Smørgrav 			tmp = TARFS_IOSHIFT_MAX;
76*69d94f4cSDag-Erling Smørgrav 		*(unsigned int *)arg1 = tmp;
77*69d94f4cSDag-Erling Smørgrav 	}
78*69d94f4cSDag-Erling Smørgrav 	return (0);
79*69d94f4cSDag-Erling Smørgrav }
80*69d94f4cSDag-Erling Smørgrav 
81*69d94f4cSDag-Erling Smørgrav SYSCTL_PROC(_vfs_tarfs, OID_AUTO, ioshift,
82*69d94f4cSDag-Erling Smørgrav     CTLTYPE_UINT | CTLFLAG_MPSAFE | CTLFLAG_RW | CTLFLAG_TUN,
83*69d94f4cSDag-Erling Smørgrav     &tarfs_ioshift, 0, tarfs_sysctl_handle_ioshift, "IU",
84*69d94f4cSDag-Erling Smørgrav     "Tar filesystem preferred I/O size (log 2)");
85*69d94f4cSDag-Erling Smørgrav 
86*69d94f4cSDag-Erling Smørgrav #ifdef TARFS_DEBUG
87*69d94f4cSDag-Erling Smørgrav int tarfs_debug;
88*69d94f4cSDag-Erling Smørgrav SYSCTL_INT(_vfs_tarfs, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN,
89*69d94f4cSDag-Erling Smørgrav     &tarfs_debug, 0, "Tar filesystem debug mask");
90*69d94f4cSDag-Erling Smørgrav #endif	/* TARFS_DEBUG */
91*69d94f4cSDag-Erling Smørgrav 
92*69d94f4cSDag-Erling Smørgrav static void
93*69d94f4cSDag-Erling Smørgrav tarfs_dump_tree_internal(struct tarfs_node *tnp, int indent)
94*69d94f4cSDag-Erling Smørgrav {
95*69d94f4cSDag-Erling Smørgrav 	struct tarfs_node *current;
96*69d94f4cSDag-Erling Smørgrav 	const char *name;
97*69d94f4cSDag-Erling Smørgrav 
98*69d94f4cSDag-Erling Smørgrav 	if (tnp->type != VDIR)
99*69d94f4cSDag-Erling Smørgrav 		return;
100*69d94f4cSDag-Erling Smørgrav 
101*69d94f4cSDag-Erling Smørgrav 	TAILQ_FOREACH(current, &tnp->dir.dirhead, dirents) {
102*69d94f4cSDag-Erling Smørgrav 		if (current->name == NULL)
103*69d94f4cSDag-Erling Smørgrav 			name = "<<root>>";
104*69d94f4cSDag-Erling Smørgrav 		else
105*69d94f4cSDag-Erling Smørgrav 			name = current->name;
106*69d94f4cSDag-Erling Smørgrav 		printf("%*s%s\n", indent * 4, "", name);
107*69d94f4cSDag-Erling Smørgrav 		if (current->type == VDIR)
108*69d94f4cSDag-Erling Smørgrav 			tarfs_dump_tree_internal(current, indent + 1);
109*69d94f4cSDag-Erling Smørgrav 	}
110*69d94f4cSDag-Erling Smørgrav }
111*69d94f4cSDag-Erling Smørgrav 
112*69d94f4cSDag-Erling Smørgrav void
113*69d94f4cSDag-Erling Smørgrav tarfs_dump_tree(struct tarfs_node *tnp)
114*69d94f4cSDag-Erling Smørgrav {
115*69d94f4cSDag-Erling Smørgrav 	const char *name;
116*69d94f4cSDag-Erling Smørgrav 
117*69d94f4cSDag-Erling Smørgrav 	if (tnp == NULL)
118*69d94f4cSDag-Erling Smørgrav 		return;
119*69d94f4cSDag-Erling Smørgrav 
120*69d94f4cSDag-Erling Smørgrav 	if (tnp->name == NULL)
121*69d94f4cSDag-Erling Smørgrav 		name = "<<root>>";
122*69d94f4cSDag-Erling Smørgrav 	else
123*69d94f4cSDag-Erling Smørgrav 		name = tnp->name;
124*69d94f4cSDag-Erling Smørgrav 	printf("%s\n", name);
125*69d94f4cSDag-Erling Smørgrav 
126*69d94f4cSDag-Erling Smørgrav 	tarfs_dump_tree_internal(tnp, 1);
127*69d94f4cSDag-Erling Smørgrav }
128*69d94f4cSDag-Erling Smørgrav 
129*69d94f4cSDag-Erling Smørgrav void
130*69d94f4cSDag-Erling Smørgrav tarfs_print_node(struct tarfs_node *tnp)
131*69d94f4cSDag-Erling Smørgrav {
132*69d94f4cSDag-Erling Smørgrav 
133*69d94f4cSDag-Erling Smørgrav 	if (tnp == NULL)
134*69d94f4cSDag-Erling Smørgrav 		return;
135*69d94f4cSDag-Erling Smørgrav 
136*69d94f4cSDag-Erling Smørgrav 	printf("%s: node %p\n", __func__, tnp);
137*69d94f4cSDag-Erling Smørgrav 	printf("\tvnode %p\n", tnp->vnode);
138*69d94f4cSDag-Erling Smørgrav 	printf("\ttmp %p\n", tnp->tmp);
139*69d94f4cSDag-Erling Smørgrav 	printf("\ttype %d\n", tnp->type);
140*69d94f4cSDag-Erling Smørgrav 	printf("\tino %lu\n", tnp->ino);
141*69d94f4cSDag-Erling Smørgrav 	printf("\tsize %zu\n", tnp->size);
142*69d94f4cSDag-Erling Smørgrav 	printf("\tname %s\n",
143*69d94f4cSDag-Erling Smørgrav 	    (tnp->name == NULL) ? "<<root>>" : tnp->name);
144*69d94f4cSDag-Erling Smørgrav 	printf("\tnamelen %zu\n", tnp->namelen);
145*69d94f4cSDag-Erling Smørgrav 	printf("\tuid %d\n", tnp->uid);
146*69d94f4cSDag-Erling Smørgrav 	printf("\tgid %d\n", tnp->gid);
147*69d94f4cSDag-Erling Smørgrav 	printf("\tmode o%o\n", tnp->mode);
148*69d94f4cSDag-Erling Smørgrav 	printf("\tflags %u\n", tnp->flags);
149*69d94f4cSDag-Erling Smørgrav 	printf("\tnlink %lu\n", tnp->nlink);
150*69d94f4cSDag-Erling Smørgrav 	printf("\tatime %d\n", (int)tnp->atime.tv_sec);
151*69d94f4cSDag-Erling Smørgrav 	printf("\tmtime %d\n", (int)tnp->mtime.tv_sec);
152*69d94f4cSDag-Erling Smørgrav 	printf("\tctime %d\n", (int)tnp->ctime.tv_sec);
153*69d94f4cSDag-Erling Smørgrav 	printf("\tbirthtime %d\n", (int)tnp->birthtime.tv_sec);
154*69d94f4cSDag-Erling Smørgrav 	printf("\tgen %lu\n", tnp->gen);
155*69d94f4cSDag-Erling Smørgrav 	printf("\tparent %p\n", tnp->parent);
156*69d94f4cSDag-Erling Smørgrav 
157*69d94f4cSDag-Erling Smørgrav 	switch (tnp->type) {
158*69d94f4cSDag-Erling Smørgrav 	case VDIR:
159*69d94f4cSDag-Erling Smørgrav 		printf("\tdir.lastcookie %jd\n",
160*69d94f4cSDag-Erling Smørgrav 		    tnp->dir.lastcookie);
161*69d94f4cSDag-Erling Smørgrav 		printf("\tdir.lastnode %p\n", tnp->dir.lastnode);
162*69d94f4cSDag-Erling Smørgrav 		break;
163*69d94f4cSDag-Erling Smørgrav 	case VBLK:
164*69d94f4cSDag-Erling Smørgrav 	case VCHR:
165*69d94f4cSDag-Erling Smørgrav 		printf("\trdev %lu\n", tnp->rdev);
166*69d94f4cSDag-Erling Smørgrav 		break;
167*69d94f4cSDag-Erling Smørgrav 	default:
168*69d94f4cSDag-Erling Smørgrav 		break;
169*69d94f4cSDag-Erling Smørgrav 	}
170*69d94f4cSDag-Erling Smørgrav }
171*69d94f4cSDag-Erling Smørgrav 
172*69d94f4cSDag-Erling Smørgrav struct tarfs_node *
173*69d94f4cSDag-Erling Smørgrav tarfs_lookup_node(struct tarfs_node *tnp, struct tarfs_node *f,
174*69d94f4cSDag-Erling Smørgrav     struct componentname *cnp)
175*69d94f4cSDag-Erling Smørgrav {
176*69d94f4cSDag-Erling Smørgrav 	boolean_t found;
177*69d94f4cSDag-Erling Smørgrav 	struct tarfs_node *entry;
178*69d94f4cSDag-Erling Smørgrav 
179*69d94f4cSDag-Erling Smørgrav 	TARFS_DPF(LOOKUP, "%s: name: %.*s\n", __func__, (int)cnp->cn_namelen,
180*69d94f4cSDag-Erling Smørgrav 	    cnp->cn_nameptr);
181*69d94f4cSDag-Erling Smørgrav 
182*69d94f4cSDag-Erling Smørgrav 	found = false;
183*69d94f4cSDag-Erling Smørgrav 	TAILQ_FOREACH(entry, &tnp->dir.dirhead, dirents) {
184*69d94f4cSDag-Erling Smørgrav 		if (f != NULL && entry != f)
185*69d94f4cSDag-Erling Smørgrav 			continue;
186*69d94f4cSDag-Erling Smørgrav 
187*69d94f4cSDag-Erling Smørgrav 		if (entry->namelen == cnp->cn_namelen &&
188*69d94f4cSDag-Erling Smørgrav 		    bcmp(entry->name, cnp->cn_nameptr,
189*69d94f4cSDag-Erling Smørgrav 		    entry->namelen) == 0) {
190*69d94f4cSDag-Erling Smørgrav 			found = 1;
191*69d94f4cSDag-Erling Smørgrav 			break;
192*69d94f4cSDag-Erling Smørgrav 		}
193*69d94f4cSDag-Erling Smørgrav 	}
194*69d94f4cSDag-Erling Smørgrav 
195*69d94f4cSDag-Erling Smørgrav 	if (found) {
196*69d94f4cSDag-Erling Smørgrav 		if (entry->type == VREG && entry->other != NULL) {
197*69d94f4cSDag-Erling Smørgrav 			TARFS_DPF_IFF(LOOKUP, "%s: following hard link %p\n",
198*69d94f4cSDag-Erling Smørgrav 			    __func__, entry);
199*69d94f4cSDag-Erling Smørgrav 			entry = entry->other;
200*69d94f4cSDag-Erling Smørgrav 		}
201*69d94f4cSDag-Erling Smørgrav 		TARFS_DPF(LOOKUP, "%s: found tarfs_node %p\n", __func__,
202*69d94f4cSDag-Erling Smørgrav 		    entry);
203*69d94f4cSDag-Erling Smørgrav 		return (entry);
204*69d94f4cSDag-Erling Smørgrav 	}
205*69d94f4cSDag-Erling Smørgrav 
206*69d94f4cSDag-Erling Smørgrav 	TARFS_DPF(LOOKUP, "%s: no match found\n", __func__);
207*69d94f4cSDag-Erling Smørgrav 	return (NULL);
208*69d94f4cSDag-Erling Smørgrav }
209*69d94f4cSDag-Erling Smørgrav 
210*69d94f4cSDag-Erling Smørgrav struct tarfs_node *
211*69d94f4cSDag-Erling Smørgrav tarfs_lookup_dir(struct tarfs_node *tnp, off_t cookie)
212*69d94f4cSDag-Erling Smørgrav {
213*69d94f4cSDag-Erling Smørgrav 	struct tarfs_node *current;
214*69d94f4cSDag-Erling Smørgrav 
215*69d94f4cSDag-Erling Smørgrav 	TARFS_DPF(LOOKUP, "%s: tarfs_node %p, cookie %jd\n", __func__, tnp,
216*69d94f4cSDag-Erling Smørgrav 	    cookie);
217*69d94f4cSDag-Erling Smørgrav 	TARFS_DPF(LOOKUP, "%s: name: %s\n", __func__,
218*69d94f4cSDag-Erling Smørgrav 	    (tnp->name == NULL) ? "<<root>>" : tnp->name);
219*69d94f4cSDag-Erling Smørgrav 
220*69d94f4cSDag-Erling Smørgrav 	if (cookie == tnp->dir.lastcookie &&
221*69d94f4cSDag-Erling Smørgrav 	    tnp->dir.lastnode != NULL) {
222*69d94f4cSDag-Erling Smørgrav 		TARFS_DPF(LOOKUP, "%s: Using cached entry: tarfs_node %p, "
223*69d94f4cSDag-Erling Smørgrav 		    "cookie %jd\n", __func__, tnp->dir.lastnode,
224*69d94f4cSDag-Erling Smørgrav 		    tnp->dir.lastcookie);
225*69d94f4cSDag-Erling Smørgrav 		return (tnp->dir.lastnode);
226*69d94f4cSDag-Erling Smørgrav 	}
227*69d94f4cSDag-Erling Smørgrav 
228*69d94f4cSDag-Erling Smørgrav 	TAILQ_FOREACH(current, &tnp->dir.dirhead, dirents) {
229*69d94f4cSDag-Erling Smørgrav 		TARFS_DPF(LOOKUP, "%s: tarfs_node %p, current %p, ino %lu\n",
230*69d94f4cSDag-Erling Smørgrav 		    __func__, tnp, current, current->ino);
231*69d94f4cSDag-Erling Smørgrav 		TARFS_DPF_IFF(LOOKUP, current->name != NULL,
232*69d94f4cSDag-Erling Smørgrav 		    "%s: name: %s\n", __func__, current->name);
233*69d94f4cSDag-Erling Smørgrav 		if (current->ino == cookie) {
234*69d94f4cSDag-Erling Smørgrav 			TARFS_DPF(LOOKUP, "%s: Found entry: tarfs_node %p, "
235*69d94f4cSDag-Erling Smørgrav 			    "cookie %lu\n", __func__, current,
236*69d94f4cSDag-Erling Smørgrav 			    current->ino);
237*69d94f4cSDag-Erling Smørgrav 			break;
238*69d94f4cSDag-Erling Smørgrav 		}
239*69d94f4cSDag-Erling Smørgrav 	}
240*69d94f4cSDag-Erling Smørgrav 
241*69d94f4cSDag-Erling Smørgrav 	return (current);
242*69d94f4cSDag-Erling Smørgrav }
243*69d94f4cSDag-Erling Smørgrav 
244*69d94f4cSDag-Erling Smørgrav int
245*69d94f4cSDag-Erling Smørgrav tarfs_alloc_node(struct tarfs_mount *tmp, const char *name, size_t namelen,
246*69d94f4cSDag-Erling Smørgrav     enum vtype type, off_t off, size_t sz, time_t mtime, uid_t uid, gid_t gid,
247*69d94f4cSDag-Erling Smørgrav     mode_t mode, unsigned int flags, const char *linkname, dev_t rdev,
248*69d94f4cSDag-Erling Smørgrav     struct tarfs_node *parent, struct tarfs_node **retnode)
249*69d94f4cSDag-Erling Smørgrav {
250*69d94f4cSDag-Erling Smørgrav 	struct tarfs_node *tnp;
251*69d94f4cSDag-Erling Smørgrav 
252*69d94f4cSDag-Erling Smørgrav 	TARFS_DPF(ALLOC, "%s(%.*s)\n", __func__, (int)namelen, name);
253*69d94f4cSDag-Erling Smørgrav 
254*69d94f4cSDag-Erling Smørgrav 	tnp = malloc(sizeof(struct tarfs_node), M_TARFSNODE, M_WAITOK | M_ZERO);
255*69d94f4cSDag-Erling Smørgrav 	mtx_init(&tnp->lock, "tarfs node lock", NULL, MTX_DEF);
256*69d94f4cSDag-Erling Smørgrav 	tnp->gen = arc4random();
257*69d94f4cSDag-Erling Smørgrav 	tnp->tmp = tmp;
258*69d94f4cSDag-Erling Smørgrav 	if (namelen > 0) {
259*69d94f4cSDag-Erling Smørgrav 		tnp->name = malloc(namelen + 1, M_TARFSNAME, M_WAITOK);
260*69d94f4cSDag-Erling Smørgrav 		tnp->namelen = namelen;
261*69d94f4cSDag-Erling Smørgrav 		memcpy(tnp->name, name, namelen);
262*69d94f4cSDag-Erling Smørgrav 		tnp->name[namelen] = '\0';
263*69d94f4cSDag-Erling Smørgrav 	}
264*69d94f4cSDag-Erling Smørgrav 	tnp->type = type;
265*69d94f4cSDag-Erling Smørgrav 	tnp->uid = uid;
266*69d94f4cSDag-Erling Smørgrav 	tnp->gid = gid;
267*69d94f4cSDag-Erling Smørgrav 	tnp->mode = mode;
268*69d94f4cSDag-Erling Smørgrav 	tnp->nlink = 1;
269*69d94f4cSDag-Erling Smørgrav 	vfs_timestamp(&tnp->atime);
270*69d94f4cSDag-Erling Smørgrav 	tnp->mtime.tv_sec = mtime;
271*69d94f4cSDag-Erling Smørgrav 	tnp->birthtime = tnp->atime;
272*69d94f4cSDag-Erling Smørgrav 	tnp->ctime = tnp->mtime;
273*69d94f4cSDag-Erling Smørgrav 	if (parent != NULL) {
274*69d94f4cSDag-Erling Smørgrav 		tnp->ino = alloc_unr(tmp->ino_unr);
275*69d94f4cSDag-Erling Smørgrav 	}
276*69d94f4cSDag-Erling Smørgrav 	tnp->offset = off;
277*69d94f4cSDag-Erling Smørgrav 	tnp->size = tnp->physize = sz;
278*69d94f4cSDag-Erling Smørgrav 	switch (type) {
279*69d94f4cSDag-Erling Smørgrav 	case VDIR:
280*69d94f4cSDag-Erling Smørgrav 		MPASS(parent != tnp);
281*69d94f4cSDag-Erling Smørgrav 		MPASS(parent != NULL || tmp->root == NULL);
282*69d94f4cSDag-Erling Smørgrav 		TAILQ_INIT(&tnp->dir.dirhead);
283*69d94f4cSDag-Erling Smørgrav 		tnp->nlink++;
284*69d94f4cSDag-Erling Smørgrav 		if (parent == NULL) {
285*69d94f4cSDag-Erling Smørgrav 			tnp->ino = TARFS_ROOTINO;
286*69d94f4cSDag-Erling Smørgrav 		}
287*69d94f4cSDag-Erling Smørgrav 		tnp->physize = 0;
288*69d94f4cSDag-Erling Smørgrav 		break;
289*69d94f4cSDag-Erling Smørgrav 	case VLNK:
290*69d94f4cSDag-Erling Smørgrav 		tnp->link.name = malloc(sz + 1, M_TARFSNAME,
291*69d94f4cSDag-Erling Smørgrav 		    M_WAITOK);
292*69d94f4cSDag-Erling Smørgrav 		tnp->link.namelen = sz;
293*69d94f4cSDag-Erling Smørgrav 		memcpy(tnp->link.name, linkname, sz);
294*69d94f4cSDag-Erling Smørgrav 		tnp->link.name[sz] = '\0';
295*69d94f4cSDag-Erling Smørgrav 		break;
296*69d94f4cSDag-Erling Smørgrav 	case VREG:
297*69d94f4cSDag-Erling Smørgrav 		/* create dummy block map */
298*69d94f4cSDag-Erling Smørgrav 		tnp->nblk = 1;
299*69d94f4cSDag-Erling Smørgrav 		tnp->blk = malloc(sizeof(*tnp->blk), M_TARFSBLK, M_WAITOK);
300*69d94f4cSDag-Erling Smørgrav 		tnp->blk[0].i = 0;
301*69d94f4cSDag-Erling Smørgrav 		tnp->blk[0].o = 0;
302*69d94f4cSDag-Erling Smørgrav 		tnp->blk[0].l = tnp->physize;
303*69d94f4cSDag-Erling Smørgrav 		break;
304*69d94f4cSDag-Erling Smørgrav 	case VFIFO:
305*69d94f4cSDag-Erling Smørgrav 		/* Nothing extra to do */
306*69d94f4cSDag-Erling Smørgrav 		break;
307*69d94f4cSDag-Erling Smørgrav 	case VBLK:
308*69d94f4cSDag-Erling Smørgrav 	case VCHR:
309*69d94f4cSDag-Erling Smørgrav 		tnp->rdev = rdev;
310*69d94f4cSDag-Erling Smørgrav 		tnp->physize = 0;
311*69d94f4cSDag-Erling Smørgrav 		break;
312*69d94f4cSDag-Erling Smørgrav 	default:
313*69d94f4cSDag-Erling Smørgrav 		panic("%s: type %d not allowed", __func__, type);
314*69d94f4cSDag-Erling Smørgrav 	}
315*69d94f4cSDag-Erling Smørgrav 	if (parent != NULL) {
316*69d94f4cSDag-Erling Smørgrav 		MPASS(parent->type == VDIR);
317*69d94f4cSDag-Erling Smørgrav 		TARFS_NODE_LOCK(parent);
318*69d94f4cSDag-Erling Smørgrav 		TAILQ_INSERT_TAIL(&parent->dir.dirhead, tnp, dirents);
319*69d94f4cSDag-Erling Smørgrav 		parent->size += sizeof(struct tarfs_node);
320*69d94f4cSDag-Erling Smørgrav 		tnp->parent = parent;
321*69d94f4cSDag-Erling Smørgrav 		if (type == VDIR) {
322*69d94f4cSDag-Erling Smørgrav 			parent->nlink++;
323*69d94f4cSDag-Erling Smørgrav 		}
324*69d94f4cSDag-Erling Smørgrav 		TARFS_NODE_UNLOCK(parent);
325*69d94f4cSDag-Erling Smørgrav 	} else {
326*69d94f4cSDag-Erling Smørgrav 		tnp->parent = tnp;
327*69d94f4cSDag-Erling Smørgrav 	}
328*69d94f4cSDag-Erling Smørgrav 	MPASS(tnp->ino != 0);
329*69d94f4cSDag-Erling Smørgrav 
330*69d94f4cSDag-Erling Smørgrav 	TARFS_ALLNODES_LOCK(tmp);
331*69d94f4cSDag-Erling Smørgrav 	TAILQ_INSERT_TAIL(&tmp->allnodes, tnp, entries);
332*69d94f4cSDag-Erling Smørgrav 	TARFS_ALLNODES_UNLOCK(tmp);
333*69d94f4cSDag-Erling Smørgrav 
334*69d94f4cSDag-Erling Smørgrav 	*retnode = tnp;
335*69d94f4cSDag-Erling Smørgrav 	tmp->nfiles++;
336*69d94f4cSDag-Erling Smørgrav 	return (0);
337*69d94f4cSDag-Erling Smørgrav }
338*69d94f4cSDag-Erling Smørgrav 
339*69d94f4cSDag-Erling Smørgrav #define is09(ch) ((ch) >= '0' && (ch) <= '9')
340*69d94f4cSDag-Erling Smørgrav 
341*69d94f4cSDag-Erling Smørgrav int
342*69d94f4cSDag-Erling Smørgrav tarfs_load_blockmap(struct tarfs_node *tnp, size_t realsize)
343*69d94f4cSDag-Erling Smørgrav {
344*69d94f4cSDag-Erling Smørgrav 	struct tarfs_blk *blk = NULL;
345*69d94f4cSDag-Erling Smørgrav 	char *map = NULL;
346*69d94f4cSDag-Erling Smørgrav 	size_t nmap = 0, nblk = 0;
347*69d94f4cSDag-Erling Smørgrav 	char *p, *q;
348*69d94f4cSDag-Erling Smørgrav 	ssize_t res;
349*69d94f4cSDag-Erling Smørgrav 	unsigned int i;
350*69d94f4cSDag-Erling Smørgrav 	long n;
351*69d94f4cSDag-Erling Smørgrav 
352*69d94f4cSDag-Erling Smørgrav 	/*
353*69d94f4cSDag-Erling Smørgrav 	 * Load the entire map into memory.  We don't know how big it is,
354*69d94f4cSDag-Erling Smørgrav 	 * but as soon as we start reading it we will know how many
355*69d94f4cSDag-Erling Smørgrav 	 * entries it contains, and then we can count newlines.
356*69d94f4cSDag-Erling Smørgrav 	 */
357*69d94f4cSDag-Erling Smørgrav 	do {
358*69d94f4cSDag-Erling Smørgrav 		nmap++;
359*69d94f4cSDag-Erling Smørgrav 		if (tnp->size < nmap * TARFS_BLOCKSIZE) {
360*69d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s: map too large\n", __func__);
361*69d94f4cSDag-Erling Smørgrav 			goto bad;
362*69d94f4cSDag-Erling Smørgrav 		}
363*69d94f4cSDag-Erling Smørgrav 		/* grow the map */
364*69d94f4cSDag-Erling Smørgrav 		map = realloc(map, nmap * TARFS_BLOCKSIZE + 1, M_TARFSBLK,
365*69d94f4cSDag-Erling Smørgrav 		    M_ZERO | M_WAITOK);
366*69d94f4cSDag-Erling Smørgrav 		/* read an additional block */
367*69d94f4cSDag-Erling Smørgrav 		res = tarfs_io_read_buf(tnp->tmp, false,
368*69d94f4cSDag-Erling Smørgrav 		    map + (nmap - 1) * TARFS_BLOCKSIZE,
369*69d94f4cSDag-Erling Smørgrav 		    tnp->offset + (nmap - 1) * TARFS_BLOCKSIZE,
370*69d94f4cSDag-Erling Smørgrav 		    TARFS_BLOCKSIZE);
371*69d94f4cSDag-Erling Smørgrav 		if (res < 0)
372*69d94f4cSDag-Erling Smørgrav 			return (-res);
373*69d94f4cSDag-Erling Smørgrav 		else if (res < TARFS_BLOCKSIZE)
374*69d94f4cSDag-Erling Smørgrav 			return (EIO);
375*69d94f4cSDag-Erling Smørgrav 		map[nmap * TARFS_BLOCKSIZE] = '\0'; /* sentinel */
376*69d94f4cSDag-Erling Smørgrav 		if (nblk == 0) {
377*69d94f4cSDag-Erling Smørgrav 			n = strtol(p = map, &q, 10);
378*69d94f4cSDag-Erling Smørgrav 			if (q == p || *q != '\n' || n < 1)
379*69d94f4cSDag-Erling Smørgrav 				goto syntax;
380*69d94f4cSDag-Erling Smørgrav 			nblk = n;
381*69d94f4cSDag-Erling Smørgrav 		}
382*69d94f4cSDag-Erling Smørgrav 		for (n = 0, p = map; *p != '\0'; ++p) {
383*69d94f4cSDag-Erling Smørgrav 			if (*p == '\n') {
384*69d94f4cSDag-Erling Smørgrav 				++n;
385*69d94f4cSDag-Erling Smørgrav 			}
386*69d94f4cSDag-Erling Smørgrav 		}
387*69d94f4cSDag-Erling Smørgrav 		TARFS_DPF(MAP, "%s: %ld newlines in map\n", __func__, n);
388*69d94f4cSDag-Erling Smørgrav 	} while (n < nblk * 2 + 1);
389*69d94f4cSDag-Erling Smørgrav 	TARFS_DPF(MAP, "%s: block map length %zu\n", __func__, nblk);
390*69d94f4cSDag-Erling Smørgrav 	blk = malloc(sizeof(*blk) * nblk, M_TARFSBLK, M_WAITOK | M_ZERO);
391*69d94f4cSDag-Erling Smørgrav 	p = strchr(map, '\n') + 1;
392*69d94f4cSDag-Erling Smørgrav 	for (i = 0; i < nblk; i++) {
393*69d94f4cSDag-Erling Smørgrav 		if (i == 0)
394*69d94f4cSDag-Erling Smørgrav 			blk[i].i = nmap * TARFS_BLOCKSIZE;
395*69d94f4cSDag-Erling Smørgrav 		else
396*69d94f4cSDag-Erling Smørgrav 			blk[i].i = blk[i - 1].i + blk[i - 1].l;
397*69d94f4cSDag-Erling Smørgrav 		n = strtol(p, &q, 10);
398*69d94f4cSDag-Erling Smørgrav 		if (q == p || *q != '\n' || n < 0)
399*69d94f4cSDag-Erling Smørgrav 			goto syntax;
400*69d94f4cSDag-Erling Smørgrav 		p = q + 1;
401*69d94f4cSDag-Erling Smørgrav 		blk[i].o = n;
402*69d94f4cSDag-Erling Smørgrav 		n = strtol(p, &q, 10);
403*69d94f4cSDag-Erling Smørgrav 		if (q == p || *q != '\n' || n < 0)
404*69d94f4cSDag-Erling Smørgrav 			goto syntax;
405*69d94f4cSDag-Erling Smørgrav 		p = q + 1;
406*69d94f4cSDag-Erling Smørgrav 		blk[i].l = n;
407*69d94f4cSDag-Erling Smørgrav 		TARFS_DPF(MAP, "%s: %3d %12zu %12zu %12zu\n", __func__,
408*69d94f4cSDag-Erling Smørgrav 		    i, blk[i].i, blk[i].o, blk[i].l);
409*69d94f4cSDag-Erling Smørgrav 		/*
410*69d94f4cSDag-Erling Smørgrav 		 * Check block alignment if the block is of non-zero
411*69d94f4cSDag-Erling Smørgrav 		 * length (a zero-length block indicates the end of a
412*69d94f4cSDag-Erling Smørgrav 		 * trailing hole).  Checking i indirectly checks the
413*69d94f4cSDag-Erling Smørgrav 		 * previous block's l.  It's ok for the final block to
414*69d94f4cSDag-Erling Smørgrav 		 * have an uneven length.
415*69d94f4cSDag-Erling Smørgrav 		 */
416*69d94f4cSDag-Erling Smørgrav 		if (blk[i].l == 0) {
417*69d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s: zero-length block\n", __func__);
418*69d94f4cSDag-Erling Smørgrav 		} else if (blk[i].i % TARFS_BLOCKSIZE != 0 ||
419*69d94f4cSDag-Erling Smørgrav 		    blk[i].o % TARFS_BLOCKSIZE != 0) {
420*69d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s: misaligned map entry\n", __func__);
421*69d94f4cSDag-Erling Smørgrav 			goto bad;
422*69d94f4cSDag-Erling Smørgrav 		}
423*69d94f4cSDag-Erling Smørgrav 		/*
424*69d94f4cSDag-Erling Smørgrav 		 * Check that this block starts after the end of the
425*69d94f4cSDag-Erling Smørgrav 		 * previous one.
426*69d94f4cSDag-Erling Smørgrav 		 */
427*69d94f4cSDag-Erling Smørgrav 		if (i > 0 && blk[i].o < blk[i - 1].o + blk[i - 1].l) {
428*69d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s: overlapping map entries\n", __func__);
429*69d94f4cSDag-Erling Smørgrav 			goto bad;
430*69d94f4cSDag-Erling Smørgrav 		}
431*69d94f4cSDag-Erling Smørgrav 		/*
432*69d94f4cSDag-Erling Smørgrav 		 * Check that the block is within the file, both
433*69d94f4cSDag-Erling Smørgrav 		 * physically and logically.
434*69d94f4cSDag-Erling Smørgrav 		 */
435*69d94f4cSDag-Erling Smørgrav 		if (blk[i].i + blk[i].l > tnp->physize ||
436*69d94f4cSDag-Erling Smørgrav 		    blk[i].o + blk[i].l > realsize) {
437*69d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s: map overflow\n", __func__);
438*69d94f4cSDag-Erling Smørgrav 			goto bad;
439*69d94f4cSDag-Erling Smørgrav 		}
440*69d94f4cSDag-Erling Smørgrav 	}
441*69d94f4cSDag-Erling Smørgrav 	free(map, M_TARFSBLK);
442*69d94f4cSDag-Erling Smørgrav 
443*69d94f4cSDag-Erling Smørgrav 	/* store in node */
444*69d94f4cSDag-Erling Smørgrav 	free(tnp->blk, M_TARFSBLK);
445*69d94f4cSDag-Erling Smørgrav 	tnp->nblk = nblk;
446*69d94f4cSDag-Erling Smørgrav 	tnp->blk = blk;
447*69d94f4cSDag-Erling Smørgrav 	tnp->size = realsize;
448*69d94f4cSDag-Erling Smørgrav 	return (0);
449*69d94f4cSDag-Erling Smørgrav syntax:
450*69d94f4cSDag-Erling Smørgrav 	TARFS_DPF(MAP, "%s: syntax error in block map\n", __func__);
451*69d94f4cSDag-Erling Smørgrav bad:
452*69d94f4cSDag-Erling Smørgrav 	free(map, M_TARFSBLK);
453*69d94f4cSDag-Erling Smørgrav 	free(blk, M_TARFSBLK);
454*69d94f4cSDag-Erling Smørgrav 	return (EINVAL);
455*69d94f4cSDag-Erling Smørgrav }
456*69d94f4cSDag-Erling Smørgrav 
457*69d94f4cSDag-Erling Smørgrav void
458*69d94f4cSDag-Erling Smørgrav tarfs_free_node(struct tarfs_node *tnp)
459*69d94f4cSDag-Erling Smørgrav {
460*69d94f4cSDag-Erling Smørgrav 	struct tarfs_mount *tmp;
461*69d94f4cSDag-Erling Smørgrav 
462*69d94f4cSDag-Erling Smørgrav 	MPASS(tnp != NULL);
463*69d94f4cSDag-Erling Smørgrav 	tmp = tnp->tmp;
464*69d94f4cSDag-Erling Smørgrav 
465*69d94f4cSDag-Erling Smørgrav 	switch (tnp->type) {
466*69d94f4cSDag-Erling Smørgrav 	case VLNK:
467*69d94f4cSDag-Erling Smørgrav 		if (tnp->link.name)
468*69d94f4cSDag-Erling Smørgrav 			free(tnp->link.name, M_TARFSNAME);
469*69d94f4cSDag-Erling Smørgrav 		break;
470*69d94f4cSDag-Erling Smørgrav 	default:
471*69d94f4cSDag-Erling Smørgrav 		break;
472*69d94f4cSDag-Erling Smørgrav 	}
473*69d94f4cSDag-Erling Smørgrav 	if (tnp->name != NULL)
474*69d94f4cSDag-Erling Smørgrav 		free(tnp->name, M_TARFSNAME);
475*69d94f4cSDag-Erling Smørgrav 	if (tnp->blk != NULL)
476*69d94f4cSDag-Erling Smørgrav 		free(tnp->blk, M_TARFSBLK);
477*69d94f4cSDag-Erling Smørgrav 	if (tnp->ino >= TARFS_MININO)
478*69d94f4cSDag-Erling Smørgrav 		free_unr(tmp->ino_unr, tnp->ino);
479*69d94f4cSDag-Erling Smørgrav 	free(tnp, M_TARFSNODE);
480*69d94f4cSDag-Erling Smørgrav 	tmp->nfiles--;
481*69d94f4cSDag-Erling Smørgrav }
482*69d94f4cSDag-Erling Smørgrav 
483*69d94f4cSDag-Erling Smørgrav int
484*69d94f4cSDag-Erling Smørgrav tarfs_read_file(struct tarfs_node *tnp, size_t len, struct uio *uiop)
485*69d94f4cSDag-Erling Smørgrav {
486*69d94f4cSDag-Erling Smørgrav 	struct uio auio;
487*69d94f4cSDag-Erling Smørgrav 	size_t resid = len;
488*69d94f4cSDag-Erling Smørgrav 	size_t copylen;
489*69d94f4cSDag-Erling Smørgrav 	unsigned int i;
490*69d94f4cSDag-Erling Smørgrav 	int error;
491*69d94f4cSDag-Erling Smørgrav 
492*69d94f4cSDag-Erling Smørgrav 	TARFS_DPF(VNODE, "%s(%s, %zu, %zu)\n", __func__,
493*69d94f4cSDag-Erling Smørgrav 	    tnp->name, uiop->uio_offset, resid);
494*69d94f4cSDag-Erling Smørgrav 	for (i = 0; i < tnp->nblk && resid > 0; ++i) {
495*69d94f4cSDag-Erling Smørgrav 		if (uiop->uio_offset > tnp->blk[i].o + tnp->blk[i].l) {
496*69d94f4cSDag-Erling Smørgrav 			/* skip this block */
497*69d94f4cSDag-Erling Smørgrav 			continue;
498*69d94f4cSDag-Erling Smørgrav 		}
499*69d94f4cSDag-Erling Smørgrav 		while (resid > 0 &&
500*69d94f4cSDag-Erling Smørgrav 		    uiop->uio_offset < tnp->blk[i].o) {
501*69d94f4cSDag-Erling Smørgrav 			/* move out some zeroes... */
502*69d94f4cSDag-Erling Smørgrav 			copylen = tnp->blk[i].o - uiop->uio_offset;
503*69d94f4cSDag-Erling Smørgrav 			if (copylen > resid)
504*69d94f4cSDag-Erling Smørgrav 				copylen = resid;
505*69d94f4cSDag-Erling Smørgrav 			if (copylen > ZERO_REGION_SIZE)
506*69d94f4cSDag-Erling Smørgrav 				copylen = ZERO_REGION_SIZE;
507*69d94f4cSDag-Erling Smørgrav 			auio = *uiop;
508*69d94f4cSDag-Erling Smørgrav 			auio.uio_offset = 0;
509*69d94f4cSDag-Erling Smørgrav 			auio.uio_resid = copylen;
510*69d94f4cSDag-Erling Smørgrav 			error = uiomove(__DECONST(void *, zero_region),
511*69d94f4cSDag-Erling Smørgrav 			    copylen, &auio);
512*69d94f4cSDag-Erling Smørgrav 			if (error != 0)
513*69d94f4cSDag-Erling Smørgrav 				return (error);
514*69d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s(%s) = zero %zu\n", __func__,
515*69d94f4cSDag-Erling Smørgrav 			    tnp->name, copylen - auio.uio_resid);
516*69d94f4cSDag-Erling Smørgrav 			uiop->uio_offset += copylen - auio.uio_resid;
517*69d94f4cSDag-Erling Smørgrav 			uiop->uio_resid -= copylen - auio.uio_resid;
518*69d94f4cSDag-Erling Smørgrav 			resid -= copylen - auio.uio_resid;
519*69d94f4cSDag-Erling Smørgrav 		}
520*69d94f4cSDag-Erling Smørgrav 		while (resid > 0 &&
521*69d94f4cSDag-Erling Smørgrav 		    uiop->uio_offset < tnp->blk[i].o + tnp->blk[i].l) {
522*69d94f4cSDag-Erling Smørgrav 			/* now actual data */
523*69d94f4cSDag-Erling Smørgrav 			copylen = tnp->blk[i].l;
524*69d94f4cSDag-Erling Smørgrav 			if (copylen > resid)
525*69d94f4cSDag-Erling Smørgrav 				copylen = resid;
526*69d94f4cSDag-Erling Smørgrav 			auio = *uiop;
527*69d94f4cSDag-Erling Smørgrav 			auio.uio_offset = tnp->offset + tnp->blk[i].i +
528*69d94f4cSDag-Erling Smørgrav 			    uiop->uio_offset - tnp->blk[i].o;
529*69d94f4cSDag-Erling Smørgrav 			auio.uio_resid = copylen;
530*69d94f4cSDag-Erling Smørgrav 			error = tarfs_io_read(tnp->tmp, false, &auio);
531*69d94f4cSDag-Erling Smørgrav 			if (error != 0)
532*69d94f4cSDag-Erling Smørgrav 				return (error);
533*69d94f4cSDag-Erling Smørgrav 			TARFS_DPF(MAP, "%s(%s) = data %zu\n", __func__,
534*69d94f4cSDag-Erling Smørgrav 			    tnp->name, copylen - auio.uio_resid);
535*69d94f4cSDag-Erling Smørgrav 			uiop->uio_offset += copylen - auio.uio_resid;
536*69d94f4cSDag-Erling Smørgrav 			uiop->uio_resid -= copylen - auio.uio_resid;
537*69d94f4cSDag-Erling Smørgrav 			resid -= copylen - auio.uio_resid;
538*69d94f4cSDag-Erling Smørgrav 		}
539*69d94f4cSDag-Erling Smørgrav 	}
540*69d94f4cSDag-Erling Smørgrav 	TARFS_DPF(VNODE, "%s(%s) = %zu\n", __func__,
541*69d94f4cSDag-Erling Smørgrav 	    tnp->name, len - resid);
542*69d94f4cSDag-Erling Smørgrav 	return (0);
543*69d94f4cSDag-Erling Smørgrav }
544*69d94f4cSDag-Erling Smørgrav 
545*69d94f4cSDag-Erling Smørgrav /*
546*69d94f4cSDag-Erling Smørgrav  * XXX ugly file flag parser which could easily be a finite state machine
547*69d94f4cSDag-Erling Smørgrav  * driven by a small precomputed table.
548*69d94f4cSDag-Erling Smørgrav  *
549*69d94f4cSDag-Erling Smørgrav  * Note that unlike strtofflags(3), we make no attempt to handle negated
550*69d94f4cSDag-Erling Smørgrav  * flags, since they shouldn't appear in tar files.
551*69d94f4cSDag-Erling Smørgrav  */
552*69d94f4cSDag-Erling Smørgrav static const struct tarfs_flag {
553*69d94f4cSDag-Erling Smørgrav 	const char *name;
554*69d94f4cSDag-Erling Smørgrav 	unsigned int flag;
555*69d94f4cSDag-Erling Smørgrav } tarfs_flags[] = {
556*69d94f4cSDag-Erling Smørgrav 	{ "nodump",	UF_NODUMP },
557*69d94f4cSDag-Erling Smørgrav 	{ "uchg",	UF_IMMUTABLE },
558*69d94f4cSDag-Erling Smørgrav 	{ "uappnd",	UF_APPEND },
559*69d94f4cSDag-Erling Smørgrav 	{ "opaque",	UF_OPAQUE },
560*69d94f4cSDag-Erling Smørgrav 	{ "uunlnk",	UF_NOUNLINK },
561*69d94f4cSDag-Erling Smørgrav 	{ "arch",	SF_ARCHIVED },
562*69d94f4cSDag-Erling Smørgrav 	{ "schg",	SF_IMMUTABLE },
563*69d94f4cSDag-Erling Smørgrav 	{ "sappnd",	SF_APPEND },
564*69d94f4cSDag-Erling Smørgrav 	{ "sunlnk",	SF_NOUNLINK },
565*69d94f4cSDag-Erling Smørgrav 	{ NULL, 0 },
566*69d94f4cSDag-Erling Smørgrav };
567*69d94f4cSDag-Erling Smørgrav 
568*69d94f4cSDag-Erling Smørgrav unsigned int
569*69d94f4cSDag-Erling Smørgrav tarfs_strtofflags(const char *str, char **end)
570*69d94f4cSDag-Erling Smørgrav {
571*69d94f4cSDag-Erling Smørgrav 	const struct tarfs_flag *tf;
572*69d94f4cSDag-Erling Smørgrav 	const char *p, *q;
573*69d94f4cSDag-Erling Smørgrav 	unsigned int ret;
574*69d94f4cSDag-Erling Smørgrav 
575*69d94f4cSDag-Erling Smørgrav 	ret = 0;
576*69d94f4cSDag-Erling Smørgrav 	for (p = q = str; *q != '\0'; p = q + 1) {
577*69d94f4cSDag-Erling Smørgrav 		for (q = p; *q != '\0' && *q != ','; ++q) {
578*69d94f4cSDag-Erling Smørgrav 			if (*q < 'a' || *q > 'z') {
579*69d94f4cSDag-Erling Smørgrav 				goto end;
580*69d94f4cSDag-Erling Smørgrav 			}
581*69d94f4cSDag-Erling Smørgrav 			/* nothing */
582*69d94f4cSDag-Erling Smørgrav 		}
583*69d94f4cSDag-Erling Smørgrav 		for (tf = tarfs_flags; tf->name != NULL; tf++) {
584*69d94f4cSDag-Erling Smørgrav 			if (strncmp(tf->name, p, q - p) == 0 &&
585*69d94f4cSDag-Erling Smørgrav 			    tf->name[q - p] == '\0') {
586*69d94f4cSDag-Erling Smørgrav 				TARFS_DPF(ALLOC, "%s: %.*s = 0x%06x\n", __func__,
587*69d94f4cSDag-Erling Smørgrav 				    (int)(q - p), p, tf->flag);
588*69d94f4cSDag-Erling Smørgrav 				ret |= tf->flag;
589*69d94f4cSDag-Erling Smørgrav 				break;
590*69d94f4cSDag-Erling Smørgrav 			}
591*69d94f4cSDag-Erling Smørgrav 		}
592*69d94f4cSDag-Erling Smørgrav 		if (tf->name == NULL) {
593*69d94f4cSDag-Erling Smørgrav 			TARFS_DPF(ALLOC, "%s: %.*s = 0x??????\n",
594*69d94f4cSDag-Erling Smørgrav 			    __func__, (int)(q - p), p);
595*69d94f4cSDag-Erling Smørgrav 			goto end;
596*69d94f4cSDag-Erling Smørgrav 		}
597*69d94f4cSDag-Erling Smørgrav 	}
598*69d94f4cSDag-Erling Smørgrav end:
599*69d94f4cSDag-Erling Smørgrav 	if (*end != NULL) {
600*69d94f4cSDag-Erling Smørgrav 		*end = __DECONST(char *, q);
601*69d94f4cSDag-Erling Smørgrav 	}
602*69d94f4cSDag-Erling Smørgrav 	return (ret);
603*69d94f4cSDag-Erling Smørgrav }
604