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 struct tarfs_node * 93 tarfs_lookup_node(struct tarfs_node *tnp, struct tarfs_node *f, 94 struct componentname *cnp) 95 { 96 boolean_t found; 97 struct tarfs_node *entry; 98 99 TARFS_DPF(LOOKUP, "%s: name: %.*s\n", __func__, (int)cnp->cn_namelen, 100 cnp->cn_nameptr); 101 102 found = false; 103 TAILQ_FOREACH(entry, &tnp->dir.dirhead, dirents) { 104 if (f != NULL && entry != f) 105 continue; 106 107 if (entry->namelen == cnp->cn_namelen && 108 bcmp(entry->name, cnp->cn_nameptr, 109 entry->namelen) == 0) { 110 found = 1; 111 break; 112 } 113 } 114 115 if (found) { 116 if (entry->type == VREG && entry->other != NULL) { 117 TARFS_DPF_IFF(LOOKUP, "%s: following hard link %p\n", 118 __func__, entry); 119 entry = entry->other; 120 } 121 TARFS_DPF(LOOKUP, "%s: found tarfs_node %p\n", __func__, 122 entry); 123 return (entry); 124 } 125 126 TARFS_DPF(LOOKUP, "%s: no match found\n", __func__); 127 return (NULL); 128 } 129 130 struct tarfs_node * 131 tarfs_lookup_dir(struct tarfs_node *tnp, off_t cookie) 132 { 133 struct tarfs_node *current; 134 135 TARFS_DPF(LOOKUP, "%s: tarfs_node %p, cookie %jd\n", __func__, tnp, 136 cookie); 137 TARFS_DPF(LOOKUP, "%s: name: %s\n", __func__, 138 (tnp->name == NULL) ? "<<root>>" : tnp->name); 139 140 if (cookie == tnp->dir.lastcookie && 141 tnp->dir.lastnode != NULL) { 142 TARFS_DPF(LOOKUP, "%s: Using cached entry: tarfs_node %p, " 143 "cookie %jd\n", __func__, tnp->dir.lastnode, 144 tnp->dir.lastcookie); 145 return (tnp->dir.lastnode); 146 } 147 148 TAILQ_FOREACH(current, &tnp->dir.dirhead, dirents) { 149 TARFS_DPF(LOOKUP, "%s: tarfs_node %p, current %p, ino %lu\n", 150 __func__, tnp, current, current->ino); 151 TARFS_DPF_IFF(LOOKUP, current->name != NULL, 152 "%s: name: %s\n", __func__, current->name); 153 if (current->ino == cookie) { 154 TARFS_DPF(LOOKUP, "%s: Found entry: tarfs_node %p, " 155 "cookie %lu\n", __func__, current, 156 current->ino); 157 break; 158 } 159 } 160 161 return (current); 162 } 163 164 int 165 tarfs_alloc_node(struct tarfs_mount *tmp, const char *name, size_t namelen, 166 enum vtype type, off_t off, size_t sz, time_t mtime, uid_t uid, gid_t gid, 167 mode_t mode, unsigned int flags, const char *linkname, dev_t rdev, 168 struct tarfs_node *parent, struct tarfs_node **retnode) 169 { 170 struct tarfs_node *tnp; 171 172 TARFS_DPF(ALLOC, "%s(%.*s)\n", __func__, (int)namelen, name); 173 174 tnp = malloc(sizeof(struct tarfs_node), M_TARFSNODE, M_WAITOK | M_ZERO); 175 mtx_init(&tnp->lock, "tarfs node lock", NULL, MTX_DEF); 176 tnp->gen = arc4random(); 177 tnp->tmp = tmp; 178 if (namelen > 0) { 179 tnp->name = malloc(namelen + 1, M_TARFSNAME, M_WAITOK); 180 tnp->namelen = namelen; 181 memcpy(tnp->name, name, namelen); 182 tnp->name[namelen] = '\0'; 183 } 184 tnp->type = type; 185 tnp->uid = uid; 186 tnp->gid = gid; 187 tnp->mode = mode; 188 tnp->nlink = 1; 189 vfs_timestamp(&tnp->atime); 190 tnp->mtime.tv_sec = mtime; 191 tnp->birthtime = tnp->atime; 192 tnp->ctime = tnp->mtime; 193 if (parent != NULL) { 194 tnp->ino = alloc_unr(tmp->ino_unr); 195 } 196 tnp->offset = off; 197 tnp->size = tnp->physize = sz; 198 switch (type) { 199 case VDIR: 200 MPASS(parent != tnp); 201 MPASS(parent != NULL || tmp->root == NULL); 202 TAILQ_INIT(&tnp->dir.dirhead); 203 tnp->nlink++; 204 if (parent == NULL) { 205 tnp->ino = TARFS_ROOTINO; 206 } 207 tnp->physize = 0; 208 break; 209 case VLNK: 210 tnp->link.name = malloc(sz + 1, M_TARFSNAME, 211 M_WAITOK); 212 tnp->link.namelen = sz; 213 memcpy(tnp->link.name, linkname, sz); 214 tnp->link.name[sz] = '\0'; 215 break; 216 case VREG: 217 /* create dummy block map */ 218 tnp->nblk = 1; 219 tnp->blk = malloc(sizeof(*tnp->blk), M_TARFSBLK, M_WAITOK); 220 tnp->blk[0].i = 0; 221 tnp->blk[0].o = 0; 222 tnp->blk[0].l = tnp->physize; 223 break; 224 case VFIFO: 225 /* Nothing extra to do */ 226 break; 227 case VBLK: 228 case VCHR: 229 tnp->rdev = rdev; 230 tnp->physize = 0; 231 break; 232 default: 233 panic("%s: type %d not allowed", __func__, type); 234 } 235 if (parent != NULL) { 236 MPASS(parent->type == VDIR); 237 TARFS_NODE_LOCK(parent); 238 TAILQ_INSERT_TAIL(&parent->dir.dirhead, tnp, dirents); 239 parent->size += sizeof(struct tarfs_node); 240 tnp->parent = parent; 241 if (type == VDIR) { 242 parent->nlink++; 243 } 244 TARFS_NODE_UNLOCK(parent); 245 } else { 246 tnp->parent = tnp; 247 } 248 MPASS(tnp->ino != 0); 249 250 TARFS_ALLNODES_LOCK(tmp); 251 TAILQ_INSERT_TAIL(&tmp->allnodes, tnp, entries); 252 TARFS_ALLNODES_UNLOCK(tmp); 253 254 *retnode = tnp; 255 tmp->nfiles++; 256 return (0); 257 } 258 259 #define is09(ch) ((ch) >= '0' && (ch) <= '9') 260 261 int 262 tarfs_load_blockmap(struct tarfs_node *tnp, size_t realsize) 263 { 264 struct tarfs_blk *blk = NULL; 265 char *map = NULL; 266 size_t nmap = 0, nblk = 0; 267 char *p, *q; 268 ssize_t res; 269 unsigned int i; 270 long n; 271 272 /* 273 * Load the entire map into memory. We don't know how big it is, 274 * but as soon as we start reading it we will know how many 275 * entries it contains, and then we can count newlines. 276 */ 277 do { 278 nmap++; 279 if (tnp->size < nmap * TARFS_BLOCKSIZE) { 280 TARFS_DPF(MAP, "%s: map too large\n", __func__); 281 goto bad; 282 } 283 /* grow the map */ 284 map = realloc(map, nmap * TARFS_BLOCKSIZE + 1, M_TARFSBLK, 285 M_ZERO | M_WAITOK); 286 /* read an additional block */ 287 res = tarfs_io_read_buf(tnp->tmp, false, 288 map + (nmap - 1) * TARFS_BLOCKSIZE, 289 tnp->offset + (nmap - 1) * TARFS_BLOCKSIZE, 290 TARFS_BLOCKSIZE); 291 if (res < 0) 292 return (-res); 293 else if (res < TARFS_BLOCKSIZE) 294 return (EIO); 295 map[nmap * TARFS_BLOCKSIZE] = '\0'; /* sentinel */ 296 if (nblk == 0) { 297 n = strtol(p = map, &q, 10); 298 if (q == p || *q != '\n' || n < 1) 299 goto syntax; 300 nblk = n; 301 } 302 for (n = 0, p = map; *p != '\0'; ++p) { 303 if (*p == '\n') { 304 ++n; 305 } 306 } 307 TARFS_DPF(MAP, "%s: %ld newlines in map\n", __func__, n); 308 } while (n < nblk * 2 + 1); 309 TARFS_DPF(MAP, "%s: block map length %zu\n", __func__, nblk); 310 blk = malloc(sizeof(*blk) * nblk, M_TARFSBLK, M_WAITOK | M_ZERO); 311 p = strchr(map, '\n') + 1; 312 for (i = 0; i < nblk; i++) { 313 if (i == 0) 314 blk[i].i = nmap * TARFS_BLOCKSIZE; 315 else 316 blk[i].i = blk[i - 1].i + blk[i - 1].l; 317 n = strtol(p, &q, 10); 318 if (q == p || *q != '\n' || n < 0) 319 goto syntax; 320 p = q + 1; 321 blk[i].o = n; 322 n = strtol(p, &q, 10); 323 if (q == p || *q != '\n' || n < 0) 324 goto syntax; 325 p = q + 1; 326 blk[i].l = n; 327 TARFS_DPF(MAP, "%s: %3d %12zu %12zu %12zu\n", __func__, 328 i, blk[i].i, blk[i].o, blk[i].l); 329 /* 330 * Check block alignment if the block is of non-zero 331 * length (a zero-length block indicates the end of a 332 * trailing hole). Checking i indirectly checks the 333 * previous block's l. It's ok for the final block to 334 * have an uneven length. 335 */ 336 if (blk[i].l == 0) { 337 TARFS_DPF(MAP, "%s: zero-length block\n", __func__); 338 } else if (blk[i].i % TARFS_BLOCKSIZE != 0 || 339 blk[i].o % TARFS_BLOCKSIZE != 0) { 340 TARFS_DPF(MAP, "%s: misaligned map entry\n", __func__); 341 goto bad; 342 } 343 /* 344 * Check that this block starts after the end of the 345 * previous one. 346 */ 347 if (i > 0 && blk[i].o < blk[i - 1].o + blk[i - 1].l) { 348 TARFS_DPF(MAP, "%s: overlapping map entries\n", __func__); 349 goto bad; 350 } 351 /* 352 * Check that the block is within the file, both 353 * physically and logically. 354 */ 355 if (blk[i].i + blk[i].l > tnp->physize || 356 blk[i].o + blk[i].l > realsize) { 357 TARFS_DPF(MAP, "%s: map overflow\n", __func__); 358 goto bad; 359 } 360 } 361 free(map, M_TARFSBLK); 362 363 /* store in node */ 364 free(tnp->blk, M_TARFSBLK); 365 tnp->nblk = nblk; 366 tnp->blk = blk; 367 tnp->size = realsize; 368 return (0); 369 syntax: 370 TARFS_DPF(MAP, "%s: syntax error in block map\n", __func__); 371 bad: 372 free(map, M_TARFSBLK); 373 free(blk, M_TARFSBLK); 374 return (EINVAL); 375 } 376 377 void 378 tarfs_free_node(struct tarfs_node *tnp) 379 { 380 struct tarfs_mount *tmp; 381 382 MPASS(tnp != NULL); 383 tmp = tnp->tmp; 384 385 switch (tnp->type) { 386 case VLNK: 387 if (tnp->link.name) 388 free(tnp->link.name, M_TARFSNAME); 389 break; 390 default: 391 break; 392 } 393 if (tnp->name != NULL) 394 free(tnp->name, M_TARFSNAME); 395 if (tnp->blk != NULL) 396 free(tnp->blk, M_TARFSBLK); 397 if (tnp->ino >= TARFS_MININO) 398 free_unr(tmp->ino_unr, tnp->ino); 399 free(tnp, M_TARFSNODE); 400 tmp->nfiles--; 401 } 402 403 int 404 tarfs_read_file(struct tarfs_node *tnp, size_t len, struct uio *uiop) 405 { 406 struct uio auio; 407 size_t resid = len; 408 size_t copylen; 409 unsigned int i; 410 int error; 411 412 TARFS_DPF(VNODE, "%s(%s, %zu, %zu)\n", __func__, 413 tnp->name, uiop->uio_offset, resid); 414 for (i = 0; i < tnp->nblk && resid > 0; ++i) { 415 if (uiop->uio_offset > tnp->blk[i].o + tnp->blk[i].l) { 416 /* skip this block */ 417 continue; 418 } 419 while (resid > 0 && 420 uiop->uio_offset < tnp->blk[i].o) { 421 /* move out some zeroes... */ 422 copylen = tnp->blk[i].o - uiop->uio_offset; 423 if (copylen > resid) 424 copylen = resid; 425 if (copylen > ZERO_REGION_SIZE) 426 copylen = ZERO_REGION_SIZE; 427 auio = *uiop; 428 auio.uio_offset = 0; 429 auio.uio_resid = copylen; 430 error = uiomove(__DECONST(void *, zero_region), 431 copylen, &auio); 432 if (error != 0) 433 return (error); 434 TARFS_DPF(MAP, "%s(%s) = zero %zu\n", __func__, 435 tnp->name, copylen - auio.uio_resid); 436 uiop->uio_offset += copylen - auio.uio_resid; 437 uiop->uio_resid -= copylen - auio.uio_resid; 438 resid -= copylen - auio.uio_resid; 439 } 440 while (resid > 0 && 441 uiop->uio_offset < tnp->blk[i].o + tnp->blk[i].l) { 442 /* now actual data */ 443 copylen = tnp->blk[i].l; 444 if (copylen > resid) 445 copylen = resid; 446 auio = *uiop; 447 auio.uio_offset = tnp->offset + tnp->blk[i].i + 448 uiop->uio_offset - tnp->blk[i].o; 449 auio.uio_resid = copylen; 450 error = tarfs_io_read(tnp->tmp, false, &auio); 451 if (error != 0) 452 return (error); 453 TARFS_DPF(MAP, "%s(%s) = data %zu\n", __func__, 454 tnp->name, copylen - auio.uio_resid); 455 uiop->uio_offset += copylen - auio.uio_resid; 456 uiop->uio_resid -= copylen - auio.uio_resid; 457 resid -= copylen - auio.uio_resid; 458 } 459 } 460 TARFS_DPF(VNODE, "%s(%s) = %zu\n", __func__, 461 tnp->name, len - resid); 462 return (0); 463 } 464 465 /* 466 * XXX ugly file flag parser which could easily be a finite state machine 467 * driven by a small precomputed table. 468 * 469 * Note that unlike strtofflags(3), we make no attempt to handle negated 470 * flags, since they shouldn't appear in tar files. 471 */ 472 static const struct tarfs_flag { 473 const char *name; 474 unsigned int flag; 475 } tarfs_flags[] = { 476 { "nodump", UF_NODUMP }, 477 { "uchg", UF_IMMUTABLE }, 478 { "uappnd", UF_APPEND }, 479 { "opaque", UF_OPAQUE }, 480 { "uunlnk", UF_NOUNLINK }, 481 { "arch", SF_ARCHIVED }, 482 { "schg", SF_IMMUTABLE }, 483 { "sappnd", SF_APPEND }, 484 { "sunlnk", SF_NOUNLINK }, 485 { NULL, 0 }, 486 }; 487 488 unsigned int 489 tarfs_strtofflags(const char *str, char **end) 490 { 491 const struct tarfs_flag *tf; 492 const char *p, *q; 493 unsigned int ret; 494 495 ret = 0; 496 for (p = q = str; *q != '\0'; p = q + 1) { 497 for (q = p; *q != '\0' && *q != ','; ++q) { 498 if (*q < 'a' || *q > 'z') { 499 goto end; 500 } 501 /* nothing */ 502 } 503 for (tf = tarfs_flags; tf->name != NULL; tf++) { 504 if (strncmp(tf->name, p, q - p) == 0 && 505 tf->name[q - p] == '\0') { 506 TARFS_DPF(ALLOC, "%s: %.*s = 0x%06x\n", __func__, 507 (int)(q - p), p, tf->flag); 508 ret |= tf->flag; 509 break; 510 } 511 } 512 if (tf->name == NULL) { 513 TARFS_DPF(ALLOC, "%s: %.*s = 0x??????\n", 514 __func__, (int)(q - p), p); 515 goto end; 516 } 517 } 518 end: 519 if (*end != NULL) { 520 *end = __DECONST(char *, q); 521 } 522 return (ret); 523 } 524