static void serve_readdir(fuse_req_t req, fuse_ino_t fuse_ino, size_t size, off_t foff, struct fuse_file_info * fi) { fdesc_t * fdesc = fi_get_fdesc(fi); uint32_t off = foff; uint32_t total_size = 0; char * buf = NULL; int r; Dprintf("%s(ino = %lu, size = %u, off = %lld)\n", __FUNCTION__, fuse_ino, size, foff); while (1) { dirent_t dirent; int nbytes; struct stat stbuf; inode_t entry_cfs_ino; size_t oldsize = total_size; nbytes = CALL(reqcfs(req), get_dirent, fdesc, &dirent, sizeof(dirent), &off); if (nbytes == -1) break; else if (nbytes < 0) { fprintf(stderr, "%s:%s(): CALL(cfs, get_dirent, fdesc = %p, off = %d) = %d (%s)\n", __FILE__, __FUNCTION__, fdesc, off, nbytes, strerror(nbytes)); assert(nbytes >= 0); } if (total_size + fuse_dirent_size(dirent.d_namelen) > size) break; Dprintf("%s: \"%s\"\n", __FUNCTION__, dirent.d_name); total_size += fuse_dirent_size(dirent.d_namelen); buf = (char *) realloc(buf, total_size); if (!buf) kpanic("realloc() failed"); memset(&stbuf, 0, sizeof(stbuf)); // Generate "." and ".." here rather than in the base file system // because they are not able to find ".."'s inode from just // "."'s inode if (!strcmp(dirent.d_name, ".")) entry_cfs_ino = fusecfsino(req, fuse_ino); else if (!strcmp(dirent.d_name, "..")) entry_cfs_ino = fdesc->common->parent; else { r = CALL(reqcfs(req), lookup, fusecfsino(req, fuse_ino), dirent.d_name, &entry_cfs_ino); assert(r >= 0); } stbuf.st_ino = cfsfuseino(req, entry_cfs_ino); fuse_add_dirent(buf + oldsize, dirent.d_name, &stbuf, off); } r = fuse_reply_buf(req, buf, total_size); fuse_reply_assert(!r); free(buf); }
static void dirbuf_add(struct dirbuf *b, const char *name, fuse_ino_t ino) { struct stat stbuf; size_t oldsize = b->size; b->size += fuse_dirent_size(strlen(name)); b->p = (char *) realloc(b->p, b->size); memset(&stbuf, 0, sizeof(stbuf)); stbuf.st_ino = ino; fuse_add_dirent(b->p + oldsize, name, &stbuf, b->size); }
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off) { size_t entsize; (void) req; entsize = fuse_dirent_size(strlen(name)); if (entsize <= bufsize && buf) fuse_add_dirent(buf, name, stbuf, off); return entsize; }
static size_t sqfs_ll_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *st, off_t off) { #if HAVE_DECL_FUSE_ADD_DIRENTRY return fuse_add_direntry(req, buf, bufsize, name, st, off); #else size_t esize = fuse_dirent_size(strlen(name)); if (bufsize >= esize) fuse_add_dirent(buf, name, st, off); return esize; #endif }
char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf, off_t off) { unsigned namelen = strlen(name); unsigned entlen = FUSE_NAME_OFFSET + namelen; unsigned entsize = fuse_dirent_size(namelen); unsigned padlen = entsize - entlen; struct fuse_dirent *dirent = (struct fuse_dirent *) buf; dirent->ino = stbuf->st_ino; dirent->off = off; dirent->namelen = namelen; dirent->type = (stbuf->st_mode & 0170000) >> 12; strncpy(dirent->name, name, namelen); if (padlen) memset(buf + entlen, 0, padlen); return buf + entsize; }
static int zfsfuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { vnode_t *vp = ((file_info_t *)(uintptr_t) fi->fh)->vp; ASSERT(vp != NULL); ASSERT(VTOZ(vp) != NULL); ASSERT(VTOZ(vp)->z_id == ino); if(vp->v_type != VDIR) return ENOTDIR; vfs_t *vfs = (vfs_t *) fuse_req_userdata(req); zfsvfs_t *zfsvfs = vfs->vfs_data; char *outbuf = kmem_alloc(size, KM_NOSLEEP); if(outbuf == NULL) return ENOMEM; ZFS_ENTER(zfsvfs); cred_t cred; zfsfuse_getcred(req, &cred); union { char buf[DIRENT64_RECLEN(MAXNAMELEN)]; struct dirent64 dirent; } entry; struct stat fstat = { 0 }; iovec_t iovec; uio_t uio; uio.uio_iov = &iovec; uio.uio_iovcnt = 1; uio.uio_segflg = UIO_SYSSPACE; uio.uio_fmode = 0; uio.uio_llimit = RLIM64_INFINITY; int eofp = 0; int outbuf_off = 0; int outbuf_resid = size; off_t next = off; int error; for(;;) { iovec.iov_base = entry.buf; iovec.iov_len = sizeof(entry.buf); uio.uio_resid = iovec.iov_len; uio.uio_loffset = next; error = VOP_READDIR(vp, &uio, &cred, &eofp, NULL, 0); if(error) goto out; /* No more directory entries */ if(iovec.iov_base == entry.buf) break; fstat.st_ino = entry.dirent.d_ino; fstat.st_mode = 0; int dsize = fuse_dirent_size(strlen(entry.dirent.d_name)); if(dsize > outbuf_resid) break; fuse_add_dirent(outbuf + outbuf_off, entry.dirent.d_name, &fstat, entry.dirent.d_off); outbuf_off += dsize; outbuf_resid -= dsize; next = entry.dirent.d_off; } out: ZFS_EXIT(zfsvfs); if(!error) fuse_reply_buf(req, outbuf, outbuf_off); kmem_free(outbuf, size); return error; }