/* * gfs_dir_readdir: does a readdir() on the given directory * * dvp - directory vnode * uiop - uio structure * eofp - eof pointer * data - arbitrary data passed to readdir callback * * This routine does all the readdir() dirty work. Even so, the caller must * supply two callbacks in order to get full compatibility. * * If the directory contains static entries, an inode callback must be * specified. This avoids having to create every vnode and call VOP_GETATTR() * when reading the directory. This function has the following arguments: * * ino_t gfs_inode_cb(struct vnode *vp, int index); * * vp - vnode for the directory * index - index in original gfs_dirent_t array * * Returns the inode number for the given entry. * * For directories with dynamic entries, a readdir callback must be provided. * This is significantly more complex, thanks to the particulars of * VOP_READDIR(). * * int gfs_readdir_cb(struct vnode *vp, void *dp, int *eofp, * offset_t *off, offset_t *nextoff, void *data, int flags) * * vp - directory vnode * dp - directory entry, sized according to maxlen given to * gfs_dir_create(). callback must fill in d_name and * d_ino (if a dirent64_t), or ed_name, ed_ino, and ed_eflags * (if an edirent_t). edirent_t is used if V_RDDIR_ENTFLAGS * is set in 'flags'. * eofp - callback must set to 1 when EOF has been reached * off - on entry, the last offset read from the directory. Callback * must set to the offset of the current entry, typically left * untouched. * nextoff - callback must set to offset of next entry. Typically * (off + 1) * data - caller-supplied data * flags - VOP_READDIR flags * * Return 0 on success, or error on failure. */ int gfs_dir_readdir(struct vnode *dvp, uio_t *uiop, int *eofp, int *ncookies, u_long **cookies, void *data, cred_t *cr, int flags) { gfs_readdir_state_t gstate; int error, eof = 0; ino64_t ino, pino; offset_t off, next; gfs_dir_t *dp = vnode_fsnode(dvp); error = gfs_get_parent_ino(dvp, cr, NULL, &pino, &ino); if (error) return (error); if ((error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1, uiop, pino, ino, flags)) != 0) return (error); while ((error = gfs_readdir_pred(&gstate, uiop, &off, ncookies, cookies)) == 0 && !eof) { if (off >= 0 && off < dp->gfsd_nstatic) { ino = dp->gfsd_inode(dvp, off); if ((error = gfs_readdir_emit(&gstate, uiop, off, ino, dp->gfsd_static[off].gfse_name, 0, ncookies, cookies)) != 0) break; } else if (dp->gfsd_readdir) { off -= dp->gfsd_nstatic; if ((error = dp->gfsd_readdir(dvp, gstate.grd_dirent, &eof, &off, &next, data, flags)) != 0 || eof) break; off += dp->gfsd_nstatic + 2; next += dp->gfsd_nstatic + 2; if ((error = gfs_readdir_emit_int(&gstate, uiop, next, ncookies, cookies)) != 0) break; } else { /* * Offset is beyond the end of the static entries, and * we have no dynamic entries. Set EOF. */ eof = 1; } } return (gfs_readdir_fini(&gstate, error, eofp, eof)); }
static int xattr_dir_readdir(vnode_t *dvp, uio_t *uiop, cred_t *cr, int *eofp, caller_context_t *ct, int flags) { vnode_t *pvp; int error; int local_eof; int reset_off = 0; int has_xattrs = 0; if (eofp == NULL) { eofp = &local_eof; } *eofp = 0; /* * See if there is a real extended attribute directory. */ error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct); if (error == 0) { has_xattrs = 1; } /* * Start by reading up the static entries. */ if (uiop->uio_loffset == 0) { ino64_t pino, ino; offset_t off; gfs_dir_t *dp = dvp->v_data; gfs_readdir_state_t gstate; if (has_xattrs) { /* * If there is a real xattr dir, skip . and .. * in the GFS dir. We'll pick them up below * when we call into the underlying fs. */ uiop->uio_loffset = GFS_STATIC_ENTRY_OFFSET; } error = gfs_get_parent_ino(dvp, cr, ct, &pino, &ino); if (error == 0) { error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1, uiop, pino, ino, flags); } if (error) { return (error); } while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 && !*eofp) { if (off >= 0 && off < dp->gfsd_nstatic) { int eflags; /* * Check to see if this sysattr set name has a * case-insensitive conflict with a real xattr * name. */ eflags = 0; if ((flags & V_RDDIR_ENTFLAGS) && has_xattrs) { error = readdir_xattr_casecmp(pvp, dp->gfsd_static[off].gfse_name, cr, ct, &eflags); if (error) break; } ino = dp->gfsd_inode(dvp, off); error = gfs_readdir_emit(&gstate, uiop, off, ino, dp->gfsd_static[off].gfse_name, eflags); if (error) break; } else { *eofp = 1; } } error = gfs_readdir_fini(&gstate, error, eofp, *eofp); if (error) { return (error); } /* * We must read all of the static entries in the first * call. Otherwise we won't know if uio_loffset in a * subsequent call refers to the static entries or to those * in an underlying fs. */ if (*eofp == 0) return (EINVAL); reset_off = 1; } if (!has_xattrs) { *eofp = 1; return (0); } *eofp = 0; if (reset_off) { uiop->uio_loffset = 0; } (void) VOP_RWLOCK(pvp, V_WRITELOCK_FALSE, NULL); error = VOP_READDIR(pvp, uiop, cr, eofp, ct, flags); VOP_RWUNLOCK(pvp, V_WRITELOCK_FALSE, NULL); return (error); }