version 1.98.2.4, 2017/12/03 11:38:43 |
version 1.99, 2012/11/05 17:24:11 |
Line 49 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 49 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/vnode.h> |
#include <sys/vnode.h> |
#include <sys/lockf.h> |
#include <sys/lockf.h> |
#include <sys/kauth.h> |
#include <sys/kauth.h> |
#include <sys/atomic.h> |
|
|
|
#include <uvm/uvm.h> |
#include <uvm/uvm.h> |
|
|
Line 74 const struct vnodeopv_entry_desc tmpfs_v |
|
Line 73 const struct vnodeopv_entry_desc tmpfs_v |
|
{ &vop_setattr_desc, tmpfs_setattr }, |
{ &vop_setattr_desc, tmpfs_setattr }, |
{ &vop_read_desc, tmpfs_read }, |
{ &vop_read_desc, tmpfs_read }, |
{ &vop_write_desc, tmpfs_write }, |
{ &vop_write_desc, tmpfs_write }, |
{ &vop_fallocate_desc, genfs_eopnotsupp }, |
|
{ &vop_fdiscard_desc, genfs_eopnotsupp }, |
|
{ &vop_ioctl_desc, tmpfs_ioctl }, |
{ &vop_ioctl_desc, tmpfs_ioctl }, |
{ &vop_fcntl_desc, tmpfs_fcntl }, |
{ &vop_fcntl_desc, tmpfs_fcntl }, |
{ &vop_poll_desc, tmpfs_poll }, |
{ &vop_poll_desc, tmpfs_poll }, |
Line 126 const struct vnodeopv_desc tmpfs_vnodeop |
|
Line 123 const struct vnodeopv_desc tmpfs_vnodeop |
|
int |
int |
tmpfs_lookup(void *v) |
tmpfs_lookup(void *v) |
{ |
{ |
struct vop_lookup_v2_args /* { |
struct vop_lookup_args /* { |
struct vnode *a_dvp; |
struct vnode *a_dvp; |
struct vnode **a_vpp; |
struct vnode **a_vpp; |
struct componentname *a_cnp; |
struct componentname *a_cnp; |
Line 164 tmpfs_lookup(void *v) |
|
Line 161 tmpfs_lookup(void *v) |
|
* Avoid doing a linear scan of the directory if the requested |
* Avoid doing a linear scan of the directory if the requested |
* directory/name couple is already in the cache. |
* directory/name couple is already in the cache. |
*/ |
*/ |
cachefound = cache_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen, |
cachefound = cache_lookup(dvp, cnp, &iswhiteout, vpp); |
cnp->cn_nameiop, cnp->cn_flags, |
|
&iswhiteout, vpp); |
|
if (iswhiteout) { |
if (iswhiteout) { |
cnp->cn_flags |= ISWHITEOUT; |
cnp->cn_flags |= ISWHITEOUT; |
} |
} |
Line 179 tmpfs_lookup(void *v) |
|
Line 174 tmpfs_lookup(void *v) |
|
goto out; |
goto out; |
} |
} |
|
|
/* |
|
* Treat an unlinked directory as empty (no "." or "..") |
|
*/ |
|
if (dnode->tn_links == 0) { |
|
KASSERT(dnode->tn_size == 0); |
|
error = ENOENT; |
|
goto out; |
|
} |
|
|
|
if (cnp->cn_flags & ISDOTDOT) { |
if (cnp->cn_flags & ISDOTDOT) { |
tmpfs_node_t *pnode; |
tmpfs_node_t *pnode; |
|
|
Line 205 tmpfs_lookup(void *v) |
|
Line 191 tmpfs_lookup(void *v) |
|
goto out; |
goto out; |
} |
} |
|
|
error = vcache_get(dvp->v_mount, &pnode, sizeof(pnode), vpp); |
/* |
|
* Lock the parent tn_vlock before releasing the vnode lock, |
|
* and thus prevents parent from disappearing. |
|
*/ |
|
mutex_enter(&pnode->tn_vlock); |
|
VOP_UNLOCK(dvp); |
|
|
|
/* |
|
* Get a vnode of the '..' entry and re-acquire the lock. |
|
* Release the tn_vlock. |
|
*/ |
|
error = tmpfs_vnode_get(dvp->v_mount, pnode, vpp); |
|
vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); |
goto out; |
goto out; |
|
|
} else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { |
} else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { |
/* |
/* |
* Lookup of "." case. |
* Lookup of "." case. |
Line 279 tmpfs_lookup(void *v) |
|
Line 278 tmpfs_lookup(void *v) |
|
} |
} |
|
|
/* Get a vnode for the matching entry. */ |
/* Get a vnode for the matching entry. */ |
error = vcache_get(dvp->v_mount, &tnode, sizeof(tnode), vpp); |
mutex_enter(&tnode->tn_vlock); |
|
error = tmpfs_vnode_get(dvp->v_mount, tnode, vpp); |
done: |
done: |
/* |
/* |
* Cache the result, unless request was for creation (as it does |
* Cache the result, unless request was for creation (as it does |
* not improve the performance). |
* not improve the performance). |
*/ |
*/ |
if (cnp->cn_nameiop != CREATE) { |
if (cnp->cn_nameiop != CREATE) { |
cache_enter(dvp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, |
cache_enter(dvp, *vpp, cnp); |
cnp->cn_flags); |
|
} |
} |
out: |
out: |
|
KASSERT((*vpp && VOP_ISLOCKED(*vpp)) || error); |
KASSERT(VOP_ISLOCKED(dvp)); |
KASSERT(VOP_ISLOCKED(dvp)); |
|
|
return error; |
return error; |
|
|
int |
int |
tmpfs_create(void *v) |
tmpfs_create(void *v) |
{ |
{ |
struct vop_create_v3_args /* { |
struct vop_create_args /* { |
struct vnode *a_dvp; |
struct vnode *a_dvp; |
struct vnode **a_vpp; |
struct vnode **a_vpp; |
struct componentname *a_cnp; |
struct componentname *a_cnp; |
Line 310 tmpfs_create(void *v) |
|
Line 310 tmpfs_create(void *v) |
|
|
|
KASSERT(VOP_ISLOCKED(dvp)); |
KASSERT(VOP_ISLOCKED(dvp)); |
KASSERT(vap->va_type == VREG || vap->va_type == VSOCK); |
KASSERT(vap->va_type == VREG || vap->va_type == VSOCK); |
return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL); |
return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); |
} |
} |
|
|
int |
int |
tmpfs_mknod(void *v) |
tmpfs_mknod(void *v) |
{ |
{ |
struct vop_mknod_v3_args /* { |
struct vop_mknod_args /* { |
struct vnode *a_dvp; |
struct vnode *a_dvp; |
struct vnode **a_vpp; |
struct vnode **a_vpp; |
struct componentname *a_cnp; |
struct componentname *a_cnp; |
Line 328 tmpfs_mknod(void *v) |
|
Line 328 tmpfs_mknod(void *v) |
|
enum vtype vt = vap->va_type; |
enum vtype vt = vap->va_type; |
|
|
if (vt != VBLK && vt != VCHR && vt != VFIFO) { |
if (vt != VBLK && vt != VCHR && vt != VFIFO) { |
*vpp = NULL; |
vput(dvp); |
return EINVAL; |
return EINVAL; |
} |
} |
return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL); |
return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); |
} |
} |
|
|
int |
int |
Line 349 tmpfs_open(void *v) |
|
Line 349 tmpfs_open(void *v) |
|
KASSERT(VOP_ISLOCKED(vp)); |
KASSERT(VOP_ISLOCKED(vp)); |
|
|
node = VP_TO_TMPFS_NODE(vp); |
node = VP_TO_TMPFS_NODE(vp); |
|
if (node->tn_links < 1) { |
|
/* |
|
* The file is still active, but all its names have been |
|
* removed (e.g. by a "rmdir $(pwd)"). It cannot be opened |
|
* any more, as it is about to be destroyed. |
|
*/ |
|
return ENOENT; |
|
} |
|
|
/* If the file is marked append-only, deny write requests. */ |
/* If the file is marked append-only, deny write requests. */ |
if ((node->tn_flags & APPEND) != 0 && |
if ((node->tn_flags & APPEND) != 0 && |
Line 366 tmpfs_close(void *v) |
|
Line 374 tmpfs_close(void *v) |
|
int a_fflag; |
int a_fflag; |
kauth_cred_t a_cred; |
kauth_cred_t a_cred; |
} */ *ap = v; |
} */ *ap = v; |
vnode_t *vp __diagused = ap->a_vp; |
vnode_t *vp = ap->a_vp; |
|
|
KASSERT(VOP_ISLOCKED(vp)); |
KASSERT(VOP_ISLOCKED(vp)); |
|
|
|
tmpfs_update(vp, NULL, NULL, NULL, UPDATE_CLOSE); |
return 0; |
return 0; |
} |
} |
|
|
Line 409 tmpfs_access(void *v) |
|
Line 419 tmpfs_access(void *v) |
|
return EPERM; |
return EPERM; |
} |
} |
|
|
return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(mode, |
return kauth_authorize_vnode(cred, kauth_access_action(mode, |
vp->v_type, node->tn_mode), vp, NULL, genfs_can_access(vp->v_type, |
vp->v_type, node->tn_mode), vp, NULL, genfs_can_access(vp->v_type, |
node->tn_mode, node->tn_uid, node->tn_gid, mode, cred)); |
node->tn_mode, node->tn_uid, node->tn_gid, mode, cred)); |
} |
} |
Line 428 tmpfs_getattr(void *v) |
|
Line 438 tmpfs_getattr(void *v) |
|
|
|
vattr_null(vap); |
vattr_null(vap); |
|
|
|
tmpfs_update(vp, NULL, NULL, NULL, 0); |
|
|
vap->va_type = vp->v_type; |
vap->va_type = vp->v_type; |
vap->va_mode = node->tn_mode; |
vap->va_mode = node->tn_mode; |
vap->va_nlink = node->tn_links; |
vap->va_nlink = node->tn_links; |
Line 453 tmpfs_getattr(void *v) |
|
Line 465 tmpfs_getattr(void *v) |
|
return 0; |
return 0; |
} |
} |
|
|
|
#define GOODTIME(tv) ((tv)->tv_sec != VNOVAL || (tv)->tv_nsec != VNOVAL) |
|
/* XXX Should this operation be atomic? I think it should, but code in |
|
* XXX other places (e.g., ufs) doesn't seem to be... */ |
int |
int |
tmpfs_setattr(void *v) |
tmpfs_setattr(void *v) |
{ |
{ |
Line 472 tmpfs_setattr(void *v) |
|
Line 487 tmpfs_setattr(void *v) |
|
/* Abort if any unsettable attribute is given. */ |
/* Abort if any unsettable attribute is given. */ |
if (vap->va_type != VNON || vap->va_nlink != VNOVAL || |
if (vap->va_type != VNON || vap->va_nlink != VNOVAL || |
vap->va_fsid != VNOVAL || vap->va_fileid != VNOVAL || |
vap->va_fsid != VNOVAL || vap->va_fileid != VNOVAL || |
vap->va_blocksize != VNOVAL || vap->va_ctime.tv_sec != VNOVAL || |
vap->va_blocksize != VNOVAL || GOODTIME(&vap->va_ctime) || |
vap->va_gen != VNOVAL || vap->va_rdev != VNOVAL || |
vap->va_gen != VNOVAL || vap->va_rdev != VNOVAL || |
vap->va_bytes != VNOVAL) { |
vap->va_bytes != VNOVAL) { |
return EINVAL; |
return EINVAL; |
} |
} |
|
if (error == 0 && (vap->va_flags != VNOVAL)) |
if (error == 0 && vap->va_flags != VNOVAL) |
|
error = tmpfs_chflags(vp, vap->va_flags, cred, l); |
error = tmpfs_chflags(vp, vap->va_flags, cred, l); |
|
|
if (error == 0 && vap->va_size != VNOVAL) |
if (error == 0 && (vap->va_size != VNOVAL)) |
error = tmpfs_chsize(vp, vap->va_size, cred, l); |
error = tmpfs_chsize(vp, vap->va_size, cred, l); |
|
|
if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL)) |
if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL)) |
error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, l); |
error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, l); |
|
|
if (error == 0 && vap->va_mode != VNOVAL) |
if (error == 0 && (vap->va_mode != VNOVAL)) |
error = tmpfs_chmod(vp, vap->va_mode, cred, l); |
error = tmpfs_chmod(vp, vap->va_mode, cred, l); |
|
|
const bool chsometime = |
if (error == 0 && (GOODTIME(&vap->va_atime) || GOODTIME(&vap->va_mtime) |
vap->va_atime.tv_sec != VNOVAL || |
|| GOODTIME(&vap->va_birthtime))) { |
vap->va_mtime.tv_sec != VNOVAL || |
|
vap->va_birthtime.tv_sec != VNOVAL; |
|
if (error == 0 && chsometime) { |
|
error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime, |
error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime, |
&vap->va_birthtime, vap->va_vaflags, cred, l); |
&vap->va_birthtime, vap->va_vaflags, cred, l); |
|
if (error == 0) |
|
return 0; |
} |
} |
|
tmpfs_update(vp, NULL, NULL, NULL, 0); |
return error; |
return error; |
} |
} |
|
|
Line 519 tmpfs_read(void *v) |
|
Line 533 tmpfs_read(void *v) |
|
|
|
KASSERT(VOP_ISLOCKED(vp)); |
KASSERT(VOP_ISLOCKED(vp)); |
|
|
if (vp->v_type == VDIR) { |
if (vp->v_type != VREG) { |
return EISDIR; |
return EISDIR; |
} |
} |
if (uio->uio_offset < 0 || vp->v_type != VREG) { |
if (uio->uio_offset < 0) { |
return EINVAL; |
return EINVAL; |
} |
} |
|
|
/* Note: reading zero bytes should not update atime. */ |
|
if (uio->uio_resid == 0) { |
|
return 0; |
|
} |
|
|
|
node = VP_TO_TMPFS_NODE(vp); |
node = VP_TO_TMPFS_NODE(vp); |
|
node->tn_status |= TMPFS_NODE_ACCESSED; |
uobj = node->tn_spec.tn_reg.tn_aobj; |
uobj = node->tn_spec.tn_reg.tn_aobj; |
error = 0; |
error = 0; |
|
|
Line 548 tmpfs_read(void *v) |
|
Line 558 tmpfs_read(void *v) |
|
error = ubc_uiomove(uobj, uio, len, IO_ADV_DECODE(ioflag), |
error = ubc_uiomove(uobj, uio, len, IO_ADV_DECODE(ioflag), |
UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); |
UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); |
} |
} |
|
|
tmpfs_update(vp, TMPFS_UPDATE_ATIME); |
|
return error; |
return error; |
} |
} |
|
|
Line 568 tmpfs_write(void *v) |
|
Line 576 tmpfs_write(void *v) |
|
tmpfs_node_t *node; |
tmpfs_node_t *node; |
struct uvm_object *uobj; |
struct uvm_object *uobj; |
off_t oldsize; |
off_t oldsize; |
|
bool extended; |
int error; |
int error; |
|
|
KASSERT(VOP_ISLOCKED(vp)); |
KASSERT(VOP_ISLOCKED(vp)); |
Line 575 tmpfs_write(void *v) |
|
Line 584 tmpfs_write(void *v) |
|
node = VP_TO_TMPFS_NODE(vp); |
node = VP_TO_TMPFS_NODE(vp); |
oldsize = node->tn_size; |
oldsize = node->tn_size; |
|
|
if ((vp->v_mount->mnt_flag & MNT_RDONLY) != 0) { |
|
error = EROFS; |
|
goto out; |
|
} |
|
|
|
if (uio->uio_offset < 0 || vp->v_type != VREG) { |
if (uio->uio_offset < 0 || vp->v_type != VREG) { |
error = EINVAL; |
error = EINVAL; |
goto out; |
goto out; |
Line 592 tmpfs_write(void *v) |
|
Line 596 tmpfs_write(void *v) |
|
uio->uio_offset = node->tn_size; |
uio->uio_offset = node->tn_size; |
} |
} |
|
|
if (uio->uio_offset + uio->uio_resid > node->tn_size) { |
extended = uio->uio_offset + uio->uio_resid > node->tn_size; |
|
if (extended) { |
error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid); |
error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid); |
if (error) |
if (error) |
goto out; |
goto out; |
Line 614 tmpfs_write(void *v) |
|
Line 619 tmpfs_write(void *v) |
|
(void)tmpfs_reg_resize(vp, oldsize); |
(void)tmpfs_reg_resize(vp, oldsize); |
} |
} |
|
|
tmpfs_update(vp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); |
node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | |
|
(extended ? TMPFS_NODE_CHANGED : 0); |
VN_KNOTE(vp, NOTE_WRITE); |
VN_KNOTE(vp, NOTE_WRITE); |
out: |
out: |
if (error) { |
if (error) { |
Line 636 tmpfs_fsync(void *v) |
|
Line 642 tmpfs_fsync(void *v) |
|
off_t a_offhi; |
off_t a_offhi; |
struct lwp *a_l; |
struct lwp *a_l; |
} */ *ap = v; |
} */ *ap = v; |
vnode_t *vp __diagused = ap->a_vp; |
vnode_t *vp = ap->a_vp; |
|
|
/* Nothing to do. Should be up to date. */ |
/* Nothing to do. Just update. */ |
KASSERT(VOP_ISLOCKED(vp)); |
KASSERT(VOP_ISLOCKED(vp)); |
|
tmpfs_update(vp, NULL, NULL, NULL, 0); |
return 0; |
return 0; |
} |
} |
|
|
Line 652 tmpfs_fsync(void *v) |
|
Line 659 tmpfs_fsync(void *v) |
|
int |
int |
tmpfs_remove(void *v) |
tmpfs_remove(void *v) |
{ |
{ |
struct vop_remove_v2_args /* { |
struct vop_remove_args /* { |
struct vnode *a_dvp; |
struct vnode *a_dvp; |
struct vnode *a_vp; |
struct vnode *a_vp; |
struct componentname *a_cnp; |
struct componentname *a_cnp; |
} */ *ap = v; |
} */ *ap = v; |
vnode_t *dvp = ap->a_dvp, *vp = ap->a_vp; |
vnode_t *dvp = ap->a_dvp, *vp = ap->a_vp; |
tmpfs_node_t *dnode, *node; |
tmpfs_node_t *node; |
tmpfs_dirent_t *de; |
tmpfs_dirent_t *de; |
int error; |
int error; |
|
|
Line 669 tmpfs_remove(void *v) |
|
Line 676 tmpfs_remove(void *v) |
|
error = EPERM; |
error = EPERM; |
goto out; |
goto out; |
} |
} |
dnode = VP_TO_TMPFS_DIR(dvp); |
|
node = VP_TO_TMPFS_NODE(vp); |
node = VP_TO_TMPFS_NODE(vp); |
|
|
/* |
/* Files marked as immutable or append-only cannot be deleted. */ |
* Files marked as immutable or append-only cannot be deleted. |
|
* Likewise, files residing on directories marked as append-only |
|
* cannot be deleted. |
|
*/ |
|
if (node->tn_flags & (IMMUTABLE | APPEND)) { |
if (node->tn_flags & (IMMUTABLE | APPEND)) { |
error = EPERM; |
error = EPERM; |
goto out; |
goto out; |
} |
} |
if (dnode->tn_flags & APPEND) { |
|
error = EPERM; |
|
goto out; |
|
} |
|
|
|
/* Lookup the directory entry (check the cached hint first). */ |
/* Lookup the directory entry (check the cached hint first). */ |
de = tmpfs_dir_cached(node); |
de = tmpfs_dir_cached(node); |
if (de == NULL) { |
if (de == NULL) { |
|
tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp); |
struct componentname *cnp = ap->a_cnp; |
struct componentname *cnp = ap->a_cnp; |
de = tmpfs_dir_lookup(dnode, cnp); |
de = tmpfs_dir_lookup(dnode, cnp); |
} |
} |
Line 696 tmpfs_remove(void *v) |
|
Line 695 tmpfs_remove(void *v) |
|
|
|
/* |
/* |
* Remove the entry from the directory (drops the link count) and |
* Remove the entry from the directory (drops the link count) and |
* destroy it or replace with a whiteout. |
* destroy it or replace it with a whiteout. |
* |
* Note: the inode referred by it will not be destroyed |
* Note: the inode referred by it will not be destroyed until the |
* until the vnode is reclaimed/recycled. |
* vnode is reclaimed/recycled. |
|
*/ |
*/ |
|
tmpfs_dir_detach(dvp, de); |
tmpfs_dir_detach(dnode, de); |
|
|
|
if (ap->a_cnp->cn_flags & DOWHITEOUT) |
if (ap->a_cnp->cn_flags & DOWHITEOUT) |
tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT); |
tmpfs_dir_attach(dvp, de, TMPFS_NODE_WHITEOUT); |
else |
else |
tmpfs_free_dirent(VFS_TO_TMPFS(vp->v_mount), de); |
tmpfs_free_dirent(VFS_TO_TMPFS(vp->v_mount), de); |
|
|
if (node->tn_links > 0) { |
|
/* We removed a hard link. */ |
|
tmpfs_update(vp, TMPFS_UPDATE_CTIME); |
|
} |
|
tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); |
|
error = 0; |
error = 0; |
out: |
out: |
/* Drop the reference and unlock the node. */ |
/* Drop the references and unlock the vnodes. */ |
|
vput(vp); |
if (dvp == vp) { |
if (dvp == vp) { |
vrele(vp); |
vrele(dvp); |
} else { |
} else { |
vput(vp); |
vput(dvp); |
} |
} |
return error; |
return error; |
} |
} |
|
|
int |
int |
tmpfs_link(void *v) |
tmpfs_link(void *v) |
{ |
{ |
struct vop_link_v2_args /* { |
struct vop_link_args /* { |
struct vnode *a_dvp; |
struct vnode *a_dvp; |
struct vnode *a_vp; |
struct vnode *a_vp; |
struct componentname *a_cnp; |
struct componentname *a_cnp; |
Line 773 tmpfs_link(void *v) |
|
Line 764 tmpfs_link(void *v) |
|
goto out; |
goto out; |
} |
} |
|
|
/* |
/* |
* Insert the entry into the directory. |
* Insert the entry into the directory. |
* It will increase the inode link count. |
* It will increase the inode link count. |
*/ |
*/ |
tmpfs_dir_attach(dnode, de, node); |
tmpfs_dir_attach(dvp, de, node); |
tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); |
|
|
|
/* Update the timestamps and trigger the event. */ |
/* Update the timestamps and trigger the event. */ |
if (node->tn_vnode) { |
if (node->tn_vnode) { |
VN_KNOTE(node->tn_vnode, NOTE_LINK); |
VN_KNOTE(node->tn_vnode, NOTE_LINK); |
} |
} |
tmpfs_update(vp, TMPFS_UPDATE_CTIME); |
node->tn_status |= TMPFS_NODE_CHANGED; |
|
tmpfs_update(vp, NULL, NULL, NULL, 0); |
error = 0; |
error = 0; |
out: |
out: |
VOP_UNLOCK(vp); |
VOP_UNLOCK(vp); |
|
vput(dvp); |
return error; |
return error; |
} |
} |
|
|
int |
int |
tmpfs_mkdir(void *v) |
tmpfs_mkdir(void *v) |
{ |
{ |
struct vop_mkdir_v3_args /* { |
struct vop_mkdir_args /* { |
struct vnode *a_dvp; |
struct vnode *a_dvp; |
struct vnode **a_vpp; |
struct vnode **a_vpp; |
struct componentname *a_cnp; |
struct componentname *a_cnp; |
Line 806 tmpfs_mkdir(void *v) |
|
Line 798 tmpfs_mkdir(void *v) |
|
struct vattr *vap = ap->a_vap; |
struct vattr *vap = ap->a_vap; |
|
|
KASSERT(vap->va_type == VDIR); |
KASSERT(vap->va_type == VDIR); |
return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL); |
return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); |
} |
} |
|
|
int |
int |
tmpfs_rmdir(void *v) |
tmpfs_rmdir(void *v) |
{ |
{ |
struct vop_rmdir_v2_args /* { |
struct vop_rmdir_args /* { |
struct vnode *a_dvp; |
struct vnode *a_dvp; |
struct vnode *a_vp; |
struct vnode *a_vp; |
struct componentname *a_cnp; |
struct componentname *a_cnp; |
Line 827 tmpfs_rmdir(void *v) |
|
Line 819 tmpfs_rmdir(void *v) |
|
|
|
KASSERT(VOP_ISLOCKED(dvp)); |
KASSERT(VOP_ISLOCKED(dvp)); |
KASSERT(VOP_ISLOCKED(vp)); |
KASSERT(VOP_ISLOCKED(vp)); |
|
KASSERT(node->tn_spec.tn_dir.tn_parent == dnode); |
|
|
/* |
/* |
* Directories with more than two entries ('.' and '..') cannot be |
* Directories with more than two non-whiteout |
* removed. There may be whiteout entries, which we will destroy. |
* entries ('.' and '..') cannot be removed. |
*/ |
*/ |
if (node->tn_size > 0) { |
if (node->tn_size > 0) { |
/* |
KASSERT(error == 0); |
* If never had whiteout entries, the directory is certainly |
|
* not empty. Otherwise, scan for any non-whiteout entry. |
|
*/ |
|
if ((node->tn_gen & TMPFS_WHITEOUT_BIT) == 0) { |
|
error = ENOTEMPTY; |
|
goto out; |
|
} |
|
TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) { |
TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) { |
if (de->td_node != TMPFS_NODE_WHITEOUT) { |
if (de->td_node != TMPFS_NODE_WHITEOUT) { |
error = ENOTEMPTY; |
error = ENOTEMPTY; |
goto out; |
break; |
} |
} |
} |
} |
KASSERT(error == 0); |
if (error) |
|
goto out; |
} |
} |
|
|
KASSERT(node->tn_spec.tn_dir.tn_parent == dnode); |
|
|
|
/* Lookup the directory entry (check the cached hint first). */ |
/* Lookup the directory entry (check the cached hint first). */ |
de = tmpfs_dir_cached(node); |
de = tmpfs_dir_cached(node); |
if (de == NULL) { |
if (de == NULL) { |
Line 868 tmpfs_rmdir(void *v) |
|
Line 853 tmpfs_rmdir(void *v) |
|
|
|
/* Decrement the link count for the virtual '.' entry. */ |
/* Decrement the link count for the virtual '.' entry. */ |
node->tn_links--; |
node->tn_links--; |
|
node->tn_status |= TMPFS_NODE_STATUSALL; |
|
|
/* Detach the directory entry from the directory. */ |
/* Detach the directory entry from the directory. */ |
tmpfs_dir_detach(dnode, de); |
tmpfs_dir_detach(dvp, de); |
|
|
/* Purge the cache for parent. */ |
/* Purge the cache for parent. */ |
cache_purge(dvp); |
cache_purge(dvp); |
|
|
/* |
/* |
* Destroy the directory entry or replace it with a whiteout. |
* Destroy the directory entry or replace it with a whiteout. |
* |
* Note: the inode referred by it will not be destroyed |
* Note: the inode referred by it will not be destroyed until the |
* until the vnode is reclaimed. |
* vnode is reclaimed. |
|
*/ |
*/ |
if (ap->a_cnp->cn_flags & DOWHITEOUT) |
if (ap->a_cnp->cn_flags & DOWHITEOUT) |
tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT); |
tmpfs_dir_attach(dvp, de, TMPFS_NODE_WHITEOUT); |
else |
else |
tmpfs_free_dirent(tmp, de); |
tmpfs_free_dirent(tmp, de); |
|
|
/* Destroy the whiteout entries from the node. */ |
/* Destroy the whiteout entries from the node. */ |
while ((de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir)) != NULL) { |
while ((de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir)) != NULL) { |
KASSERT(de->td_node == TMPFS_NODE_WHITEOUT); |
KASSERT(de->td_node == TMPFS_NODE_WHITEOUT); |
tmpfs_dir_detach(node, de); |
tmpfs_dir_detach(vp, de); |
tmpfs_free_dirent(tmp, de); |
tmpfs_free_dirent(tmp, de); |
} |
} |
tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); |
|
|
|
KASSERT(node->tn_size == 0); |
|
KASSERT(node->tn_links == 0); |
KASSERT(node->tn_links == 0); |
out: |
out: |
/* Release the node. */ |
/* Release the nodes. */ |
KASSERT(dvp != vp); |
vput(dvp); |
vput(vp); |
vput(vp); |
return error; |
return error; |
} |
} |
|
|
int |
int |
tmpfs_symlink(void *v) |
tmpfs_symlink(void *v) |
{ |
{ |
struct vop_symlink_v3_args /* { |
struct vop_symlink_args /* { |
struct vnode *a_dvp; |
struct vnode *a_dvp; |
struct vnode **a_vpp; |
struct vnode **a_vpp; |
struct componentname *a_cnp; |
struct componentname *a_cnp; |
Line 920 tmpfs_symlink(void *v) |
|
Line 903 tmpfs_symlink(void *v) |
|
char *target = ap->a_target; |
char *target = ap->a_target; |
|
|
KASSERT(vap->va_type == VLNK); |
KASSERT(vap->va_type == VLNK); |
return tmpfs_construct_node(dvp, vpp, vap, cnp, target); |
return tmpfs_alloc_file(dvp, vpp, vap, cnp, target); |
} |
} |
|
|
int |
int |
Line 952 tmpfs_readdir(void *v) |
|
Line 935 tmpfs_readdir(void *v) |
|
node = VP_TO_TMPFS_DIR(vp); |
node = VP_TO_TMPFS_DIR(vp); |
startoff = uio->uio_offset; |
startoff = uio->uio_offset; |
cnt = 0; |
cnt = 0; |
|
if (node->tn_links == 0) { |
/* |
|
* Retrieve the directory entries, unless it is being destroyed. |
|
*/ |
|
if (node->tn_links) { |
|
error = tmpfs_dir_getdents(node, uio, &cnt); |
|
} else { |
|
error = 0; |
error = 0; |
|
goto out; |
} |
} |
|
|
|
if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) { |
|
error = tmpfs_dir_getdotdent(node, uio); |
|
if (error != 0) { |
|
if (error == -1) |
|
error = 0; |
|
goto out; |
|
} |
|
cnt++; |
|
} |
|
if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) { |
|
error = tmpfs_dir_getdotdotdent(node, uio); |
|
if (error != 0) { |
|
if (error == -1) |
|
error = 0; |
|
goto out; |
|
} |
|
cnt++; |
|
} |
|
error = tmpfs_dir_getdents(node, uio, &cnt); |
|
if (error == -1) { |
|
error = 0; |
|
} |
|
KASSERT(error >= 0); |
|
out: |
if (eofflag != NULL) { |
if (eofflag != NULL) { |
*eofflag = !error && uio->uio_offset == TMPFS_DIRSEQ_EOF; |
*eofflag = (!error && uio->uio_offset == TMPFS_DIRCOOKIE_EOF); |
} |
} |
if (error || cookies == NULL || ncookies == NULL) { |
if (error || cookies == NULL || ncookies == NULL) { |
return error; |
return error; |
} |
} |
|
|
/* Update NFS-related variables, if any. */ |
/* Update NFS-related variables, if any. */ |
tmpfs_dirent_t *de = NULL; |
|
off_t i, off = startoff; |
off_t i, off = startoff; |
|
tmpfs_dirent_t *de = NULL; |
|
|
*cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK); |
*cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK); |
*ncookies = cnt; |
*ncookies = cnt; |
|
|
for (i = 0; i < cnt; i++) { |
for (i = 0; i < cnt; i++) { |
KASSERT(off != TMPFS_DIRSEQ_EOF); |
KASSERT(off != TMPFS_DIRCOOKIE_EOF); |
if (off != TMPFS_DIRSEQ_DOT) { |
if (off != TMPFS_DIRCOOKIE_DOT) { |
if (off == TMPFS_DIRSEQ_DOTDOT) { |
if (off == TMPFS_DIRCOOKIE_DOTDOT) { |
de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir); |
de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir); |
} else if (de != NULL) { |
} else if (de != NULL) { |
de = TAILQ_NEXT(de, td_entries); |
de = TAILQ_NEXT(de, td_entries); |
} else { |
} else { |
de = tmpfs_dir_lookupbyseq(node, off); |
de = tmpfs_dir_lookupbycookie(node, off); |
KASSERT(de != NULL); |
KASSERT(de != NULL); |
de = TAILQ_NEXT(de, td_entries); |
de = TAILQ_NEXT(de, td_entries); |
} |
} |
if (de == NULL) { |
if (de == NULL) { |
off = TMPFS_DIRSEQ_EOF; |
off = TMPFS_DIRCOOKIE_EOF; |
} else { |
} else { |
off = tmpfs_dir_getseq(node, de); |
off = tmpfs_dircookie(de); |
} |
} |
} else { |
} else { |
off = TMPFS_DIRSEQ_DOTDOT; |
off = TMPFS_DIRCOOKIE_DOTDOT; |
} |
} |
(*cookies)[i] = off; |
(*cookies)[i] = off; |
} |
} |
Line 1012 tmpfs_readlink(void *v) |
|
Line 1014 tmpfs_readlink(void *v) |
|
} */ *ap = v; |
} */ *ap = v; |
vnode_t *vp = ap->a_vp; |
vnode_t *vp = ap->a_vp; |
struct uio *uio = ap->a_uio; |
struct uio *uio = ap->a_uio; |
tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); |
tmpfs_node_t *node; |
int error; |
int error; |
|
|
KASSERT(VOP_ISLOCKED(vp)); |
KASSERT(VOP_ISLOCKED(vp)); |
KASSERT(uio->uio_offset == 0); |
KASSERT(uio->uio_offset == 0); |
KASSERT(vp->v_type == VLNK); |
KASSERT(vp->v_type == VLNK); |
|
|
/* Note: readlink(2) returns the path without NUL terminator. */ |
node = VP_TO_TMPFS_NODE(vp); |
if (node->tn_size > 0) { |
error = uiomove(node->tn_spec.tn_lnk.tn_link, |
error = uiomove(node->tn_spec.tn_lnk.tn_link, |
MIN(node->tn_size, uio->uio_resid), uio); |
MIN(node->tn_size, uio->uio_resid), uio); |
node->tn_status |= TMPFS_NODE_ACCESSED; |
} else { |
|
error = 0; |
|
} |
|
tmpfs_update(vp, TMPFS_UPDATE_ATIME); |
|
|
|
return error; |
return error; |
} |
} |
Line 1034 tmpfs_readlink(void *v) |
|
Line 1032 tmpfs_readlink(void *v) |
|
int |
int |
tmpfs_inactive(void *v) |
tmpfs_inactive(void *v) |
{ |
{ |
struct vop_inactive_v2_args /* { |
struct vop_inactive_args /* { |
struct vnode *a_vp; |
struct vnode *a_vp; |
bool *a_recycle; |
bool *a_recycle; |
} */ *ap = v; |
} */ *ap = v; |
Line 1044 tmpfs_inactive(void *v) |
|
Line 1042 tmpfs_inactive(void *v) |
|
KASSERT(VOP_ISLOCKED(vp)); |
KASSERT(VOP_ISLOCKED(vp)); |
|
|
node = VP_TO_TMPFS_NODE(vp); |
node = VP_TO_TMPFS_NODE(vp); |
if (node->tn_links == 0) { |
*ap->a_recycle = (node->tn_links == 0); |
/* |
VOP_UNLOCK(vp); |
* Mark node as dead by setting its generation to zero. |
|
*/ |
|
atomic_and_32(&node->tn_gen, ~TMPFS_NODE_GEN_MASK); |
|
*ap->a_recycle = true; |
|
} else { |
|
*ap->a_recycle = false; |
|
} |
|
|
|
return 0; |
return 0; |
} |
} |
Line 1060 tmpfs_inactive(void *v) |
|
Line 1051 tmpfs_inactive(void *v) |
|
int |
int |
tmpfs_reclaim(void *v) |
tmpfs_reclaim(void *v) |
{ |
{ |
struct vop_reclaim_v2_args /* { |
struct vop_reclaim_args /* { |
struct vnode *a_vp; |
struct vnode *a_vp; |
} */ *ap = v; |
} */ *ap = v; |
vnode_t *vp = ap->a_vp; |
vnode_t *vp = ap->a_vp; |
tmpfs_mount_t *tmp = VFS_TO_TMPFS(vp->v_mount); |
tmpfs_mount_t *tmp = VFS_TO_TMPFS(vp->v_mount); |
tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); |
tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); |
|
bool racing; |
/* Unlock vnode. We still have exclusive access to it. */ |
|
VOP_UNLOCK(vp); |
|
|
|
/* Disassociate inode from vnode. */ |
/* Disassociate inode from vnode. */ |
|
mutex_enter(&node->tn_vlock); |
node->tn_vnode = NULL; |
node->tn_vnode = NULL; |
vp->v_data = NULL; |
vp->v_data = NULL; |
|
/* Check if tmpfs_vnode_get() is racing with us. */ |
|
racing = TMPFS_NODE_RECLAIMING(node); |
|
mutex_exit(&node->tn_vlock); |
|
|
/* If inode is not referenced, i.e. no links, then destroy it. */ |
/* |
if (node->tn_links == 0) |
* If inode is not referenced, i.e. no links, then destroy it. |
|
* Note: if racing - inode is about to get a new vnode, leave it. |
|
*/ |
|
if (node->tn_links == 0 && !racing) { |
tmpfs_free_node(tmp, node); |
tmpfs_free_node(tmp, node); |
|
} |
return 0; |
return 0; |
} |
} |
|
|
Line 1166 tmpfs_getpages(void *v) |
|
Line 1163 tmpfs_getpages(void *v) |
|
KASSERT(vp->v_type == VREG); |
KASSERT(vp->v_type == VREG); |
KASSERT(mutex_owned(vp->v_interlock)); |
KASSERT(mutex_owned(vp->v_interlock)); |
|
|
|
node = VP_TO_TMPFS_NODE(vp); |
|
uobj = node->tn_spec.tn_reg.tn_aobj; |
|
|
/* |
/* |
* Currently, PGO_PASTEOF is not supported. |
* Currently, PGO_PASTEOF is not supported. |
*/ |
*/ |
Line 1182 tmpfs_getpages(void *v) |
|
Line 1182 tmpfs_getpages(void *v) |
|
if ((flags & PGO_LOCKED) != 0) |
if ((flags & PGO_LOCKED) != 0) |
return EBUSY; |
return EBUSY; |
|
|
if (vdead_check(vp, VDEAD_NOWAIT) != 0) |
|
return ENOENT; |
|
|
|
node = VP_TO_TMPFS_NODE(vp); |
|
uobj = node->tn_spec.tn_reg.tn_aobj; |
|
|
|
if ((flags & PGO_NOTIMESTAMP) == 0) { |
if ((flags & PGO_NOTIMESTAMP) == 0) { |
u_int tflags = 0; |
|
|
|
if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0) |
if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0) |
tflags |= TMPFS_UPDATE_ATIME; |
node->tn_status |= TMPFS_NODE_ACCESSED; |
|
|
if ((access_type & VM_PROT_WRITE) != 0) { |
if ((access_type & VM_PROT_WRITE) != 0) { |
tflags |= TMPFS_UPDATE_MTIME; |
node->tn_status |= TMPFS_NODE_MODIFIED; |
if (vp->v_mount->mnt_flag & MNT_RELATIME) |
if (vp->v_mount->mnt_flag & MNT_RELATIME) |
tflags |= TMPFS_UPDATE_ATIME; |
node->tn_status |= TMPFS_NODE_ACCESSED; |
} |
} |
tmpfs_update(vp, tflags); |
|
} |
} |
|
|
/* |
/* |
Line 1273 tmpfs_whiteout(void *v) |
|
Line 1264 tmpfs_whiteout(void *v) |
|
struct componentname *cnp = ap->a_cnp; |
struct componentname *cnp = ap->a_cnp; |
const int flags = ap->a_flags; |
const int flags = ap->a_flags; |
tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount); |
tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount); |
tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp); |
|
tmpfs_dirent_t *de; |
tmpfs_dirent_t *de; |
int error; |
int error; |
|
|
Line 1285 tmpfs_whiteout(void *v) |
|
Line 1275 tmpfs_whiteout(void *v) |
|
cnp->cn_namelen, &de); |
cnp->cn_namelen, &de); |
if (error) |
if (error) |
return error; |
return error; |
tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT); |
tmpfs_dir_attach(dvp, de, TMPFS_NODE_WHITEOUT); |
break; |
break; |
case DELETE: |
case DELETE: |
cnp->cn_flags &= ~DOWHITEOUT; /* when in doubt, cargo cult */ |
cnp->cn_flags &= ~DOWHITEOUT; /* when in doubt, cargo cult */ |
de = tmpfs_dir_lookup(dnode, cnp); |
de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), cnp); |
if (de == NULL) |
if (de == NULL) |
return ENOENT; |
return ENOENT; |
tmpfs_dir_detach(dnode, de); |
tmpfs_dir_detach(dvp, de); |
tmpfs_free_dirent(tmp, de); |
tmpfs_free_dirent(tmp, de); |
break; |
break; |
} |
} |
tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); |
|
return 0; |
return 0; |
} |
} |
|
|
Line 1310 tmpfs_print(void *v) |
|
Line 1299 tmpfs_print(void *v) |
|
tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); |
tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); |
|
|
printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n" |
printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n" |
"\tmode 0%o, owner %d, group %d, size %" PRIdMAX, |
"\tmode 0%o, owner %d, group %d, size %" PRIdMAX ", status 0x%x", |
node, node->tn_flags, node->tn_links, node->tn_mode, node->tn_uid, |
node, node->tn_flags, node->tn_links, node->tn_mode, node->tn_uid, |
node->tn_gid, (uintmax_t)node->tn_size); |
node->tn_gid, (uintmax_t)node->tn_size, node->tn_status); |
if (vp->v_type == VFIFO) { |
if (vp->v_type == VFIFO) { |
VOCALL(fifo_vnodeop_p, VOFFSET(vop_print), v); |
VOCALL(fifo_vnodeop_p, VOFFSET(vop_print), v); |
} |
} |