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