int tmpfs_reclaim(struct vop_reclaim_args *v) { struct vnode *vp = v->a_vp; struct tmpfs_mount *tmp; struct tmpfs_node *node; node = VP_TO_TMPFS_NODE(vp); tmp = VFS_TO_TMPFS(vp->v_mount); vnode_destroy_vobject(vp); cache_purge(vp); TMPFS_NODE_LOCK(node); TMPFS_ASSERT_ELOCKED(node); tmpfs_free_vp(vp); /* If the node referenced by this vnode was deleted by the user, * we must free its associated data structures (now that the vnode * is being reclaimed). */ if (node->tn_links == 0 && (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0) { node->tn_vpstate = TMPFS_VNODE_DOOMED; TMPFS_NODE_UNLOCK(node); tmpfs_free_node(tmp, node); } else TMPFS_NODE_UNLOCK(node); MPASS(vp->v_data == NULL); return 0; }
/* * Frees a directory entry. It is the caller's responsibility to destroy * the node referenced by it if needed. * * The link count of node is decreased by one to reflect the removal of an * object that referenced it. This only happens if 'node_exists' is true; * otherwise the function will not access the node referred to by the * directory entry, as it may already have been released from the outside. */ void tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de) { struct tmpfs_node *node; node = de->td_node; TMPFS_NODE_LOCK(node); TMPFS_ASSERT_ELOCKED(node); KKASSERT(node->tn_links > 0); node->tn_links--; TMPFS_NODE_UNLOCK(node); kfree(de->td_name, tmp->tm_name_zone); de->td_namelen = 0; de->td_name = NULL; de->td_node = NULL; objcache_put(tmp->tm_dirent_pool, de); }
static int tmpfs_nrmdir(struct vop_nrmdir_args *v) { struct vnode *dvp = v->a_dvp; struct namecache *ncp = v->a_nch->ncp; struct vnode *vp; struct tmpfs_dirent *de; struct tmpfs_mount *tmp; struct tmpfs_node *dnode; struct tmpfs_node *node; struct mount *mp; int error; mp = dvp->v_mount; /* * We have to acquire the vp from v->a_nch because we will likely * unresolve the namecache entry, and a vrele/vput is needed to * trigger the tmpfs_inactive/tmpfs_reclaim sequence. * * We have to use vget to clear any inactive state on the vnode, * otherwise the vnode may remain inactive and thus tmpfs_inactive * will not get called when we release it. */ error = cache_vget(v->a_nch, v->a_cred, LK_SHARED, &vp); KKASSERT(error == 0); vn_unlock(vp); /* * Prevalidate so we don't hit an assertion later */ if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } tmp = VFS_TO_TMPFS(dvp->v_mount); dnode = VP_TO_TMPFS_DIR(dvp); node = VP_TO_TMPFS_DIR(vp); /* * Directories with more than two entries ('.' and '..') cannot * be removed. */ if (node->tn_size > 0) { error = ENOTEMPTY; goto out; } if ((dnode->tn_flags & APPEND) || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) { error = EPERM; goto out; } /* * This invariant holds only if we are not trying to * remove "..". We checked for that above so this is safe now. */ KKASSERT(node->tn_dir.tn_parent == dnode); /* * Get the directory entry associated with node (vp). This * was filled by tmpfs_lookup while looking up the entry. */ TMPFS_NODE_LOCK(dnode); de = tmpfs_dir_lookup(dnode, node, ncp); KKASSERT(TMPFS_DIRENT_MATCHES(de, ncp->nc_name, ncp->nc_nlen)); /* Check flags to see if we are allowed to remove the directory. */ if ((dnode->tn_flags & APPEND) || node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) { error = EPERM; TMPFS_NODE_UNLOCK(dnode); goto out; } /* Detach the directory entry from the directory (dnode). */ tmpfs_dir_detach(dnode, de); TMPFS_NODE_UNLOCK(dnode); /* No vnode should be allocated for this entry from this point */ TMPFS_NODE_LOCK(dnode); TMPFS_ASSERT_ELOCKED(dnode); TMPFS_NODE_LOCK(node); TMPFS_ASSERT_ELOCKED(node); /* * Must set parent linkage to NULL (tested by ncreate to disallow * the creation of new files/dirs in a deleted directory) */ node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; TMPFS_NODE_UNLOCK(node); TMPFS_NODE_UNLOCK(dnode); /* Free the directory entry we just deleted. Note that the node * referred by it will not be removed until the vnode is really * reclaimed. */ tmpfs_free_dirent(tmp, de); /* Release the deleted vnode (will destroy the node, notify * interested parties and clean it from the cache). */ TMPFS_NODE_LOCK(dnode); dnode->tn_status |= TMPFS_NODE_CHANGED; TMPFS_NODE_UNLOCK(dnode); tmpfs_update(dvp); cache_unlink(v->a_nch); tmpfs_knote(dvp, NOTE_WRITE | NOTE_LINK); error = 0; out: vrele(vp); return error; }
static int tmpfs_rmdir(struct vop_rmdir_args *v) { struct vnode *dvp = v->a_dvp; struct vnode *vp = v->a_vp; int error; struct tmpfs_dirent *de; struct tmpfs_mount *tmp; struct tmpfs_node *dnode; struct tmpfs_node *node; MPASS(VOP_ISLOCKED(dvp)); MPASS(VOP_ISLOCKED(vp)); tmp = VFS_TO_TMPFS(dvp->v_mount); dnode = VP_TO_TMPFS_DIR(dvp); node = VP_TO_TMPFS_DIR(vp); /* Directories with more than two entries ('.' and '..') cannot be * removed. */ if (node->tn_size > 0) { error = ENOTEMPTY; goto out; } if ((dnode->tn_flags & APPEND) || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) { error = EPERM; goto out; } /* This invariant holds only if we are not trying to remove "..". * We checked for that above so this is safe now. */ MPASS(node->tn_dir.tn_parent == dnode); /* Get the directory entry associated with node (vp). This was * filled by tmpfs_lookup while looking up the entry. */ de = tmpfs_dir_lookup(dnode, node, v->a_cnp); MPASS(TMPFS_DIRENT_MATCHES(de, v->a_cnp->cn_nameptr, v->a_cnp->cn_namelen)); /* Check flags to see if we are allowed to remove the directory. */ if (dnode->tn_flags & APPEND || node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) { error = EPERM; goto out; } /* Detach the directory entry from the directory (dnode). */ tmpfs_dir_detach(dvp, de); if (v->a_cnp->cn_flags & DOWHITEOUT) tmpfs_dir_whiteout_add(dvp, v->a_cnp); /* No vnode should be allocated for this entry from this point */ TMPFS_NODE_LOCK(node); TMPFS_ASSERT_ELOCKED(node); node->tn_links--; node->tn_dir.tn_parent = NULL; node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ TMPFS_NODE_MODIFIED; TMPFS_NODE_UNLOCK(node); TMPFS_NODE_LOCK(dnode); TMPFS_ASSERT_ELOCKED(dnode); dnode->tn_links--; dnode->tn_status |= TMPFS_NODE_ACCESSED | \ TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; TMPFS_NODE_UNLOCK(dnode); cache_purge(dvp); cache_purge(vp); /* Free the directory entry we just deleted. Note that the node * referred by it will not be removed until the vnode is really * reclaimed. */ tmpfs_free_dirent(tmp, de, TRUE); /* Release the deleted vnode (will destroy the node, notify * interested parties and clean it from the cache). */ dnode->tn_status |= TMPFS_NODE_CHANGED; tmpfs_update(dvp); error = 0; out: return error; }
/* * Destroys the node pointed to by node from the file system 'tmp'. * If the node does not belong to the given mount point, the results are * unpredicted. * * If the node references a directory; no entries are allowed because * their removal could need a recursive algorithm, something forbidden in * kernel space. Furthermore, there is not need to provide such * functionality (recursive removal) because the only primitives offered * to the user are the removal of empty directories and the deletion of * individual files. * * Note that nodes are not really deleted; in fact, when a node has been * allocated, it cannot be deleted during the whole life of the file * system. Instead, they are moved to the available list and remain there * until reused. * * A caller must have TMPFS_NODE_LOCK(node) and this function unlocks it. */ void tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node) { vm_pindex_t pages = 0; #ifdef INVARIANTS TMPFS_ASSERT_ELOCKED(node); KKASSERT(node->tn_vnode == NULL); KKASSERT((node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0); #endif TMPFS_LOCK(tmp); LIST_REMOVE(node, tn_entries); tmp->tm_nodes_inuse--; TMPFS_UNLOCK(tmp); TMPFS_NODE_UNLOCK(node); /* Caller has this lock */ switch (node->tn_type) { case VNON: /* Do not do anything. VNON is provided to let the * allocation routine clean itself easily by avoiding * duplicating code in it. */ /* FALLTHROUGH */ case VBLK: /* FALLTHROUGH */ case VCHR: /* FALLTHROUGH */ break; case VDIR: /* * The parent link can be NULL if this is the root * node or if it is a directory node that was rmdir'd. * * XXX what if node is a directory which still contains * directory entries (e.g. due to a forced umount) ? */ node->tn_size = 0; KKASSERT(node->tn_dir.tn_parent == NULL); /* * If the root node is being destroyed don't leave a * dangling pointer in tmpfs_mount. */ if (node == tmp->tm_root) tmp->tm_root = NULL; break; case VFIFO: /* FALLTHROUGH */ case VSOCK: break; case VLNK: kfree(node->tn_link, tmp->tm_name_zone); node->tn_link = NULL; node->tn_size = 0; break; case VREG: if (node->tn_reg.tn_aobj != NULL) vm_object_deallocate(node->tn_reg.tn_aobj); node->tn_reg.tn_aobj = NULL; pages = node->tn_reg.tn_aobj_pages; break; default: panic("tmpfs_free_node: type %p %d", node, (int)node->tn_type); } /* * Clean up fields for the next allocation. The objcache only ctors * new allocations. */ tmpfs_node_ctor(node, NULL, 0); objcache_put(tmp->tm_node_pool, node); /* node is now invalid */ if (pages) atomic_add_long(&tmp->tm_pages_used, -(long)pages); }