1 /* $NetBSD: tmpfs_subr.c,v 1.35 2007/07/09 21:10:50 ad Exp $ */ 2 3 /*- 4 * Copyright (c) 2005 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 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Efficient memory file system supporting functions. 35 */ 36 #include <sys/cdefs.h> 37 __FBSDID("$FreeBSD$"); 38 39 #include <sys/param.h> 40 #include <sys/namei.h> 41 #include <sys/priv.h> 42 #include <sys/proc.h> 43 #include <sys/stat.h> 44 #include <sys/systm.h> 45 #include <sys/sysctl.h> 46 #include <sys/vnode.h> 47 #include <sys/vmmeter.h> 48 49 #include <vm/vm.h> 50 #include <vm/vm_object.h> 51 #include <vm/vm_page.h> 52 #include <vm/vm_pageout.h> 53 #include <vm/vm_pager.h> 54 #include <vm/vm_extern.h> 55 56 #include <fs/tmpfs/tmpfs.h> 57 #include <fs/tmpfs/tmpfs_fifoops.h> 58 #include <fs/tmpfs/tmpfs_vnops.h> 59 60 SYSCTL_NODE(_vfs, OID_AUTO, tmpfs, CTLFLAG_RW, 0, "tmpfs file system"); 61 62 static long tmpfs_pages_reserved = TMPFS_PAGES_MINRESERVED; 63 64 static int 65 sysctl_mem_reserved(SYSCTL_HANDLER_ARGS) 66 { 67 int error; 68 long pages, bytes; 69 70 pages = *(long *)arg1; 71 bytes = pages * PAGE_SIZE; 72 73 error = sysctl_handle_long(oidp, &bytes, 0, req); 74 if (error || !req->newptr) 75 return (error); 76 77 pages = bytes / PAGE_SIZE; 78 if (pages < TMPFS_PAGES_MINRESERVED) 79 return (EINVAL); 80 81 *(long *)arg1 = pages; 82 return (0); 83 } 84 85 SYSCTL_PROC(_vfs_tmpfs, OID_AUTO, memory_reserved, CTLTYPE_LONG|CTLFLAG_RW, 86 &tmpfs_pages_reserved, 0, sysctl_mem_reserved, "L", "reserved memory"); 87 88 size_t 89 tmpfs_mem_avail(void) 90 { 91 vm_ooffset_t avail; 92 93 avail = swap_pager_avail + cnt.v_free_count + cnt.v_cache_count - 94 tmpfs_pages_reserved; 95 if (__predict_false(avail < 0)) 96 avail = 0; 97 return (avail); 98 } 99 100 size_t 101 tmpfs_pages_used(struct tmpfs_mount *tmp) 102 { 103 const size_t node_size = sizeof(struct tmpfs_node) + 104 sizeof(struct tmpfs_dirent); 105 size_t meta_pages; 106 107 meta_pages = howmany((uintmax_t)tmp->tm_nodes_inuse * node_size, 108 PAGE_SIZE); 109 return (meta_pages + tmp->tm_pages_used); 110 } 111 112 static size_t 113 tmpfs_pages_check_avail(struct tmpfs_mount *tmp, size_t req_pages) 114 { 115 if (tmpfs_mem_avail() < req_pages) 116 return (0); 117 118 if (tmp->tm_pages_max != SIZE_MAX && 119 tmp->tm_pages_max < req_pages + tmpfs_pages_used(tmp)) 120 return (0); 121 122 return (1); 123 } 124 125 /* --------------------------------------------------------------------- */ 126 127 /* 128 * Allocates a new node of type 'type' inside the 'tmp' mount point, with 129 * its owner set to 'uid', its group to 'gid' and its mode set to 'mode', 130 * using the credentials of the process 'p'. 131 * 132 * If the node type is set to 'VDIR', then the parent parameter must point 133 * to the parent directory of the node being created. It may only be NULL 134 * while allocating the root node. 135 * 136 * If the node type is set to 'VBLK' or 'VCHR', then the rdev parameter 137 * specifies the device the node represents. 138 * 139 * If the node type is set to 'VLNK', then the parameter target specifies 140 * the file name of the target file for the symbolic link that is being 141 * created. 142 * 143 * Note that new nodes are retrieved from the available list if it has 144 * items or, if it is empty, from the node pool as long as there is enough 145 * space to create them. 146 * 147 * Returns zero on success or an appropriate error code on failure. 148 */ 149 int 150 tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type, 151 uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *parent, 152 char *target, dev_t rdev, struct tmpfs_node **node) 153 { 154 struct tmpfs_node *nnode; 155 156 /* If the root directory of the 'tmp' file system is not yet 157 * allocated, this must be the request to do it. */ 158 MPASS(IMPLIES(tmp->tm_root == NULL, parent == NULL && type == VDIR)); 159 160 MPASS(IFF(type == VLNK, target != NULL)); 161 MPASS(IFF(type == VBLK || type == VCHR, rdev != VNOVAL)); 162 163 if (tmp->tm_nodes_inuse >= tmp->tm_nodes_max) 164 return (ENOSPC); 165 if (tmpfs_pages_check_avail(tmp, 1) == 0) 166 return (ENOSPC); 167 168 nnode = (struct tmpfs_node *)uma_zalloc_arg( 169 tmp->tm_node_pool, tmp, M_WAITOK); 170 171 /* Generic initialization. */ 172 nnode->tn_type = type; 173 vfs_timestamp(&nnode->tn_atime); 174 nnode->tn_birthtime = nnode->tn_ctime = nnode->tn_mtime = 175 nnode->tn_atime; 176 nnode->tn_uid = uid; 177 nnode->tn_gid = gid; 178 nnode->tn_mode = mode; 179 nnode->tn_id = alloc_unr(tmp->tm_ino_unr); 180 181 /* Type-specific initialization. */ 182 switch (nnode->tn_type) { 183 case VBLK: 184 case VCHR: 185 nnode->tn_rdev = rdev; 186 break; 187 188 case VDIR: 189 TAILQ_INIT(&nnode->tn_dir.tn_dirhead); 190 MPASS(parent != nnode); 191 MPASS(IMPLIES(parent == NULL, tmp->tm_root == NULL)); 192 nnode->tn_dir.tn_parent = (parent == NULL) ? nnode : parent; 193 nnode->tn_dir.tn_readdir_lastn = 0; 194 nnode->tn_dir.tn_readdir_lastp = NULL; 195 nnode->tn_links++; 196 TMPFS_NODE_LOCK(nnode->tn_dir.tn_parent); 197 nnode->tn_dir.tn_parent->tn_links++; 198 TMPFS_NODE_UNLOCK(nnode->tn_dir.tn_parent); 199 break; 200 201 case VFIFO: 202 /* FALLTHROUGH */ 203 case VSOCK: 204 break; 205 206 case VLNK: 207 MPASS(strlen(target) < MAXPATHLEN); 208 nnode->tn_size = strlen(target); 209 nnode->tn_link = malloc(nnode->tn_size, M_TMPFSNAME, 210 M_WAITOK); 211 memcpy(nnode->tn_link, target, nnode->tn_size); 212 break; 213 214 case VREG: 215 nnode->tn_reg.tn_aobj = 216 vm_pager_allocate(OBJT_SWAP, NULL, 0, VM_PROT_DEFAULT, 0, 217 NULL /* XXXKIB - tmpfs needs swap reservation */); 218 break; 219 220 default: 221 panic("tmpfs_alloc_node: type %p %d", nnode, (int)nnode->tn_type); 222 } 223 224 TMPFS_LOCK(tmp); 225 LIST_INSERT_HEAD(&tmp->tm_nodes_used, nnode, tn_entries); 226 tmp->tm_nodes_inuse++; 227 TMPFS_UNLOCK(tmp); 228 229 *node = nnode; 230 return 0; 231 } 232 233 /* --------------------------------------------------------------------- */ 234 235 /* 236 * Destroys the node pointed to by node from the file system 'tmp'. 237 * If the node does not belong to the given mount point, the results are 238 * unpredicted. 239 * 240 * If the node references a directory; no entries are allowed because 241 * their removal could need a recursive algorithm, something forbidden in 242 * kernel space. Furthermore, there is not need to provide such 243 * functionality (recursive removal) because the only primitives offered 244 * to the user are the removal of empty directories and the deletion of 245 * individual files. 246 * 247 * Note that nodes are not really deleted; in fact, when a node has been 248 * allocated, it cannot be deleted during the whole life of the file 249 * system. Instead, they are moved to the available list and remain there 250 * until reused. 251 */ 252 void 253 tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node) 254 { 255 vm_object_t uobj; 256 257 #ifdef INVARIANTS 258 TMPFS_NODE_LOCK(node); 259 MPASS(node->tn_vnode == NULL); 260 MPASS((node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0); 261 TMPFS_NODE_UNLOCK(node); 262 #endif 263 264 TMPFS_LOCK(tmp); 265 LIST_REMOVE(node, tn_entries); 266 tmp->tm_nodes_inuse--; 267 TMPFS_UNLOCK(tmp); 268 269 switch (node->tn_type) { 270 case VNON: 271 /* Do not do anything. VNON is provided to let the 272 * allocation routine clean itself easily by avoiding 273 * duplicating code in it. */ 274 /* FALLTHROUGH */ 275 case VBLK: 276 /* FALLTHROUGH */ 277 case VCHR: 278 /* FALLTHROUGH */ 279 case VDIR: 280 /* FALLTHROUGH */ 281 case VFIFO: 282 /* FALLTHROUGH */ 283 case VSOCK: 284 break; 285 286 case VLNK: 287 free(node->tn_link, M_TMPFSNAME); 288 break; 289 290 case VREG: 291 uobj = node->tn_reg.tn_aobj; 292 if (uobj != NULL) { 293 TMPFS_LOCK(tmp); 294 tmp->tm_pages_used -= uobj->size; 295 TMPFS_UNLOCK(tmp); 296 vm_object_deallocate(uobj); 297 } 298 break; 299 300 default: 301 panic("tmpfs_free_node: type %p %d", node, (int)node->tn_type); 302 } 303 304 free_unr(tmp->tm_ino_unr, node->tn_id); 305 uma_zfree(tmp->tm_node_pool, node); 306 } 307 308 /* --------------------------------------------------------------------- */ 309 310 /* 311 * Allocates a new directory entry for the node node with a name of name. 312 * The new directory entry is returned in *de. 313 * 314 * The link count of node is increased by one to reflect the new object 315 * referencing it. 316 * 317 * Returns zero on success or an appropriate error code on failure. 318 */ 319 int 320 tmpfs_alloc_dirent(struct tmpfs_mount *tmp, struct tmpfs_node *node, 321 const char *name, uint16_t len, struct tmpfs_dirent **de) 322 { 323 struct tmpfs_dirent *nde; 324 325 nde = (struct tmpfs_dirent *)uma_zalloc( 326 tmp->tm_dirent_pool, M_WAITOK); 327 nde->td_name = malloc(len, M_TMPFSNAME, M_WAITOK); 328 nde->td_namelen = len; 329 memcpy(nde->td_name, name, len); 330 331 nde->td_node = node; 332 if (node != NULL) 333 node->tn_links++; 334 335 *de = nde; 336 337 return 0; 338 } 339 340 /* --------------------------------------------------------------------- */ 341 342 /* 343 * Frees a directory entry. It is the caller's responsibility to destroy 344 * the node referenced by it if needed. 345 * 346 * The link count of node is decreased by one to reflect the removal of an 347 * object that referenced it. This only happens if 'node_exists' is true; 348 * otherwise the function will not access the node referred to by the 349 * directory entry, as it may already have been released from the outside. 350 */ 351 void 352 tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de, 353 boolean_t node_exists) 354 { 355 if (node_exists) { 356 struct tmpfs_node *node; 357 358 node = de->td_node; 359 if (node != NULL) { 360 MPASS(node->tn_links > 0); 361 node->tn_links--; 362 } 363 } 364 365 free(de->td_name, M_TMPFSNAME); 366 uma_zfree(tmp->tm_dirent_pool, de); 367 } 368 369 /* --------------------------------------------------------------------- */ 370 371 /* 372 * Allocates a new vnode for the node node or returns a new reference to 373 * an existing one if the node had already a vnode referencing it. The 374 * resulting locked vnode is returned in *vpp. 375 * 376 * Returns zero on success or an appropriate error code on failure. 377 */ 378 int 379 tmpfs_alloc_vp(struct mount *mp, struct tmpfs_node *node, int lkflag, 380 struct vnode **vpp) 381 { 382 int error = 0; 383 struct vnode *vp; 384 385 loop: 386 TMPFS_NODE_LOCK(node); 387 if ((vp = node->tn_vnode) != NULL) { 388 MPASS((node->tn_vpstate & TMPFS_VNODE_DOOMED) == 0); 389 VI_LOCK(vp); 390 TMPFS_NODE_UNLOCK(node); 391 error = vget(vp, lkflag | LK_INTERLOCK, curthread); 392 if (error != 0) { 393 vp = NULL; 394 goto out; 395 } 396 397 /* 398 * Make sure the vnode is still there after 399 * getting the interlock to avoid racing a free. 400 */ 401 if (node->tn_vnode == NULL || node->tn_vnode != vp) { 402 vput(vp); 403 goto loop; 404 } 405 406 goto out; 407 } 408 409 if ((node->tn_vpstate & TMPFS_VNODE_DOOMED) || 410 (node->tn_type == VDIR && node->tn_dir.tn_parent == NULL)) { 411 TMPFS_NODE_UNLOCK(node); 412 error = ENOENT; 413 vp = NULL; 414 goto out; 415 } 416 417 /* 418 * otherwise lock the vp list while we call getnewvnode 419 * since that can block. 420 */ 421 if (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) { 422 node->tn_vpstate |= TMPFS_VNODE_WANT; 423 error = msleep((caddr_t) &node->tn_vpstate, 424 TMPFS_NODE_MTX(node), PDROP | PCATCH, 425 "tmpfs_alloc_vp", 0); 426 if (error) 427 return error; 428 429 goto loop; 430 } else 431 node->tn_vpstate |= TMPFS_VNODE_ALLOCATING; 432 433 TMPFS_NODE_UNLOCK(node); 434 435 /* Get a new vnode and associate it with our node. */ 436 error = getnewvnode("tmpfs", mp, &tmpfs_vnodeop_entries, &vp); 437 if (error != 0) 438 goto unlock; 439 MPASS(vp != NULL); 440 441 (void) vn_lock(vp, lkflag | LK_RETRY); 442 443 vp->v_data = node; 444 vp->v_type = node->tn_type; 445 446 /* Type-specific initialization. */ 447 switch (node->tn_type) { 448 case VBLK: 449 /* FALLTHROUGH */ 450 case VCHR: 451 /* FALLTHROUGH */ 452 case VLNK: 453 /* FALLTHROUGH */ 454 case VREG: 455 /* FALLTHROUGH */ 456 case VSOCK: 457 break; 458 case VFIFO: 459 vp->v_op = &tmpfs_fifoop_entries; 460 break; 461 case VDIR: 462 MPASS(node->tn_dir.tn_parent != NULL); 463 if (node->tn_dir.tn_parent == node) 464 vp->v_vflag |= VV_ROOT; 465 break; 466 467 default: 468 panic("tmpfs_alloc_vp: type %p %d", node, (int)node->tn_type); 469 } 470 471 vnode_pager_setsize(vp, node->tn_size); 472 error = insmntque(vp, mp); 473 if (error) 474 vp = NULL; 475 476 unlock: 477 TMPFS_NODE_LOCK(node); 478 479 MPASS(node->tn_vpstate & TMPFS_VNODE_ALLOCATING); 480 node->tn_vpstate &= ~TMPFS_VNODE_ALLOCATING; 481 node->tn_vnode = vp; 482 483 if (node->tn_vpstate & TMPFS_VNODE_WANT) { 484 node->tn_vpstate &= ~TMPFS_VNODE_WANT; 485 TMPFS_NODE_UNLOCK(node); 486 wakeup((caddr_t) &node->tn_vpstate); 487 } else 488 TMPFS_NODE_UNLOCK(node); 489 490 out: 491 *vpp = vp; 492 493 #ifdef INVARIANTS 494 if (error == 0) { 495 MPASS(*vpp != NULL && VOP_ISLOCKED(*vpp)); 496 TMPFS_NODE_LOCK(node); 497 MPASS(*vpp == node->tn_vnode); 498 TMPFS_NODE_UNLOCK(node); 499 } 500 #endif 501 502 return error; 503 } 504 505 /* --------------------------------------------------------------------- */ 506 507 /* 508 * Destroys the association between the vnode vp and the node it 509 * references. 510 */ 511 void 512 tmpfs_free_vp(struct vnode *vp) 513 { 514 struct tmpfs_node *node; 515 516 node = VP_TO_TMPFS_NODE(vp); 517 518 mtx_assert(TMPFS_NODE_MTX(node), MA_OWNED); 519 node->tn_vnode = NULL; 520 vp->v_data = NULL; 521 } 522 523 /* --------------------------------------------------------------------- */ 524 525 /* 526 * Allocates a new file of type 'type' and adds it to the parent directory 527 * 'dvp'; this addition is done using the component name given in 'cnp'. 528 * The ownership of the new file is automatically assigned based on the 529 * credentials of the caller (through 'cnp'), the group is set based on 530 * the parent directory and the mode is determined from the 'vap' argument. 531 * If successful, *vpp holds a vnode to the newly created file and zero 532 * is returned. Otherwise *vpp is NULL and the function returns an 533 * appropriate error code. 534 */ 535 int 536 tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, 537 struct componentname *cnp, char *target) 538 { 539 int error; 540 struct tmpfs_dirent *de; 541 struct tmpfs_mount *tmp; 542 struct tmpfs_node *dnode; 543 struct tmpfs_node *node; 544 struct tmpfs_node *parent; 545 546 MPASS(VOP_ISLOCKED(dvp)); 547 MPASS(cnp->cn_flags & HASBUF); 548 549 tmp = VFS_TO_TMPFS(dvp->v_mount); 550 dnode = VP_TO_TMPFS_DIR(dvp); 551 *vpp = NULL; 552 553 /* If the entry we are creating is a directory, we cannot overflow 554 * the number of links of its parent, because it will get a new 555 * link. */ 556 if (vap->va_type == VDIR) { 557 /* Ensure that we do not overflow the maximum number of links 558 * imposed by the system. */ 559 MPASS(dnode->tn_links <= LINK_MAX); 560 if (dnode->tn_links == LINK_MAX) { 561 error = EMLINK; 562 goto out; 563 } 564 565 parent = dnode; 566 MPASS(parent != NULL); 567 } else 568 parent = NULL; 569 570 /* Allocate a node that represents the new file. */ 571 error = tmpfs_alloc_node(tmp, vap->va_type, cnp->cn_cred->cr_uid, 572 dnode->tn_gid, vap->va_mode, parent, target, vap->va_rdev, &node); 573 if (error != 0) 574 goto out; 575 576 /* Allocate a directory entry that points to the new file. */ 577 error = tmpfs_alloc_dirent(tmp, node, cnp->cn_nameptr, cnp->cn_namelen, 578 &de); 579 if (error != 0) { 580 tmpfs_free_node(tmp, node); 581 goto out; 582 } 583 584 /* Allocate a vnode for the new file. */ 585 error = tmpfs_alloc_vp(dvp->v_mount, node, LK_EXCLUSIVE, vpp); 586 if (error != 0) { 587 tmpfs_free_dirent(tmp, de, TRUE); 588 tmpfs_free_node(tmp, node); 589 goto out; 590 } 591 592 /* Now that all required items are allocated, we can proceed to 593 * insert the new node into the directory, an operation that 594 * cannot fail. */ 595 if (cnp->cn_flags & ISWHITEOUT) 596 tmpfs_dir_whiteout_remove(dvp, cnp); 597 tmpfs_dir_attach(dvp, de); 598 599 out: 600 601 return error; 602 } 603 604 /* --------------------------------------------------------------------- */ 605 606 /* 607 * Attaches the directory entry de to the directory represented by vp. 608 * Note that this does not change the link count of the node pointed by 609 * the directory entry, as this is done by tmpfs_alloc_dirent. 610 */ 611 void 612 tmpfs_dir_attach(struct vnode *vp, struct tmpfs_dirent *de) 613 { 614 struct tmpfs_node *dnode; 615 616 ASSERT_VOP_ELOCKED(vp, __func__); 617 dnode = VP_TO_TMPFS_DIR(vp); 618 TAILQ_INSERT_TAIL(&dnode->tn_dir.tn_dirhead, de, td_entries); 619 dnode->tn_size += sizeof(struct tmpfs_dirent); 620 dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ 621 TMPFS_NODE_MODIFIED; 622 } 623 624 /* --------------------------------------------------------------------- */ 625 626 /* 627 * Detaches the directory entry de from the directory represented by vp. 628 * Note that this does not change the link count of the node pointed by 629 * the directory entry, as this is done by tmpfs_free_dirent. 630 */ 631 void 632 tmpfs_dir_detach(struct vnode *vp, struct tmpfs_dirent *de) 633 { 634 struct tmpfs_node *dnode; 635 636 ASSERT_VOP_ELOCKED(vp, __func__); 637 dnode = VP_TO_TMPFS_DIR(vp); 638 639 if (dnode->tn_dir.tn_readdir_lastp == de) { 640 dnode->tn_dir.tn_readdir_lastn = 0; 641 dnode->tn_dir.tn_readdir_lastp = NULL; 642 } 643 644 TAILQ_REMOVE(&dnode->tn_dir.tn_dirhead, de, td_entries); 645 dnode->tn_size -= sizeof(struct tmpfs_dirent); 646 dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ 647 TMPFS_NODE_MODIFIED; 648 } 649 650 /* --------------------------------------------------------------------- */ 651 652 /* 653 * Looks for a directory entry in the directory represented by node. 654 * 'cnp' describes the name of the entry to look for. Note that the . 655 * and .. components are not allowed as they do not physically exist 656 * within directories. 657 * 658 * Returns a pointer to the entry when found, otherwise NULL. 659 */ 660 struct tmpfs_dirent * 661 tmpfs_dir_lookup(struct tmpfs_node *node, struct tmpfs_node *f, 662 struct componentname *cnp) 663 { 664 boolean_t found; 665 struct tmpfs_dirent *de; 666 667 MPASS(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.')); 668 MPASS(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' && 669 cnp->cn_nameptr[1] == '.'))); 670 TMPFS_VALIDATE_DIR(node); 671 672 found = 0; 673 TAILQ_FOREACH(de, &node->tn_dir.tn_dirhead, td_entries) { 674 if (f != NULL && de->td_node != f) 675 continue; 676 MPASS(cnp->cn_namelen < 0xffff); 677 if (de->td_namelen == (uint16_t)cnp->cn_namelen && 678 bcmp(de->td_name, cnp->cn_nameptr, de->td_namelen) == 0) { 679 found = 1; 680 break; 681 } 682 } 683 node->tn_status |= TMPFS_NODE_ACCESSED; 684 685 return found ? de : NULL; 686 } 687 688 /* --------------------------------------------------------------------- */ 689 690 /* 691 * Helper function for tmpfs_readdir. Creates a '.' entry for the given 692 * directory and returns it in the uio space. The function returns 0 693 * on success, -1 if there was not enough space in the uio structure to 694 * hold the directory entry or an appropriate error code if another 695 * error happens. 696 */ 697 int 698 tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio) 699 { 700 int error; 701 struct dirent dent; 702 703 TMPFS_VALIDATE_DIR(node); 704 MPASS(uio->uio_offset == TMPFS_DIRCOOKIE_DOT); 705 706 dent.d_fileno = node->tn_id; 707 dent.d_type = DT_DIR; 708 dent.d_namlen = 1; 709 dent.d_name[0] = '.'; 710 dent.d_name[1] = '\0'; 711 dent.d_reclen = GENERIC_DIRSIZ(&dent); 712 713 if (dent.d_reclen > uio->uio_resid) 714 error = -1; 715 else { 716 error = uiomove(&dent, dent.d_reclen, uio); 717 if (error == 0) 718 uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT; 719 } 720 721 node->tn_status |= TMPFS_NODE_ACCESSED; 722 723 return error; 724 } 725 726 /* --------------------------------------------------------------------- */ 727 728 /* 729 * Helper function for tmpfs_readdir. Creates a '..' entry for the given 730 * directory and returns it in the uio space. The function returns 0 731 * on success, -1 if there was not enough space in the uio structure to 732 * hold the directory entry or an appropriate error code if another 733 * error happens. 734 */ 735 int 736 tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio) 737 { 738 int error; 739 struct dirent dent; 740 741 TMPFS_VALIDATE_DIR(node); 742 MPASS(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT); 743 744 /* 745 * Return ENOENT if the current node is already removed. 746 */ 747 TMPFS_ASSERT_LOCKED(node); 748 if (node->tn_dir.tn_parent == NULL) { 749 return (ENOENT); 750 } 751 752 TMPFS_NODE_LOCK(node->tn_dir.tn_parent); 753 dent.d_fileno = node->tn_dir.tn_parent->tn_id; 754 TMPFS_NODE_UNLOCK(node->tn_dir.tn_parent); 755 756 dent.d_type = DT_DIR; 757 dent.d_namlen = 2; 758 dent.d_name[0] = '.'; 759 dent.d_name[1] = '.'; 760 dent.d_name[2] = '\0'; 761 dent.d_reclen = GENERIC_DIRSIZ(&dent); 762 763 if (dent.d_reclen > uio->uio_resid) 764 error = -1; 765 else { 766 error = uiomove(&dent, dent.d_reclen, uio); 767 if (error == 0) { 768 struct tmpfs_dirent *de; 769 770 de = TAILQ_FIRST(&node->tn_dir.tn_dirhead); 771 if (de == NULL) 772 uio->uio_offset = TMPFS_DIRCOOKIE_EOF; 773 else 774 uio->uio_offset = tmpfs_dircookie(de); 775 } 776 } 777 778 node->tn_status |= TMPFS_NODE_ACCESSED; 779 780 return error; 781 } 782 783 /* --------------------------------------------------------------------- */ 784 785 /* 786 * Lookup a directory entry by its associated cookie. 787 */ 788 struct tmpfs_dirent * 789 tmpfs_dir_lookupbycookie(struct tmpfs_node *node, off_t cookie) 790 { 791 struct tmpfs_dirent *de; 792 793 if (cookie == node->tn_dir.tn_readdir_lastn && 794 node->tn_dir.tn_readdir_lastp != NULL) { 795 return node->tn_dir.tn_readdir_lastp; 796 } 797 798 TAILQ_FOREACH(de, &node->tn_dir.tn_dirhead, td_entries) { 799 if (tmpfs_dircookie(de) == cookie) { 800 break; 801 } 802 } 803 804 return de; 805 } 806 807 /* --------------------------------------------------------------------- */ 808 809 /* 810 * Helper function for tmpfs_readdir. Returns as much directory entries 811 * as can fit in the uio space. The read starts at uio->uio_offset. 812 * The function returns 0 on success, -1 if there was not enough space 813 * in the uio structure to hold the directory entry or an appropriate 814 * error code if another error happens. 815 */ 816 int 817 tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, off_t *cntp) 818 { 819 int error; 820 off_t startcookie; 821 struct tmpfs_dirent *de; 822 823 TMPFS_VALIDATE_DIR(node); 824 825 /* Locate the first directory entry we have to return. We have cached 826 * the last readdir in the node, so use those values if appropriate. 827 * Otherwise do a linear scan to find the requested entry. */ 828 startcookie = uio->uio_offset; 829 MPASS(startcookie != TMPFS_DIRCOOKIE_DOT); 830 MPASS(startcookie != TMPFS_DIRCOOKIE_DOTDOT); 831 if (startcookie == TMPFS_DIRCOOKIE_EOF) { 832 return 0; 833 } else { 834 de = tmpfs_dir_lookupbycookie(node, startcookie); 835 } 836 if (de == NULL) { 837 return EINVAL; 838 } 839 840 /* Read as much entries as possible; i.e., until we reach the end of 841 * the directory or we exhaust uio space. */ 842 do { 843 struct dirent d; 844 845 /* Create a dirent structure representing the current 846 * tmpfs_node and fill it. */ 847 if (de->td_node == NULL) { 848 d.d_fileno = 1; 849 d.d_type = DT_WHT; 850 } else { 851 d.d_fileno = de->td_node->tn_id; 852 switch (de->td_node->tn_type) { 853 case VBLK: 854 d.d_type = DT_BLK; 855 break; 856 857 case VCHR: 858 d.d_type = DT_CHR; 859 break; 860 861 case VDIR: 862 d.d_type = DT_DIR; 863 break; 864 865 case VFIFO: 866 d.d_type = DT_FIFO; 867 break; 868 869 case VLNK: 870 d.d_type = DT_LNK; 871 break; 872 873 case VREG: 874 d.d_type = DT_REG; 875 break; 876 877 case VSOCK: 878 d.d_type = DT_SOCK; 879 break; 880 881 default: 882 panic("tmpfs_dir_getdents: type %p %d", 883 de->td_node, (int)de->td_node->tn_type); 884 } 885 } 886 d.d_namlen = de->td_namelen; 887 MPASS(de->td_namelen < sizeof(d.d_name)); 888 (void)memcpy(d.d_name, de->td_name, de->td_namelen); 889 d.d_name[de->td_namelen] = '\0'; 890 d.d_reclen = GENERIC_DIRSIZ(&d); 891 892 /* Stop reading if the directory entry we are treating is 893 * bigger than the amount of data that can be returned. */ 894 if (d.d_reclen > uio->uio_resid) { 895 error = -1; 896 break; 897 } 898 899 /* Copy the new dirent structure into the output buffer and 900 * advance pointers. */ 901 error = uiomove(&d, d.d_reclen, uio); 902 if (error == 0) { 903 (*cntp)++; 904 de = TAILQ_NEXT(de, td_entries); 905 } 906 } while (error == 0 && uio->uio_resid > 0 && de != NULL); 907 908 /* Update the offset and cache. */ 909 if (de == NULL) { 910 uio->uio_offset = TMPFS_DIRCOOKIE_EOF; 911 node->tn_dir.tn_readdir_lastn = 0; 912 node->tn_dir.tn_readdir_lastp = NULL; 913 } else { 914 node->tn_dir.tn_readdir_lastn = uio->uio_offset = tmpfs_dircookie(de); 915 node->tn_dir.tn_readdir_lastp = de; 916 } 917 918 node->tn_status |= TMPFS_NODE_ACCESSED; 919 return error; 920 } 921 922 int 923 tmpfs_dir_whiteout_add(struct vnode *dvp, struct componentname *cnp) 924 { 925 struct tmpfs_dirent *de; 926 int error; 927 928 error = tmpfs_alloc_dirent(VFS_TO_TMPFS(dvp->v_mount), NULL, 929 cnp->cn_nameptr, cnp->cn_namelen, &de); 930 if (error != 0) 931 return (error); 932 tmpfs_dir_attach(dvp, de); 933 return (0); 934 } 935 936 void 937 tmpfs_dir_whiteout_remove(struct vnode *dvp, struct componentname *cnp) 938 { 939 struct tmpfs_dirent *de; 940 941 de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp); 942 MPASS(de != NULL && de->td_node == NULL); 943 tmpfs_dir_detach(dvp, de); 944 tmpfs_free_dirent(VFS_TO_TMPFS(dvp->v_mount), de, TRUE); 945 } 946 947 /* --------------------------------------------------------------------- */ 948 949 /* 950 * Resizes the aobj associated with the regular file pointed to by 'vp' to the 951 * size 'newsize'. 'vp' must point to a vnode that represents a regular file. 952 * 'newsize' must be positive. 953 * 954 * Returns zero on success or an appropriate error code on failure. 955 */ 956 int 957 tmpfs_reg_resize(struct vnode *vp, off_t newsize, boolean_t ignerr) 958 { 959 struct tmpfs_mount *tmp; 960 struct tmpfs_node *node; 961 vm_object_t uobj; 962 vm_page_t m, ma[1]; 963 vm_pindex_t idx, newpages, oldpages; 964 off_t oldsize; 965 int base, rv; 966 967 MPASS(vp->v_type == VREG); 968 MPASS(newsize >= 0); 969 970 node = VP_TO_TMPFS_NODE(vp); 971 uobj = node->tn_reg.tn_aobj; 972 tmp = VFS_TO_TMPFS(vp->v_mount); 973 974 /* 975 * Convert the old and new sizes to the number of pages needed to 976 * store them. It may happen that we do not need to do anything 977 * because the last allocated page can accommodate the change on 978 * its own. 979 */ 980 oldsize = node->tn_size; 981 oldpages = OFF_TO_IDX(oldsize + PAGE_MASK); 982 MPASS(oldpages == uobj->size); 983 newpages = OFF_TO_IDX(newsize + PAGE_MASK); 984 if (newpages > oldpages && 985 tmpfs_pages_check_avail(tmp, newpages - oldpages) == 0) 986 return (ENOSPC); 987 988 VM_OBJECT_LOCK(uobj); 989 if (newsize < oldsize) { 990 /* 991 * Zero the truncated part of the last page. 992 */ 993 base = newsize & PAGE_MASK; 994 if (base != 0) { 995 idx = OFF_TO_IDX(newsize); 996 retry: 997 m = vm_page_lookup(uobj, idx); 998 if (m != NULL) { 999 if ((m->oflags & VPO_BUSY) != 0 || 1000 m->busy != 0) { 1001 vm_page_sleep(m, "tmfssz"); 1002 goto retry; 1003 } 1004 MPASS(m->valid == VM_PAGE_BITS_ALL); 1005 } else if (vm_pager_has_page(uobj, idx, NULL, NULL)) { 1006 m = vm_page_alloc(uobj, idx, VM_ALLOC_NORMAL); 1007 if (m == NULL) { 1008 VM_OBJECT_UNLOCK(uobj); 1009 VM_WAIT; 1010 VM_OBJECT_LOCK(uobj); 1011 goto retry; 1012 } else if (m->valid != VM_PAGE_BITS_ALL) { 1013 ma[0] = m; 1014 rv = vm_pager_get_pages(uobj, ma, 1, 0); 1015 m = vm_page_lookup(uobj, idx); 1016 } else 1017 /* A cached page was reactivated. */ 1018 rv = VM_PAGER_OK; 1019 vm_page_lock(m); 1020 if (rv == VM_PAGER_OK) { 1021 vm_page_deactivate(m); 1022 vm_page_unlock(m); 1023 vm_page_wakeup(m); 1024 } else { 1025 vm_page_free(m); 1026 vm_page_unlock(m); 1027 if (ignerr) 1028 m = NULL; 1029 else { 1030 VM_OBJECT_UNLOCK(uobj); 1031 return (EIO); 1032 } 1033 } 1034 } 1035 if (m != NULL) { 1036 pmap_zero_page_area(m, base, PAGE_SIZE - base); 1037 vm_page_dirty(m); 1038 vm_pager_page_unswapped(m); 1039 } 1040 } 1041 1042 /* 1043 * Release any swap space and free any whole pages. 1044 */ 1045 if (newpages < oldpages) { 1046 swap_pager_freespace(uobj, newpages, oldpages - 1047 newpages); 1048 vm_object_page_remove(uobj, newpages, 0, 0); 1049 } 1050 } 1051 uobj->size = newpages; 1052 VM_OBJECT_UNLOCK(uobj); 1053 1054 TMPFS_LOCK(tmp); 1055 tmp->tm_pages_used += (newpages - oldpages); 1056 TMPFS_UNLOCK(tmp); 1057 1058 node->tn_size = newsize; 1059 vnode_pager_setsize(vp, newsize); 1060 return (0); 1061 } 1062 1063 /* --------------------------------------------------------------------- */ 1064 1065 /* 1066 * Change flags of the given vnode. 1067 * Caller should execute tmpfs_update on vp after a successful execution. 1068 * The vnode must be locked on entry and remain locked on exit. 1069 */ 1070 int 1071 tmpfs_chflags(struct vnode *vp, int flags, struct ucred *cred, struct thread *p) 1072 { 1073 int error; 1074 struct tmpfs_node *node; 1075 1076 MPASS(VOP_ISLOCKED(vp)); 1077 1078 node = VP_TO_TMPFS_NODE(vp); 1079 1080 /* Disallow this operation if the file system is mounted read-only. */ 1081 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1082 return EROFS; 1083 1084 /* 1085 * Callers may only modify the file flags on objects they 1086 * have VADMIN rights for. 1087 */ 1088 if ((error = VOP_ACCESS(vp, VADMIN, cred, p))) 1089 return (error); 1090 /* 1091 * Unprivileged processes are not permitted to unset system 1092 * flags, or modify flags if any system flags are set. 1093 */ 1094 if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) { 1095 if (node->tn_flags 1096 & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) { 1097 error = securelevel_gt(cred, 0); 1098 if (error) 1099 return (error); 1100 } 1101 /* Snapshot flag cannot be set or cleared */ 1102 if (((flags & SF_SNAPSHOT) != 0 && 1103 (node->tn_flags & SF_SNAPSHOT) == 0) || 1104 ((flags & SF_SNAPSHOT) == 0 && 1105 (node->tn_flags & SF_SNAPSHOT) != 0)) 1106 return (EPERM); 1107 node->tn_flags = flags; 1108 } else { 1109 if (node->tn_flags 1110 & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) || 1111 (flags & UF_SETTABLE) != flags) 1112 return (EPERM); 1113 node->tn_flags &= SF_SETTABLE; 1114 node->tn_flags |= (flags & UF_SETTABLE); 1115 } 1116 node->tn_status |= TMPFS_NODE_CHANGED; 1117 1118 MPASS(VOP_ISLOCKED(vp)); 1119 1120 return 0; 1121 } 1122 1123 /* --------------------------------------------------------------------- */ 1124 1125 /* 1126 * Change access mode on the given vnode. 1127 * Caller should execute tmpfs_update on vp after a successful execution. 1128 * The vnode must be locked on entry and remain locked on exit. 1129 */ 1130 int 1131 tmpfs_chmod(struct vnode *vp, mode_t mode, struct ucred *cred, struct thread *p) 1132 { 1133 int error; 1134 struct tmpfs_node *node; 1135 1136 MPASS(VOP_ISLOCKED(vp)); 1137 1138 node = VP_TO_TMPFS_NODE(vp); 1139 1140 /* Disallow this operation if the file system is mounted read-only. */ 1141 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1142 return EROFS; 1143 1144 /* Immutable or append-only files cannot be modified, either. */ 1145 if (node->tn_flags & (IMMUTABLE | APPEND)) 1146 return EPERM; 1147 1148 /* 1149 * To modify the permissions on a file, must possess VADMIN 1150 * for that file. 1151 */ 1152 if ((error = VOP_ACCESS(vp, VADMIN, cred, p))) 1153 return (error); 1154 1155 /* 1156 * Privileged processes may set the sticky bit on non-directories, 1157 * as well as set the setgid bit on a file with a group that the 1158 * process is not a member of. 1159 */ 1160 if (vp->v_type != VDIR && (mode & S_ISTXT)) { 1161 if (priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0)) 1162 return (EFTYPE); 1163 } 1164 if (!groupmember(node->tn_gid, cred) && (mode & S_ISGID)) { 1165 error = priv_check_cred(cred, PRIV_VFS_SETGID, 0); 1166 if (error) 1167 return (error); 1168 } 1169 1170 1171 node->tn_mode &= ~ALLPERMS; 1172 node->tn_mode |= mode & ALLPERMS; 1173 1174 node->tn_status |= TMPFS_NODE_CHANGED; 1175 1176 MPASS(VOP_ISLOCKED(vp)); 1177 1178 return 0; 1179 } 1180 1181 /* --------------------------------------------------------------------- */ 1182 1183 /* 1184 * Change ownership of the given vnode. At least one of uid or gid must 1185 * be different than VNOVAL. If one is set to that value, the attribute 1186 * is unchanged. 1187 * Caller should execute tmpfs_update on vp after a successful execution. 1188 * The vnode must be locked on entry and remain locked on exit. 1189 */ 1190 int 1191 tmpfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, 1192 struct thread *p) 1193 { 1194 int error; 1195 struct tmpfs_node *node; 1196 uid_t ouid; 1197 gid_t ogid; 1198 1199 MPASS(VOP_ISLOCKED(vp)); 1200 1201 node = VP_TO_TMPFS_NODE(vp); 1202 1203 /* Assign default values if they are unknown. */ 1204 MPASS(uid != VNOVAL || gid != VNOVAL); 1205 if (uid == VNOVAL) 1206 uid = node->tn_uid; 1207 if (gid == VNOVAL) 1208 gid = node->tn_gid; 1209 MPASS(uid != VNOVAL && gid != VNOVAL); 1210 1211 /* Disallow this operation if the file system is mounted read-only. */ 1212 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1213 return EROFS; 1214 1215 /* Immutable or append-only files cannot be modified, either. */ 1216 if (node->tn_flags & (IMMUTABLE | APPEND)) 1217 return EPERM; 1218 1219 /* 1220 * To modify the ownership of a file, must possess VADMIN for that 1221 * file. 1222 */ 1223 if ((error = VOP_ACCESS(vp, VADMIN, cred, p))) 1224 return (error); 1225 1226 /* 1227 * To change the owner of a file, or change the group of a file to a 1228 * group of which we are not a member, the caller must have 1229 * privilege. 1230 */ 1231 if ((uid != node->tn_uid || 1232 (gid != node->tn_gid && !groupmember(gid, cred))) && 1233 (error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0))) 1234 return (error); 1235 1236 ogid = node->tn_gid; 1237 ouid = node->tn_uid; 1238 1239 node->tn_uid = uid; 1240 node->tn_gid = gid; 1241 1242 node->tn_status |= TMPFS_NODE_CHANGED; 1243 1244 if ((node->tn_mode & (S_ISUID | S_ISGID)) && (ouid != uid || ogid != gid)) { 1245 if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0)) 1246 node->tn_mode &= ~(S_ISUID | S_ISGID); 1247 } 1248 1249 MPASS(VOP_ISLOCKED(vp)); 1250 1251 return 0; 1252 } 1253 1254 /* --------------------------------------------------------------------- */ 1255 1256 /* 1257 * Change size of the given vnode. 1258 * Caller should execute tmpfs_update on vp after a successful execution. 1259 * The vnode must be locked on entry and remain locked on exit. 1260 */ 1261 int 1262 tmpfs_chsize(struct vnode *vp, u_quad_t size, struct ucred *cred, 1263 struct thread *p) 1264 { 1265 int error; 1266 struct tmpfs_node *node; 1267 1268 MPASS(VOP_ISLOCKED(vp)); 1269 1270 node = VP_TO_TMPFS_NODE(vp); 1271 1272 /* Decide whether this is a valid operation based on the file type. */ 1273 error = 0; 1274 switch (vp->v_type) { 1275 case VDIR: 1276 return EISDIR; 1277 1278 case VREG: 1279 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1280 return EROFS; 1281 break; 1282 1283 case VBLK: 1284 /* FALLTHROUGH */ 1285 case VCHR: 1286 /* FALLTHROUGH */ 1287 case VFIFO: 1288 /* Allow modifications of special files even if in the file 1289 * system is mounted read-only (we are not modifying the 1290 * files themselves, but the objects they represent). */ 1291 return 0; 1292 1293 default: 1294 /* Anything else is unsupported. */ 1295 return EOPNOTSUPP; 1296 } 1297 1298 /* Immutable or append-only files cannot be modified, either. */ 1299 if (node->tn_flags & (IMMUTABLE | APPEND)) 1300 return EPERM; 1301 1302 error = tmpfs_truncate(vp, size); 1303 /* tmpfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents 1304 * for us, as will update tn_status; no need to do that here. */ 1305 1306 MPASS(VOP_ISLOCKED(vp)); 1307 1308 return error; 1309 } 1310 1311 /* --------------------------------------------------------------------- */ 1312 1313 /* 1314 * Change access and modification times of the given vnode. 1315 * Caller should execute tmpfs_update on vp after a successful execution. 1316 * The vnode must be locked on entry and remain locked on exit. 1317 */ 1318 int 1319 tmpfs_chtimes(struct vnode *vp, struct timespec *atime, struct timespec *mtime, 1320 struct timespec *birthtime, int vaflags, struct ucred *cred, struct thread *l) 1321 { 1322 int error; 1323 struct tmpfs_node *node; 1324 1325 MPASS(VOP_ISLOCKED(vp)); 1326 1327 node = VP_TO_TMPFS_NODE(vp); 1328 1329 /* Disallow this operation if the file system is mounted read-only. */ 1330 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1331 return EROFS; 1332 1333 /* Immutable or append-only files cannot be modified, either. */ 1334 if (node->tn_flags & (IMMUTABLE | APPEND)) 1335 return EPERM; 1336 1337 /* Determine if the user have proper privilege to update time. */ 1338 if (vaflags & VA_UTIMES_NULL) { 1339 error = VOP_ACCESS(vp, VADMIN, cred, l); 1340 if (error) 1341 error = VOP_ACCESS(vp, VWRITE, cred, l); 1342 } else 1343 error = VOP_ACCESS(vp, VADMIN, cred, l); 1344 if (error) 1345 return (error); 1346 1347 if (atime->tv_sec != VNOVAL && atime->tv_nsec != VNOVAL) 1348 node->tn_status |= TMPFS_NODE_ACCESSED; 1349 1350 if (mtime->tv_sec != VNOVAL && mtime->tv_nsec != VNOVAL) 1351 node->tn_status |= TMPFS_NODE_MODIFIED; 1352 1353 if (birthtime->tv_nsec != VNOVAL && birthtime->tv_nsec != VNOVAL) 1354 node->tn_status |= TMPFS_NODE_MODIFIED; 1355 1356 tmpfs_itimes(vp, atime, mtime); 1357 1358 if (birthtime->tv_nsec != VNOVAL && birthtime->tv_nsec != VNOVAL) 1359 node->tn_birthtime = *birthtime; 1360 MPASS(VOP_ISLOCKED(vp)); 1361 1362 return 0; 1363 } 1364 1365 /* --------------------------------------------------------------------- */ 1366 /* Sync timestamps */ 1367 void 1368 tmpfs_itimes(struct vnode *vp, const struct timespec *acc, 1369 const struct timespec *mod) 1370 { 1371 struct tmpfs_node *node; 1372 struct timespec now; 1373 1374 node = VP_TO_TMPFS_NODE(vp); 1375 1376 if ((node->tn_status & (TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | 1377 TMPFS_NODE_CHANGED)) == 0) 1378 return; 1379 1380 vfs_timestamp(&now); 1381 if (node->tn_status & TMPFS_NODE_ACCESSED) { 1382 if (acc == NULL) 1383 acc = &now; 1384 node->tn_atime = *acc; 1385 } 1386 if (node->tn_status & TMPFS_NODE_MODIFIED) { 1387 if (mod == NULL) 1388 mod = &now; 1389 node->tn_mtime = *mod; 1390 } 1391 if (node->tn_status & TMPFS_NODE_CHANGED) { 1392 node->tn_ctime = now; 1393 } 1394 node->tn_status &= 1395 ~(TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED); 1396 } 1397 1398 /* --------------------------------------------------------------------- */ 1399 1400 void 1401 tmpfs_update(struct vnode *vp) 1402 { 1403 1404 tmpfs_itimes(vp, NULL, NULL); 1405 } 1406 1407 /* --------------------------------------------------------------------- */ 1408 1409 int 1410 tmpfs_truncate(struct vnode *vp, off_t length) 1411 { 1412 int error; 1413 struct tmpfs_node *node; 1414 1415 node = VP_TO_TMPFS_NODE(vp); 1416 1417 if (length < 0) { 1418 error = EINVAL; 1419 goto out; 1420 } 1421 1422 if (node->tn_size == length) { 1423 error = 0; 1424 goto out; 1425 } 1426 1427 if (length > VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize) 1428 return (EFBIG); 1429 1430 error = tmpfs_reg_resize(vp, length, FALSE); 1431 if (error == 0) { 1432 node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; 1433 } 1434 1435 out: 1436 tmpfs_update(vp); 1437 1438 return error; 1439 } 1440