162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci  FUSE: Filesystem in Userspace
362306a36Sopenharmony_ci  Copyright (C) 2001-2018  Miklos Szeredi <miklos@szeredi.hu>
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci  This program can be distributed under the terms of the GNU GPL.
662306a36Sopenharmony_ci  See the file COPYING.
762306a36Sopenharmony_ci*/
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "fuse_i.h"
1162306a36Sopenharmony_ci#include <linux/iversion.h>
1262306a36Sopenharmony_ci#include <linux/posix_acl.h>
1362306a36Sopenharmony_ci#include <linux/pagemap.h>
1462306a36Sopenharmony_ci#include <linux/highmem.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	struct fuse_conn *fc = get_fuse_conn(dir);
1962306a36Sopenharmony_ci	struct fuse_inode *fi = get_fuse_inode(dir);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	if (!fc->do_readdirplus)
2262306a36Sopenharmony_ci		return false;
2362306a36Sopenharmony_ci	if (!fc->readdirplus_auto)
2462306a36Sopenharmony_ci		return true;
2562306a36Sopenharmony_ci	if (test_and_clear_bit(FUSE_I_ADVISE_RDPLUS, &fi->state))
2662306a36Sopenharmony_ci		return true;
2762306a36Sopenharmony_ci	if (ctx->pos == 0)
2862306a36Sopenharmony_ci		return true;
2962306a36Sopenharmony_ci	return false;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic void fuse_add_dirent_to_cache(struct file *file,
3362306a36Sopenharmony_ci				     struct fuse_dirent *dirent, loff_t pos)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct fuse_inode *fi = get_fuse_inode(file_inode(file));
3662306a36Sopenharmony_ci	size_t reclen = FUSE_DIRENT_SIZE(dirent);
3762306a36Sopenharmony_ci	pgoff_t index;
3862306a36Sopenharmony_ci	struct page *page;
3962306a36Sopenharmony_ci	loff_t size;
4062306a36Sopenharmony_ci	u64 version;
4162306a36Sopenharmony_ci	unsigned int offset;
4262306a36Sopenharmony_ci	void *addr;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	spin_lock(&fi->rdc.lock);
4562306a36Sopenharmony_ci	/*
4662306a36Sopenharmony_ci	 * Is cache already completed?  Or this entry does not go at the end of
4762306a36Sopenharmony_ci	 * cache?
4862306a36Sopenharmony_ci	 */
4962306a36Sopenharmony_ci	if (fi->rdc.cached || pos != fi->rdc.pos) {
5062306a36Sopenharmony_ci		spin_unlock(&fi->rdc.lock);
5162306a36Sopenharmony_ci		return;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci	version = fi->rdc.version;
5462306a36Sopenharmony_ci	size = fi->rdc.size;
5562306a36Sopenharmony_ci	offset = size & ~PAGE_MASK;
5662306a36Sopenharmony_ci	index = size >> PAGE_SHIFT;
5762306a36Sopenharmony_ci	/* Dirent doesn't fit in current page?  Jump to next page. */
5862306a36Sopenharmony_ci	if (offset + reclen > PAGE_SIZE) {
5962306a36Sopenharmony_ci		index++;
6062306a36Sopenharmony_ci		offset = 0;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci	spin_unlock(&fi->rdc.lock);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (offset) {
6562306a36Sopenharmony_ci		page = find_lock_page(file->f_mapping, index);
6662306a36Sopenharmony_ci	} else {
6762306a36Sopenharmony_ci		page = find_or_create_page(file->f_mapping, index,
6862306a36Sopenharmony_ci					   mapping_gfp_mask(file->f_mapping));
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci	if (!page)
7162306a36Sopenharmony_ci		return;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	spin_lock(&fi->rdc.lock);
7462306a36Sopenharmony_ci	/* Raced with another readdir */
7562306a36Sopenharmony_ci	if (fi->rdc.version != version || fi->rdc.size != size ||
7662306a36Sopenharmony_ci	    WARN_ON(fi->rdc.pos != pos))
7762306a36Sopenharmony_ci		goto unlock;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	addr = kmap_local_page(page);
8062306a36Sopenharmony_ci	if (!offset) {
8162306a36Sopenharmony_ci		clear_page(addr);
8262306a36Sopenharmony_ci		SetPageUptodate(page);
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci	memcpy(addr + offset, dirent, reclen);
8562306a36Sopenharmony_ci	kunmap_local(addr);
8662306a36Sopenharmony_ci	fi->rdc.size = (index << PAGE_SHIFT) + offset + reclen;
8762306a36Sopenharmony_ci	fi->rdc.pos = dirent->off;
8862306a36Sopenharmony_ciunlock:
8962306a36Sopenharmony_ci	spin_unlock(&fi->rdc.lock);
9062306a36Sopenharmony_ci	unlock_page(page);
9162306a36Sopenharmony_ci	put_page(page);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void fuse_readdir_cache_end(struct file *file, loff_t pos)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct fuse_inode *fi = get_fuse_inode(file_inode(file));
9762306a36Sopenharmony_ci	loff_t end;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	spin_lock(&fi->rdc.lock);
10062306a36Sopenharmony_ci	/* does cache end position match current position? */
10162306a36Sopenharmony_ci	if (fi->rdc.pos != pos) {
10262306a36Sopenharmony_ci		spin_unlock(&fi->rdc.lock);
10362306a36Sopenharmony_ci		return;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	fi->rdc.cached = true;
10762306a36Sopenharmony_ci	end = ALIGN(fi->rdc.size, PAGE_SIZE);
10862306a36Sopenharmony_ci	spin_unlock(&fi->rdc.lock);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* truncate unused tail of cache */
11162306a36Sopenharmony_ci	truncate_inode_pages(file->f_mapping, end);
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic bool fuse_emit(struct file *file, struct dir_context *ctx,
11562306a36Sopenharmony_ci		      struct fuse_dirent *dirent)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct fuse_file *ff = file->private_data;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (ff->open_flags & FOPEN_CACHE_DIR)
12062306a36Sopenharmony_ci		fuse_add_dirent_to_cache(file, dirent, ctx->pos);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino,
12362306a36Sopenharmony_ci			dirent->type);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int parse_dirfile(char *buf, size_t nbytes, struct file *file,
12762306a36Sopenharmony_ci			 struct dir_context *ctx)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	while (nbytes >= FUSE_NAME_OFFSET) {
13062306a36Sopenharmony_ci		struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
13162306a36Sopenharmony_ci		size_t reclen = FUSE_DIRENT_SIZE(dirent);
13262306a36Sopenharmony_ci		if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
13362306a36Sopenharmony_ci			return -EIO;
13462306a36Sopenharmony_ci		if (reclen > nbytes)
13562306a36Sopenharmony_ci			break;
13662306a36Sopenharmony_ci		if (memchr(dirent->name, '/', dirent->namelen) != NULL)
13762306a36Sopenharmony_ci			return -EIO;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		if (!fuse_emit(file, ctx, dirent))
14062306a36Sopenharmony_ci			break;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		buf += reclen;
14362306a36Sopenharmony_ci		nbytes -= reclen;
14462306a36Sopenharmony_ci		ctx->pos = dirent->off;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int fuse_direntplus_link(struct file *file,
15162306a36Sopenharmony_ci				struct fuse_direntplus *direntplus,
15262306a36Sopenharmony_ci				u64 attr_version)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct fuse_entry_out *o = &direntplus->entry_out;
15562306a36Sopenharmony_ci	struct fuse_dirent *dirent = &direntplus->dirent;
15662306a36Sopenharmony_ci	struct dentry *parent = file->f_path.dentry;
15762306a36Sopenharmony_ci	struct qstr name = QSTR_INIT(dirent->name, dirent->namelen);
15862306a36Sopenharmony_ci	struct dentry *dentry;
15962306a36Sopenharmony_ci	struct dentry *alias;
16062306a36Sopenharmony_ci	struct inode *dir = d_inode(parent);
16162306a36Sopenharmony_ci	struct fuse_conn *fc;
16262306a36Sopenharmony_ci	struct inode *inode;
16362306a36Sopenharmony_ci	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (!o->nodeid) {
16662306a36Sopenharmony_ci		/*
16762306a36Sopenharmony_ci		 * Unlike in the case of fuse_lookup, zero nodeid does not mean
16862306a36Sopenharmony_ci		 * ENOENT. Instead, it only means the userspace filesystem did
16962306a36Sopenharmony_ci		 * not want to return attributes/handle for this entry.
17062306a36Sopenharmony_ci		 *
17162306a36Sopenharmony_ci		 * So do nothing.
17262306a36Sopenharmony_ci		 */
17362306a36Sopenharmony_ci		return 0;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (name.name[0] == '.') {
17762306a36Sopenharmony_ci		/*
17862306a36Sopenharmony_ci		 * We could potentially refresh the attributes of the directory
17962306a36Sopenharmony_ci		 * and its parent?
18062306a36Sopenharmony_ci		 */
18162306a36Sopenharmony_ci		if (name.len == 1)
18262306a36Sopenharmony_ci			return 0;
18362306a36Sopenharmony_ci		if (name.name[1] == '.' && name.len == 2)
18462306a36Sopenharmony_ci			return 0;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (invalid_nodeid(o->nodeid))
18862306a36Sopenharmony_ci		return -EIO;
18962306a36Sopenharmony_ci	if (fuse_invalid_attr(&o->attr))
19062306a36Sopenharmony_ci		return -EIO;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	fc = get_fuse_conn(dir);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	name.hash = full_name_hash(parent, name.name, name.len);
19562306a36Sopenharmony_ci	dentry = d_lookup(parent, &name);
19662306a36Sopenharmony_ci	if (!dentry) {
19762306a36Sopenharmony_ciretry:
19862306a36Sopenharmony_ci		dentry = d_alloc_parallel(parent, &name, &wq);
19962306a36Sopenharmony_ci		if (IS_ERR(dentry))
20062306a36Sopenharmony_ci			return PTR_ERR(dentry);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	if (!d_in_lookup(dentry)) {
20362306a36Sopenharmony_ci		struct fuse_inode *fi;
20462306a36Sopenharmony_ci		inode = d_inode(dentry);
20562306a36Sopenharmony_ci		if (inode && get_node_id(inode) != o->nodeid)
20662306a36Sopenharmony_ci			inode = NULL;
20762306a36Sopenharmony_ci		if (!inode ||
20862306a36Sopenharmony_ci		    fuse_stale_inode(inode, o->generation, &o->attr)) {
20962306a36Sopenharmony_ci			if (inode)
21062306a36Sopenharmony_ci				fuse_make_bad(inode);
21162306a36Sopenharmony_ci			d_invalidate(dentry);
21262306a36Sopenharmony_ci			dput(dentry);
21362306a36Sopenharmony_ci			goto retry;
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci		if (fuse_is_bad(inode)) {
21662306a36Sopenharmony_ci			dput(dentry);
21762306a36Sopenharmony_ci			return -EIO;
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		fi = get_fuse_inode(inode);
22162306a36Sopenharmony_ci		spin_lock(&fi->lock);
22262306a36Sopenharmony_ci		fi->nlookup++;
22362306a36Sopenharmony_ci		spin_unlock(&fi->lock);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		forget_all_cached_acls(inode);
22662306a36Sopenharmony_ci		fuse_change_attributes(inode, &o->attr, NULL,
22762306a36Sopenharmony_ci				       ATTR_TIMEOUT(o),
22862306a36Sopenharmony_ci				       attr_version);
22962306a36Sopenharmony_ci		/*
23062306a36Sopenharmony_ci		 * The other branch comes via fuse_iget()
23162306a36Sopenharmony_ci		 * which bumps nlookup inside
23262306a36Sopenharmony_ci		 */
23362306a36Sopenharmony_ci	} else {
23462306a36Sopenharmony_ci		inode = fuse_iget(dir->i_sb, o->nodeid, o->generation,
23562306a36Sopenharmony_ci				  &o->attr, ATTR_TIMEOUT(o),
23662306a36Sopenharmony_ci				  attr_version);
23762306a36Sopenharmony_ci		if (!inode)
23862306a36Sopenharmony_ci			inode = ERR_PTR(-ENOMEM);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		alias = d_splice_alias(inode, dentry);
24162306a36Sopenharmony_ci		d_lookup_done(dentry);
24262306a36Sopenharmony_ci		if (alias) {
24362306a36Sopenharmony_ci			dput(dentry);
24462306a36Sopenharmony_ci			dentry = alias;
24562306a36Sopenharmony_ci		}
24662306a36Sopenharmony_ci		if (IS_ERR(dentry)) {
24762306a36Sopenharmony_ci			if (!IS_ERR(inode)) {
24862306a36Sopenharmony_ci				struct fuse_inode *fi = get_fuse_inode(inode);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci				spin_lock(&fi->lock);
25162306a36Sopenharmony_ci				fi->nlookup--;
25262306a36Sopenharmony_ci				spin_unlock(&fi->lock);
25362306a36Sopenharmony_ci			}
25462306a36Sopenharmony_ci			return PTR_ERR(dentry);
25562306a36Sopenharmony_ci		}
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci	if (fc->readdirplus_auto)
25862306a36Sopenharmony_ci		set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
25962306a36Sopenharmony_ci	fuse_change_entry_timeout(dentry, o);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	dput(dentry);
26262306a36Sopenharmony_ci	return 0;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic void fuse_force_forget(struct file *file, u64 nodeid)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
26862306a36Sopenharmony_ci	struct fuse_mount *fm = get_fuse_mount(inode);
26962306a36Sopenharmony_ci	struct fuse_forget_in inarg;
27062306a36Sopenharmony_ci	FUSE_ARGS(args);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	memset(&inarg, 0, sizeof(inarg));
27362306a36Sopenharmony_ci	inarg.nlookup = 1;
27462306a36Sopenharmony_ci	args.opcode = FUSE_FORGET;
27562306a36Sopenharmony_ci	args.nodeid = nodeid;
27662306a36Sopenharmony_ci	args.in_numargs = 1;
27762306a36Sopenharmony_ci	args.in_args[0].size = sizeof(inarg);
27862306a36Sopenharmony_ci	args.in_args[0].value = &inarg;
27962306a36Sopenharmony_ci	args.force = true;
28062306a36Sopenharmony_ci	args.noreply = true;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	fuse_simple_request(fm, &args);
28362306a36Sopenharmony_ci	/* ignore errors */
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
28762306a36Sopenharmony_ci			     struct dir_context *ctx, u64 attr_version)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct fuse_direntplus *direntplus;
29062306a36Sopenharmony_ci	struct fuse_dirent *dirent;
29162306a36Sopenharmony_ci	size_t reclen;
29262306a36Sopenharmony_ci	int over = 0;
29362306a36Sopenharmony_ci	int ret;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	while (nbytes >= FUSE_NAME_OFFSET_DIRENTPLUS) {
29662306a36Sopenharmony_ci		direntplus = (struct fuse_direntplus *) buf;
29762306a36Sopenharmony_ci		dirent = &direntplus->dirent;
29862306a36Sopenharmony_ci		reclen = FUSE_DIRENTPLUS_SIZE(direntplus);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
30162306a36Sopenharmony_ci			return -EIO;
30262306a36Sopenharmony_ci		if (reclen > nbytes)
30362306a36Sopenharmony_ci			break;
30462306a36Sopenharmony_ci		if (memchr(dirent->name, '/', dirent->namelen) != NULL)
30562306a36Sopenharmony_ci			return -EIO;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		if (!over) {
30862306a36Sopenharmony_ci			/* We fill entries into dstbuf only as much as
30962306a36Sopenharmony_ci			   it can hold. But we still continue iterating
31062306a36Sopenharmony_ci			   over remaining entries to link them. If not,
31162306a36Sopenharmony_ci			   we need to send a FORGET for each of those
31262306a36Sopenharmony_ci			   which we did not link.
31362306a36Sopenharmony_ci			*/
31462306a36Sopenharmony_ci			over = !fuse_emit(file, ctx, dirent);
31562306a36Sopenharmony_ci			if (!over)
31662306a36Sopenharmony_ci				ctx->pos = dirent->off;
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		buf += reclen;
32062306a36Sopenharmony_ci		nbytes -= reclen;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		ret = fuse_direntplus_link(file, direntplus, attr_version);
32362306a36Sopenharmony_ci		if (ret)
32462306a36Sopenharmony_ci			fuse_force_forget(file, direntplus->entry_out.nodeid);
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return 0;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int fuse_readdir_uncached(struct file *file, struct dir_context *ctx)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	int plus;
33362306a36Sopenharmony_ci	ssize_t res;
33462306a36Sopenharmony_ci	struct page *page;
33562306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
33662306a36Sopenharmony_ci	struct fuse_mount *fm = get_fuse_mount(inode);
33762306a36Sopenharmony_ci	struct fuse_io_args ia = {};
33862306a36Sopenharmony_ci	struct fuse_args_pages *ap = &ia.ap;
33962306a36Sopenharmony_ci	struct fuse_page_desc desc = { .length = PAGE_SIZE };
34062306a36Sopenharmony_ci	u64 attr_version = 0;
34162306a36Sopenharmony_ci	bool locked;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	page = alloc_page(GFP_KERNEL);
34462306a36Sopenharmony_ci	if (!page)
34562306a36Sopenharmony_ci		return -ENOMEM;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	plus = fuse_use_readdirplus(inode, ctx);
34862306a36Sopenharmony_ci	ap->args.out_pages = true;
34962306a36Sopenharmony_ci	ap->num_pages = 1;
35062306a36Sopenharmony_ci	ap->pages = &page;
35162306a36Sopenharmony_ci	ap->descs = &desc;
35262306a36Sopenharmony_ci	if (plus) {
35362306a36Sopenharmony_ci		attr_version = fuse_get_attr_version(fm->fc);
35462306a36Sopenharmony_ci		fuse_read_args_fill(&ia, file, ctx->pos, PAGE_SIZE,
35562306a36Sopenharmony_ci				    FUSE_READDIRPLUS);
35662306a36Sopenharmony_ci	} else {
35762306a36Sopenharmony_ci		fuse_read_args_fill(&ia, file, ctx->pos, PAGE_SIZE,
35862306a36Sopenharmony_ci				    FUSE_READDIR);
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci	locked = fuse_lock_inode(inode);
36162306a36Sopenharmony_ci	res = fuse_simple_request(fm, &ap->args);
36262306a36Sopenharmony_ci	fuse_unlock_inode(inode, locked);
36362306a36Sopenharmony_ci	if (res >= 0) {
36462306a36Sopenharmony_ci		if (!res) {
36562306a36Sopenharmony_ci			struct fuse_file *ff = file->private_data;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci			if (ff->open_flags & FOPEN_CACHE_DIR)
36862306a36Sopenharmony_ci				fuse_readdir_cache_end(file, ctx->pos);
36962306a36Sopenharmony_ci		} else if (plus) {
37062306a36Sopenharmony_ci			res = parse_dirplusfile(page_address(page), res,
37162306a36Sopenharmony_ci						file, ctx, attr_version);
37262306a36Sopenharmony_ci		} else {
37362306a36Sopenharmony_ci			res = parse_dirfile(page_address(page), res, file,
37462306a36Sopenharmony_ci					    ctx);
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	__free_page(page);
37962306a36Sopenharmony_ci	fuse_invalidate_atime(inode);
38062306a36Sopenharmony_ci	return res;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cienum fuse_parse_result {
38462306a36Sopenharmony_ci	FOUND_ERR = -1,
38562306a36Sopenharmony_ci	FOUND_NONE = 0,
38662306a36Sopenharmony_ci	FOUND_SOME,
38762306a36Sopenharmony_ci	FOUND_ALL,
38862306a36Sopenharmony_ci};
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic enum fuse_parse_result fuse_parse_cache(struct fuse_file *ff,
39162306a36Sopenharmony_ci					       void *addr, unsigned int size,
39262306a36Sopenharmony_ci					       struct dir_context *ctx)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	unsigned int offset = ff->readdir.cache_off & ~PAGE_MASK;
39562306a36Sopenharmony_ci	enum fuse_parse_result res = FOUND_NONE;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	WARN_ON(offset >= size);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	for (;;) {
40062306a36Sopenharmony_ci		struct fuse_dirent *dirent = addr + offset;
40162306a36Sopenharmony_ci		unsigned int nbytes = size - offset;
40262306a36Sopenharmony_ci		size_t reclen;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		if (nbytes < FUSE_NAME_OFFSET || !dirent->namelen)
40562306a36Sopenharmony_ci			break;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		reclen = FUSE_DIRENT_SIZE(dirent); /* derefs ->namelen */
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		if (WARN_ON(dirent->namelen > FUSE_NAME_MAX))
41062306a36Sopenharmony_ci			return FOUND_ERR;
41162306a36Sopenharmony_ci		if (WARN_ON(reclen > nbytes))
41262306a36Sopenharmony_ci			return FOUND_ERR;
41362306a36Sopenharmony_ci		if (WARN_ON(memchr(dirent->name, '/', dirent->namelen) != NULL))
41462306a36Sopenharmony_ci			return FOUND_ERR;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		if (ff->readdir.pos == ctx->pos) {
41762306a36Sopenharmony_ci			res = FOUND_SOME;
41862306a36Sopenharmony_ci			if (!dir_emit(ctx, dirent->name, dirent->namelen,
41962306a36Sopenharmony_ci				      dirent->ino, dirent->type))
42062306a36Sopenharmony_ci				return FOUND_ALL;
42162306a36Sopenharmony_ci			ctx->pos = dirent->off;
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci		ff->readdir.pos = dirent->off;
42462306a36Sopenharmony_ci		ff->readdir.cache_off += reclen;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		offset += reclen;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	return res;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic void fuse_rdc_reset(struct inode *inode)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct fuse_inode *fi = get_fuse_inode(inode);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	fi->rdc.cached = false;
43762306a36Sopenharmony_ci	fi->rdc.version++;
43862306a36Sopenharmony_ci	fi->rdc.size = 0;
43962306a36Sopenharmony_ci	fi->rdc.pos = 0;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci#define UNCACHED 1
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct fuse_file *ff = file->private_data;
44762306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
44862306a36Sopenharmony_ci	struct fuse_conn *fc = get_fuse_conn(inode);
44962306a36Sopenharmony_ci	struct fuse_inode *fi = get_fuse_inode(inode);
45062306a36Sopenharmony_ci	enum fuse_parse_result res;
45162306a36Sopenharmony_ci	pgoff_t index;
45262306a36Sopenharmony_ci	unsigned int size;
45362306a36Sopenharmony_ci	struct page *page;
45462306a36Sopenharmony_ci	void *addr;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/* Seeked?  If so, reset the cache stream */
45762306a36Sopenharmony_ci	if (ff->readdir.pos != ctx->pos) {
45862306a36Sopenharmony_ci		ff->readdir.pos = 0;
45962306a36Sopenharmony_ci		ff->readdir.cache_off = 0;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/*
46362306a36Sopenharmony_ci	 * We're just about to start reading into the cache or reading the
46462306a36Sopenharmony_ci	 * cache; both cases require an up-to-date mtime value.
46562306a36Sopenharmony_ci	 */
46662306a36Sopenharmony_ci	if (!ctx->pos && fc->auto_inval_data) {
46762306a36Sopenharmony_ci		int err = fuse_update_attributes(inode, file, STATX_MTIME);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		if (err)
47062306a36Sopenharmony_ci			return err;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ciretry:
47462306a36Sopenharmony_ci	spin_lock(&fi->rdc.lock);
47562306a36Sopenharmony_ciretry_locked:
47662306a36Sopenharmony_ci	if (!fi->rdc.cached) {
47762306a36Sopenharmony_ci		/* Starting cache? Set cache mtime. */
47862306a36Sopenharmony_ci		if (!ctx->pos && !fi->rdc.size) {
47962306a36Sopenharmony_ci			fi->rdc.mtime = inode->i_mtime;
48062306a36Sopenharmony_ci			fi->rdc.iversion = inode_query_iversion(inode);
48162306a36Sopenharmony_ci		}
48262306a36Sopenharmony_ci		spin_unlock(&fi->rdc.lock);
48362306a36Sopenharmony_ci		return UNCACHED;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci	/*
48662306a36Sopenharmony_ci	 * When at the beginning of the directory (i.e. just after opendir(3) or
48762306a36Sopenharmony_ci	 * rewinddir(3)), then need to check whether directory contents have
48862306a36Sopenharmony_ci	 * changed, and reset the cache if so.
48962306a36Sopenharmony_ci	 */
49062306a36Sopenharmony_ci	if (!ctx->pos) {
49162306a36Sopenharmony_ci		if (inode_peek_iversion(inode) != fi->rdc.iversion ||
49262306a36Sopenharmony_ci		    !timespec64_equal(&fi->rdc.mtime, &inode->i_mtime)) {
49362306a36Sopenharmony_ci			fuse_rdc_reset(inode);
49462306a36Sopenharmony_ci			goto retry_locked;
49562306a36Sopenharmony_ci		}
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	/*
49962306a36Sopenharmony_ci	 * If cache version changed since the last getdents() call, then reset
50062306a36Sopenharmony_ci	 * the cache stream.
50162306a36Sopenharmony_ci	 */
50262306a36Sopenharmony_ci	if (ff->readdir.version != fi->rdc.version) {
50362306a36Sopenharmony_ci		ff->readdir.pos = 0;
50462306a36Sopenharmony_ci		ff->readdir.cache_off = 0;
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci	/*
50762306a36Sopenharmony_ci	 * If at the beginning of the cache, than reset version to
50862306a36Sopenharmony_ci	 * current.
50962306a36Sopenharmony_ci	 */
51062306a36Sopenharmony_ci	if (ff->readdir.pos == 0)
51162306a36Sopenharmony_ci		ff->readdir.version = fi->rdc.version;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	WARN_ON(fi->rdc.size < ff->readdir.cache_off);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	index = ff->readdir.cache_off >> PAGE_SHIFT;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (index == (fi->rdc.size >> PAGE_SHIFT))
51862306a36Sopenharmony_ci		size = fi->rdc.size & ~PAGE_MASK;
51962306a36Sopenharmony_ci	else
52062306a36Sopenharmony_ci		size = PAGE_SIZE;
52162306a36Sopenharmony_ci	spin_unlock(&fi->rdc.lock);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* EOF? */
52462306a36Sopenharmony_ci	if ((ff->readdir.cache_off & ~PAGE_MASK) == size)
52562306a36Sopenharmony_ci		return 0;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	page = find_get_page_flags(file->f_mapping, index,
52862306a36Sopenharmony_ci				   FGP_ACCESSED | FGP_LOCK);
52962306a36Sopenharmony_ci	/* Page gone missing, then re-added to cache, but not initialized? */
53062306a36Sopenharmony_ci	if (page && !PageUptodate(page)) {
53162306a36Sopenharmony_ci		unlock_page(page);
53262306a36Sopenharmony_ci		put_page(page);
53362306a36Sopenharmony_ci		page = NULL;
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci	spin_lock(&fi->rdc.lock);
53662306a36Sopenharmony_ci	if (!page) {
53762306a36Sopenharmony_ci		/*
53862306a36Sopenharmony_ci		 * Uh-oh: page gone missing, cache is useless
53962306a36Sopenharmony_ci		 */
54062306a36Sopenharmony_ci		if (fi->rdc.version == ff->readdir.version)
54162306a36Sopenharmony_ci			fuse_rdc_reset(inode);
54262306a36Sopenharmony_ci		goto retry_locked;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	/* Make sure it's still the same version after getting the page. */
54662306a36Sopenharmony_ci	if (ff->readdir.version != fi->rdc.version) {
54762306a36Sopenharmony_ci		spin_unlock(&fi->rdc.lock);
54862306a36Sopenharmony_ci		unlock_page(page);
54962306a36Sopenharmony_ci		put_page(page);
55062306a36Sopenharmony_ci		goto retry;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci	spin_unlock(&fi->rdc.lock);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	/*
55562306a36Sopenharmony_ci	 * Contents of the page are now protected against changing by holding
55662306a36Sopenharmony_ci	 * the page lock.
55762306a36Sopenharmony_ci	 */
55862306a36Sopenharmony_ci	addr = kmap_local_page(page);
55962306a36Sopenharmony_ci	res = fuse_parse_cache(ff, addr, size, ctx);
56062306a36Sopenharmony_ci	kunmap_local(addr);
56162306a36Sopenharmony_ci	unlock_page(page);
56262306a36Sopenharmony_ci	put_page(page);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (res == FOUND_ERR)
56562306a36Sopenharmony_ci		return -EIO;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (res == FOUND_ALL)
56862306a36Sopenharmony_ci		return 0;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (size == PAGE_SIZE) {
57162306a36Sopenharmony_ci		/* We hit end of page: skip to next page. */
57262306a36Sopenharmony_ci		ff->readdir.cache_off = ALIGN(ff->readdir.cache_off, PAGE_SIZE);
57362306a36Sopenharmony_ci		goto retry;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	/*
57762306a36Sopenharmony_ci	 * End of cache reached.  If found position, then we are done, otherwise
57862306a36Sopenharmony_ci	 * need to fall back to uncached, since the position we were looking for
57962306a36Sopenharmony_ci	 * wasn't in the cache.
58062306a36Sopenharmony_ci	 */
58162306a36Sopenharmony_ci	return res == FOUND_SOME ? 0 : UNCACHED;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ciint fuse_readdir(struct file *file, struct dir_context *ctx)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct fuse_file *ff = file->private_data;
58762306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
58862306a36Sopenharmony_ci	int err;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (fuse_is_bad(inode))
59162306a36Sopenharmony_ci		return -EIO;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	mutex_lock(&ff->readdir.lock);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	err = UNCACHED;
59662306a36Sopenharmony_ci	if (ff->open_flags & FOPEN_CACHE_DIR)
59762306a36Sopenharmony_ci		err = fuse_readdir_cached(file, ctx);
59862306a36Sopenharmony_ci	if (err == UNCACHED)
59962306a36Sopenharmony_ci		err = fuse_readdir_uncached(file, ctx);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	mutex_unlock(&ff->readdir.lock);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	return err;
60462306a36Sopenharmony_ci}
605