version 1.77, 2011/08/27 15:32:28 |
version 1.77.2.2, 2014/05/22 11:41:02 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
|
|
/* |
/* |
* Copyright (c) 2005-2011 The NetBSD Foundation, Inc. |
* Copyright (c) 2005-2013 The NetBSD Foundation, Inc. |
* All rights reserved. |
* All rights reserved. |
* |
* |
* This code is derived from software contributed to The NetBSD Foundation |
* This code is derived from software contributed to The NetBSD Foundation |
|
|
* reference counting and link counting. That is, an inode can only be |
* reference counting and link counting. That is, an inode can only be |
* destroyed if its associated vnode is inactive. The destruction is |
* destroyed if its associated vnode is inactive. The destruction is |
* done on vnode reclamation i.e. tmpfs_reclaim(). It should be noted |
* done on vnode reclamation i.e. tmpfs_reclaim(). It should be noted |
* that tmpfs_node_t::tn_links being 0 is a destruction criterion. |
* that tmpfs_node_t::tn_links being 0 is a destruction criterion. |
* |
* |
* If an inode has references within the file system (tn_links > 0) and |
* If an inode has references within the file system (tn_links > 0) and |
* its inactive vnode gets reclaimed/recycled - then the association is |
* its inactive vnode gets reclaimed/recycled - then the association is |
|
|
__KERNEL_RCSID(0, "$NetBSD$"); |
__KERNEL_RCSID(0, "$NetBSD$"); |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
|
#include <sys/cprng.h> |
#include <sys/dirent.h> |
#include <sys/dirent.h> |
#include <sys/event.h> |
#include <sys/event.h> |
#include <sys/kmem.h> |
#include <sys/kmem.h> |
Line 98 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 99 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <fs/tmpfs/tmpfs_specops.h> |
#include <fs/tmpfs/tmpfs_specops.h> |
#include <fs/tmpfs/tmpfs_vnops.h> |
#include <fs/tmpfs/tmpfs_vnops.h> |
|
|
|
static void tmpfs_dir_putseq(tmpfs_node_t *, tmpfs_dirent_t *); |
|
|
/* |
/* |
* tmpfs_alloc_node: allocate a new inode of a specified type and |
* tmpfs_alloc_node: allocate a new inode of a specified type and |
* insert it into the list of specified mount point. |
* insert it into the list of specified mount point. |
Line 124 tmpfs_alloc_node(tmpfs_mount_t *tmp, enu |
|
Line 127 tmpfs_alloc_node(tmpfs_mount_t *tmp, enu |
|
* for applications that do not understand 64-bit ino_t. |
* for applications that do not understand 64-bit ino_t. |
*/ |
*/ |
nnode->tn_id = (ino_t)((uintptr_t)nnode / sizeof(*nnode)); |
nnode->tn_id = (ino_t)((uintptr_t)nnode / sizeof(*nnode)); |
nnode->tn_gen = TMPFS_NODE_GEN_MASK & arc4random(); |
/* |
|
* Make sure the generation number is not zero. |
|
* tmpfs_inactive() uses generation zero to mark dead nodes. |
|
*/ |
|
do { |
|
nnode->tn_gen = TMPFS_NODE_GEN_MASK & cprng_fast32(); |
|
} while (nnode->tn_gen == 0); |
|
|
/* Generic initialization. */ |
/* Generic initialization. */ |
nnode->tn_type = type; |
nnode->tn_type = type; |
nnode->tn_size = 0; |
nnode->tn_size = 0; |
nnode->tn_status = 0; |
|
nnode->tn_flags = 0; |
nnode->tn_flags = 0; |
nnode->tn_lockf = NULL; |
nnode->tn_lockf = NULL; |
|
|
Line 155 tmpfs_alloc_node(tmpfs_mount_t *tmp, enu |
|
Line 163 tmpfs_alloc_node(tmpfs_mount_t *tmp, enu |
|
/* Directory. */ |
/* Directory. */ |
TAILQ_INIT(&nnode->tn_spec.tn_dir.tn_dir); |
TAILQ_INIT(&nnode->tn_spec.tn_dir.tn_dir); |
nnode->tn_spec.tn_dir.tn_parent = NULL; |
nnode->tn_spec.tn_dir.tn_parent = NULL; |
nnode->tn_spec.tn_dir.tn_readdir_lastn = 0; |
nnode->tn_spec.tn_dir.tn_seq_arena = NULL; |
|
nnode->tn_spec.tn_dir.tn_next_seq = TMPFS_DIRSEQ_START; |
nnode->tn_spec.tn_dir.tn_readdir_lastp = NULL; |
nnode->tn_spec.tn_dir.tn_readdir_lastp = NULL; |
|
|
/* Extra link count for the virtual '.' entry. */ |
/* Extra link count for the virtual '.' entry. */ |
Line 166 tmpfs_alloc_node(tmpfs_mount_t *tmp, enu |
|
Line 175 tmpfs_alloc_node(tmpfs_mount_t *tmp, enu |
|
break; |
break; |
case VLNK: |
case VLNK: |
/* Symbolic link. Target specifies the file name. */ |
/* Symbolic link. Target specifies the file name. */ |
KASSERT(target && strlen(target) < MAXPATHLEN); |
KASSERT(target != NULL); |
|
|
nnode->tn_size = strlen(target); |
nnode->tn_size = strlen(target); |
|
|
if (nnode->tn_size == 0) { |
if (nnode->tn_size == 0) { |
|
/* Zero-length targets are supported. */ |
nnode->tn_spec.tn_lnk.tn_link = NULL; |
nnode->tn_spec.tn_lnk.tn_link = NULL; |
break; |
break; |
} |
} |
|
|
|
KASSERT(nnode->tn_size < MAXPATHLEN); |
|
nnode->tn_size++; /* include the NUL terminator */ |
|
|
nnode->tn_spec.tn_lnk.tn_link = |
nnode->tn_spec.tn_lnk.tn_link = |
tmpfs_strname_alloc(tmp, nnode->tn_size); |
tmpfs_strname_alloc(tmp, nnode->tn_size); |
if (nnode->tn_spec.tn_lnk.tn_link == NULL) { |
if (nnode->tn_spec.tn_lnk.tn_link == NULL) { |
Line 235 tmpfs_free_node(tmpfs_mount_t *tmp, tmpf |
|
Line 249 tmpfs_free_node(tmpfs_mount_t *tmp, tmpf |
|
} |
} |
break; |
break; |
case VDIR: |
case VDIR: |
/* |
KASSERT(node->tn_size == 0); |
* KASSERT(TAILQ_EMPTY(&node->tn_spec.tn_dir.tn_dir)); |
KASSERT(node->tn_spec.tn_dir.tn_seq_arena == NULL); |
* KASSERT(node->tn_spec.tn_dir.tn_parent == NULL || |
KASSERT(TAILQ_EMPTY(&node->tn_spec.tn_dir.tn_dir)); |
* node == tmp->tm_root); |
KASSERT(node->tn_spec.tn_dir.tn_parent == NULL || |
*/ |
node == tmp->tm_root); |
break; |
break; |
default: |
default: |
break; |
break; |
} |
} |
|
KASSERT(node->tn_vnode == NULL); |
|
KASSERT(node->tn_links == 0); |
|
|
mutex_destroy(&node->tn_vlock); |
mutex_destroy(&node->tn_vlock); |
tmpfs_node_put(tmp, node); |
tmpfs_node_put(tmp, node); |
Line 264 tmpfs_vnode_get(struct mount *mp, tmpfs_ |
|
Line 280 tmpfs_vnode_get(struct mount *mp, tmpfs_ |
|
again: |
again: |
/* If there is already a vnode, try to reclaim it. */ |
/* If there is already a vnode, try to reclaim it. */ |
if ((vp = node->tn_vnode) != NULL) { |
if ((vp = node->tn_vnode) != NULL) { |
atomic_or_ulong(&node->tn_gen, TMPFS_RECLAIMING_BIT); |
atomic_or_32(&node->tn_gen, TMPFS_RECLAIMING_BIT); |
mutex_enter(vp->v_interlock); |
mutex_enter(vp->v_interlock); |
mutex_exit(&node->tn_vlock); |
mutex_exit(&node->tn_vlock); |
error = vget(vp, LK_EXCLUSIVE); |
error = vget(vp, LK_EXCLUSIVE); |
|
|
mutex_enter(&node->tn_vlock); |
mutex_enter(&node->tn_vlock); |
goto again; |
goto again; |
} |
} |
atomic_and_ulong(&node->tn_gen, ~TMPFS_RECLAIMING_BIT); |
atomic_and_32(&node->tn_gen, ~TMPFS_RECLAIMING_BIT); |
*vpp = vp; |
*vpp = vp; |
return error; |
return error; |
} |
} |
if (TMPFS_NODE_RECLAIMING(node)) { |
if (TMPFS_NODE_RECLAIMING(node)) { |
atomic_and_ulong(&node->tn_gen, ~TMPFS_RECLAIMING_BIT); |
atomic_and_32(&node->tn_gen, ~TMPFS_RECLAIMING_BIT); |
} |
} |
|
|
/* |
/* |
|
|
} |
} |
|
|
/* |
/* |
* tmpfs_alloc_file: allocate a new file of specified type and adds it |
* tmpfs_construct_node: allocate a new file of specified type and adds it |
* into the parent directory. |
* into the parent directory. |
* |
* |
* => Credentials of the caller are used. |
* => Credentials of the caller are used. |
*/ |
*/ |
int |
int |
tmpfs_alloc_file(vnode_t *dvp, vnode_t **vpp, struct vattr *vap, |
tmpfs_construct_node(vnode_t *dvp, vnode_t **vpp, struct vattr *vap, |
struct componentname *cnp, char *target) |
struct componentname *cnp, char *target) |
{ |
{ |
tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount); |
tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount); |
Line 349 tmpfs_alloc_file(vnode_t *dvp, vnode_t * |
|
Line 365 tmpfs_alloc_file(vnode_t *dvp, vnode_t * |
|
KASSERT(VOP_ISLOCKED(dvp)); |
KASSERT(VOP_ISLOCKED(dvp)); |
*vpp = NULL; |
*vpp = NULL; |
|
|
|
/* |
|
* If directory was removed, prevent from node creation. The vnode |
|
* might still be referenced, but it is about to be reclaimed. |
|
*/ |
|
if (dnode->tn_links == 0) { |
|
error = ENOENT; |
|
goto out; |
|
} |
|
|
/* Check for the maximum number of links limit. */ |
/* Check for the maximum number of links limit. */ |
if (vap->va_type == VDIR) { |
if (vap->va_type == VDIR) { |
/* Check for maximum links limit. */ |
/* Check for maximum links limit. */ |
Line 385 tmpfs_alloc_file(vnode_t *dvp, vnode_t * |
|
Line 410 tmpfs_alloc_file(vnode_t *dvp, vnode_t * |
|
if (cnp->cn_flags & ISWHITEOUT) { |
if (cnp->cn_flags & ISWHITEOUT) { |
wde = tmpfs_dir_lookup(dnode, cnp); |
wde = tmpfs_dir_lookup(dnode, cnp); |
KASSERT(wde != NULL && wde->td_node == TMPFS_NODE_WHITEOUT); |
KASSERT(wde != NULL && wde->td_node == TMPFS_NODE_WHITEOUT); |
tmpfs_dir_detach(dvp, wde); |
tmpfs_dir_detach(dnode, wde); |
tmpfs_free_dirent(tmp, wde); |
tmpfs_free_dirent(tmp, wde); |
} |
} |
|
|
/* Associate inode and attach the entry into the directory. */ |
/* Associate inode and attach the entry into the directory. */ |
tmpfs_dir_attach(dvp, de, node); |
tmpfs_dir_attach(dnode, de, node); |
|
|
/* Make node opaque if requested. */ |
/* Make node opaque if requested. */ |
if (cnp->cn_flags & ISWHITEOUT) |
if (cnp->cn_flags & ISWHITEOUT) |
node->tn_flags |= UF_OPAQUE; |
node->tn_flags |= UF_OPAQUE; |
|
|
|
/* Update the parent's timestamps. */ |
|
tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); |
out: |
out: |
vput(dvp); |
if (error == 0) |
|
VOP_UNLOCK(*vpp); |
|
|
return error; |
return error; |
} |
} |
|
|
Line 421 tmpfs_alloc_dirent(tmpfs_mount_t *tmp, c |
|
Line 451 tmpfs_alloc_dirent(tmpfs_mount_t *tmp, c |
|
} |
} |
nde->td_namelen = len; |
nde->td_namelen = len; |
memcpy(nde->td_name, name, len); |
memcpy(nde->td_name, name, len); |
|
nde->td_seq = TMPFS_DIRSEQ_NONE; |
|
|
*de = nde; |
*de = nde; |
return 0; |
return 0; |
Line 432 tmpfs_alloc_dirent(tmpfs_mount_t *tmp, c |
|
Line 463 tmpfs_alloc_dirent(tmpfs_mount_t *tmp, c |
|
void |
void |
tmpfs_free_dirent(tmpfs_mount_t *tmp, tmpfs_dirent_t *de) |
tmpfs_free_dirent(tmpfs_mount_t *tmp, tmpfs_dirent_t *de) |
{ |
{ |
|
KASSERT(de->td_node == NULL); |
/* KASSERT(de->td_node == NULL); */ |
KASSERT(de->td_seq == TMPFS_DIRSEQ_NONE); |
tmpfs_strname_free(tmp, de->td_name, de->td_namelen); |
tmpfs_strname_free(tmp, de->td_name, de->td_namelen); |
tmpfs_dirent_put(tmp, de); |
tmpfs_dirent_put(tmp, de); |
} |
} |
Line 443 tmpfs_free_dirent(tmpfs_mount_t *tmp, tm |
|
Line 474 tmpfs_free_dirent(tmpfs_mount_t *tmp, tm |
|
* and attach the entry into the directory, specified by vnode. |
* and attach the entry into the directory, specified by vnode. |
* |
* |
* => Increases link count on the associated node. |
* => Increases link count on the associated node. |
* => Increases link count on directory node, if our node is VDIR. |
* => Increases link count on directory node if our node is VDIR. |
* It is caller's responsibility to check for the LINK_MAX limit. |
* => It is caller's responsibility to check for the LINK_MAX limit. |
* => Triggers kqueue events here. |
* => Triggers kqueue events here. |
*/ |
*/ |
void |
void |
tmpfs_dir_attach(vnode_t *dvp, tmpfs_dirent_t *de, tmpfs_node_t *node) |
tmpfs_dir_attach(tmpfs_node_t *dnode, tmpfs_dirent_t *de, tmpfs_node_t *node) |
{ |
{ |
tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp); |
vnode_t *dvp = dnode->tn_vnode; |
int events = NOTE_WRITE; |
int events = NOTE_WRITE; |
|
|
|
KASSERT(dvp != NULL); |
KASSERT(VOP_ISLOCKED(dvp)); |
KASSERT(VOP_ISLOCKED(dvp)); |
|
|
|
/* Get a new sequence number. */ |
|
KASSERT(de->td_seq == TMPFS_DIRSEQ_NONE); |
|
de->td_seq = tmpfs_dir_getseq(dnode, de); |
|
|
/* Associate directory entry and the inode. */ |
/* Associate directory entry and the inode. */ |
de->td_node = node; |
de->td_node = node; |
if (node != TMPFS_NODE_WHITEOUT) { |
if (node != TMPFS_NODE_WHITEOUT) { |
Line 463 tmpfs_dir_attach(vnode_t *dvp, tmpfs_dir |
|
Line 499 tmpfs_dir_attach(vnode_t *dvp, tmpfs_dir |
|
|
|
/* Save the hint (might overwrite). */ |
/* Save the hint (might overwrite). */ |
node->tn_dirent_hint = de; |
node->tn_dirent_hint = de; |
|
} else if ((dnode->tn_gen & TMPFS_WHITEOUT_BIT) == 0) { |
|
/* Flag that there are whiteout entries. */ |
|
atomic_or_32(&dnode->tn_gen, TMPFS_WHITEOUT_BIT); |
} |
} |
|
|
/* Insert the entry to the directory (parent of inode). */ |
/* Insert the entry to the directory (parent of inode). */ |
TAILQ_INSERT_TAIL(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries); |
TAILQ_INSERT_TAIL(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries); |
dnode->tn_size += sizeof(tmpfs_dirent_t); |
dnode->tn_size += sizeof(tmpfs_dirent_t); |
dnode->tn_status |= TMPFS_NODE_STATUSALL; |
|
uvm_vnp_setsize(dvp, dnode->tn_size); |
uvm_vnp_setsize(dvp, dnode->tn_size); |
|
|
if (node != TMPFS_NODE_WHITEOUT && node->tn_type == VDIR) { |
if (node != TMPFS_NODE_WHITEOUT && node->tn_type == VDIR) { |
Line 493 tmpfs_dir_attach(vnode_t *dvp, tmpfs_dir |
|
Line 531 tmpfs_dir_attach(vnode_t *dvp, tmpfs_dir |
|
* => Decreases link count on the associated node. |
* => Decreases link count on the associated node. |
* => Decreases the link count on directory node, if our node is VDIR. |
* => Decreases the link count on directory node, if our node is VDIR. |
* => Triggers kqueue events here. |
* => Triggers kqueue events here. |
|
* |
|
* => Note: dvp and vp may be NULL only if called by tmpfs_unmount(). |
*/ |
*/ |
void |
void |
tmpfs_dir_detach(vnode_t *dvp, tmpfs_dirent_t *de) |
tmpfs_dir_detach(tmpfs_node_t *dnode, tmpfs_dirent_t *de) |
{ |
{ |
tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp); |
|
tmpfs_node_t *node = de->td_node; |
tmpfs_node_t *node = de->td_node; |
|
vnode_t *vp, *dvp = dnode->tn_vnode; |
int events = NOTE_WRITE; |
int events = NOTE_WRITE; |
|
|
KASSERT(VOP_ISLOCKED(dvp)); |
KASSERT(dvp == NULL || VOP_ISLOCKED(dvp)); |
|
|
if (node != TMPFS_NODE_WHITEOUT) { |
|
vnode_t *vp = node->tn_vnode; |
|
|
|
KASSERT(VOP_ISLOCKED(vp)); |
|
|
|
|
if (__predict_true(node != TMPFS_NODE_WHITEOUT)) { |
/* Deassociate the inode and entry. */ |
/* Deassociate the inode and entry. */ |
de->td_node = NULL; |
|
node->tn_dirent_hint = NULL; |
node->tn_dirent_hint = NULL; |
|
|
KASSERT(node->tn_links > 0); |
KASSERT(node->tn_links > 0); |
node->tn_links--; |
node->tn_links--; |
if (vp) { |
|
VN_KNOTE(vp, node->tn_links ? |
if ((vp = node->tn_vnode) != NULL) { |
NOTE_LINK : NOTE_DELETE); |
KASSERT(VOP_ISLOCKED(vp)); |
|
VN_KNOTE(vp, node->tn_links ? NOTE_LINK : NOTE_DELETE); |
} |
} |
|
|
/* If directory - decrease the link count of parent. */ |
/* If directory - decrease the link count of parent. */ |
Line 529 tmpfs_dir_detach(vnode_t *dvp, tmpfs_dir |
|
Line 565 tmpfs_dir_detach(vnode_t *dvp, tmpfs_dir |
|
events |= NOTE_LINK; |
events |= NOTE_LINK; |
} |
} |
} |
} |
|
de->td_node = NULL; |
|
|
/* Remove the entry from the directory. */ |
/* Remove the entry from the directory. */ |
if (dnode->tn_spec.tn_dir.tn_readdir_lastp == de) { |
if (dnode->tn_spec.tn_dir.tn_readdir_lastp == de) { |
dnode->tn_spec.tn_dir.tn_readdir_lastn = 0; |
|
dnode->tn_spec.tn_dir.tn_readdir_lastp = NULL; |
dnode->tn_spec.tn_dir.tn_readdir_lastp = NULL; |
} |
} |
TAILQ_REMOVE(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries); |
TAILQ_REMOVE(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries); |
|
|
dnode->tn_size -= sizeof(tmpfs_dirent_t); |
dnode->tn_size -= sizeof(tmpfs_dirent_t); |
dnode->tn_status |= TMPFS_NODE_STATUSALL; |
tmpfs_dir_putseq(dnode, de); |
uvm_vnp_setsize(dvp, dnode->tn_size); |
|
VN_KNOTE(dvp, events); |
if (dvp) { |
|
uvm_vnp_setsize(dvp, dnode->tn_size); |
|
VN_KNOTE(dvp, events); |
|
} |
} |
} |
|
|
/* |
/* |
Line 568 tmpfs_dir_lookup(tmpfs_node_t *node, str |
|
Line 606 tmpfs_dir_lookup(tmpfs_node_t *node, str |
|
continue; |
continue; |
break; |
break; |
} |
} |
node->tn_status |= TMPFS_NODE_ACCESSED; |
|
return de; |
return de; |
} |
} |
|
|
/* |
/* |
* tmpfs_dir_cached: get a cached directory entry if it is valid. Used to |
* tmpfs_dir_cached: get a cached directory entry if it is valid. Used to |
* avoid unnecessary tmpds_dir_lookup(). |
* avoid unnecessary tmpfs_dir_lookup(). |
* |
* |
* => The vnode must be locked. |
* => The vnode must be locked. |
*/ |
*/ |
Line 598 tmpfs_dir_cached(tmpfs_node_t *node) |
|
Line 635 tmpfs_dir_cached(tmpfs_node_t *node) |
|
} |
} |
|
|
/* |
/* |
* tmpfs_dir_getdotdent: helper function for tmpfs_readdir. Creates a |
* tmpfs_dir_getseq: get a per-directory sequence number for the entry. |
* '.' entry for the given directory and returns it in the uio space. |
* |
|
* => Shall not be larger than 2^31 for linux32 compatibility. |
*/ |
*/ |
int |
uint32_t |
tmpfs_dir_getdotdent(tmpfs_node_t *node, struct uio *uio) |
tmpfs_dir_getseq(tmpfs_node_t *dnode, tmpfs_dirent_t *de) |
{ |
{ |
struct dirent *dentp; |
uint32_t seq = de->td_seq; |
int error; |
vmem_t *seq_arena; |
|
vmem_addr_t off; |
|
int error __diagused; |
|
|
TMPFS_VALIDATE_DIR(node); |
TMPFS_VALIDATE_DIR(dnode); |
KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOT); |
|
|
|
dentp = kmem_alloc(sizeof(struct dirent), KM_SLEEP); |
if (__predict_true(seq != TMPFS_DIRSEQ_NONE)) { |
dentp->d_fileno = node->tn_id; |
/* Already set. */ |
dentp->d_type = DT_DIR; |
KASSERT(seq >= TMPFS_DIRSEQ_START); |
dentp->d_namlen = 1; |
return seq; |
dentp->d_name[0] = '.'; |
} |
dentp->d_name[1] = '\0'; |
|
dentp->d_reclen = _DIRENT_SIZE(dentp); |
/* |
|
* The "." and ".." and the end-of-directory have reserved numbers. |
if (dentp->d_reclen > uio->uio_resid) |
* The other sequence numbers are allocated as following: |
error = -1; |
* |
else { |
* - The first half of the 2^31 is assigned incrementally. |
error = uiomove(dentp, dentp->d_reclen, uio); |
* |
if (error == 0) |
* - If that range is exceeded, then the second half of 2^31 |
uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT; |
* is used, but managed by vmem(9). |
|
*/ |
|
|
|
seq = dnode->tn_spec.tn_dir.tn_next_seq; |
|
KASSERT(seq >= TMPFS_DIRSEQ_START); |
|
|
|
if (__predict_true(seq < TMPFS_DIRSEQ_END)) { |
|
/* First half: just increment and return. */ |
|
dnode->tn_spec.tn_dir.tn_next_seq++; |
|
return seq; |
|
} |
|
|
|
/* |
|
* First half exceeded, use the second half. May need to create |
|
* vmem(9) arena for the directory first. |
|
*/ |
|
if ((seq_arena = dnode->tn_spec.tn_dir.tn_seq_arena) == NULL) { |
|
seq_arena = vmem_create("tmpfscoo", 0, |
|
TMPFS_DIRSEQ_END - 1, 1, NULL, NULL, NULL, 0, |
|
VM_SLEEP, IPL_NONE); |
|
dnode->tn_spec.tn_dir.tn_seq_arena = seq_arena; |
|
KASSERT(seq_arena != NULL); |
|
} |
|
error = vmem_alloc(seq_arena, 1, VM_SLEEP | VM_BESTFIT, &off); |
|
KASSERT(error == 0); |
|
|
|
KASSERT(off < TMPFS_DIRSEQ_END); |
|
seq = off | TMPFS_DIRSEQ_END; |
|
return seq; |
|
} |
|
|
|
static void |
|
tmpfs_dir_putseq(tmpfs_node_t *dnode, tmpfs_dirent_t *de) |
|
{ |
|
vmem_t *seq_arena = dnode->tn_spec.tn_dir.tn_seq_arena; |
|
uint32_t seq = de->td_seq; |
|
|
|
TMPFS_VALIDATE_DIR(dnode); |
|
|
|
if (seq == TMPFS_DIRSEQ_NONE || seq < TMPFS_DIRSEQ_END) { |
|
/* First half (or no sequence number set yet). */ |
|
KASSERT(de->td_seq >= TMPFS_DIRSEQ_START); |
|
} else { |
|
/* Second half. */ |
|
KASSERT(seq_arena != NULL); |
|
KASSERT(seq >= TMPFS_DIRSEQ_END); |
|
seq &= ~TMPFS_DIRSEQ_END; |
|
vmem_free(seq_arena, seq, 1); |
|
} |
|
de->td_seq = TMPFS_DIRSEQ_NONE; |
|
|
|
/* Empty? We can reset. */ |
|
if (seq_arena && dnode->tn_size == 0) { |
|
dnode->tn_spec.tn_dir.tn_seq_arena = NULL; |
|
dnode->tn_spec.tn_dir.tn_next_seq = TMPFS_DIRSEQ_START; |
|
vmem_destroy(seq_arena); |
} |
} |
node->tn_status |= TMPFS_NODE_ACCESSED; |
|
kmem_free(dentp, sizeof(struct dirent)); |
|
return error; |
|
} |
} |
|
|
/* |
/* |
* tmpfs_dir_getdotdotdent: helper function for tmpfs_readdir. Creates a |
* tmpfs_dir_lookupbyseq: lookup a directory entry by the sequence number. |
* '..' entry for the given directory and returns it in the uio space. |
|
*/ |
*/ |
int |
tmpfs_dirent_t * |
tmpfs_dir_getdotdotdent(tmpfs_node_t *node, struct uio *uio) |
tmpfs_dir_lookupbyseq(tmpfs_node_t *node, off_t seq) |
{ |
{ |
struct dirent *dentp; |
tmpfs_dirent_t *de = node->tn_spec.tn_dir.tn_readdir_lastp; |
int error; |
|
|
|
TMPFS_VALIDATE_DIR(node); |
TMPFS_VALIDATE_DIR(node); |
KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT); |
|
|
|
dentp = kmem_alloc(sizeof(struct dirent), KM_SLEEP); |
/* |
dentp->d_fileno = node->tn_spec.tn_dir.tn_parent->tn_id; |
* First, check the cache. If does not match - perform a lookup. |
dentp->d_type = DT_DIR; |
*/ |
dentp->d_namlen = 2; |
if (de && de->td_seq == seq) { |
dentp->d_name[0] = '.'; |
KASSERT(de->td_seq >= TMPFS_DIRSEQ_START); |
dentp->d_name[1] = '.'; |
KASSERT(de->td_seq != TMPFS_DIRSEQ_NONE); |
dentp->d_name[2] = '\0'; |
return de; |
dentp->d_reclen = _DIRENT_SIZE(dentp); |
|
|
|
if (dentp->d_reclen > uio->uio_resid) |
|
error = -1; |
|
else { |
|
error = uiomove(dentp, dentp->d_reclen, uio); |
|
if (error == 0) { |
|
tmpfs_dirent_t *de; |
|
|
|
de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir); |
|
if (de == NULL) |
|
uio->uio_offset = TMPFS_DIRCOOKIE_EOF; |
|
else |
|
uio->uio_offset = tmpfs_dircookie(de); |
|
} |
|
} |
} |
node->tn_status |= TMPFS_NODE_ACCESSED; |
TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) { |
kmem_free(dentp, sizeof(struct dirent)); |
KASSERT(de->td_seq >= TMPFS_DIRSEQ_START); |
return error; |
KASSERT(de->td_seq != TMPFS_DIRSEQ_NONE); |
|
if (de->td_seq == seq) |
|
return de; |
|
} |
|
return NULL; |
} |
} |
|
|
/* |
/* |
* tmpfs_dir_lookupbycookie: lookup a directory entry by associated cookie. |
* tmpfs_dir_getdotents: helper function for tmpfs_readdir() to get the |
|
* dot meta entries, that is, "." or "..". Copy it to the UIO space. |
*/ |
*/ |
tmpfs_dirent_t * |
static int |
tmpfs_dir_lookupbycookie(tmpfs_node_t *node, off_t cookie) |
tmpfs_dir_getdotents(tmpfs_node_t *node, struct dirent *dp, struct uio *uio) |
{ |
{ |
tmpfs_dirent_t *de; |
tmpfs_dirent_t *de; |
|
off_t next = 0; |
|
int error; |
|
|
KASSERT(VOP_ISLOCKED(node->tn_vnode)); |
switch (uio->uio_offset) { |
|
case TMPFS_DIRSEQ_DOT: |
|
dp->d_fileno = node->tn_id; |
|
strlcpy(dp->d_name, ".", sizeof(dp->d_name)); |
|
next = TMPFS_DIRSEQ_DOTDOT; |
|
break; |
|
case TMPFS_DIRSEQ_DOTDOT: |
|
dp->d_fileno = node->tn_spec.tn_dir.tn_parent->tn_id; |
|
strlcpy(dp->d_name, "..", sizeof(dp->d_name)); |
|
de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir); |
|
next = de ? tmpfs_dir_getseq(node, de) : TMPFS_DIRSEQ_EOF; |
|
break; |
|
default: |
|
KASSERT(false); |
|
} |
|
dp->d_type = DT_DIR; |
|
dp->d_namlen = strlen(dp->d_name); |
|
dp->d_reclen = _DIRENT_SIZE(dp); |
|
|
if (cookie == node->tn_spec.tn_dir.tn_readdir_lastn && |
if (dp->d_reclen > uio->uio_resid) { |
node->tn_spec.tn_dir.tn_readdir_lastp != NULL) { |
return EJUSTRETURN; |
return node->tn_spec.tn_dir.tn_readdir_lastp; |
|
} |
} |
TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) { |
if ((error = uiomove(dp, dp->d_reclen, uio)) != 0) { |
if (tmpfs_dircookie(de) == cookie) { |
return error; |
break; |
|
} |
|
} |
} |
return de; |
|
|
uio->uio_offset = next; |
|
return error; |
} |
} |
|
|
/* |
/* |
* tmpfs_dir_getdents: relper function for tmpfs_readdir. |
* tmpfs_dir_getdents: helper function for tmpfs_readdir. |
* |
* |
* => Returns as much directory entries as can fit in the uio space. |
* => Returns as much directory entries as can fit in the uio space. |
* => The read starts at uio->uio_offset. |
* => The read starts at uio->uio_offset. |
|
|
tmpfs_dir_getdents(tmpfs_node_t *node, struct uio *uio, off_t *cntp) |
tmpfs_dir_getdents(tmpfs_node_t *node, struct uio *uio, off_t *cntp) |
{ |
{ |
tmpfs_dirent_t *de; |
tmpfs_dirent_t *de; |
struct dirent *dentp; |
struct dirent dent; |
off_t startcookie; |
int error = 0; |
int error; |
|
|
|
KASSERT(VOP_ISLOCKED(node->tn_vnode)); |
KASSERT(VOP_ISLOCKED(node->tn_vnode)); |
TMPFS_VALIDATE_DIR(node); |
TMPFS_VALIDATE_DIR(node); |
|
|
/* |
/* |
* Locate the first directory entry we have to return. We have cached |
* First check for the "." and ".." cases. |
* the last readdir in the node, so use those values if appropriate. |
* Note: tmpfs_dir_getdotents() will "seek" for us. |
* Otherwise do a linear scan to find the requested entry. |
|
*/ |
*/ |
startcookie = uio->uio_offset; |
memset(&dent, 0, sizeof(dent)); |
KASSERT(startcookie != TMPFS_DIRCOOKIE_DOT); |
|
KASSERT(startcookie != TMPFS_DIRCOOKIE_DOTDOT); |
if (uio->uio_offset == TMPFS_DIRSEQ_DOT) { |
if (startcookie == TMPFS_DIRCOOKIE_EOF) { |
if ((error = tmpfs_dir_getdotents(node, &dent, uio)) != 0) { |
return 0; |
goto done; |
} else { |
} |
de = tmpfs_dir_lookupbycookie(node, startcookie); |
(*cntp)++; |
} |
} |
|
if (uio->uio_offset == TMPFS_DIRSEQ_DOTDOT) { |
|
if ((error = tmpfs_dir_getdotents(node, &dent, uio)) != 0) { |
|
goto done; |
|
} |
|
(*cntp)++; |
|
} |
|
|
|
/* Done if we reached the end. */ |
|
if (uio->uio_offset == TMPFS_DIRSEQ_EOF) { |
|
goto done; |
|
} |
|
|
|
/* Locate the directory entry given by the given sequence number. */ |
|
de = tmpfs_dir_lookupbyseq(node, uio->uio_offset); |
if (de == NULL) { |
if (de == NULL) { |
return EINVAL; |
error = EINVAL; |
|
goto done; |
} |
} |
|
|
/* |
/* |
* Read as much entries as possible; i.e., until we reach the end |
* Read as many entries as possible; i.e., until we reach the end |
* of the directory or we exhaust uio space. |
* of the directory or we exhaust UIO space. |
*/ |
*/ |
dentp = kmem_alloc(sizeof(struct dirent), KM_SLEEP); |
|
do { |
do { |
/* |
|
* Create a dirent structure representing the current |
|
* inode and fill it. |
|
*/ |
|
if (de->td_node == TMPFS_NODE_WHITEOUT) { |
if (de->td_node == TMPFS_NODE_WHITEOUT) { |
dentp->d_fileno = 1; |
dent.d_fileno = 1; |
dentp->d_type = DT_WHT; |
dent.d_type = DT_WHT; |
} else { |
} else { |
dentp->d_fileno = de->td_node->tn_id; |
dent.d_fileno = de->td_node->tn_id; |
switch (de->td_node->tn_type) { |
dent.d_type = vtype2dt(de->td_node->tn_type); |
case VBLK: |
|
dentp->d_type = DT_BLK; |
|
break; |
|
case VCHR: |
|
dentp->d_type = DT_CHR; |
|
break; |
|
case VDIR: |
|
dentp->d_type = DT_DIR; |
|
break; |
|
case VFIFO: |
|
dentp->d_type = DT_FIFO; |
|
break; |
|
case VLNK: |
|
dentp->d_type = DT_LNK; |
|
break; |
|
case VREG: |
|
dentp->d_type = DT_REG; |
|
break; |
|
case VSOCK: |
|
dentp->d_type = DT_SOCK; |
|
break; |
|
default: |
|
KASSERT(false); |
|
} |
|
} |
} |
dentp->d_namlen = de->td_namelen; |
dent.d_namlen = de->td_namelen; |
KASSERT(de->td_namelen < sizeof(dentp->d_name)); |
KASSERT(de->td_namelen < sizeof(dent.d_name)); |
memcpy(dentp->d_name, de->td_name, de->td_namelen); |
memcpy(dent.d_name, de->td_name, de->td_namelen); |
dentp->d_name[de->td_namelen] = '\0'; |
dent.d_name[de->td_namelen] = '\0'; |
dentp->d_reclen = _DIRENT_SIZE(dentp); |
dent.d_reclen = _DIRENT_SIZE(&dent); |
|
|
/* Stop reading if the directory entry we are treating is |
if (dent.d_reclen > uio->uio_resid) { |
* bigger than the amount of data that can be returned. */ |
/* Exhausted UIO space. */ |
if (dentp->d_reclen > uio->uio_resid) { |
error = EJUSTRETURN; |
error = -1; |
|
break; |
break; |
} |
} |
|
|
/* |
/* Copy out the directory entry and continue. */ |
* Copy the new dirent structure into the output buffer and |
error = uiomove(&dent, dent.d_reclen, uio); |
* advance pointers. |
if (error) { |
*/ |
break; |
error = uiomove(dentp, dentp->d_reclen, uio); |
} |
|
|
(*cntp)++; |
(*cntp)++; |
de = TAILQ_NEXT(de, td_entries); |
de = TAILQ_NEXT(de, td_entries); |
} while (error == 0 && uio->uio_resid > 0 && de != NULL); |
|
|
|
/* Update the offset and cache. */ |
} while (uio->uio_resid > 0 && de); |
if (de == NULL) { |
|
uio->uio_offset = TMPFS_DIRCOOKIE_EOF; |
/* Cache the last entry or clear and mark EOF. */ |
node->tn_spec.tn_dir.tn_readdir_lastn = 0; |
uio->uio_offset = de ? tmpfs_dir_getseq(node, de) : TMPFS_DIRSEQ_EOF; |
node->tn_spec.tn_dir.tn_readdir_lastp = NULL; |
node->tn_spec.tn_dir.tn_readdir_lastp = de; |
} else { |
done: |
node->tn_spec.tn_dir.tn_readdir_lastn = uio->uio_offset = |
tmpfs_update(node->tn_vnode, TMPFS_UPDATE_ATIME); |
tmpfs_dircookie(de); |
|
node->tn_spec.tn_dir.tn_readdir_lastp = de; |
if (error == EJUSTRETURN) { |
|
/* Exhausted UIO space - just return. */ |
|
error = 0; |
} |
} |
node->tn_status |= TMPFS_NODE_ACCESSED; |
KASSERT(error >= 0); |
kmem_free(dentp, sizeof(struct dirent)); |
|
return error; |
return error; |
} |
} |
|
|
Line 833 tmpfs_reg_resize(struct vnode *vp, off_t |
|
Line 911 tmpfs_reg_resize(struct vnode *vp, off_t |
|
return ENOSPC; |
return ENOSPC; |
} |
} |
} else if (newsize < oldsize) { |
} else if (newsize < oldsize) { |
int zerolen = MIN(round_page(newsize), node->tn_size) - newsize; |
size_t zerolen; |
|
|
|
zerolen = MIN(round_page(newsize), node->tn_size) - newsize; |
ubc_zerorange(uobj, newsize, zerolen, UBC_UNMAP_FLAG(vp)); |
ubc_zerorange(uobj, newsize, zerolen, UBC_UNMAP_FLAG(vp)); |
} |
} |
|
|
Line 863 tmpfs_reg_resize(struct vnode *vp, off_t |
|
Line 942 tmpfs_reg_resize(struct vnode *vp, off_t |
|
|
|
/* |
/* |
* tmpfs_chflags: change flags of the given vnode. |
* tmpfs_chflags: change flags of the given vnode. |
* |
|
* => Caller should perform tmpfs_update(). |
|
*/ |
*/ |
int |
int |
tmpfs_chflags(vnode_t *vp, int flags, kauth_cred_t cred, lwp_t *l) |
tmpfs_chflags(vnode_t *vp, int flags, kauth_cred_t cred, lwp_t *l) |
{ |
{ |
tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); |
tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); |
kauth_action_t action = KAUTH_VNODE_WRITE_FLAGS; |
kauth_action_t action = KAUTH_VNODE_WRITE_FLAGS; |
int error, fs_decision = 0; |
int error; |
|
bool changing_sysflags = false; |
|
|
KASSERT(VOP_ISLOCKED(vp)); |
KASSERT(VOP_ISLOCKED(vp)); |
|
|
Line 879 tmpfs_chflags(vnode_t *vp, int flags, ka |
|
Line 957 tmpfs_chflags(vnode_t *vp, int flags, ka |
|
if (vp->v_mount->mnt_flag & MNT_RDONLY) |
if (vp->v_mount->mnt_flag & MNT_RDONLY) |
return EROFS; |
return EROFS; |
|
|
if (kauth_cred_geteuid(cred) != node->tn_uid) { |
|
fs_decision = EACCES; |
|
} |
|
|
|
/* |
/* |
* If the new flags have non-user flags that are different than |
* If the new flags have non-user flags that are different than |
* those on the node, we need special permission to change them. |
* those on the node, we need special permission to change them. |
*/ |
*/ |
if ((flags & SF_SETTABLE) != (node->tn_flags & SF_SETTABLE)) { |
if ((flags & SF_SETTABLE) != (node->tn_flags & SF_SETTABLE)) { |
action |= KAUTH_VNODE_WRITE_SYSFLAGS; |
action |= KAUTH_VNODE_WRITE_SYSFLAGS; |
if (!fs_decision) { |
changing_sysflags = true; |
fs_decision = EPERM; |
|
} |
|
} |
} |
|
|
/* |
/* |
Line 902 tmpfs_chflags(vnode_t *vp, int flags, ka |
|
Line 974 tmpfs_chflags(vnode_t *vp, int flags, ka |
|
action |= KAUTH_VNODE_HAS_SYSFLAGS; |
action |= KAUTH_VNODE_HAS_SYSFLAGS; |
} |
} |
|
|
error = kauth_authorize_vnode(cred, action, vp, NULL, fs_decision); |
error = kauth_authorize_vnode(cred, action, vp, NULL, |
|
genfs_can_chflags(cred, vp->v_type, node->tn_uid, |
|
changing_sysflags)); |
if (error) |
if (error) |
return error; |
return error; |
|
|
Line 915 tmpfs_chflags(vnode_t *vp, int flags, ka |
|
Line 989 tmpfs_chflags(vnode_t *vp, int flags, ka |
|
* proper permissions, and if we're here it means it's okay to |
* proper permissions, and if we're here it means it's okay to |
* change them... |
* change them... |
*/ |
*/ |
if ((action & KAUTH_VNODE_WRITE_SYSFLAGS) == 0) { |
if (!changing_sysflags) { |
/* Clear all user-settable flags and re-set them. */ |
/* Clear all user-settable flags and re-set them. */ |
node->tn_flags &= SF_SETTABLE; |
node->tn_flags &= SF_SETTABLE; |
node->tn_flags |= (flags & UF_SETTABLE); |
node->tn_flags |= (flags & UF_SETTABLE); |
} else { |
} else { |
node->tn_flags = flags; |
node->tn_flags = flags; |
} |
} |
node->tn_status |= TMPFS_NODE_CHANGED; |
tmpfs_update(vp, TMPFS_UPDATE_CTIME); |
VN_KNOTE(vp, NOTE_ATTRIB); |
VN_KNOTE(vp, NOTE_ATTRIB); |
return 0; |
return 0; |
} |
} |
|
|
/* |
/* |
* tmpfs_chmod: change access mode on the given vnode. |
* tmpfs_chmod: change access mode on the given vnode. |
* |
|
* => Caller should perform tmpfs_update(). |
|
*/ |
*/ |
int |
int |
tmpfs_chmod(vnode_t *vp, mode_t mode, kauth_cred_t cred, lwp_t *l) |
tmpfs_chmod(vnode_t *vp, mode_t mode, kauth_cred_t cred, lwp_t *l) |
Line 949 tmpfs_chmod(vnode_t *vp, mode_t mode, ka |
|
Line 1021 tmpfs_chmod(vnode_t *vp, mode_t mode, ka |
|
return EPERM; |
return EPERM; |
|
|
error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp, |
error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp, |
NULL, genfs_can_chmod(vp, cred, node->tn_uid, node->tn_gid, mode)); |
NULL, genfs_can_chmod(vp->v_type, cred, node->tn_uid, node->tn_gid, mode)); |
if (error) { |
if (error) { |
return error; |
return error; |
} |
} |
node->tn_mode = (mode & ALLPERMS); |
node->tn_mode = (mode & ALLPERMS); |
node->tn_status |= TMPFS_NODE_CHANGED; |
tmpfs_update(vp, TMPFS_UPDATE_CTIME); |
VN_KNOTE(vp, NOTE_ATTRIB); |
VN_KNOTE(vp, NOTE_ATTRIB); |
return 0; |
return 0; |
} |
} |
Line 964 tmpfs_chmod(vnode_t *vp, mode_t mode, ka |
|
Line 1036 tmpfs_chmod(vnode_t *vp, mode_t mode, ka |
|
* |
* |
* => At least one of uid or gid must be different than VNOVAL. |
* => At least one of uid or gid must be different than VNOVAL. |
* => Attribute is unchanged for VNOVAL case. |
* => Attribute is unchanged for VNOVAL case. |
* => Caller should perform tmpfs_update(). |
|
*/ |
*/ |
int |
int |
tmpfs_chown(vnode_t *vp, uid_t uid, gid_t gid, kauth_cred_t cred, lwp_t *l) |
tmpfs_chown(vnode_t *vp, uid_t uid, gid_t gid, kauth_cred_t cred, lwp_t *l) |
Line 992 tmpfs_chown(vnode_t *vp, uid_t uid, gid_ |
|
Line 1063 tmpfs_chown(vnode_t *vp, uid_t uid, gid_ |
|
return EPERM; |
return EPERM; |
|
|
error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, vp, |
error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, vp, |
NULL, genfs_can_chown(vp, cred, node->tn_uid, node->tn_gid, uid, |
NULL, genfs_can_chown(cred, node->tn_uid, node->tn_gid, uid, |
gid)); |
gid)); |
if (error) { |
if (error) { |
return error; |
return error; |
} |
} |
node->tn_uid = uid; |
node->tn_uid = uid; |
node->tn_gid = gid; |
node->tn_gid = gid; |
node->tn_status |= TMPFS_NODE_CHANGED; |
tmpfs_update(vp, TMPFS_UPDATE_CTIME); |
VN_KNOTE(vp, NOTE_ATTRIB); |
VN_KNOTE(vp, NOTE_ATTRIB); |
return 0; |
return 0; |
} |
} |
|
|
tmpfs_chsize(vnode_t *vp, u_quad_t size, kauth_cred_t cred, lwp_t *l) |
tmpfs_chsize(vnode_t *vp, u_quad_t size, kauth_cred_t cred, lwp_t *l) |
{ |
{ |
tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); |
tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); |
|
const off_t length = size; |
|
int error; |
|
|
KASSERT(VOP_ISLOCKED(vp)); |
KASSERT(VOP_ISLOCKED(vp)); |
|
|
Line 1041 tmpfs_chsize(vnode_t *vp, u_quad_t size, |
|
Line 1114 tmpfs_chsize(vnode_t *vp, u_quad_t size, |
|
return EPERM; |
return EPERM; |
} |
} |
|
|
/* Note: tmpfs_truncate() will raise NOTE_EXTEND and NOTE_ATTRIB. */ |
if (length < 0) { |
return tmpfs_truncate(vp, size); |
return EINVAL; |
|
} |
|
if (node->tn_size == length) { |
|
return 0; |
|
} |
|
|
|
/* Note: tmpfs_reg_resize() will raise NOTE_EXTEND and NOTE_ATTRIB. */ |
|
if ((error = tmpfs_reg_resize(vp, length)) != 0) { |
|
return error; |
|
} |
|
tmpfs_update(vp, TMPFS_UPDATE_CTIME | TMPFS_UPDATE_MTIME); |
|
return 0; |
} |
} |
|
|
/* |
/* |
Line 1071 tmpfs_chtimes(vnode_t *vp, const struct |
|
Line 1155 tmpfs_chtimes(vnode_t *vp, const struct |
|
if (error) |
if (error) |
return error; |
return error; |
|
|
if (atime->tv_sec != VNOVAL && atime->tv_nsec != VNOVAL) |
if (atime->tv_sec != VNOVAL) { |
node->tn_status |= TMPFS_NODE_ACCESSED; |
node->tn_atime = *atime; |
|
} |
if (mtime->tv_sec != VNOVAL && mtime->tv_nsec != VNOVAL) |
if (mtime->tv_sec != VNOVAL) { |
node->tn_status |= TMPFS_NODE_MODIFIED; |
node->tn_mtime = *mtime; |
|
} |
if (btime->tv_sec == VNOVAL && btime->tv_nsec == VNOVAL) |
if (btime->tv_sec != VNOVAL) { |
btime = NULL; |
node->tn_birthtime = *btime; |
|
} |
tmpfs_update(vp, atime, mtime, btime, 0); |
|
VN_KNOTE(vp, NOTE_ATTRIB); |
VN_KNOTE(vp, NOTE_ATTRIB); |
return 0; |
return 0; |
} |
} |
|
|
/* |
/* |
* tmpfs_update: update timestamps, et al. |
* tmpfs_update: update the timestamps as indicated by the flags. |
*/ |
*/ |
void |
void |
tmpfs_update(vnode_t *vp, const struct timespec *acc, |
tmpfs_update(vnode_t *vp, unsigned tflags) |
const struct timespec *mod, const struct timespec *birth, int flags) |
|
{ |
{ |
tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); |
tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); |
struct timespec nowtm; |
struct timespec nowtm; |
|
|
/* KASSERT(VOP_ISLOCKED(vp)); */ |
if (tflags == 0) { |
|
|
if (flags & UPDATE_CLOSE) { |
|
/* XXX Need to do anything special? */ |
|
} |
|
if ((node->tn_status & TMPFS_NODE_STATUSALL) == 0) { |
|
return; |
return; |
} |
} |
if (birth != NULL) { |
|
node->tn_birthtime = *birth; |
|
} |
|
vfs_timestamp(&nowtm); |
vfs_timestamp(&nowtm); |
|
|
if (node->tn_status & TMPFS_NODE_ACCESSED) { |
if (tflags & TMPFS_UPDATE_ATIME) { |
node->tn_atime = acc ? *acc : nowtm; |
node->tn_atime = nowtm; |
} |
} |
if (node->tn_status & TMPFS_NODE_MODIFIED) { |
if (tflags & TMPFS_UPDATE_MTIME) { |
node->tn_mtime = mod ? *mod : nowtm; |
node->tn_mtime = nowtm; |
} |
} |
if (node->tn_status & TMPFS_NODE_CHANGED) { |
if (tflags & TMPFS_UPDATE_CTIME) { |
node->tn_ctime = nowtm; |
node->tn_ctime = nowtm; |
} |
} |
|
|
node->tn_status &= ~TMPFS_NODE_STATUSALL; |
|
} |
|
|
|
int |
|
tmpfs_truncate(vnode_t *vp, off_t length) |
|
{ |
|
tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); |
|
int error; |
|
|
|
if (length < 0) { |
|
error = EINVAL; |
|
goto out; |
|
} |
|
if (node->tn_size == length) { |
|
error = 0; |
|
goto out; |
|
} |
|
error = tmpfs_reg_resize(vp, length); |
|
if (error == 0) { |
|
node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; |
|
} |
|
out: |
|
tmpfs_update(vp, NULL, NULL, NULL, 0); |
|
return error; |
|
} |
} |