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 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, 8269d94f4cSDag-Erling Smørgrav CTLTYPE_UINT | CTLFLAG_MPSAFE | CTLFLAG_RW | CTLFLAG_TUN, 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; 8869d94f4cSDag-Erling Smørgrav SYSCTL_INT(_vfs_tarfs, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, 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 * 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 * 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 16569d94f4cSDag-Erling Smørgrav tarfs_alloc_node(struct tarfs_mount *tmp, const char *name, size_t namelen, 16669d94f4cSDag-Erling Smørgrav enum 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 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 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) { 387*fd8c98a5SDag-Erling Smørgrav case VREG: 388*fd8c98a5SDag-Erling Smørgrav if (tnp->nlink-- > 1) 389*fd8c98a5SDag-Erling Smørgrav return; 390*fd8c98a5SDag-Erling Smørgrav if (tnp->other != NULL) 391*fd8c98a5SDag-Erling Smørgrav tarfs_free_node(tnp->other); 392*fd8c98a5SDag-Erling Smørgrav break; 393*fd8c98a5SDag-Erling Smørgrav case VDIR: 394*fd8c98a5SDag-Erling Smørgrav if (tnp->nlink-- > 2) 395*fd8c98a5SDag-Erling Smørgrav return; 396*fd8c98a5SDag-Erling Smørgrav if (tnp->parent != NULL && tnp->parent != tnp) 397*fd8c98a5SDag-Erling Smørgrav tarfs_free_node(tnp->parent); 398*fd8c98a5SDag-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); 412*fd8c98a5SDag-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 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 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