xref: /openbmc/linux/fs/fuse/readdir.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1d123d8e1SMiklos Szeredi /*
2d123d8e1SMiklos Szeredi   FUSE: Filesystem in Userspace
3d123d8e1SMiklos Szeredi   Copyright (C) 2001-2018  Miklos Szeredi <miklos@szeredi.hu>
4d123d8e1SMiklos Szeredi 
5d123d8e1SMiklos Szeredi   This program can be distributed under the terms of the GNU GPL.
6d123d8e1SMiklos Szeredi   See the file COPYING.
7d123d8e1SMiklos Szeredi */
8d123d8e1SMiklos Szeredi 
9d123d8e1SMiklos Szeredi 
10d123d8e1SMiklos Szeredi #include "fuse_i.h"
11261aaba7SMiklos Szeredi #include <linux/iversion.h>
12d123d8e1SMiklos Szeredi #include <linux/posix_acl.h>
1369e34551SMiklos Szeredi #include <linux/pagemap.h>
1469e34551SMiklos Szeredi #include <linux/highmem.h>
15d123d8e1SMiklos Szeredi 
fuse_use_readdirplus(struct inode * dir,struct dir_context * ctx)16d123d8e1SMiklos Szeredi static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
17d123d8e1SMiklos Szeredi {
18d123d8e1SMiklos Szeredi 	struct fuse_conn *fc = get_fuse_conn(dir);
19d123d8e1SMiklos Szeredi 	struct fuse_inode *fi = get_fuse_inode(dir);
20d123d8e1SMiklos Szeredi 
21d123d8e1SMiklos Szeredi 	if (!fc->do_readdirplus)
22d123d8e1SMiklos Szeredi 		return false;
23d123d8e1SMiklos Szeredi 	if (!fc->readdirplus_auto)
24d123d8e1SMiklos Szeredi 		return true;
25d123d8e1SMiklos Szeredi 	if (test_and_clear_bit(FUSE_I_ADVISE_RDPLUS, &fi->state))
26d123d8e1SMiklos Szeredi 		return true;
27d123d8e1SMiklos Szeredi 	if (ctx->pos == 0)
28d123d8e1SMiklos Szeredi 		return true;
29d123d8e1SMiklos Szeredi 	return false;
30d123d8e1SMiklos Szeredi }
31d123d8e1SMiklos Szeredi 
fuse_add_dirent_to_cache(struct file * file,struct fuse_dirent * dirent,loff_t pos)3269e34551SMiklos Szeredi static void fuse_add_dirent_to_cache(struct file *file,
3369e34551SMiklos Szeredi 				     struct fuse_dirent *dirent, loff_t pos)
3469e34551SMiklos Szeredi {
3569e34551SMiklos Szeredi 	struct fuse_inode *fi = get_fuse_inode(file_inode(file));
3669e34551SMiklos Szeredi 	size_t reclen = FUSE_DIRENT_SIZE(dirent);
3769e34551SMiklos Szeredi 	pgoff_t index;
3869e34551SMiklos Szeredi 	struct page *page;
3969e34551SMiklos Szeredi 	loff_t size;
403494927eSMiklos Szeredi 	u64 version;
4169e34551SMiklos Szeredi 	unsigned int offset;
4269e34551SMiklos Szeredi 	void *addr;
4369e34551SMiklos Szeredi 
4469e34551SMiklos Szeredi 	spin_lock(&fi->rdc.lock);
4569e34551SMiklos Szeredi 	/*
4669e34551SMiklos Szeredi 	 * Is cache already completed?  Or this entry does not go at the end of
4769e34551SMiklos Szeredi 	 * cache?
4869e34551SMiklos Szeredi 	 */
4969e34551SMiklos Szeredi 	if (fi->rdc.cached || pos != fi->rdc.pos) {
5069e34551SMiklos Szeredi 		spin_unlock(&fi->rdc.lock);
5169e34551SMiklos Szeredi 		return;
5269e34551SMiklos Szeredi 	}
533494927eSMiklos Szeredi 	version = fi->rdc.version;
5469e34551SMiklos Szeredi 	size = fi->rdc.size;
5569e34551SMiklos Szeredi 	offset = size & ~PAGE_MASK;
5669e34551SMiklos Szeredi 	index = size >> PAGE_SHIFT;
5769e34551SMiklos Szeredi 	/* Dirent doesn't fit in current page?  Jump to next page. */
5869e34551SMiklos Szeredi 	if (offset + reclen > PAGE_SIZE) {
5969e34551SMiklos Szeredi 		index++;
6069e34551SMiklos Szeredi 		offset = 0;
6169e34551SMiklos Szeredi 	}
6269e34551SMiklos Szeredi 	spin_unlock(&fi->rdc.lock);
6369e34551SMiklos Szeredi 
6469e34551SMiklos Szeredi 	if (offset) {
6569e34551SMiklos Szeredi 		page = find_lock_page(file->f_mapping, index);
6669e34551SMiklos Szeredi 	} else {
6769e34551SMiklos Szeredi 		page = find_or_create_page(file->f_mapping, index,
6869e34551SMiklos Szeredi 					   mapping_gfp_mask(file->f_mapping));
6969e34551SMiklos Szeredi 	}
7069e34551SMiklos Szeredi 	if (!page)
7169e34551SMiklos Szeredi 		return;
7269e34551SMiklos Szeredi 
7369e34551SMiklos Szeredi 	spin_lock(&fi->rdc.lock);
7469e34551SMiklos Szeredi 	/* Raced with another readdir */
753494927eSMiklos Szeredi 	if (fi->rdc.version != version || fi->rdc.size != size ||
763494927eSMiklos Szeredi 	    WARN_ON(fi->rdc.pos != pos))
7769e34551SMiklos Szeredi 		goto unlock;
7869e34551SMiklos Szeredi 
795fe0fc9fSPeng Hao 	addr = kmap_local_page(page);
809fa248c6SMiklos Szeredi 	if (!offset) {
8169e34551SMiklos Szeredi 		clear_page(addr);
829fa248c6SMiklos Szeredi 		SetPageUptodate(page);
839fa248c6SMiklos Szeredi 	}
8469e34551SMiklos Szeredi 	memcpy(addr + offset, dirent, reclen);
855fe0fc9fSPeng Hao 	kunmap_local(addr);
8669e34551SMiklos Szeredi 	fi->rdc.size = (index << PAGE_SHIFT) + offset + reclen;
8769e34551SMiklos Szeredi 	fi->rdc.pos = dirent->off;
8869e34551SMiklos Szeredi unlock:
8969e34551SMiklos Szeredi 	spin_unlock(&fi->rdc.lock);
9069e34551SMiklos Szeredi 	unlock_page(page);
9169e34551SMiklos Szeredi 	put_page(page);
9269e34551SMiklos Szeredi }
9369e34551SMiklos Szeredi 
fuse_readdir_cache_end(struct file * file,loff_t pos)9469e34551SMiklos Szeredi static void fuse_readdir_cache_end(struct file *file, loff_t pos)
9569e34551SMiklos Szeredi {
9669e34551SMiklos Szeredi 	struct fuse_inode *fi = get_fuse_inode(file_inode(file));
9769e34551SMiklos Szeredi 	loff_t end;
9869e34551SMiklos Szeredi 
9969e34551SMiklos Szeredi 	spin_lock(&fi->rdc.lock);
10069e34551SMiklos Szeredi 	/* does cache end position match current position? */
10169e34551SMiklos Szeredi 	if (fi->rdc.pos != pos) {
10269e34551SMiklos Szeredi 		spin_unlock(&fi->rdc.lock);
10369e34551SMiklos Szeredi 		return;
10469e34551SMiklos Szeredi 	}
10569e34551SMiklos Szeredi 
10669e34551SMiklos Szeredi 	fi->rdc.cached = true;
10769e34551SMiklos Szeredi 	end = ALIGN(fi->rdc.size, PAGE_SIZE);
10869e34551SMiklos Szeredi 	spin_unlock(&fi->rdc.lock);
10969e34551SMiklos Szeredi 
11069e34551SMiklos Szeredi 	/* truncate unused tail of cache */
11169e34551SMiklos Szeredi 	truncate_inode_pages(file->f_mapping, end);
11269e34551SMiklos Szeredi }
11369e34551SMiklos Szeredi 
fuse_emit(struct file * file,struct dir_context * ctx,struct fuse_dirent * dirent)11418172b10SMiklos Szeredi static bool fuse_emit(struct file *file, struct dir_context *ctx,
11518172b10SMiklos Szeredi 		      struct fuse_dirent *dirent)
11618172b10SMiklos Szeredi {
11769e34551SMiklos Szeredi 	struct fuse_file *ff = file->private_data;
11869e34551SMiklos Szeredi 
11969e34551SMiklos Szeredi 	if (ff->open_flags & FOPEN_CACHE_DIR)
12069e34551SMiklos Szeredi 		fuse_add_dirent_to_cache(file, dirent, ctx->pos);
12169e34551SMiklos Szeredi 
12218172b10SMiklos Szeredi 	return dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino,
12318172b10SMiklos Szeredi 			dirent->type);
12418172b10SMiklos Szeredi }
12518172b10SMiklos Szeredi 
parse_dirfile(char * buf,size_t nbytes,struct file * file,struct dir_context * ctx)126d123d8e1SMiklos Szeredi static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
127d123d8e1SMiklos Szeredi 			 struct dir_context *ctx)
128d123d8e1SMiklos Szeredi {
129d123d8e1SMiklos Szeredi 	while (nbytes >= FUSE_NAME_OFFSET) {
130d123d8e1SMiklos Szeredi 		struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
131d123d8e1SMiklos Szeredi 		size_t reclen = FUSE_DIRENT_SIZE(dirent);
132d123d8e1SMiklos Szeredi 		if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
133d123d8e1SMiklos Szeredi 			return -EIO;
134d123d8e1SMiklos Szeredi 		if (reclen > nbytes)
135d123d8e1SMiklos Szeredi 			break;
136d123d8e1SMiklos Szeredi 		if (memchr(dirent->name, '/', dirent->namelen) != NULL)
137d123d8e1SMiklos Szeredi 			return -EIO;
138d123d8e1SMiklos Szeredi 
13918172b10SMiklos Szeredi 		if (!fuse_emit(file, ctx, dirent))
140d123d8e1SMiklos Szeredi 			break;
141d123d8e1SMiklos Szeredi 
142d123d8e1SMiklos Szeredi 		buf += reclen;
143d123d8e1SMiklos Szeredi 		nbytes -= reclen;
144d123d8e1SMiklos Szeredi 		ctx->pos = dirent->off;
145d123d8e1SMiklos Szeredi 	}
146d123d8e1SMiklos Szeredi 
147d123d8e1SMiklos Szeredi 	return 0;
148d123d8e1SMiklos Szeredi }
149d123d8e1SMiklos Szeredi 
fuse_direntplus_link(struct file * file,struct fuse_direntplus * direntplus,u64 attr_version)150d123d8e1SMiklos Szeredi static int fuse_direntplus_link(struct file *file,
151d123d8e1SMiklos Szeredi 				struct fuse_direntplus *direntplus,
152d123d8e1SMiklos Szeredi 				u64 attr_version)
153d123d8e1SMiklos Szeredi {
154d123d8e1SMiklos Szeredi 	struct fuse_entry_out *o = &direntplus->entry_out;
155d123d8e1SMiklos Szeredi 	struct fuse_dirent *dirent = &direntplus->dirent;
156d123d8e1SMiklos Szeredi 	struct dentry *parent = file->f_path.dentry;
157d123d8e1SMiklos Szeredi 	struct qstr name = QSTR_INIT(dirent->name, dirent->namelen);
158d123d8e1SMiklos Szeredi 	struct dentry *dentry;
159d123d8e1SMiklos Szeredi 	struct dentry *alias;
160d123d8e1SMiklos Szeredi 	struct inode *dir = d_inode(parent);
161d123d8e1SMiklos Szeredi 	struct fuse_conn *fc;
162d123d8e1SMiklos Szeredi 	struct inode *inode;
163d123d8e1SMiklos Szeredi 	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
164d123d8e1SMiklos Szeredi 
165d123d8e1SMiklos Szeredi 	if (!o->nodeid) {
166d123d8e1SMiklos Szeredi 		/*
167d123d8e1SMiklos Szeredi 		 * Unlike in the case of fuse_lookup, zero nodeid does not mean
168d123d8e1SMiklos Szeredi 		 * ENOENT. Instead, it only means the userspace filesystem did
169d123d8e1SMiklos Szeredi 		 * not want to return attributes/handle for this entry.
170d123d8e1SMiklos Szeredi 		 *
171d123d8e1SMiklos Szeredi 		 * So do nothing.
172d123d8e1SMiklos Szeredi 		 */
173d123d8e1SMiklos Szeredi 		return 0;
174d123d8e1SMiklos Szeredi 	}
175d123d8e1SMiklos Szeredi 
176d123d8e1SMiklos Szeredi 	if (name.name[0] == '.') {
177d123d8e1SMiklos Szeredi 		/*
178d123d8e1SMiklos Szeredi 		 * We could potentially refresh the attributes of the directory
179d123d8e1SMiklos Szeredi 		 * and its parent?
180d123d8e1SMiklos Szeredi 		 */
181d123d8e1SMiklos Szeredi 		if (name.len == 1)
182d123d8e1SMiklos Szeredi 			return 0;
183d123d8e1SMiklos Szeredi 		if (name.name[1] == '.' && name.len == 2)
184d123d8e1SMiklos Szeredi 			return 0;
185d123d8e1SMiklos Szeredi 	}
186d123d8e1SMiklos Szeredi 
187d123d8e1SMiklos Szeredi 	if (invalid_nodeid(o->nodeid))
188d123d8e1SMiklos Szeredi 		return -EIO;
189eb59bd17SMiklos Szeredi 	if (fuse_invalid_attr(&o->attr))
190d123d8e1SMiklos Szeredi 		return -EIO;
191d123d8e1SMiklos Szeredi 
192d123d8e1SMiklos Szeredi 	fc = get_fuse_conn(dir);
193d123d8e1SMiklos Szeredi 
194d123d8e1SMiklos Szeredi 	name.hash = full_name_hash(parent, name.name, name.len);
195d123d8e1SMiklos Szeredi 	dentry = d_lookup(parent, &name);
196d123d8e1SMiklos Szeredi 	if (!dentry) {
197d123d8e1SMiklos Szeredi retry:
198d123d8e1SMiklos Szeredi 		dentry = d_alloc_parallel(parent, &name, &wq);
199d123d8e1SMiklos Szeredi 		if (IS_ERR(dentry))
200d123d8e1SMiklos Szeredi 			return PTR_ERR(dentry);
201d123d8e1SMiklos Szeredi 	}
202d123d8e1SMiklos Szeredi 	if (!d_in_lookup(dentry)) {
203d123d8e1SMiklos Szeredi 		struct fuse_inode *fi;
204d123d8e1SMiklos Szeredi 		inode = d_inode(dentry);
20515db1683SAmir Goldstein 		if (inode && get_node_id(inode) != o->nodeid)
20615db1683SAmir Goldstein 			inode = NULL;
207d123d8e1SMiklos Szeredi 		if (!inode ||
20815db1683SAmir Goldstein 		    fuse_stale_inode(inode, o->generation, &o->attr)) {
20915db1683SAmir Goldstein 			if (inode)
21015db1683SAmir Goldstein 				fuse_make_bad(inode);
211d123d8e1SMiklos Szeredi 			d_invalidate(dentry);
212d123d8e1SMiklos Szeredi 			dput(dentry);
213d123d8e1SMiklos Szeredi 			goto retry;
214d123d8e1SMiklos Szeredi 		}
2155d069dbeSMiklos Szeredi 		if (fuse_is_bad(inode)) {
216d123d8e1SMiklos Szeredi 			dput(dentry);
217d123d8e1SMiklos Szeredi 			return -EIO;
218d123d8e1SMiklos Szeredi 		}
219d123d8e1SMiklos Szeredi 
220d123d8e1SMiklos Szeredi 		fi = get_fuse_inode(inode);
221c9d8f5f0SKirill Tkhai 		spin_lock(&fi->lock);
222d123d8e1SMiklos Szeredi 		fi->nlookup++;
223c9d8f5f0SKirill Tkhai 		spin_unlock(&fi->lock);
224d123d8e1SMiklos Szeredi 
225d123d8e1SMiklos Szeredi 		forget_all_cached_acls(inode);
226*972f4c46SMiklos Szeredi 		fuse_change_attributes(inode, &o->attr, NULL,
2279dc10a54SMiklos Szeredi 				       ATTR_TIMEOUT(o),
228d123d8e1SMiklos Szeredi 				       attr_version);
229d123d8e1SMiklos Szeredi 		/*
230d123d8e1SMiklos Szeredi 		 * The other branch comes via fuse_iget()
231d123d8e1SMiklos Szeredi 		 * which bumps nlookup inside
232d123d8e1SMiklos Szeredi 		 */
233d123d8e1SMiklos Szeredi 	} else {
234d123d8e1SMiklos Szeredi 		inode = fuse_iget(dir->i_sb, o->nodeid, o->generation,
2359dc10a54SMiklos Szeredi 				  &o->attr, ATTR_TIMEOUT(o),
236d123d8e1SMiklos Szeredi 				  attr_version);
237d123d8e1SMiklos Szeredi 		if (!inode)
238d123d8e1SMiklos Szeredi 			inode = ERR_PTR(-ENOMEM);
239d123d8e1SMiklos Szeredi 
240d123d8e1SMiklos Szeredi 		alias = d_splice_alias(inode, dentry);
241d123d8e1SMiklos Szeredi 		d_lookup_done(dentry);
242d123d8e1SMiklos Szeredi 		if (alias) {
243d123d8e1SMiklos Szeredi 			dput(dentry);
244d123d8e1SMiklos Szeredi 			dentry = alias;
245d123d8e1SMiklos Szeredi 		}
246b8bd342dSruanmeisi 		if (IS_ERR(dentry)) {
247b8bd342dSruanmeisi 			if (!IS_ERR(inode)) {
248b8bd342dSruanmeisi 				struct fuse_inode *fi = get_fuse_inode(inode);
249b8bd342dSruanmeisi 
250b8bd342dSruanmeisi 				spin_lock(&fi->lock);
251b8bd342dSruanmeisi 				fi->nlookup--;
252b8bd342dSruanmeisi 				spin_unlock(&fi->lock);
253b8bd342dSruanmeisi 			}
254d123d8e1SMiklos Szeredi 			return PTR_ERR(dentry);
255d123d8e1SMiklos Szeredi 		}
256b8bd342dSruanmeisi 	}
257d123d8e1SMiklos Szeredi 	if (fc->readdirplus_auto)
258d123d8e1SMiklos Szeredi 		set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
259d123d8e1SMiklos Szeredi 	fuse_change_entry_timeout(dentry, o);
260d123d8e1SMiklos Szeredi 
261d123d8e1SMiklos Szeredi 	dput(dentry);
262d123d8e1SMiklos Szeredi 	return 0;
263d123d8e1SMiklos Szeredi }
264d123d8e1SMiklos Szeredi 
fuse_force_forget(struct file * file,u64 nodeid)2653545fe21SMiklos Szeredi static void fuse_force_forget(struct file *file, u64 nodeid)
2663545fe21SMiklos Szeredi {
2673545fe21SMiklos Szeredi 	struct inode *inode = file_inode(file);
268fcee216bSMax Reitz 	struct fuse_mount *fm = get_fuse_mount(inode);
2693545fe21SMiklos Szeredi 	struct fuse_forget_in inarg;
2703545fe21SMiklos Szeredi 	FUSE_ARGS(args);
2713545fe21SMiklos Szeredi 
2723545fe21SMiklos Szeredi 	memset(&inarg, 0, sizeof(inarg));
2733545fe21SMiklos Szeredi 	inarg.nlookup = 1;
2743545fe21SMiklos Szeredi 	args.opcode = FUSE_FORGET;
2753545fe21SMiklos Szeredi 	args.nodeid = nodeid;
2763545fe21SMiklos Szeredi 	args.in_numargs = 1;
2773545fe21SMiklos Szeredi 	args.in_args[0].size = sizeof(inarg);
2783545fe21SMiklos Szeredi 	args.in_args[0].value = &inarg;
2793545fe21SMiklos Szeredi 	args.force = true;
2803545fe21SMiklos Szeredi 	args.noreply = true;
2813545fe21SMiklos Szeredi 
282fcee216bSMax Reitz 	fuse_simple_request(fm, &args);
2833545fe21SMiklos Szeredi 	/* ignore errors */
2843545fe21SMiklos Szeredi }
2853545fe21SMiklos Szeredi 
parse_dirplusfile(char * buf,size_t nbytes,struct file * file,struct dir_context * ctx,u64 attr_version)286d123d8e1SMiklos Szeredi static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
287d123d8e1SMiklos Szeredi 			     struct dir_context *ctx, u64 attr_version)
288d123d8e1SMiklos Szeredi {
289d123d8e1SMiklos Szeredi 	struct fuse_direntplus *direntplus;
290d123d8e1SMiklos Szeredi 	struct fuse_dirent *dirent;
291d123d8e1SMiklos Szeredi 	size_t reclen;
292d123d8e1SMiklos Szeredi 	int over = 0;
293d123d8e1SMiklos Szeredi 	int ret;
294d123d8e1SMiklos Szeredi 
295d123d8e1SMiklos Szeredi 	while (nbytes >= FUSE_NAME_OFFSET_DIRENTPLUS) {
296d123d8e1SMiklos Szeredi 		direntplus = (struct fuse_direntplus *) buf;
297d123d8e1SMiklos Szeredi 		dirent = &direntplus->dirent;
298d123d8e1SMiklos Szeredi 		reclen = FUSE_DIRENTPLUS_SIZE(direntplus);
299d123d8e1SMiklos Szeredi 
300d123d8e1SMiklos Szeredi 		if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
301d123d8e1SMiklos Szeredi 			return -EIO;
302d123d8e1SMiklos Szeredi 		if (reclen > nbytes)
303d123d8e1SMiklos Szeredi 			break;
304d123d8e1SMiklos Szeredi 		if (memchr(dirent->name, '/', dirent->namelen) != NULL)
305d123d8e1SMiklos Szeredi 			return -EIO;
306d123d8e1SMiklos Szeredi 
307d123d8e1SMiklos Szeredi 		if (!over) {
308d123d8e1SMiklos Szeredi 			/* We fill entries into dstbuf only as much as
309d123d8e1SMiklos Szeredi 			   it can hold. But we still continue iterating
310d123d8e1SMiklos Szeredi 			   over remaining entries to link them. If not,
311d123d8e1SMiklos Szeredi 			   we need to send a FORGET for each of those
312d123d8e1SMiklos Szeredi 			   which we did not link.
313d123d8e1SMiklos Szeredi 			*/
31418172b10SMiklos Szeredi 			over = !fuse_emit(file, ctx, dirent);
315d123d8e1SMiklos Szeredi 			if (!over)
316d123d8e1SMiklos Szeredi 				ctx->pos = dirent->off;
317d123d8e1SMiklos Szeredi 		}
318d123d8e1SMiklos Szeredi 
319d123d8e1SMiklos Szeredi 		buf += reclen;
320d123d8e1SMiklos Szeredi 		nbytes -= reclen;
321d123d8e1SMiklos Szeredi 
322d123d8e1SMiklos Szeredi 		ret = fuse_direntplus_link(file, direntplus, attr_version);
323d123d8e1SMiklos Szeredi 		if (ret)
324d123d8e1SMiklos Szeredi 			fuse_force_forget(file, direntplus->entry_out.nodeid);
325d123d8e1SMiklos Szeredi 	}
326d123d8e1SMiklos Szeredi 
327d123d8e1SMiklos Szeredi 	return 0;
328d123d8e1SMiklos Szeredi }
329d123d8e1SMiklos Szeredi 
fuse_readdir_uncached(struct file * file,struct dir_context * ctx)3305d7bc7e8SMiklos Szeredi static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx)
331d123d8e1SMiklos Szeredi {
33243f5098eSMiklos Szeredi 	int plus;
33343f5098eSMiklos Szeredi 	ssize_t res;
334d123d8e1SMiklos Szeredi 	struct page *page;
335d123d8e1SMiklos Szeredi 	struct inode *inode = file_inode(file);
336fcee216bSMax Reitz 	struct fuse_mount *fm = get_fuse_mount(inode);
33743f5098eSMiklos Szeredi 	struct fuse_io_args ia = {};
33843f5098eSMiklos Szeredi 	struct fuse_args_pages *ap = &ia.ap;
33943f5098eSMiklos Szeredi 	struct fuse_page_desc desc = { .length = PAGE_SIZE };
340d123d8e1SMiklos Szeredi 	u64 attr_version = 0;
341d123d8e1SMiklos Szeredi 	bool locked;
342d123d8e1SMiklos Szeredi 
343d123d8e1SMiklos Szeredi 	page = alloc_page(GFP_KERNEL);
34443f5098eSMiklos Szeredi 	if (!page)
345d123d8e1SMiklos Szeredi 		return -ENOMEM;
346d123d8e1SMiklos Szeredi 
347d123d8e1SMiklos Szeredi 	plus = fuse_use_readdirplus(inode, ctx);
348cabdb4faSzhengbin 	ap->args.out_pages = true;
34943f5098eSMiklos Szeredi 	ap->num_pages = 1;
35043f5098eSMiklos Szeredi 	ap->pages = &page;
35143f5098eSMiklos Szeredi 	ap->descs = &desc;
352d123d8e1SMiklos Szeredi 	if (plus) {
353fcee216bSMax Reitz 		attr_version = fuse_get_attr_version(fm->fc);
35443f5098eSMiklos Szeredi 		fuse_read_args_fill(&ia, file, ctx->pos, PAGE_SIZE,
355d123d8e1SMiklos Szeredi 				    FUSE_READDIRPLUS);
356d123d8e1SMiklos Szeredi 	} else {
35743f5098eSMiklos Szeredi 		fuse_read_args_fill(&ia, file, ctx->pos, PAGE_SIZE,
358d123d8e1SMiklos Szeredi 				    FUSE_READDIR);
359d123d8e1SMiklos Szeredi 	}
360d123d8e1SMiklos Szeredi 	locked = fuse_lock_inode(inode);
361fcee216bSMax Reitz 	res = fuse_simple_request(fm, &ap->args);
362d123d8e1SMiklos Szeredi 	fuse_unlock_inode(inode, locked);
36343f5098eSMiklos Szeredi 	if (res >= 0) {
36443f5098eSMiklos Szeredi 		if (!res) {
36569e34551SMiklos Szeredi 			struct fuse_file *ff = file->private_data;
36669e34551SMiklos Szeredi 
36769e34551SMiklos Szeredi 			if (ff->open_flags & FOPEN_CACHE_DIR)
36869e34551SMiklos Szeredi 				fuse_readdir_cache_end(file, ctx->pos);
36969e34551SMiklos Szeredi 		} else if (plus) {
37043f5098eSMiklos Szeredi 			res = parse_dirplusfile(page_address(page), res,
371d123d8e1SMiklos Szeredi 						file, ctx, attr_version);
372d123d8e1SMiklos Szeredi 		} else {
37343f5098eSMiklos Szeredi 			res = parse_dirfile(page_address(page), res, file,
374d123d8e1SMiklos Szeredi 					    ctx);
375d123d8e1SMiklos Szeredi 		}
376d123d8e1SMiklos Szeredi 	}
377d123d8e1SMiklos Szeredi 
378d123d8e1SMiklos Szeredi 	__free_page(page);
379d123d8e1SMiklos Szeredi 	fuse_invalidate_atime(inode);
38043f5098eSMiklos Szeredi 	return res;
381d123d8e1SMiklos Szeredi }
3825d7bc7e8SMiklos Szeredi 
3835d7bc7e8SMiklos Szeredi enum fuse_parse_result {
3845d7bc7e8SMiklos Szeredi 	FOUND_ERR = -1,
3855d7bc7e8SMiklos Szeredi 	FOUND_NONE = 0,
3865d7bc7e8SMiklos Szeredi 	FOUND_SOME,
3875d7bc7e8SMiklos Szeredi 	FOUND_ALL,
3885d7bc7e8SMiklos Szeredi };
3895d7bc7e8SMiklos Szeredi 
fuse_parse_cache(struct fuse_file * ff,void * addr,unsigned int size,struct dir_context * ctx)3905d7bc7e8SMiklos Szeredi static enum fuse_parse_result fuse_parse_cache(struct fuse_file *ff,
3915d7bc7e8SMiklos Szeredi 					       void *addr, unsigned int size,
3925d7bc7e8SMiklos Szeredi 					       struct dir_context *ctx)
3935d7bc7e8SMiklos Szeredi {
3945d7bc7e8SMiklos Szeredi 	unsigned int offset = ff->readdir.cache_off & ~PAGE_MASK;
3955d7bc7e8SMiklos Szeredi 	enum fuse_parse_result res = FOUND_NONE;
3965d7bc7e8SMiklos Szeredi 
3975d7bc7e8SMiklos Szeredi 	WARN_ON(offset >= size);
3985d7bc7e8SMiklos Szeredi 
3995d7bc7e8SMiklos Szeredi 	for (;;) {
4005d7bc7e8SMiklos Szeredi 		struct fuse_dirent *dirent = addr + offset;
4015d7bc7e8SMiklos Szeredi 		unsigned int nbytes = size - offset;
402e5854b1cSTejun Heo 		size_t reclen;
4035d7bc7e8SMiklos Szeredi 
4045d7bc7e8SMiklos Szeredi 		if (nbytes < FUSE_NAME_OFFSET || !dirent->namelen)
4055d7bc7e8SMiklos Szeredi 			break;
4065d7bc7e8SMiklos Szeredi 
407e5854b1cSTejun Heo 		reclen = FUSE_DIRENT_SIZE(dirent); /* derefs ->namelen */
408e5854b1cSTejun Heo 
4095d7bc7e8SMiklos Szeredi 		if (WARN_ON(dirent->namelen > FUSE_NAME_MAX))
4105d7bc7e8SMiklos Szeredi 			return FOUND_ERR;
4115d7bc7e8SMiklos Szeredi 		if (WARN_ON(reclen > nbytes))
4125d7bc7e8SMiklos Szeredi 			return FOUND_ERR;
4135d7bc7e8SMiklos Szeredi 		if (WARN_ON(memchr(dirent->name, '/', dirent->namelen) != NULL))
4145d7bc7e8SMiklos Szeredi 			return FOUND_ERR;
4155d7bc7e8SMiklos Szeredi 
4165d7bc7e8SMiklos Szeredi 		if (ff->readdir.pos == ctx->pos) {
4175d7bc7e8SMiklos Szeredi 			res = FOUND_SOME;
4185d7bc7e8SMiklos Szeredi 			if (!dir_emit(ctx, dirent->name, dirent->namelen,
4195d7bc7e8SMiklos Szeredi 				      dirent->ino, dirent->type))
4205d7bc7e8SMiklos Szeredi 				return FOUND_ALL;
4215d7bc7e8SMiklos Szeredi 			ctx->pos = dirent->off;
4225d7bc7e8SMiklos Szeredi 		}
4235d7bc7e8SMiklos Szeredi 		ff->readdir.pos = dirent->off;
4245d7bc7e8SMiklos Szeredi 		ff->readdir.cache_off += reclen;
4255d7bc7e8SMiklos Szeredi 
4265d7bc7e8SMiklos Szeredi 		offset += reclen;
4275d7bc7e8SMiklos Szeredi 	}
4285d7bc7e8SMiklos Szeredi 
4295d7bc7e8SMiklos Szeredi 	return res;
4305d7bc7e8SMiklos Szeredi }
4315d7bc7e8SMiklos Szeredi 
fuse_rdc_reset(struct inode * inode)4327118883bSMiklos Szeredi static void fuse_rdc_reset(struct inode *inode)
4333494927eSMiklos Szeredi {
4347118883bSMiklos Szeredi 	struct fuse_inode *fi = get_fuse_inode(inode);
4357118883bSMiklos Szeredi 
4363494927eSMiklos Szeredi 	fi->rdc.cached = false;
4373494927eSMiklos Szeredi 	fi->rdc.version++;
4383494927eSMiklos Szeredi 	fi->rdc.size = 0;
4393494927eSMiklos Szeredi 	fi->rdc.pos = 0;
4403494927eSMiklos Szeredi }
4413494927eSMiklos Szeredi 
4425d7bc7e8SMiklos Szeredi #define UNCACHED 1
4435d7bc7e8SMiklos Szeredi 
fuse_readdir_cached(struct file * file,struct dir_context * ctx)4445d7bc7e8SMiklos Szeredi static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
4455d7bc7e8SMiklos Szeredi {
4465d7bc7e8SMiklos Szeredi 	struct fuse_file *ff = file->private_data;
4475d7bc7e8SMiklos Szeredi 	struct inode *inode = file_inode(file);
4487118883bSMiklos Szeredi 	struct fuse_conn *fc = get_fuse_conn(inode);
4495d7bc7e8SMiklos Szeredi 	struct fuse_inode *fi = get_fuse_inode(inode);
4505d7bc7e8SMiklos Szeredi 	enum fuse_parse_result res;
4515d7bc7e8SMiklos Szeredi 	pgoff_t index;
4525d7bc7e8SMiklos Szeredi 	unsigned int size;
4535d7bc7e8SMiklos Szeredi 	struct page *page;
4545d7bc7e8SMiklos Szeredi 	void *addr;
4555d7bc7e8SMiklos Szeredi 
4565d7bc7e8SMiklos Szeredi 	/* Seeked?  If so, reset the cache stream */
4575d7bc7e8SMiklos Szeredi 	if (ff->readdir.pos != ctx->pos) {
4585d7bc7e8SMiklos Szeredi 		ff->readdir.pos = 0;
4595d7bc7e8SMiklos Szeredi 		ff->readdir.cache_off = 0;
4605d7bc7e8SMiklos Szeredi 	}
4615d7bc7e8SMiklos Szeredi 
4627118883bSMiklos Szeredi 	/*
4637118883bSMiklos Szeredi 	 * We're just about to start reading into the cache or reading the
4647118883bSMiklos Szeredi 	 * cache; both cases require an up-to-date mtime value.
4657118883bSMiklos Szeredi 	 */
4667118883bSMiklos Szeredi 	if (!ctx->pos && fc->auto_inval_data) {
467c6c745b8SMiklos Szeredi 		int err = fuse_update_attributes(inode, file, STATX_MTIME);
4687118883bSMiklos Szeredi 
4697118883bSMiklos Szeredi 		if (err)
4707118883bSMiklos Szeredi 			return err;
4717118883bSMiklos Szeredi 	}
4727118883bSMiklos Szeredi 
4735d7bc7e8SMiklos Szeredi retry:
4745d7bc7e8SMiklos Szeredi 	spin_lock(&fi->rdc.lock);
4757118883bSMiklos Szeredi retry_locked:
4765d7bc7e8SMiklos Szeredi 	if (!fi->rdc.cached) {
4777118883bSMiklos Szeredi 		/* Starting cache? Set cache mtime. */
4787118883bSMiklos Szeredi 		if (!ctx->pos && !fi->rdc.size) {
4797118883bSMiklos Szeredi 			fi->rdc.mtime = inode->i_mtime;
480261aaba7SMiklos Szeredi 			fi->rdc.iversion = inode_query_iversion(inode);
4817118883bSMiklos Szeredi 		}
4825d7bc7e8SMiklos Szeredi 		spin_unlock(&fi->rdc.lock);
4835d7bc7e8SMiklos Szeredi 		return UNCACHED;
4845d7bc7e8SMiklos Szeredi 	}
4853494927eSMiklos Szeredi 	/*
4867118883bSMiklos Szeredi 	 * When at the beginning of the directory (i.e. just after opendir(3) or
4877118883bSMiklos Szeredi 	 * rewinddir(3)), then need to check whether directory contents have
4887118883bSMiklos Szeredi 	 * changed, and reset the cache if so.
4897118883bSMiklos Szeredi 	 */
4907118883bSMiklos Szeredi 	if (!ctx->pos) {
491261aaba7SMiklos Szeredi 		if (inode_peek_iversion(inode) != fi->rdc.iversion ||
492261aaba7SMiklos Szeredi 		    !timespec64_equal(&fi->rdc.mtime, &inode->i_mtime)) {
4937118883bSMiklos Szeredi 			fuse_rdc_reset(inode);
4947118883bSMiklos Szeredi 			goto retry_locked;
4957118883bSMiklos Szeredi 		}
4967118883bSMiklos Szeredi 	}
4977118883bSMiklos Szeredi 
4987118883bSMiklos Szeredi 	/*
4993494927eSMiklos Szeredi 	 * If cache version changed since the last getdents() call, then reset
5003494927eSMiklos Szeredi 	 * the cache stream.
5013494927eSMiklos Szeredi 	 */
5023494927eSMiklos Szeredi 	if (ff->readdir.version != fi->rdc.version) {
5033494927eSMiklos Szeredi 		ff->readdir.pos = 0;
5043494927eSMiklos Szeredi 		ff->readdir.cache_off = 0;
5053494927eSMiklos Szeredi 	}
5063494927eSMiklos Szeredi 	/*
5073494927eSMiklos Szeredi 	 * If at the beginning of the cache, than reset version to
5083494927eSMiklos Szeredi 	 * current.
5093494927eSMiklos Szeredi 	 */
5103494927eSMiklos Szeredi 	if (ff->readdir.pos == 0)
5113494927eSMiklos Szeredi 		ff->readdir.version = fi->rdc.version;
5123494927eSMiklos Szeredi 
5135d7bc7e8SMiklos Szeredi 	WARN_ON(fi->rdc.size < ff->readdir.cache_off);
5145d7bc7e8SMiklos Szeredi 
5155d7bc7e8SMiklos Szeredi 	index = ff->readdir.cache_off >> PAGE_SHIFT;
5165d7bc7e8SMiklos Szeredi 
5175d7bc7e8SMiklos Szeredi 	if (index == (fi->rdc.size >> PAGE_SHIFT))
5185d7bc7e8SMiklos Szeredi 		size = fi->rdc.size & ~PAGE_MASK;
5195d7bc7e8SMiklos Szeredi 	else
5205d7bc7e8SMiklos Szeredi 		size = PAGE_SIZE;
5215d7bc7e8SMiklos Szeredi 	spin_unlock(&fi->rdc.lock);
5225d7bc7e8SMiklos Szeredi 
5235d7bc7e8SMiklos Szeredi 	/* EOF? */
5245d7bc7e8SMiklos Szeredi 	if ((ff->readdir.cache_off & ~PAGE_MASK) == size)
5255d7bc7e8SMiklos Szeredi 		return 0;
5265d7bc7e8SMiklos Szeredi 
5275d7bc7e8SMiklos Szeredi 	page = find_get_page_flags(file->f_mapping, index,
5285d7bc7e8SMiklos Szeredi 				   FGP_ACCESSED | FGP_LOCK);
5299fa248c6SMiklos Szeredi 	/* Page gone missing, then re-added to cache, but not initialized? */
5309fa248c6SMiklos Szeredi 	if (page && !PageUptodate(page)) {
5319fa248c6SMiklos Szeredi 		unlock_page(page);
5329fa248c6SMiklos Szeredi 		put_page(page);
5339fa248c6SMiklos Szeredi 		page = NULL;
5349fa248c6SMiklos Szeredi 	}
5353494927eSMiklos Szeredi 	spin_lock(&fi->rdc.lock);
5365d7bc7e8SMiklos Szeredi 	if (!page) {
5375d7bc7e8SMiklos Szeredi 		/*
5385d7bc7e8SMiklos Szeredi 		 * Uh-oh: page gone missing, cache is useless
5395d7bc7e8SMiklos Szeredi 		 */
5403494927eSMiklos Szeredi 		if (fi->rdc.version == ff->readdir.version)
5417118883bSMiklos Szeredi 			fuse_rdc_reset(inode);
5427118883bSMiklos Szeredi 		goto retry_locked;
5435d7bc7e8SMiklos Szeredi 	}
5445d7bc7e8SMiklos Szeredi 
5453494927eSMiklos Szeredi 	/* Make sure it's still the same version after getting the page. */
5463494927eSMiklos Szeredi 	if (ff->readdir.version != fi->rdc.version) {
5473494927eSMiklos Szeredi 		spin_unlock(&fi->rdc.lock);
5483494927eSMiklos Szeredi 		unlock_page(page);
5493494927eSMiklos Szeredi 		put_page(page);
5503494927eSMiklos Szeredi 		goto retry;
5513494927eSMiklos Szeredi 	}
5523494927eSMiklos Szeredi 	spin_unlock(&fi->rdc.lock);
5533494927eSMiklos Szeredi 
5543494927eSMiklos Szeredi 	/*
5553494927eSMiklos Szeredi 	 * Contents of the page are now protected against changing by holding
5563494927eSMiklos Szeredi 	 * the page lock.
5573494927eSMiklos Szeredi 	 */
558a1db2f7eSFabio M. De Francesco 	addr = kmap_local_page(page);
5595d7bc7e8SMiklos Szeredi 	res = fuse_parse_cache(ff, addr, size, ctx);
560a1db2f7eSFabio M. De Francesco 	kunmap_local(addr);
5615d7bc7e8SMiklos Szeredi 	unlock_page(page);
5625d7bc7e8SMiklos Szeredi 	put_page(page);
5635d7bc7e8SMiklos Szeredi 
5645d7bc7e8SMiklos Szeredi 	if (res == FOUND_ERR)
5655d7bc7e8SMiklos Szeredi 		return -EIO;
5665d7bc7e8SMiklos Szeredi 
5675d7bc7e8SMiklos Szeredi 	if (res == FOUND_ALL)
5685d7bc7e8SMiklos Szeredi 		return 0;
5695d7bc7e8SMiklos Szeredi 
5705d7bc7e8SMiklos Szeredi 	if (size == PAGE_SIZE) {
5715d7bc7e8SMiklos Szeredi 		/* We hit end of page: skip to next page. */
5725d7bc7e8SMiklos Szeredi 		ff->readdir.cache_off = ALIGN(ff->readdir.cache_off, PAGE_SIZE);
5735d7bc7e8SMiklos Szeredi 		goto retry;
5745d7bc7e8SMiklos Szeredi 	}
5755d7bc7e8SMiklos Szeredi 
5765d7bc7e8SMiklos Szeredi 	/*
5775d7bc7e8SMiklos Szeredi 	 * End of cache reached.  If found position, then we are done, otherwise
5785d7bc7e8SMiklos Szeredi 	 * need to fall back to uncached, since the position we were looking for
5795d7bc7e8SMiklos Szeredi 	 * wasn't in the cache.
5805d7bc7e8SMiklos Szeredi 	 */
5815d7bc7e8SMiklos Szeredi 	return res == FOUND_SOME ? 0 : UNCACHED;
5825d7bc7e8SMiklos Szeredi }
5835d7bc7e8SMiklos Szeredi 
fuse_readdir(struct file * file,struct dir_context * ctx)5845d7bc7e8SMiklos Szeredi int fuse_readdir(struct file *file, struct dir_context *ctx)
5855d7bc7e8SMiklos Szeredi {
5865d7bc7e8SMiklos Szeredi 	struct fuse_file *ff = file->private_data;
5875d7bc7e8SMiklos Szeredi 	struct inode *inode = file_inode(file);
5885d7bc7e8SMiklos Szeredi 	int err;
5895d7bc7e8SMiklos Szeredi 
5905d069dbeSMiklos Szeredi 	if (fuse_is_bad(inode))
5915d7bc7e8SMiklos Szeredi 		return -EIO;
5925d7bc7e8SMiklos Szeredi 
5935d7bc7e8SMiklos Szeredi 	mutex_lock(&ff->readdir.lock);
5945d7bc7e8SMiklos Szeredi 
5955d7bc7e8SMiklos Szeredi 	err = UNCACHED;
5965d7bc7e8SMiklos Szeredi 	if (ff->open_flags & FOPEN_CACHE_DIR)
5975d7bc7e8SMiklos Szeredi 		err = fuse_readdir_cached(file, ctx);
5985d7bc7e8SMiklos Szeredi 	if (err == UNCACHED)
5995d7bc7e8SMiklos Szeredi 		err = fuse_readdir_uncached(file, ctx);
6005d7bc7e8SMiklos Szeredi 
6015d7bc7e8SMiklos Szeredi 	mutex_unlock(&ff->readdir.lock);
6025d7bc7e8SMiklos Szeredi 
6035d7bc7e8SMiklos Szeredi 	return err;
6045d7bc7e8SMiklos Szeredi }
605