diff options
author | 2013-12-14 18:01:52 +0000 | |
---|---|---|
committer | 2013-12-14 18:01:52 +0000 | |
commit | 5733124666a65758dfca766c657c543e40ed7ff8 (patch) | |
tree | 6a616f0c379ff4148d03a03350a600d34189f767 /sys/tmpfs/tmpfs_subr.c | |
parent | Start cleanup/fixup of pretty printing of option data. Use snprintf() (diff) | |
download | wireguard-openbsd-5733124666a65758dfca766c657c543e40ed7ff8.tar.xz wireguard-openbsd-5733124666a65758dfca766c657c543e40ed7ff8.zip |
bring in a few improvements from pedro, guenther, me...
thanks to krw@ for the original cherry-picking, millert@ for useful comment.
so:
- no longer expose internal kernel addresses, uses seq numbers instead
- make sure the numbers don't overflow (millert@ UINT64_MAX)... a
conservative estimate is that tmpfs will run out of seqs in >600 years...
- don't malloc dents, put them on the stack and zero them
- gc whiteout code
- gc getpage/putpage code (shrink uvm instead)
okay krw@, millert@
Diffstat (limited to 'sys/tmpfs/tmpfs_subr.c')
-rw-r--r-- | sys/tmpfs/tmpfs_subr.c | 468 |
1 files changed, 241 insertions, 227 deletions
diff --git a/sys/tmpfs/tmpfs_subr.c b/sys/tmpfs/tmpfs_subr.c index 2955dead36c..d72edbd2eab 100644 --- a/sys/tmpfs/tmpfs_subr.c +++ b/sys/tmpfs/tmpfs_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tmpfs_subr.c,v 1.4 2013/09/22 03:34:31 guenther Exp $ */ +/* $OpenBSD: tmpfs_subr.c,v 1.5 2013/12/14 18:01:52 espie Exp $ */ /* $NetBSD: tmpfs_subr.c,v 1.79 2012/03/13 18:40:50 elad Exp $ */ /* @@ -90,13 +90,19 @@ __KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.79 2012/03/13 18:40:50 elad Exp $") #include <sys/stat.h> #include <sys/systm.h> #include <sys/vnode.h> -#include <sys/malloc.h> #include <uvm/uvm.h> +#include <dev/rndvar.h> + #include <tmpfs/tmpfs.h> #include <tmpfs/tmpfs_vnops.h> + +/* Local functions. */ +void tmpfs_dir_putseq(tmpfs_node_t *, tmpfs_dirent_t *); +int tmpfs_dir_getdotents(tmpfs_node_t *, struct dirent *, struct uio *); + /* * tmpfs_alloc_node: allocate a new inode of a specified type and * insert it into the list of specified mount point. @@ -118,19 +124,22 @@ tmpfs_alloc_node(tmpfs_mount_t *tmp, enum vtype type, uid_t uid, gid_t gid, nnode->tn_vnode = NULL; nnode->tn_dirent_hint = NULL; - /* - * XXX Where the pool is backed by a map larger than (4GB * - * sizeof(*nnode)), this may produce duplicate inode numbers - * for applications that do not understand 64-bit ino_t. - */ - nnode->tn_id = (ino_t)((uintptr_t)nnode / sizeof(*nnode)); - nnode->tn_gen = TMPFS_NODE_GEN_MASK & random(); + rw_enter_write(&tmp->tm_acc_lock); + nnode->tn_id = ++tmp->tm_highest_inode; + if (nnode->tn_id == 0) { + --tmp->tm_highest_inode; + rw_exit_write(&tmp->tm_acc_lock); + tmpfs_node_put(tmp, nnode); + return ENOSPC; + } + rw_exit_write(&tmp->tm_acc_lock); /* Generic initialization. */ nnode->tn_type = type; nnode->tn_size = 0; nnode->tn_flags = 0; nnode->tn_lockf = NULL; + nnode->tn_gen = TMPFS_NODE_GEN_MASK & arc4random(); nanotime(&nnode->tn_atime); nnode->tn_birthtime = nnode->tn_atime; @@ -156,7 +165,7 @@ tmpfs_alloc_node(tmpfs_mount_t *tmp, enum vtype type, uid_t uid, gid_t gid, /* Directory. */ TAILQ_INIT(&nnode->tn_spec.tn_dir.tn_dir); nnode->tn_spec.tn_dir.tn_parent = NULL; - nnode->tn_spec.tn_dir.tn_readdir_lastn = 0; + nnode->tn_spec.tn_dir.tn_next_seq = TMPFS_DIRSEQ_START; nnode->tn_spec.tn_dir.tn_readdir_lastp = NULL; /* Extra link count for the virtual '.' entry. */ @@ -244,16 +253,19 @@ tmpfs_free_node(tmpfs_mount_t *tmp, tmpfs_node_t *node) } break; case VDIR: - /* - * KASSERT(TAILQ_EMPTY(&node->tn_spec.tn_dir.tn_dir)); - * KASSERT(node->tn_spec.tn_dir.tn_parent == NULL || - * node == tmp->tm_root); - */ + KASSERT(TAILQ_EMPTY(&node->tn_spec.tn_dir.tn_dir)); + KASSERT(node->tn_spec.tn_dir.tn_parent == NULL || + node == tmp->tm_root); break; default: break; } + rw_enter_write(&tmp->tm_acc_lock); + if (node->tn_id == tmp->tm_highest_inode) + --tmp->tm_highest_inode; + rw_exit_write(&tmp->tm_acc_lock); + /* mutex_destroy(&node->tn_nlock); */ tmpfs_node_put(tmp, node); } @@ -383,6 +395,11 @@ tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, KASSERT(dnode->tn_links < LINK_MAX); } + if (TMPFS_DIRSEQ_FULL(dnode)) { + error = ENOSPC; + goto out; + } + /* Allocate a node that represents the new file. */ error = tmpfs_alloc_node(tmp, vap->va_type, cnp->cn_cred->cr_uid, dnode->tn_gid, vap->va_mode, target, vap->va_rdev, &node); @@ -405,24 +422,8 @@ tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, goto out; } -#if 0 /* ISWHITEOUT doesn't exist in OpenBSD */ - /* Remove whiteout before adding the new entry. */ - if (cnp->cn_flags & ISWHITEOUT) { - wde = tmpfs_dir_lookup(dnode, cnp); - KASSERT(wde != NULL && wde->td_node == TMPFS_NODE_WHITEOUT); - tmpfs_dir_detach(dvp, wde); - tmpfs_free_dirent(tmp, wde); - } -#endif - /* Associate inode and attach the entry into the directory. */ - tmpfs_dir_attach(dvp, de, node); - -#if 0 /* ISWHITEOUT doesn't exist in OpenBSD */ - /* Make node opaque if requested. */ - if (cnp->cn_flags & ISWHITEOUT) - node->tn_flags |= UF_OPAQUE; -#endif + tmpfs_dir_attach(dnode, de, node); out: if (error == 0 && (cnp->cn_flags & SAVESTART) == 0) @@ -452,6 +453,7 @@ tmpfs_alloc_dirent(tmpfs_mount_t *tmp, const char *name, uint16_t len, } nde->td_namelen = len; memcpy(nde->td_name, name, len); + nde->td_seq = TMPFS_DIRSEQ_NONE; *de = nde; return 0; @@ -464,7 +466,8 @@ void 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_dirent_put(tmp, de); } @@ -479,22 +482,25 @@ tmpfs_free_dirent(tmpfs_mount_t *tmp, tmpfs_dirent_t *de) * => Triggers kqueue events here. */ void -tmpfs_dir_attach(struct vnode *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); + struct vnode *dvp = dnode->tn_vnode; int events = NOTE_WRITE; + KASSERT(dvp != NULL); 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. */ de->td_node = node; - if (node != TMPFS_NODE_WHITEOUT) { - KASSERT(node->tn_links < LINK_MAX); - node->tn_links++; + KASSERT(node->tn_links < LINK_MAX); + node->tn_links++; - /* Save the hint (might overwrite). */ - node->tn_dirent_hint = de; - } + /* Save the hint (might overwrite). */ + node->tn_dirent_hint = de; /* Insert the entry to the directory (parent of inode). */ TAILQ_INSERT_TAIL(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries); @@ -502,7 +508,7 @@ tmpfs_dir_attach(struct vnode *dvp, tmpfs_dirent_t *de, tmpfs_node_t *node) tmpfs_update(dnode, TMPFS_NODE_STATUSALL); uvm_vnp_setsize(dvp, dnode->tn_size); - if (node != TMPFS_NODE_WHITEOUT && node->tn_type == VDIR) { + if (node->tn_type == VDIR) { /* Set parent. */ KASSERT(node->tn_spec.tn_dir.tn_parent == NULL); node->tn_spec.tn_dir.tn_parent = dnode; @@ -526,52 +532,49 @@ tmpfs_dir_attach(struct vnode *dvp, tmpfs_dirent_t *de, tmpfs_node_t *node) * => Triggers kqueue events here. */ void -tmpfs_dir_detach(struct vnode *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; + struct vnode *vp, *dvp = dnode->tn_vnode; int events = NOTE_WRITE; - KASSERT(VOP_ISLOCKED(dvp)); + KASSERT(dvp == NULL || VOP_ISLOCKED(dvp)); - if (node != TMPFS_NODE_WHITEOUT) { - struct vnode *vp = node->tn_vnode; + /* Deassociate the inode and entry. */ + de->td_node = NULL; + node->tn_dirent_hint = NULL; + KASSERT(node->tn_links > 0); + node->tn_links--; + if ((vp = node->tn_vnode) != NULL) { KASSERT(VOP_ISLOCKED(vp)); + VN_KNOTE(vp, node->tn_links ? NOTE_LINK : NOTE_DELETE); + } - /* Deassociate the inode and entry. */ - de->td_node = NULL; - node->tn_dirent_hint = NULL; - - KASSERT(node->tn_links > 0); - node->tn_links--; - if (vp) { - VN_KNOTE(vp, node->tn_links ? - NOTE_LINK : NOTE_DELETE); - } - - /* If directory - decrease the link count of parent. */ - if (node->tn_type == VDIR) { - KASSERT(node->tn_spec.tn_dir.tn_parent == dnode); - node->tn_spec.tn_dir.tn_parent = NULL; + /* If directory - decrease the link count of parent. */ + if (node->tn_type == VDIR) { + KASSERT(node->tn_spec.tn_dir.tn_parent == dnode); + node->tn_spec.tn_dir.tn_parent = NULL; - KASSERT(dnode->tn_links > 0); - dnode->tn_links--; - events |= NOTE_LINK; - } + KASSERT(dnode->tn_links > 0); + dnode->tn_links--; + events |= NOTE_LINK; } /* Remove the entry from the directory. */ 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; } TAILQ_REMOVE(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries); dnode->tn_size -= sizeof(tmpfs_dirent_t); - tmpfs_update(dnode, TMPFS_NODE_STATUSALL); - uvm_vnp_setsize(dvp, dnode->tn_size); - VN_KNOTE(dvp, events); + tmpfs_update(dnode, TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED); + tmpfs_dir_putseq(dnode, de); + if (dvp) { + tmpfs_update(dnode, 0); + uvm_vnp_setsize(dvp, dnode->tn_size); + VN_KNOTE(dvp, events); + } } /* @@ -605,7 +608,7 @@ tmpfs_dir_lookup(tmpfs_node_t *node, struct componentname *cnp) /* * 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. */ @@ -629,106 +632,122 @@ tmpfs_dir_cached(tmpfs_node_t *node) } /* - * tmpfs_dir_getdotdent: helper function for tmpfs_readdir. Creates a - * '.' entry for the given directory and returns it in the uio space. + * tmpfs_dir_getseq: get a per-directory sequence number for the entry. */ -int -tmpfs_dir_getdotdent(tmpfs_node_t *node, struct uio *uio) +uint64_t +tmpfs_dir_getseq(tmpfs_node_t *dnode, tmpfs_dirent_t *de) { - struct dirent *dentp; - int error; + uint64_t seq = de->td_seq; - TMPFS_VALIDATE_DIR(node); - KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOT); - - /* dentp = kmem_alloc(sizeof(struct dirent), KM_SLEEP); */ - dentp = malloc(sizeof(struct dirent), M_TEMP, M_WAITOK); - dentp->d_fileno = node->tn_id; - dentp->d_off = TMPFS_DIRCOOKIE_DOTDOT; - dentp->d_type = DT_DIR; - dentp->d_namlen = 1; - dentp->d_name[0] = '.'; - dentp->d_name[1] = '\0'; - 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) - uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT; + TMPFS_VALIDATE_DIR(dnode); + + if (__predict_true(seq != TMPFS_DIRSEQ_NONE)) { + /* Already set. */ + KASSERT(seq >= TMPFS_DIRSEQ_START); + return seq; + } + + /* + * The "." and ".." and the end-of-directory have reserved numbers. + * The other sequence numbers are allocated incrementally. + */ + + seq = dnode->tn_spec.tn_dir.tn_next_seq; + KASSERT(seq >= TMPFS_DIRSEQ_START); + KASSERT(seq < TMPFS_DIRSEQ_END); + dnode->tn_spec.tn_dir.tn_next_seq++; + return seq; +} + +void +tmpfs_dir_putseq(tmpfs_node_t *dnode, tmpfs_dirent_t *de) +{ + uint64_t seq = de->td_seq; + + TMPFS_VALIDATE_DIR(dnode); + KASSERT(seq == TMPFS_DIRSEQ_NONE || seq >= TMPFS_DIRSEQ_START); + KASSERT(seq == TMPFS_DIRSEQ_NONE || seq < TMPFS_DIRSEQ_END); + + de->td_seq = TMPFS_DIRSEQ_NONE; + + /* Empty? We can reset. */ + if (dnode->tn_size == 0) { + dnode->tn_spec.tn_dir.tn_next_seq = TMPFS_DIRSEQ_START; + } else if (seq != TMPFS_DIRSEQ_NONE && + seq == dnode->tn_spec.tn_dir.tn_next_seq - 1) { + dnode->tn_spec.tn_dir.tn_next_seq--; } - tmpfs_update(node, TMPFS_NODE_ACCESSED); - /* kmem_free(dentp, sizeof(struct dirent)); */ - free(dentp, M_TEMP); - return error; } /* - * tmpfs_dir_getdotdotdent: helper function for tmpfs_readdir. Creates a - * '..' entry for the given directory and returns it in the uio space. + * tmpfs_dir_lookupbyseq: lookup a directory entry by the sequence number. */ -int -tmpfs_dir_getdotdotdent(tmpfs_node_t *node, struct uio *uio) +tmpfs_dirent_t * +tmpfs_dir_lookupbyseq(tmpfs_node_t *node, off_t seq) { - struct dirent *dentp; - tmpfs_dirent_t *de; - off_t next; - int error; + tmpfs_dirent_t *de = node->tn_spec.tn_dir.tn_readdir_lastp; TMPFS_VALIDATE_DIR(node); - KASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT); - de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir); - if (de == NULL) - next = TMPFS_DIRCOOKIE_EOF; - else - next = tmpfs_dircookie(de); - - /* dentp = kmem_alloc(sizeof(struct dirent), KM_SLEEP); */ - dentp = malloc(sizeof(struct dirent), M_TEMP, M_WAITOK); - dentp->d_fileno = node->tn_spec.tn_dir.tn_parent->tn_id; - dentp->d_off = next; - dentp->d_type = DT_DIR; - dentp->d_namlen = 2; - dentp->d_name[0] = '.'; - dentp->d_name[1] = '.'; - dentp->d_name[2] = '\0'; - 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) - uio->uio_offset = next; + /* + * First, check the cache. If does not match - perform a lookup. + */ + if (de && de->td_seq == seq) { + KASSERT(de->td_seq >= TMPFS_DIRSEQ_START); + KASSERT(de->td_seq != TMPFS_DIRSEQ_NONE); + return de; } - tmpfs_update(node, TMPFS_NODE_ACCESSED); - /* kmem_free(dentp, sizeof(struct dirent)); */ - free(dentp, M_TEMP); - return error; + + TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) { + KASSERT(de->td_seq >= TMPFS_DIRSEQ_START); + 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 * -tmpfs_dir_lookupbycookie(tmpfs_node_t *node, off_t cookie) +int +tmpfs_dir_getdotents(tmpfs_node_t *node, struct dirent *dp, struct uio *uio) { 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); + dp->d_off = next; - if (cookie == node->tn_spec.tn_dir.tn_readdir_lastn && - node->tn_spec.tn_dir.tn_readdir_lastp != NULL) { - return node->tn_spec.tn_dir.tn_readdir_lastp; + if (dp->d_reclen > uio->uio_resid) { + return EJUSTRETURN; } - TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) { - if (tmpfs_dircookie(de) == cookie) { - break; - } + + if ((error = uiomove(dp, dp->d_reclen, uio)) != 0) { + return error; } - return de; + + uio->uio_offset = next; + return error; } /* @@ -741,109 +760,104 @@ int tmpfs_dir_getdents(tmpfs_node_t *node, struct uio *uio) { tmpfs_dirent_t *de, *next_de; - struct dirent *dentp; - off_t cookie, next_cookie; - int error; + struct dirent dent; + int error = 0; KASSERT(VOP_ISLOCKED(node->tn_vnode)); TMPFS_VALIDATE_DIR(node); + memset(&dent, 0, sizeof(dent)); - /* - * Locate the first directory entry we have to return. We have cached - * the last readdir in the node, so use those values if appropriate. - * Otherwise do a linear scan to find the requested entry. - */ - cookie = uio->uio_offset; - KASSERT(cookie != TMPFS_DIRCOOKIE_DOT); - KASSERT(cookie != TMPFS_DIRCOOKIE_DOTDOT); - if (cookie == TMPFS_DIRCOOKIE_EOF) { - return 0; - } else { - de = tmpfs_dir_lookupbycookie(node, cookie); + if (uio->uio_offset == TMPFS_DIRSEQ_DOT) { + if ((error = tmpfs_dir_getdotents(node, &dent, uio)) != 0) { + goto done; + } + } + if (uio->uio_offset == TMPFS_DIRSEQ_DOTDOT) { + if ((error = tmpfs_dir_getdotents(node, &dent, uio)) != 0) { + goto done; + } } + /* 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) { - return EINVAL; + error = EINVAL; + goto done; } /* - * Read as much entries as possible; i.e., until we reach the end - * of the directory or we exhaust uio space. + * Read as many entries as possible; i.e., until we reach the end + * of the directory or we exhaust UIO space. */ - /* dentp = kmem_alloc(sizeof(struct dirent), KM_SLEEP); */ - dentp = malloc(sizeof(struct dirent), M_TEMP, M_WAITOK); do { - /* - * Create a dirent structure representing the current - * inode and fill it. - */ - if (de->td_node == TMPFS_NODE_WHITEOUT || 0) { - dentp->d_fileno = 1; - /* dentp->d_type = DT_WHT; */ - } else { - dentp->d_fileno = de->td_node->tn_id; - switch (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(0); - } + dent.d_fileno = de->td_node->tn_id; + switch (de->td_node->tn_type) { + case VBLK: + dent.d_type = DT_BLK; + break; + case VCHR: + dent.d_type = DT_CHR; + break; + case VDIR: + dent.d_type = DT_DIR; + break; + case VFIFO: + dent.d_type = DT_FIFO; + break; + case VLNK: + dent.d_type = DT_LNK; + break; + case VREG: + dent.d_type = DT_REG; + break; + case VSOCK: + dent.d_type = DT_SOCK; + break; + default: + KASSERT(0); } - dentp->d_namlen = de->td_namelen; - KASSERT(de->td_namelen < sizeof(dentp->d_name)); - memcpy(dentp->d_name, de->td_name, de->td_namelen); - dentp->d_name[de->td_namelen] = '\0'; - dentp->d_reclen = DIRENT_SIZE(dentp); + dent.d_namlen = de->td_namelen; + KASSERT(de->td_namelen < sizeof(dent.d_name)); + memcpy(dent.d_name, de->td_name, de->td_namelen); + dent.d_name[de->td_namelen] = '\0'; + dent.d_reclen = DIRENT_SIZE(&dent); next_de = TAILQ_NEXT(de, td_entries); if (next_de == NULL) - next_cookie = TMPFS_DIRCOOKIE_EOF; + dent.d_off = TMPFS_DIRSEQ_EOF; else - next_cookie = tmpfs_dircookie(next_de); - dentp->d_off = next_cookie; + dent.d_off = tmpfs_dir_getseq(node, next_de); - /* Stop reading if the directory entry we are treating is - * bigger than the amount of data that can be returned. */ - if (dentp->d_reclen > uio->uio_resid) { - error = -1; + if (dent.d_reclen > uio->uio_resid) { + /* Exhausted UIO space. */ + error = EJUSTRETURN; break; } - /* - * Copy the new dirent structure into the output buffer and - * advance pointers. - */ - error = uiomove(dentp, dentp->d_reclen, uio); - if (error == 0) { - de = next_de; - cookie = next_cookie; + /* Copy out the directory entry and continue. */ + error = uiomove(&dent, dent.d_reclen, uio); + if (error) { + break; } - } while (error == 0 && uio->uio_resid > 0 && de != NULL); + de = TAILQ_NEXT(de, td_entries); + + } while (uio->uio_resid > 0 && de); - /* Update the offset and cache. */ - node->tn_spec.tn_dir.tn_readdir_lastn = uio->uio_offset = cookie; + /* Cache the last entry or clear and mark EOF. */ + uio->uio_offset = de ? tmpfs_dir_getseq(node, de) : TMPFS_DIRSEQ_EOF; node->tn_spec.tn_dir.tn_readdir_lastp = de; +done: tmpfs_update(node, TMPFS_NODE_ACCESSED); - /* kmem_free(dentp, sizeof(struct dirent)); */ - free(dentp, M_TEMP); + + if (error == EJUSTRETURN) { + /* Exhausted UIO space - just return. */ + error = 0; + } + KASSERT(error >= 0); return error; } |