1 /* $NetBSD: tmpfs_subr.c,v 1.42 2007/12/08 19:29:45 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Julio M. Merino Vidal, developed as part of Google's Summer of Code 9 * 2005 program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * Efficient memory file system supporting functions. 42 */ 43 44 #include <sys/cdefs.h> 45 __KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.42 2007/12/08 19:29:45 pooka Exp $"); 46 47 #include <sys/param.h> 48 #include <sys/dirent.h> 49 #include <sys/event.h> 50 #include <sys/malloc.h> 51 #include <sys/mount.h> 52 #include <sys/namei.h> 53 #include <sys/time.h> 54 #include <sys/stat.h> 55 #include <sys/systm.h> 56 #include <sys/swap.h> 57 #include <sys/vnode.h> 58 #include <sys/kauth.h> 59 #include <sys/proc.h> 60 61 #include <uvm/uvm.h> 62 63 #include <miscfs/specfs/specdev.h> 64 #include <fs/tmpfs/tmpfs.h> 65 #include <fs/tmpfs/tmpfs_fifoops.h> 66 #include <fs/tmpfs/tmpfs_specops.h> 67 #include <fs/tmpfs/tmpfs_vnops.h> 68 69 MALLOC_DECLARE(M_TMPFSTMP); 70 71 /* --------------------------------------------------------------------- */ 72 73 /* 74 * Allocates a new node of type 'type' inside the 'tmp' mount point, with 75 * its owner set to 'uid', its group to 'gid' and its mode set to 'mode', 76 * using the credentials of the process 'p'. 77 * 78 * If the node type is set to 'VDIR', then the parent parameter must point 79 * to the parent directory of the node being created. It may only be NULL 80 * while allocating the root node. 81 * 82 * If the node type is set to 'VBLK' or 'VCHR', then the rdev parameter 83 * specifies the device the node represents. 84 * 85 * If the node type is set to 'VLNK', then the parameter target specifies 86 * the file name of the target file for the symbolic link that is being 87 * created. 88 * 89 * Note that new nodes are retrieved from the available list if it has 90 * items or, if it is empty, from the node pool as long as there is enough 91 * space to create them. 92 * 93 * Returns zero on success or an appropriate error code on failure. 94 */ 95 int 96 tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type, 97 uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *parent, 98 char *target, dev_t rdev, struct tmpfs_node **node) 99 { 100 struct tmpfs_node *nnode; 101 102 /* If the root directory of the 'tmp' file system is not yet 103 * allocated, this must be the request to do it. */ 104 KASSERT(IMPLIES(tmp->tm_root == NULL, parent == NULL && type == VDIR)); 105 106 KASSERT(IFF(type == VLNK, target != NULL)); 107 KASSERT(IFF(type == VBLK || type == VCHR, rdev != VNOVAL)); 108 109 KASSERT(uid != VNOVAL && gid != VNOVAL && mode != VNOVAL); 110 111 nnode = NULL; 112 if (LIST_EMPTY(&tmp->tm_nodes_avail)) { 113 KASSERT(tmp->tm_nodes_last <= tmp->tm_nodes_max); 114 if (tmp->tm_nodes_last == tmp->tm_nodes_max) 115 return ENOSPC; 116 117 nnode = 118 (struct tmpfs_node *)TMPFS_POOL_GET(&tmp->tm_node_pool, 0); 119 if (nnode == NULL) 120 return ENOSPC; 121 nnode->tn_id = tmp->tm_nodes_last++; 122 nnode->tn_gen = arc4random(); 123 } else { 124 nnode = LIST_FIRST(&tmp->tm_nodes_avail); 125 LIST_REMOVE(nnode, tn_entries); 126 nnode->tn_gen++; 127 } 128 KASSERT(nnode != NULL); 129 LIST_INSERT_HEAD(&tmp->tm_nodes_used, nnode, tn_entries); 130 131 /* Generic initialization. */ 132 nnode->tn_type = type; 133 nnode->tn_size = 0; 134 nnode->tn_status = 0; 135 nnode->tn_flags = 0; 136 nnode->tn_links = 0; 137 getnanotime(&nnode->tn_atime); 138 nnode->tn_birthtime = nnode->tn_ctime = nnode->tn_mtime = 139 nnode->tn_atime; 140 nnode->tn_uid = uid; 141 nnode->tn_gid = gid; 142 nnode->tn_mode = mode; 143 nnode->tn_lockf = NULL; 144 nnode->tn_vnode = NULL; 145 146 /* Type-specific initialization. */ 147 switch (nnode->tn_type) { 148 case VBLK: 149 case VCHR: 150 nnode->tn_spec.tn_dev.tn_rdev = rdev; 151 break; 152 153 case VDIR: 154 TAILQ_INIT(&nnode->tn_spec.tn_dir.tn_dir); 155 nnode->tn_spec.tn_dir.tn_parent = 156 (parent == NULL) ? nnode : parent; 157 nnode->tn_spec.tn_dir.tn_readdir_lastn = 0; 158 nnode->tn_spec.tn_dir.tn_readdir_lastp = NULL; 159 nnode->tn_links++; 160 nnode->tn_spec.tn_dir.tn_parent->tn_links++; 161 if (parent != NULL) { 162 KASSERT(parent->tn_vnode != NULL); 163 VN_KNOTE(parent->tn_vnode, NOTE_LINK); 164 } 165 break; 166 167 case VFIFO: 168 /* FALLTHROUGH */ 169 case VSOCK: 170 break; 171 172 case VLNK: 173 KASSERT(strlen(target) < MAXPATHLEN); 174 nnode->tn_size = strlen(target); 175 nnode->tn_spec.tn_lnk.tn_link = 176 tmpfs_str_pool_get(&tmp->tm_str_pool, nnode->tn_size, 0); 177 if (nnode->tn_spec.tn_lnk.tn_link == NULL) { 178 nnode->tn_type = VNON; 179 tmpfs_free_node(tmp, nnode); 180 return ENOSPC; 181 } 182 memcpy(nnode->tn_spec.tn_lnk.tn_link, target, nnode->tn_size); 183 break; 184 185 case VREG: 186 nnode->tn_spec.tn_reg.tn_aobj = 187 uao_create(INT32_MAX - PAGE_SIZE, 0); 188 nnode->tn_spec.tn_reg.tn_aobj_pages = 0; 189 break; 190 191 default: 192 KASSERT(0); 193 } 194 195 *node = nnode; 196 return 0; 197 } 198 199 /* --------------------------------------------------------------------- */ 200 201 /* 202 * Destroys the node pointed to by node from the file system 'tmp'. 203 * If the node does not belong to the given mount point, the results are 204 * unpredicted. 205 * 206 * If the node references a directory; no entries are allowed because 207 * their removal could need a recursive algorithm, something forbidden in 208 * kernel space. Furthermore, there is not need to provide such 209 * functionality (recursive removal) because the only primitives offered 210 * to the user are the removal of empty directories and the deletion of 211 * individual files. 212 * 213 * Note that nodes are not really deleted; in fact, when a node has been 214 * allocated, it cannot be deleted during the whole life of the file 215 * system. Instead, they are moved to the available list and remain there 216 * until reused. 217 */ 218 void 219 tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node) 220 { 221 ino_t id; 222 unsigned long gen; 223 size_t pages; 224 225 switch (node->tn_type) { 226 case VNON: 227 /* Do not do anything. VNON is provided to let the 228 * allocation routine clean itself easily by avoiding 229 * duplicating code in it. */ 230 /* FALLTHROUGH */ 231 case VBLK: 232 /* FALLTHROUGH */ 233 case VCHR: 234 /* FALLTHROUGH */ 235 case VDIR: 236 /* FALLTHROUGH */ 237 case VFIFO: 238 /* FALLTHROUGH */ 239 case VSOCK: 240 pages = 0; 241 break; 242 243 case VLNK: 244 tmpfs_str_pool_put(&tmp->tm_str_pool, 245 node->tn_spec.tn_lnk.tn_link, node->tn_size); 246 pages = 0; 247 break; 248 249 case VREG: 250 if (node->tn_spec.tn_reg.tn_aobj != NULL) 251 uao_detach(node->tn_spec.tn_reg.tn_aobj); 252 pages = node->tn_spec.tn_reg.tn_aobj_pages; 253 break; 254 255 default: 256 KASSERT(0); 257 pages = 0; /* Shut up gcc when !DIAGNOSTIC. */ 258 break; 259 } 260 261 tmp->tm_pages_used -= pages; 262 263 LIST_REMOVE(node, tn_entries); 264 id = node->tn_id; 265 gen = node->tn_gen; 266 memset(node, 0, sizeof(struct tmpfs_node)); 267 node->tn_id = id; 268 node->tn_type = VNON; 269 node->tn_gen = gen; 270 LIST_INSERT_HEAD(&tmp->tm_nodes_avail, node, tn_entries); 271 } 272 273 /* --------------------------------------------------------------------- */ 274 275 /* 276 * Allocates a new directory entry for the node node with a name of name. 277 * The new directory entry is returned in *de. 278 * 279 * The link count of node is increased by one to reflect the new object 280 * referencing it. This takes care of notifying kqueue listeners about 281 * this change. 282 * 283 * Returns zero on success or an appropriate error code on failure. 284 */ 285 int 286 tmpfs_alloc_dirent(struct tmpfs_mount *tmp, struct tmpfs_node *node, 287 const char *name, uint16_t len, struct tmpfs_dirent **de) 288 { 289 struct tmpfs_dirent *nde; 290 291 nde = (struct tmpfs_dirent *)TMPFS_POOL_GET(&tmp->tm_dirent_pool, 0); 292 if (nde == NULL) 293 return ENOSPC; 294 295 nde->td_name = tmpfs_str_pool_get(&tmp->tm_str_pool, len, 0); 296 if (nde->td_name == NULL) { 297 TMPFS_POOL_PUT(&tmp->tm_dirent_pool, nde); 298 return ENOSPC; 299 } 300 nde->td_namelen = len; 301 memcpy(nde->td_name, name, len); 302 nde->td_node = node; 303 304 node->tn_links++; 305 if (node->tn_links > 1 && node->tn_vnode != NULL) 306 VN_KNOTE(node->tn_vnode, NOTE_LINK); 307 *de = nde; 308 309 return 0; 310 } 311 312 /* --------------------------------------------------------------------- */ 313 314 /* 315 * Frees a directory entry. It is the caller's responsibility to destroy 316 * the node referenced by it if needed. 317 * 318 * The link count of node is decreased by one to reflect the removal of an 319 * object that referenced it. This only happens if 'node_exists' is true; 320 * otherwise the function will not access the node referred to by the 321 * directory entry, as it may already have been released from the outside. 322 * 323 * Interested parties (kqueue) are notified of the link count change; note 324 * that this can include both the node pointed to by the directory entry 325 * as well as its parent. 326 */ 327 void 328 tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de, 329 bool node_exists) 330 { 331 if (node_exists) { 332 struct tmpfs_node *node; 333 334 node = de->td_node; 335 336 KASSERT(node->tn_links > 0); 337 node->tn_links--; 338 if (node->tn_vnode != NULL) 339 VN_KNOTE(node->tn_vnode, node->tn_links == 0 ? 340 NOTE_DELETE : NOTE_LINK); 341 if (node->tn_type == VDIR) 342 VN_KNOTE(node->tn_spec.tn_dir.tn_parent->tn_vnode, 343 NOTE_LINK); 344 } 345 346 tmpfs_str_pool_put(&tmp->tm_str_pool, de->td_name, de->td_namelen); 347 TMPFS_POOL_PUT(&tmp->tm_dirent_pool, de); 348 } 349 350 /* --------------------------------------------------------------------- */ 351 352 /* 353 * Allocates a new vnode for the node node or returns a new reference to 354 * an existing one if the node had already a vnode referencing it. The 355 * resulting locked vnode is returned in *vpp. 356 * 357 * Returns zero on success or an appropriate error code on failure. 358 */ 359 int 360 tmpfs_alloc_vp(struct mount *mp, struct tmpfs_node *node, struct vnode **vpp) 361 { 362 int error; 363 struct vnode *nvp; 364 struct vnode *vp; 365 366 vp = NULL; 367 368 if (node->tn_vnode != NULL) { 369 vp = node->tn_vnode; 370 vget(vp, LK_EXCLUSIVE | LK_RETRY); 371 error = 0; 372 goto out; 373 } 374 375 /* Get a new vnode and associate it with our node. */ 376 error = getnewvnode(VT_TMPFS, mp, tmpfs_vnodeop_p, &vp); 377 if (error != 0) 378 goto out; 379 KASSERT(vp != NULL); 380 381 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 382 if (error != 0) { 383 vp->v_data = NULL; 384 ungetnewvnode(vp); 385 vp = NULL; 386 goto out; 387 } 388 389 vp->v_data = node; 390 vp->v_type = node->tn_type; 391 392 /* Type-specific initialization. */ 393 switch (node->tn_type) { 394 case VBLK: 395 /* FALLTHROUGH */ 396 case VCHR: 397 vp->v_op = tmpfs_specop_p; 398 nvp = checkalias(vp, node->tn_spec.tn_dev.tn_rdev, mp); 399 if (nvp != NULL) { 400 /* Discard unneeded vnode, but save its inode. */ 401 nvp->v_data = vp->v_data; 402 vp->v_data = NULL; 403 404 /* XXX spec_vnodeops has no locking, so we have to 405 * do it explicitly. */ 406 vp->v_vflag &= ~VV_LOCKSWORK; 407 VOP_UNLOCK(vp, 0); 408 vp->v_op = spec_vnodeop_p; 409 vrele(vp); 410 vgone(vp); 411 412 /* Reinitialize aliased node. */ 413 vp = nvp; 414 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 415 if (error != 0) { 416 vp->v_data = NULL; 417 vp = NULL; 418 goto out; 419 } 420 } 421 break; 422 423 case VDIR: 424 vp->v_vflag |= node->tn_spec.tn_dir.tn_parent == node ? 425 VV_ROOT : 0; 426 break; 427 428 case VFIFO: 429 vp->v_op = tmpfs_fifoop_p; 430 break; 431 432 case VLNK: 433 /* FALLTHROUGH */ 434 case VREG: 435 /* FALLTHROUGH */ 436 case VSOCK: 437 break; 438 439 default: 440 KASSERT(0); 441 } 442 443 uvm_vnp_setsize(vp, node->tn_size); 444 445 error = 0; 446 447 out: 448 *vpp = node->tn_vnode = vp; 449 450 KASSERT(IFF(error == 0, *vpp != NULL && VOP_ISLOCKED(*vpp))); 451 KASSERT(*vpp == node->tn_vnode); 452 453 return error; 454 } 455 456 /* --------------------------------------------------------------------- */ 457 458 /* 459 * Destroys the association between the vnode vp and the node it 460 * references. 461 */ 462 void 463 tmpfs_free_vp(struct vnode *vp) 464 { 465 struct tmpfs_node *node; 466 467 node = VP_TO_TMPFS_NODE(vp); 468 469 node->tn_vnode = NULL; 470 vp->v_data = NULL; 471 } 472 473 /* --------------------------------------------------------------------- */ 474 475 /* 476 * Allocates a new file of type 'type' and adds it to the parent directory 477 * 'dvp'; this addition is done using the component name given in 'cnp'. 478 * The ownership of the new file is automatically assigned based on the 479 * credentials of the caller (through 'cnp'), the group is set based on 480 * the parent directory and the mode is determined from the 'vap' argument. 481 * If successful, *vpp holds a vnode to the newly created file and zero 482 * is returned. Otherwise *vpp is NULL and the function returns an 483 * appropriate error code. 484 */ 485 int 486 tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, 487 struct componentname *cnp, char *target) 488 { 489 int error; 490 struct tmpfs_dirent *de; 491 struct tmpfs_mount *tmp; 492 struct tmpfs_node *dnode; 493 struct tmpfs_node *node; 494 struct tmpfs_node *parent; 495 496 KASSERT(VOP_ISLOCKED(dvp)); 497 KASSERT(cnp->cn_flags & HASBUF); 498 499 tmp = VFS_TO_TMPFS(dvp->v_mount); 500 dnode = VP_TO_TMPFS_DIR(dvp); 501 *vpp = NULL; 502 503 /* If the entry we are creating is a directory, we cannot overflow 504 * the number of links of its parent, because it will get a new 505 * link. */ 506 if (vap->va_type == VDIR) { 507 /* Ensure that we do not overflow the maximum number of links 508 * imposed by the system. */ 509 KASSERT(dnode->tn_links <= LINK_MAX); 510 if (dnode->tn_links == LINK_MAX) { 511 error = EMLINK; 512 goto out; 513 } 514 515 parent = dnode; 516 } else 517 parent = NULL; 518 519 /* Allocate a node that represents the new file. */ 520 error = tmpfs_alloc_node(tmp, vap->va_type, kauth_cred_geteuid(cnp->cn_cred), 521 dnode->tn_gid, vap->va_mode, parent, target, vap->va_rdev, &node); 522 if (error != 0) 523 goto out; 524 525 /* Allocate a directory entry that points to the new file. */ 526 error = tmpfs_alloc_dirent(tmp, node, cnp->cn_nameptr, cnp->cn_namelen, 527 &de); 528 if (error != 0) { 529 tmpfs_free_node(tmp, node); 530 goto out; 531 } 532 533 /* Allocate a vnode for the new file. */ 534 error = tmpfs_alloc_vp(dvp->v_mount, node, vpp); 535 if (error != 0) { 536 tmpfs_free_dirent(tmp, de, true); 537 tmpfs_free_node(tmp, node); 538 goto out; 539 } 540 541 /* Now that all required items are allocated, we can proceed to 542 * insert the new node into the directory, an operation that 543 * cannot fail. */ 544 tmpfs_dir_attach(dvp, de); 545 546 out: 547 if (error != 0 || !(cnp->cn_flags & SAVESTART)) 548 PNBUF_PUT(cnp->cn_pnbuf); 549 vput(dvp); 550 551 KASSERT(!VOP_ISLOCKED(dvp)); 552 KASSERT(IFF(error == 0, *vpp != NULL)); 553 554 return error; 555 } 556 557 /* --------------------------------------------------------------------- */ 558 559 /* 560 * Attaches the directory entry de to the directory represented by vp. 561 * Note that this does not change the link count of the node pointed by 562 * the directory entry, as this is done by tmpfs_alloc_dirent. 563 * 564 * As the "parent" directory changes, interested parties are notified of 565 * a write to it. 566 */ 567 void 568 tmpfs_dir_attach(struct vnode *vp, struct tmpfs_dirent *de) 569 { 570 struct tmpfs_node *dnode; 571 572 dnode = VP_TO_TMPFS_DIR(vp); 573 574 TAILQ_INSERT_TAIL(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries); 575 dnode->tn_size += sizeof(struct tmpfs_dirent); 576 dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ 577 TMPFS_NODE_MODIFIED; 578 uvm_vnp_setsize(vp, dnode->tn_size); 579 580 VN_KNOTE(vp, NOTE_WRITE); 581 } 582 583 /* --------------------------------------------------------------------- */ 584 585 /* 586 * Detaches the directory entry de from the directory represented by vp. 587 * Note that this does not change the link count of the node pointed by 588 * the directory entry, as this is done by tmpfs_free_dirent. 589 * 590 * As the "parent" directory changes, interested parties are notified of 591 * a write to it. 592 */ 593 void 594 tmpfs_dir_detach(struct vnode *vp, struct tmpfs_dirent *de) 595 { 596 struct tmpfs_node *dnode; 597 598 KASSERT(VOP_ISLOCKED(vp)); 599 600 dnode = VP_TO_TMPFS_DIR(vp); 601 602 if (dnode->tn_spec.tn_dir.tn_readdir_lastp == de) { 603 dnode->tn_spec.tn_dir.tn_readdir_lastn = 0; 604 dnode->tn_spec.tn_dir.tn_readdir_lastp = NULL; 605 } 606 607 TAILQ_REMOVE(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries); 608 dnode->tn_size -= sizeof(struct tmpfs_dirent); 609 dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ 610 TMPFS_NODE_MODIFIED; 611 uvm_vnp_setsize(vp, dnode->tn_size); 612 613 VN_KNOTE(vp, NOTE_WRITE); 614 } 615 616 /* --------------------------------------------------------------------- */ 617 618 /* 619 * Looks for a directory entry in the directory represented by node. 620 * 'cnp' describes the name of the entry to look for. Note that the . 621 * and .. components are not allowed as they do not physically exist 622 * within directories. 623 * 624 * Returns a pointer to the entry when found, otherwise NULL. 625 */ 626 struct tmpfs_dirent * 627 tmpfs_dir_lookup(struct tmpfs_node *node, struct componentname *cnp) 628 { 629 bool found; 630 struct tmpfs_dirent *de; 631 632 KASSERT(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.')); 633 KASSERT(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' && 634 cnp->cn_nameptr[1] == '.'))); 635 TMPFS_VALIDATE_DIR(node); 636 637 node->tn_status |= TMPFS_NODE_ACCESSED; 638 639 found = 0; 640 TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) { 641 KASSERT(cnp->cn_namelen < 0xffff); 642 if (de->td_namelen == (uint16_t)cnp->cn_namelen && 643 memcmp(de->td_name, cnp->cn_nameptr, de->td_namelen) == 0) { 644 found = 1; 645 break; 646 } 647 } 648 649 return found ? de : NULL; 650 } 651 652 /* --------------------------------------------------------------------- */ 653 654 /* 655 * Helper function for tmpfs_readdir. Creates a '.' entry for the given 656 * directory and returns it in the uio space. The function returns 0 657 * on success, -1 if there was not enough space in the uio structure to 658 * hold the directory entry or an appropriate error code if another 659 * error happens. 660 */ 661 int 662 tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio) 663 { 664 int error; 665 struct dirent *dentp; 666 667 TMPFS_VALIDATE_DIR(node); 668 KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOT); 669 670 dentp = malloc(sizeof(struct dirent), M_TMPFSTMP, M_WAITOK | M_ZERO); 671 672 dentp->d_fileno = node->tn_id; 673 dentp->d_type = DT_DIR; 674 dentp->d_namlen = 1; 675 dentp->d_name[0] = '.'; 676 dentp->d_name[1] = '\0'; 677 dentp->d_reclen = _DIRENT_SIZE(dentp); 678 679 if (dentp->d_reclen > uio->uio_resid) 680 error = -1; 681 else { 682 error = uiomove(dentp, dentp->d_reclen, uio); 683 if (error == 0) 684 uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT; 685 } 686 687 node->tn_status |= TMPFS_NODE_ACCESSED; 688 689 free(dentp, M_TMPFSTMP); 690 return error; 691 } 692 693 /* --------------------------------------------------------------------- */ 694 695 /* 696 * Helper function for tmpfs_readdir. Creates a '..' entry for the given 697 * directory and returns it in the uio space. The function returns 0 698 * on success, -1 if there was not enough space in the uio structure to 699 * hold the directory entry or an appropriate error code if another 700 * error happens. 701 */ 702 int 703 tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio) 704 { 705 int error; 706 struct dirent *dentp; 707 708 TMPFS_VALIDATE_DIR(node); 709 KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT); 710 711 dentp = malloc(sizeof(struct dirent), M_TMPFSTMP, M_WAITOK | M_ZERO); 712 713 dentp->d_fileno = node->tn_spec.tn_dir.tn_parent->tn_id; 714 dentp->d_type = DT_DIR; 715 dentp->d_namlen = 2; 716 dentp->d_name[0] = '.'; 717 dentp->d_name[1] = '.'; 718 dentp->d_name[2] = '\0'; 719 dentp->d_reclen = _DIRENT_SIZE(dentp); 720 721 if (dentp->d_reclen > uio->uio_resid) 722 error = -1; 723 else { 724 error = uiomove(dentp, dentp->d_reclen, uio); 725 if (error == 0) { 726 struct tmpfs_dirent *de; 727 728 de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir); 729 if (de == NULL) 730 uio->uio_offset = TMPFS_DIRCOOKIE_EOF; 731 else 732 uio->uio_offset = tmpfs_dircookie(de); 733 } 734 } 735 736 node->tn_status |= TMPFS_NODE_ACCESSED; 737 738 free(dentp, M_TMPFSTMP); 739 return error; 740 } 741 742 /* --------------------------------------------------------------------- */ 743 744 /* 745 * Lookup a directory entry by its associated cookie. 746 */ 747 struct tmpfs_dirent * 748 tmpfs_dir_lookupbycookie(struct tmpfs_node *node, off_t cookie) 749 { 750 struct tmpfs_dirent *de; 751 752 if (cookie == node->tn_spec.tn_dir.tn_readdir_lastn && 753 node->tn_spec.tn_dir.tn_readdir_lastp != NULL) { 754 return node->tn_spec.tn_dir.tn_readdir_lastp; 755 } 756 757 TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) { 758 if (tmpfs_dircookie(de) == cookie) { 759 break; 760 } 761 } 762 763 return de; 764 } 765 766 /* --------------------------------------------------------------------- */ 767 768 /* 769 * Helper function for tmpfs_readdir. Returns as much directory entries 770 * as can fit in the uio space. The read starts at uio->uio_offset. 771 * The function returns 0 on success, -1 if there was not enough space 772 * in the uio structure to hold the directory entry or an appropriate 773 * error code if another error happens. 774 */ 775 int 776 tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, off_t *cntp) 777 { 778 int error; 779 off_t startcookie; 780 struct dirent *dentp; 781 struct tmpfs_dirent *de; 782 783 TMPFS_VALIDATE_DIR(node); 784 785 /* Locate the first directory entry we have to return. We have cached 786 * the last readdir in the node, so use those values if appropriate. 787 * Otherwise do a linear scan to find the requested entry. */ 788 startcookie = uio->uio_offset; 789 KASSERT(startcookie != TMPFS_DIRCOOKIE_DOT); 790 KASSERT(startcookie != TMPFS_DIRCOOKIE_DOTDOT); 791 if (startcookie == TMPFS_DIRCOOKIE_EOF) { 792 return 0; 793 } else { 794 de = tmpfs_dir_lookupbycookie(node, startcookie); 795 } 796 if (de == NULL) { 797 return EINVAL; 798 } 799 800 dentp = malloc(sizeof(struct dirent), M_TMPFSTMP, M_WAITOK | M_ZERO); 801 802 /* Read as much entries as possible; i.e., until we reach the end of 803 * the directory or we exhaust uio space. */ 804 do { 805 /* Create a dirent structure representing the current 806 * tmpfs_node and fill it. */ 807 dentp->d_fileno = de->td_node->tn_id; 808 switch (de->td_node->tn_type) { 809 case VBLK: 810 dentp->d_type = DT_BLK; 811 break; 812 813 case VCHR: 814 dentp->d_type = DT_CHR; 815 break; 816 817 case VDIR: 818 dentp->d_type = DT_DIR; 819 break; 820 821 case VFIFO: 822 dentp->d_type = DT_FIFO; 823 break; 824 825 case VLNK: 826 dentp->d_type = DT_LNK; 827 break; 828 829 case VREG: 830 dentp->d_type = DT_REG; 831 break; 832 833 case VSOCK: 834 dentp->d_type = DT_SOCK; 835 break; 836 837 default: 838 KASSERT(0); 839 } 840 dentp->d_namlen = de->td_namelen; 841 KASSERT(de->td_namelen < sizeof(dentp->d_name)); 842 (void)memcpy(dentp->d_name, de->td_name, de->td_namelen); 843 dentp->d_name[de->td_namelen] = '\0'; 844 dentp->d_reclen = _DIRENT_SIZE(dentp); 845 846 /* Stop reading if the directory entry we are treating is 847 * bigger than the amount of data that can be returned. */ 848 if (dentp->d_reclen > uio->uio_resid) { 849 error = -1; 850 break; 851 } 852 853 /* Copy the new dirent structure into the output buffer and 854 * advance pointers. */ 855 error = uiomove(dentp, dentp->d_reclen, uio); 856 857 (*cntp)++; 858 de = TAILQ_NEXT(de, td_entries); 859 } while (error == 0 && uio->uio_resid > 0 && de != NULL); 860 861 /* Update the offset and cache. */ 862 if (de == NULL) { 863 uio->uio_offset = TMPFS_DIRCOOKIE_EOF; 864 node->tn_spec.tn_dir.tn_readdir_lastn = 0; 865 node->tn_spec.tn_dir.tn_readdir_lastp = NULL; 866 } else { 867 node->tn_spec.tn_dir.tn_readdir_lastn = uio->uio_offset = 868 tmpfs_dircookie(de); 869 node->tn_spec.tn_dir.tn_readdir_lastp = de; 870 } 871 872 node->tn_status |= TMPFS_NODE_ACCESSED; 873 874 free(dentp, M_TMPFSTMP); 875 return error; 876 } 877 878 /* --------------------------------------------------------------------- */ 879 880 /* 881 * Resizes the aobj associated to the regular file pointed to by vp to 882 * the size newsize. 'vp' must point to a vnode that represents a regular 883 * file. 'newsize' must be positive. 884 * 885 * If the file is extended, the appropriate kevent is raised. This does 886 * not rise a write event though because resizing is not the same as 887 * writing. 888 * 889 * Returns zero on success or an appropriate error code on failure. 890 */ 891 int 892 tmpfs_reg_resize(struct vnode *vp, off_t newsize) 893 { 894 int error; 895 size_t newpages, oldpages; 896 struct tmpfs_mount *tmp; 897 struct tmpfs_node *node; 898 off_t oldsize; 899 900 KASSERT(vp->v_type == VREG); 901 KASSERT(newsize >= 0); 902 903 node = VP_TO_TMPFS_NODE(vp); 904 tmp = VFS_TO_TMPFS(vp->v_mount); 905 906 /* Convert the old and new sizes to the number of pages needed to 907 * store them. It may happen that we do not need to do anything 908 * because the last allocated page can accommodate the change on 909 * its own. */ 910 oldsize = node->tn_size; 911 oldpages = round_page(oldsize) / PAGE_SIZE; 912 KASSERT(oldpages == node->tn_spec.tn_reg.tn_aobj_pages); 913 newpages = round_page(newsize) / PAGE_SIZE; 914 915 if (newpages > oldpages && 916 newpages - oldpages > TMPFS_PAGES_AVAIL(tmp)) { 917 error = ENOSPC; 918 goto out; 919 } 920 921 if (newsize < oldsize) { 922 int zerolen = MIN(round_page(newsize), node->tn_size) - newsize; 923 924 /* 925 * free "backing store" 926 */ 927 928 if (newpages < oldpages) { 929 struct uvm_object *uobj; 930 931 uobj = node->tn_spec.tn_reg.tn_aobj; 932 933 simple_lock(&uobj->vmobjlock); 934 uao_dropswap_range(uobj, newpages, oldpages); 935 simple_unlock(&uobj->vmobjlock); 936 } 937 938 /* 939 * zero out the truncated part of the last page. 940 */ 941 942 uvm_vnp_zerorange(vp, newsize, zerolen); 943 } 944 945 node->tn_spec.tn_reg.tn_aobj_pages = newpages; 946 node->tn_size = newsize; 947 uvm_vnp_setsize(vp, newsize); 948 949 tmp->tm_pages_used += (newpages - oldpages); 950 951 error = 0; 952 953 if (newsize > oldsize) 954 VN_KNOTE(vp, NOTE_EXTEND); 955 956 out: 957 return error; 958 } 959 960 /* --------------------------------------------------------------------- */ 961 962 /* 963 * Returns information about the number of available memory pages, 964 * including physical and virtual ones. 965 * 966 * If 'total' is true, the value returned is the total amount of memory 967 * pages configured for the system (either in use or free). 968 * If it is FALSE, the value returned is the amount of free memory pages. 969 * 970 * Remember to remove TMPFS_PAGES_RESERVED from the returned value to avoid 971 * excessive memory usage. 972 * 973 */ 974 size_t 975 tmpfs_mem_info(bool total) 976 { 977 size_t size; 978 979 size = 0; 980 size += uvmexp.swpgavail; 981 if (!total) { 982 size -= uvmexp.swpgonly; 983 } 984 size += uvmexp.free; 985 size += uvmexp.filepages; 986 if (size > uvmexp.wired) { 987 size -= uvmexp.wired; 988 } else { 989 size = 0; 990 } 991 992 return size; 993 } 994 995 /* --------------------------------------------------------------------- */ 996 997 /* 998 * Change flags of the given vnode. 999 * Caller should execute tmpfs_update on vp after a successful execution. 1000 * The vnode must be locked on entry and remain locked on exit. 1001 */ 1002 int 1003 tmpfs_chflags(struct vnode *vp, int flags, kauth_cred_t cred, struct lwp *l) 1004 { 1005 int error; 1006 struct tmpfs_node *node; 1007 1008 KASSERT(VOP_ISLOCKED(vp)); 1009 1010 node = VP_TO_TMPFS_NODE(vp); 1011 1012 /* Disallow this operation if the file system is mounted read-only. */ 1013 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1014 return EROFS; 1015 1016 /* XXX: The following comes from UFS code, and can be found in 1017 * several other file systems. Shouldn't this be centralized 1018 * somewhere? */ 1019 if (kauth_cred_geteuid(cred) != node->tn_uid && 1020 (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, 1021 NULL))) 1022 return error; 1023 if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL) == 0) { 1024 /* The super-user is only allowed to change flags if the file 1025 * wasn't protected before and the securelevel is zero. */ 1026 if ((node->tn_flags & (SF_IMMUTABLE | SF_APPEND)) && 1027 kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_CHSYSFLAGS, 1028 0, NULL, NULL, NULL)) 1029 return EPERM; 1030 node->tn_flags = flags; 1031 } else { 1032 /* Regular users can change flags provided they only want to 1033 * change user-specific ones, not those reserved for the 1034 * super-user. */ 1035 if ((node->tn_flags & (SF_IMMUTABLE | SF_APPEND)) || 1036 (flags & UF_SETTABLE) != flags) 1037 return EPERM; 1038 if ((node->tn_flags & SF_SETTABLE) != (flags & SF_SETTABLE)) 1039 return EPERM; 1040 node->tn_flags &= SF_SETTABLE; 1041 node->tn_flags |= (flags & UF_SETTABLE); 1042 } 1043 1044 node->tn_status |= TMPFS_NODE_CHANGED; 1045 VN_KNOTE(vp, NOTE_ATTRIB); 1046 1047 KASSERT(VOP_ISLOCKED(vp)); 1048 1049 return 0; 1050 } 1051 1052 /* --------------------------------------------------------------------- */ 1053 1054 /* 1055 * Change access mode on the given vnode. 1056 * Caller should execute tmpfs_update on vp after a successful execution. 1057 * The vnode must be locked on entry and remain locked on exit. 1058 */ 1059 int 1060 tmpfs_chmod(struct vnode *vp, mode_t mode, kauth_cred_t cred, struct lwp *l) 1061 { 1062 int error, ismember = 0; 1063 struct tmpfs_node *node; 1064 1065 KASSERT(VOP_ISLOCKED(vp)); 1066 1067 node = VP_TO_TMPFS_NODE(vp); 1068 1069 /* Disallow this operation if the file system is mounted read-only. */ 1070 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1071 return EROFS; 1072 1073 /* Immutable or append-only files cannot be modified, either. */ 1074 if (node->tn_flags & (IMMUTABLE | APPEND)) 1075 return EPERM; 1076 1077 /* XXX: The following comes from UFS code, and can be found in 1078 * several other file systems. Shouldn't this be centralized 1079 * somewhere? */ 1080 if (kauth_cred_geteuid(cred) != node->tn_uid && 1081 (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, 1082 NULL))) 1083 return error; 1084 if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL) != 0) { 1085 if (vp->v_type != VDIR && (mode & S_ISTXT)) 1086 return EFTYPE; 1087 1088 if ((kauth_cred_ismember_gid(cred, node->tn_gid, 1089 &ismember) != 0 || !ismember) && (mode & S_ISGID)) 1090 return EPERM; 1091 } 1092 1093 node->tn_mode = (mode & ALLPERMS); 1094 1095 node->tn_status |= TMPFS_NODE_CHANGED; 1096 VN_KNOTE(vp, NOTE_ATTRIB); 1097 1098 KASSERT(VOP_ISLOCKED(vp)); 1099 1100 return 0; 1101 } 1102 1103 /* --------------------------------------------------------------------- */ 1104 1105 /* 1106 * Change ownership of the given vnode. At least one of uid or gid must 1107 * be different than VNOVAL. If one is set to that value, the attribute 1108 * is unchanged. 1109 * Caller should execute tmpfs_update on vp after a successful execution. 1110 * The vnode must be locked on entry and remain locked on exit. 1111 */ 1112 int 1113 tmpfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred, 1114 struct lwp *l) 1115 { 1116 int error, ismember = 0; 1117 struct tmpfs_node *node; 1118 1119 KASSERT(VOP_ISLOCKED(vp)); 1120 1121 node = VP_TO_TMPFS_NODE(vp); 1122 1123 /* Assign default values if they are unknown. */ 1124 KASSERT(uid != VNOVAL || gid != VNOVAL); 1125 if (uid == VNOVAL) 1126 uid = node->tn_uid; 1127 if (gid == VNOVAL) 1128 gid = node->tn_gid; 1129 KASSERT(uid != VNOVAL && gid != VNOVAL); 1130 1131 /* Disallow this operation if the file system is mounted read-only. */ 1132 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1133 return EROFS; 1134 1135 /* Immutable or append-only files cannot be modified, either. */ 1136 if (node->tn_flags & (IMMUTABLE | APPEND)) 1137 return EPERM; 1138 1139 /* XXX: The following comes from UFS code, and can be found in 1140 * several other file systems. Shouldn't this be centralized 1141 * somewhere? */ 1142 if ((kauth_cred_geteuid(cred) != node->tn_uid || uid != node->tn_uid || 1143 (gid != node->tn_gid && !(kauth_cred_getegid(cred) == node->tn_gid || 1144 (kauth_cred_ismember_gid(cred, gid, &ismember) == 0 && ismember)))) && 1145 ((error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, 1146 NULL)) != 0)) 1147 return error; 1148 1149 node->tn_uid = uid; 1150 node->tn_gid = gid; 1151 1152 node->tn_status |= TMPFS_NODE_CHANGED; 1153 VN_KNOTE(vp, NOTE_ATTRIB); 1154 1155 KASSERT(VOP_ISLOCKED(vp)); 1156 1157 return 0; 1158 } 1159 1160 /* --------------------------------------------------------------------- */ 1161 1162 /* 1163 * Change size of the given vnode. 1164 * Caller should execute tmpfs_update on vp after a successful execution. 1165 * The vnode must be locked on entry and remain locked on exit. 1166 */ 1167 int 1168 tmpfs_chsize(struct vnode *vp, u_quad_t size, kauth_cred_t cred, 1169 struct lwp *l) 1170 { 1171 int error; 1172 struct tmpfs_node *node; 1173 1174 KASSERT(VOP_ISLOCKED(vp)); 1175 1176 node = VP_TO_TMPFS_NODE(vp); 1177 1178 /* Decide whether this is a valid operation based on the file type. */ 1179 error = 0; 1180 switch (vp->v_type) { 1181 case VDIR: 1182 return EISDIR; 1183 1184 case VREG: 1185 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1186 return EROFS; 1187 break; 1188 1189 case VBLK: 1190 /* FALLTHROUGH */ 1191 case VCHR: 1192 /* FALLTHROUGH */ 1193 case VFIFO: 1194 /* Allow modifications of special files even if in the file 1195 * system is mounted read-only (we are not modifying the 1196 * files themselves, but the objects they represent). */ 1197 return 0; 1198 1199 default: 1200 /* Anything else is unsupported. */ 1201 return EOPNOTSUPP; 1202 } 1203 1204 /* Immutable or append-only files cannot be modified, either. */ 1205 if (node->tn_flags & (IMMUTABLE | APPEND)) 1206 return EPERM; 1207 1208 error = tmpfs_truncate(vp, size); 1209 /* tmpfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents 1210 * for us, as will update tn_status; no need to do that here. */ 1211 1212 KASSERT(VOP_ISLOCKED(vp)); 1213 1214 return error; 1215 } 1216 1217 /* --------------------------------------------------------------------- */ 1218 1219 /* 1220 * Change access and modification times of the given vnode. 1221 * Caller should execute tmpfs_update on vp after a successful execution. 1222 * The vnode must be locked on entry and remain locked on exit. 1223 */ 1224 int 1225 tmpfs_chtimes(struct vnode *vp, struct timespec *atime, struct timespec *mtime, 1226 int vaflags, kauth_cred_t cred, struct lwp *l) 1227 { 1228 int error; 1229 struct tmpfs_node *node; 1230 1231 KASSERT(VOP_ISLOCKED(vp)); 1232 1233 node = VP_TO_TMPFS_NODE(vp); 1234 1235 /* Disallow this operation if the file system is mounted read-only. */ 1236 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1237 return EROFS; 1238 1239 /* Immutable or append-only files cannot be modified, either. */ 1240 if (node->tn_flags & (IMMUTABLE | APPEND)) 1241 return EPERM; 1242 1243 /* XXX: The following comes from UFS code, and can be found in 1244 * several other file systems. Shouldn't this be centralized 1245 * somewhere? */ 1246 if (kauth_cred_geteuid(cred) != node->tn_uid && 1247 (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, 1248 NULL)) && ((vaflags & VA_UTIMES_NULL) == 0 || 1249 (error = VOP_ACCESS(vp, VWRITE, cred)))) 1250 return error; 1251 1252 if (atime->tv_sec != VNOVAL && atime->tv_nsec != VNOVAL) 1253 node->tn_status |= TMPFS_NODE_ACCESSED; 1254 1255 if (mtime->tv_sec != VNOVAL && mtime->tv_nsec != VNOVAL) 1256 node->tn_status |= TMPFS_NODE_MODIFIED; 1257 1258 tmpfs_update(vp, atime, mtime, 0); 1259 VN_KNOTE(vp, NOTE_ATTRIB); 1260 1261 KASSERT(VOP_ISLOCKED(vp)); 1262 1263 return 0; 1264 } 1265 1266 /* --------------------------------------------------------------------- */ 1267 1268 /* Sync timestamps */ 1269 void 1270 tmpfs_itimes(struct vnode *vp, const struct timespec *acc, 1271 const struct timespec *mod) 1272 { 1273 struct timespec now; 1274 struct tmpfs_node *node; 1275 1276 node = VP_TO_TMPFS_NODE(vp); 1277 1278 if ((node->tn_status & (TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | 1279 TMPFS_NODE_CHANGED)) == 0) 1280 return; 1281 1282 getnanotime(&now); 1283 if (node->tn_status & TMPFS_NODE_ACCESSED) { 1284 if (acc == NULL) 1285 acc = &now; 1286 node->tn_atime = *acc; 1287 } 1288 if (node->tn_status & TMPFS_NODE_MODIFIED) { 1289 if (mod == NULL) 1290 mod = &now; 1291 node->tn_mtime = *mod; 1292 } 1293 if (node->tn_status & TMPFS_NODE_CHANGED) 1294 node->tn_ctime = now; 1295 1296 node->tn_status &= 1297 ~(TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED); 1298 } 1299 1300 /* --------------------------------------------------------------------- */ 1301 1302 void 1303 tmpfs_update(struct vnode *vp, const struct timespec *acc, 1304 const struct timespec *mod, int flags) 1305 { 1306 1307 struct tmpfs_node *node; 1308 1309 KASSERT(VOP_ISLOCKED(vp)); 1310 1311 node = VP_TO_TMPFS_NODE(vp); 1312 1313 #if 0 1314 if (flags & UPDATE_CLOSE) 1315 ; /* XXX Need to do anything special? */ 1316 #endif 1317 1318 tmpfs_itimes(vp, acc, mod); 1319 1320 KASSERT(VOP_ISLOCKED(vp)); 1321 } 1322 1323 /* --------------------------------------------------------------------- */ 1324 1325 int 1326 tmpfs_truncate(struct vnode *vp, off_t length) 1327 { 1328 bool extended; 1329 int error; 1330 struct tmpfs_node *node; 1331 1332 node = VP_TO_TMPFS_NODE(vp); 1333 extended = length > node->tn_size; 1334 1335 if (length < 0) { 1336 error = EINVAL; 1337 goto out; 1338 } 1339 1340 if (node->tn_size == length) { 1341 error = 0; 1342 goto out; 1343 } 1344 1345 error = tmpfs_reg_resize(vp, length); 1346 if (error == 0) 1347 node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; 1348 1349 out: 1350 tmpfs_update(vp, NULL, NULL, 0); 1351 1352 return error; 1353 } 1354