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