if (!fc->do_readdirplus) returnfalse; if (!fc->readdirplus_auto) returntrue; if (test_and_clear_bit(FUSE_I_ADVISE_RDPLUS, &fi->state)) returntrue; if (ctx->pos == 0) returntrue; returnfalse;
}
spin_lock(&fi->rdc.lock); /* * Is cache already completed? Or this entry does not go at the end of * cache?
*/ if (fi->rdc.cached || pos != fi->rdc.pos) {
spin_unlock(&fi->rdc.lock); return;
}
version = fi->rdc.version;
size = fi->rdc.size;
offset = size & ~PAGE_MASK;
index = size >> PAGE_SHIFT; /* Dirent doesn't fit in current page? Jump to next page. */ if (offset + reclen > PAGE_SIZE) {
index++;
offset = 0;
}
spin_unlock(&fi->rdc.lock);
if (offset) {
page = find_lock_page(file->f_mapping, index);
} else {
page = find_or_create_page(file->f_mapping, index,
mapping_gfp_mask(file->f_mapping));
} if (!page) return;
spin_lock(&fi->rdc.lock); /* Raced with another readdir */ if (fi->rdc.version != version || fi->rdc.size != size ||
WARN_ON(fi->rdc.pos != pos)) goto unlock;
if (!o->nodeid) { /* * Unlike in the case of fuse_lookup, zero nodeid does not mean * ENOENT. Instead, it only means the userspace filesystem did * not want to return attributes/handle for this entry. * * So do nothing.
*/ return 0;
}
if (name.name[0] == '.') { /* * We could potentially refresh the attributes of the directory * and its parent?
*/ if (name.len == 1) return 0; if (name.name[1] == '.' && name.len == 2) return 0;
}
if (invalid_nodeid(o->nodeid)) return -EIO; if (fuse_invalid_attr(&o->attr)) return -EIO;
fc = get_fuse_conn(dir);
epoch = atomic_read(&fc->epoch);
name.hash = full_name_hash(parent, name.name, name.len);
dentry = d_lookup(parent, &name); if (!dentry) {
retry:
dentry = d_alloc_parallel(parent, &name, &wq); if (IS_ERR(dentry)) return PTR_ERR(dentry);
} if (!d_in_lookup(dentry)) { struct fuse_inode *fi;
inode = d_inode(dentry); if (inode && get_node_id(inode) != o->nodeid)
inode = NULL; if (!inode ||
fuse_stale_inode(inode, o->generation, &o->attr)) { if (inode)
fuse_make_bad(inode);
d_invalidate(dentry);
dput(dentry); goto retry;
} if (fuse_is_bad(inode)) {
dput(dentry); return -EIO;
}
fi = get_fuse_inode(inode);
spin_lock(&fi->lock);
fi->nlookup++;
spin_unlock(&fi->lock);
forget_all_cached_acls(inode);
fuse_change_attributes(inode, &o->attr, NULL,
ATTR_TIMEOUT(o),
attr_version); /* * The other branch comes via fuse_iget() * which bumps nlookup inside
*/
} else {
inode = fuse_iget(dir->i_sb, o->nodeid, o->generation,
&o->attr, ATTR_TIMEOUT(o),
attr_version, evict_ctr); if (!inode)
inode = ERR_PTR(-ENOMEM);
alias = d_splice_alias(inode, dentry);
d_lookup_done(dentry); if (alias) {
dput(dentry);
dentry = alias;
} if (IS_ERR(dentry)) { if (!IS_ERR(inode)) { struct fuse_inode *fi = get_fuse_inode(inode);
if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX) return -EIO; if (reclen > nbytes) break; if (memchr(dirent->name, '/', dirent->namelen) != NULL) return -EIO;
if (!over) { /* We fill entries into dstbuf only as much as it can hold. But we still continue iterating over remaining entries to link them. If not, we need to send a FORGET for each of those which we did not link.
*/
over = !fuse_emit(file, ctx, dirent); if (!over)
ctx->pos = dirent->off;
}
buf += reclen;
nbytes -= reclen;
ret = fuse_direntplus_link(file, direntplus, attr_version, evict_ctr); if (ret)
fuse_force_forget(file, direntplus->entry_out.nodeid);
}
/* Seeked? If so, reset the cache stream */ if (ff->readdir.pos != ctx->pos) {
ff->readdir.pos = 0;
ff->readdir.cache_off = 0;
}
/* * We're just about to start reading into the cache or reading the * cache; both cases require an up-to-date mtime value.
*/ if (!ctx->pos && fc->auto_inval_data) { int err = fuse_update_attributes(inode, file, STATX_MTIME);
if (err) return err;
}
retry:
spin_lock(&fi->rdc.lock);
retry_locked: if (!fi->rdc.cached) { /* Starting cache? Set cache mtime. */ if (!ctx->pos && !fi->rdc.size) {
fi->rdc.mtime = inode_get_mtime(inode);
fi->rdc.iversion = inode_query_iversion(inode);
}
spin_unlock(&fi->rdc.lock); return UNCACHED;
} /* * When at the beginning of the directory (i.e. just after opendir(3) or * rewinddir(3)), then need to check whether directory contents have * changed, and reset the cache if so.
*/ if (!ctx->pos) { struct timespec64 mtime = inode_get_mtime(inode);
/* * If cache version changed since the last getdents() call, then reset * the cache stream.
*/ if (ff->readdir.version != fi->rdc.version) {
ff->readdir.pos = 0;
ff->readdir.cache_off = 0;
} /* * If at the beginning of the cache, than reset version to * current.
*/ if (ff->readdir.pos == 0)
ff->readdir.version = fi->rdc.version;
page = find_get_page_flags(file->f_mapping, index,
FGP_ACCESSED | FGP_LOCK); /* Page gone missing, then re-added to cache, but not initialized? */ if (page && !PageUptodate(page)) {
unlock_page(page);
put_page(page);
page = NULL;
}
spin_lock(&fi->rdc.lock); if (!page) { /* * Uh-oh: page gone missing, cache is useless
*/ if (fi->rdc.version == ff->readdir.version)
fuse_rdc_reset(inode); goto retry_locked;
}
/* Make sure it's still the same version after getting the page. */ if (ff->readdir.version != fi->rdc.version) {
spin_unlock(&fi->rdc.lock);
unlock_page(page);
put_page(page); goto retry;
}
spin_unlock(&fi->rdc.lock);
/* * Contents of the page are now protected against changing by holding * the page lock.
*/
addr = kmap_local_page(page);
res = fuse_parse_cache(ff, addr, size, ctx);
kunmap_local(addr);
unlock_page(page);
put_page(page);
if (res == FOUND_ERR) return -EIO;
if (res == FOUND_ALL) return 0;
if (size == PAGE_SIZE) { /* We hit end of page: skip to next page. */
ff->readdir.cache_off = ALIGN(ff->readdir.cache_off, PAGE_SIZE); goto retry;
}
/* * End of cache reached. If found position, then we are done, otherwise * need to fall back to uncached, since the position we were looking for * wasn't in the cache.
*/ return res == FOUND_SOME ? 0 : UNCACHED;
}
int fuse_readdir(struct file *file, struct dir_context *ctx)
{ struct fuse_file *ff = file->private_data; struct inode *inode = file_inode(file); int err;
if (fuse_is_bad(inode)) return -EIO;
err = UNCACHED; if (ff->open_flags & FOPEN_CACHE_DIR)
err = fuse_readdir_cached(file, ctx); if (err == UNCACHED)
err = fuse_readdir_uncached(file, ctx);
return err;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.