162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* dir.c: AFS filesystem directory handling
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2002, 2018 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/fs.h>
1062306a36Sopenharmony_ci#include <linux/namei.h>
1162306a36Sopenharmony_ci#include <linux/pagemap.h>
1262306a36Sopenharmony_ci#include <linux/swap.h>
1362306a36Sopenharmony_ci#include <linux/ctype.h>
1462306a36Sopenharmony_ci#include <linux/sched.h>
1562306a36Sopenharmony_ci#include <linux/task_io_accounting_ops.h>
1662306a36Sopenharmony_ci#include "internal.h"
1762306a36Sopenharmony_ci#include "afs_fs.h"
1862306a36Sopenharmony_ci#include "xdr_fs.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
2162306a36Sopenharmony_ci				 unsigned int flags);
2262306a36Sopenharmony_cistatic int afs_dir_open(struct inode *inode, struct file *file);
2362306a36Sopenharmony_cistatic int afs_readdir(struct file *file, struct dir_context *ctx);
2462306a36Sopenharmony_cistatic int afs_d_revalidate(struct dentry *dentry, unsigned int flags);
2562306a36Sopenharmony_cistatic int afs_d_delete(const struct dentry *dentry);
2662306a36Sopenharmony_cistatic void afs_d_iput(struct dentry *dentry, struct inode *inode);
2762306a36Sopenharmony_cistatic bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name, int nlen,
2862306a36Sopenharmony_ci				  loff_t fpos, u64 ino, unsigned dtype);
2962306a36Sopenharmony_cistatic bool afs_lookup_filldir(struct dir_context *ctx, const char *name, int nlen,
3062306a36Sopenharmony_ci			      loff_t fpos, u64 ino, unsigned dtype);
3162306a36Sopenharmony_cistatic int afs_create(struct mnt_idmap *idmap, struct inode *dir,
3262306a36Sopenharmony_ci		      struct dentry *dentry, umode_t mode, bool excl);
3362306a36Sopenharmony_cistatic int afs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
3462306a36Sopenharmony_ci		     struct dentry *dentry, umode_t mode);
3562306a36Sopenharmony_cistatic int afs_rmdir(struct inode *dir, struct dentry *dentry);
3662306a36Sopenharmony_cistatic int afs_unlink(struct inode *dir, struct dentry *dentry);
3762306a36Sopenharmony_cistatic int afs_link(struct dentry *from, struct inode *dir,
3862306a36Sopenharmony_ci		    struct dentry *dentry);
3962306a36Sopenharmony_cistatic int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,
4062306a36Sopenharmony_ci		       struct dentry *dentry, const char *content);
4162306a36Sopenharmony_cistatic int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
4262306a36Sopenharmony_ci		      struct dentry *old_dentry, struct inode *new_dir,
4362306a36Sopenharmony_ci		      struct dentry *new_dentry, unsigned int flags);
4462306a36Sopenharmony_cistatic bool afs_dir_release_folio(struct folio *folio, gfp_t gfp_flags);
4562306a36Sopenharmony_cistatic void afs_dir_invalidate_folio(struct folio *folio, size_t offset,
4662306a36Sopenharmony_ci				   size_t length);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic bool afs_dir_dirty_folio(struct address_space *mapping,
4962306a36Sopenharmony_ci		struct folio *folio)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	BUG(); /* This should never happen. */
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciconst struct file_operations afs_dir_file_operations = {
5562306a36Sopenharmony_ci	.open		= afs_dir_open,
5662306a36Sopenharmony_ci	.release	= afs_release,
5762306a36Sopenharmony_ci	.iterate_shared	= afs_readdir,
5862306a36Sopenharmony_ci	.lock		= afs_lock,
5962306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciconst struct inode_operations afs_dir_inode_operations = {
6362306a36Sopenharmony_ci	.create		= afs_create,
6462306a36Sopenharmony_ci	.lookup		= afs_lookup,
6562306a36Sopenharmony_ci	.link		= afs_link,
6662306a36Sopenharmony_ci	.unlink		= afs_unlink,
6762306a36Sopenharmony_ci	.symlink	= afs_symlink,
6862306a36Sopenharmony_ci	.mkdir		= afs_mkdir,
6962306a36Sopenharmony_ci	.rmdir		= afs_rmdir,
7062306a36Sopenharmony_ci	.rename		= afs_rename,
7162306a36Sopenharmony_ci	.permission	= afs_permission,
7262306a36Sopenharmony_ci	.getattr	= afs_getattr,
7362306a36Sopenharmony_ci	.setattr	= afs_setattr,
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciconst struct address_space_operations afs_dir_aops = {
7762306a36Sopenharmony_ci	.dirty_folio	= afs_dir_dirty_folio,
7862306a36Sopenharmony_ci	.release_folio	= afs_dir_release_folio,
7962306a36Sopenharmony_ci	.invalidate_folio = afs_dir_invalidate_folio,
8062306a36Sopenharmony_ci	.migrate_folio	= filemap_migrate_folio,
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciconst struct dentry_operations afs_fs_dentry_operations = {
8462306a36Sopenharmony_ci	.d_revalidate	= afs_d_revalidate,
8562306a36Sopenharmony_ci	.d_delete	= afs_d_delete,
8662306a36Sopenharmony_ci	.d_release	= afs_d_release,
8762306a36Sopenharmony_ci	.d_automount	= afs_d_automount,
8862306a36Sopenharmony_ci	.d_iput		= afs_d_iput,
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistruct afs_lookup_one_cookie {
9262306a36Sopenharmony_ci	struct dir_context	ctx;
9362306a36Sopenharmony_ci	struct qstr		name;
9462306a36Sopenharmony_ci	bool			found;
9562306a36Sopenharmony_ci	struct afs_fid		fid;
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistruct afs_lookup_cookie {
9962306a36Sopenharmony_ci	struct dir_context	ctx;
10062306a36Sopenharmony_ci	struct qstr		name;
10162306a36Sopenharmony_ci	bool			found;
10262306a36Sopenharmony_ci	bool			one_only;
10362306a36Sopenharmony_ci	unsigned short		nr_fids;
10462306a36Sopenharmony_ci	struct afs_fid		fids[50];
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * Drop the refs that we're holding on the folios we were reading into.  We've
10962306a36Sopenharmony_ci * got refs on the first nr_pages pages.
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistatic void afs_dir_read_cleanup(struct afs_read *req)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct address_space *mapping = req->vnode->netfs.inode.i_mapping;
11462306a36Sopenharmony_ci	struct folio *folio;
11562306a36Sopenharmony_ci	pgoff_t last = req->nr_pages - 1;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	XA_STATE(xas, &mapping->i_pages, 0);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (unlikely(!req->nr_pages))
12062306a36Sopenharmony_ci		return;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	rcu_read_lock();
12362306a36Sopenharmony_ci	xas_for_each(&xas, folio, last) {
12462306a36Sopenharmony_ci		if (xas_retry(&xas, folio))
12562306a36Sopenharmony_ci			continue;
12662306a36Sopenharmony_ci		BUG_ON(xa_is_value(folio));
12762306a36Sopenharmony_ci		ASSERTCMP(folio_file_mapping(folio), ==, mapping);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		folio_put(folio);
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	rcu_read_unlock();
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/*
13662306a36Sopenharmony_ci * check that a directory folio is valid
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_cistatic bool afs_dir_check_folio(struct afs_vnode *dvnode, struct folio *folio,
13962306a36Sopenharmony_ci				loff_t i_size)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	union afs_xdr_dir_block *block;
14262306a36Sopenharmony_ci	size_t offset, size;
14362306a36Sopenharmony_ci	loff_t pos;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* Determine how many magic numbers there should be in this folio, but
14662306a36Sopenharmony_ci	 * we must take care because the directory may change size under us.
14762306a36Sopenharmony_ci	 */
14862306a36Sopenharmony_ci	pos = folio_pos(folio);
14962306a36Sopenharmony_ci	if (i_size <= pos)
15062306a36Sopenharmony_ci		goto checked;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	size = min_t(loff_t, folio_size(folio), i_size - pos);
15362306a36Sopenharmony_ci	for (offset = 0; offset < size; offset += sizeof(*block)) {
15462306a36Sopenharmony_ci		block = kmap_local_folio(folio, offset);
15562306a36Sopenharmony_ci		if (block->hdr.magic != AFS_DIR_MAGIC) {
15662306a36Sopenharmony_ci			printk("kAFS: %s(%lx): [%llx] bad magic %zx/%zx is %04hx\n",
15762306a36Sopenharmony_ci			       __func__, dvnode->netfs.inode.i_ino,
15862306a36Sopenharmony_ci			       pos, offset, size, ntohs(block->hdr.magic));
15962306a36Sopenharmony_ci			trace_afs_dir_check_failed(dvnode, pos + offset, i_size);
16062306a36Sopenharmony_ci			kunmap_local(block);
16162306a36Sopenharmony_ci			trace_afs_file_error(dvnode, -EIO, afs_file_error_dir_bad_magic);
16262306a36Sopenharmony_ci			goto error;
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		/* Make sure each block is NUL terminated so we can reasonably
16662306a36Sopenharmony_ci		 * use string functions on it.  The filenames in the folio
16762306a36Sopenharmony_ci		 * *should* be NUL-terminated anyway.
16862306a36Sopenharmony_ci		 */
16962306a36Sopenharmony_ci		((u8 *)block)[AFS_DIR_BLOCK_SIZE - 1] = 0;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		kunmap_local(block);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_cichecked:
17462306a36Sopenharmony_ci	afs_stat_v(dvnode, n_read_dir);
17562306a36Sopenharmony_ci	return true;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cierror:
17862306a36Sopenharmony_ci	return false;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/*
18262306a36Sopenharmony_ci * Dump the contents of a directory.
18362306a36Sopenharmony_ci */
18462306a36Sopenharmony_cistatic void afs_dir_dump(struct afs_vnode *dvnode, struct afs_read *req)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	union afs_xdr_dir_block *block;
18762306a36Sopenharmony_ci	struct address_space *mapping = dvnode->netfs.inode.i_mapping;
18862306a36Sopenharmony_ci	struct folio *folio;
18962306a36Sopenharmony_ci	pgoff_t last = req->nr_pages - 1;
19062306a36Sopenharmony_ci	size_t offset, size;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	XA_STATE(xas, &mapping->i_pages, 0);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	pr_warn("DIR %llx:%llx f=%llx l=%llx al=%llx\n",
19562306a36Sopenharmony_ci		dvnode->fid.vid, dvnode->fid.vnode,
19662306a36Sopenharmony_ci		req->file_size, req->len, req->actual_len);
19762306a36Sopenharmony_ci	pr_warn("DIR %llx %x %zx %zx\n",
19862306a36Sopenharmony_ci		req->pos, req->nr_pages,
19962306a36Sopenharmony_ci		req->iter->iov_offset,  iov_iter_count(req->iter));
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	xas_for_each(&xas, folio, last) {
20262306a36Sopenharmony_ci		if (xas_retry(&xas, folio))
20362306a36Sopenharmony_ci			continue;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		BUG_ON(folio_file_mapping(folio) != mapping);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		size = min_t(loff_t, folio_size(folio), req->actual_len - folio_pos(folio));
20862306a36Sopenharmony_ci		for (offset = 0; offset < size; offset += sizeof(*block)) {
20962306a36Sopenharmony_ci			block = kmap_local_folio(folio, offset);
21062306a36Sopenharmony_ci			pr_warn("[%02lx] %32phN\n", folio_index(folio) + offset, block);
21162306a36Sopenharmony_ci			kunmap_local(block);
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/*
21762306a36Sopenharmony_ci * Check all the blocks in a directory.  All the folios are held pinned.
21862306a36Sopenharmony_ci */
21962306a36Sopenharmony_cistatic int afs_dir_check(struct afs_vnode *dvnode, struct afs_read *req)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct address_space *mapping = dvnode->netfs.inode.i_mapping;
22262306a36Sopenharmony_ci	struct folio *folio;
22362306a36Sopenharmony_ci	pgoff_t last = req->nr_pages - 1;
22462306a36Sopenharmony_ci	int ret = 0;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	XA_STATE(xas, &mapping->i_pages, 0);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (unlikely(!req->nr_pages))
22962306a36Sopenharmony_ci		return 0;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	rcu_read_lock();
23262306a36Sopenharmony_ci	xas_for_each(&xas, folio, last) {
23362306a36Sopenharmony_ci		if (xas_retry(&xas, folio))
23462306a36Sopenharmony_ci			continue;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		BUG_ON(folio_file_mapping(folio) != mapping);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		if (!afs_dir_check_folio(dvnode, folio, req->actual_len)) {
23962306a36Sopenharmony_ci			afs_dir_dump(dvnode, req);
24062306a36Sopenharmony_ci			ret = -EIO;
24162306a36Sopenharmony_ci			break;
24262306a36Sopenharmony_ci		}
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	rcu_read_unlock();
24662306a36Sopenharmony_ci	return ret;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci/*
25062306a36Sopenharmony_ci * open an AFS directory file
25162306a36Sopenharmony_ci */
25262306a36Sopenharmony_cistatic int afs_dir_open(struct inode *inode, struct file *file)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	_enter("{%lu}", inode->i_ino);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048);
25762306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(union afs_xdr_dirent) != 32);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(inode)->flags))
26062306a36Sopenharmony_ci		return -ENOENT;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	return afs_open(inode, file);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/*
26662306a36Sopenharmony_ci * Read the directory into the pagecache in one go, scrubbing the previous
26762306a36Sopenharmony_ci * contents.  The list of folios is returned, pinning them so that they don't
26862306a36Sopenharmony_ci * get reclaimed during the iteration.
26962306a36Sopenharmony_ci */
27062306a36Sopenharmony_cistatic struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
27162306a36Sopenharmony_ci	__acquires(&dvnode->validate_lock)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct address_space *mapping = dvnode->netfs.inode.i_mapping;
27462306a36Sopenharmony_ci	struct afs_read *req;
27562306a36Sopenharmony_ci	loff_t i_size;
27662306a36Sopenharmony_ci	int nr_pages, i;
27762306a36Sopenharmony_ci	int ret;
27862306a36Sopenharmony_ci	loff_t remote_size = 0;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	_enter("");
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
28362306a36Sopenharmony_ci	if (!req)
28462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	refcount_set(&req->usage, 1);
28762306a36Sopenharmony_ci	req->vnode = dvnode;
28862306a36Sopenharmony_ci	req->key = key_get(key);
28962306a36Sopenharmony_ci	req->cleanup = afs_dir_read_cleanup;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ciexpand:
29262306a36Sopenharmony_ci	i_size = i_size_read(&dvnode->netfs.inode);
29362306a36Sopenharmony_ci	if (i_size < remote_size)
29462306a36Sopenharmony_ci	    i_size = remote_size;
29562306a36Sopenharmony_ci	if (i_size < 2048) {
29662306a36Sopenharmony_ci		ret = afs_bad(dvnode, afs_file_error_dir_small);
29762306a36Sopenharmony_ci		goto error;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci	if (i_size > 2048 * 1024) {
30062306a36Sopenharmony_ci		trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big);
30162306a36Sopenharmony_ci		ret = -EFBIG;
30262306a36Sopenharmony_ci		goto error;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	_enter("%llu", i_size);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	nr_pages = (i_size + PAGE_SIZE - 1) / PAGE_SIZE;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	req->actual_len = i_size; /* May change */
31062306a36Sopenharmony_ci	req->len = nr_pages * PAGE_SIZE; /* We can ask for more than there is */
31162306a36Sopenharmony_ci	req->data_version = dvnode->status.data_version; /* May change */
31262306a36Sopenharmony_ci	iov_iter_xarray(&req->def_iter, ITER_DEST, &dvnode->netfs.inode.i_mapping->i_pages,
31362306a36Sopenharmony_ci			0, i_size);
31462306a36Sopenharmony_ci	req->iter = &req->def_iter;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* Fill in any gaps that we might find where the memory reclaimer has
31762306a36Sopenharmony_ci	 * been at work and pin all the folios.  If there are any gaps, we will
31862306a36Sopenharmony_ci	 * need to reread the entire directory contents.
31962306a36Sopenharmony_ci	 */
32062306a36Sopenharmony_ci	i = req->nr_pages;
32162306a36Sopenharmony_ci	while (i < nr_pages) {
32262306a36Sopenharmony_ci		struct folio *folio;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		folio = filemap_get_folio(mapping, i);
32562306a36Sopenharmony_ci		if (IS_ERR(folio)) {
32662306a36Sopenharmony_ci			if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
32762306a36Sopenharmony_ci				afs_stat_v(dvnode, n_inval);
32862306a36Sopenharmony_ci			folio = __filemap_get_folio(mapping,
32962306a36Sopenharmony_ci						    i, FGP_LOCK | FGP_CREAT,
33062306a36Sopenharmony_ci						    mapping->gfp_mask);
33162306a36Sopenharmony_ci			if (IS_ERR(folio)) {
33262306a36Sopenharmony_ci				ret = PTR_ERR(folio);
33362306a36Sopenharmony_ci				goto error;
33462306a36Sopenharmony_ci			}
33562306a36Sopenharmony_ci			folio_attach_private(folio, (void *)1);
33662306a36Sopenharmony_ci			folio_unlock(folio);
33762306a36Sopenharmony_ci		}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		req->nr_pages += folio_nr_pages(folio);
34062306a36Sopenharmony_ci		i += folio_nr_pages(folio);
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* If we're going to reload, we need to lock all the pages to prevent
34462306a36Sopenharmony_ci	 * races.
34562306a36Sopenharmony_ci	 */
34662306a36Sopenharmony_ci	ret = -ERESTARTSYS;
34762306a36Sopenharmony_ci	if (down_read_killable(&dvnode->validate_lock) < 0)
34862306a36Sopenharmony_ci		goto error;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
35162306a36Sopenharmony_ci		goto success;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	up_read(&dvnode->validate_lock);
35462306a36Sopenharmony_ci	if (down_write_killable(&dvnode->validate_lock) < 0)
35562306a36Sopenharmony_ci		goto error;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (!test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
35862306a36Sopenharmony_ci		trace_afs_reload_dir(dvnode);
35962306a36Sopenharmony_ci		ret = afs_fetch_data(dvnode, req);
36062306a36Sopenharmony_ci		if (ret < 0)
36162306a36Sopenharmony_ci			goto error_unlock;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		task_io_account_read(PAGE_SIZE * req->nr_pages);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		if (req->len < req->file_size) {
36662306a36Sopenharmony_ci			/* The content has grown, so we need to expand the
36762306a36Sopenharmony_ci			 * buffer.
36862306a36Sopenharmony_ci			 */
36962306a36Sopenharmony_ci			up_write(&dvnode->validate_lock);
37062306a36Sopenharmony_ci			remote_size = req->file_size;
37162306a36Sopenharmony_ci			goto expand;
37262306a36Sopenharmony_ci		}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		/* Validate the data we just read. */
37562306a36Sopenharmony_ci		ret = afs_dir_check(dvnode, req);
37662306a36Sopenharmony_ci		if (ret < 0)
37762306a36Sopenharmony_ci			goto error_unlock;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		// TODO: Trim excess pages
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		set_bit(AFS_VNODE_DIR_VALID, &dvnode->flags);
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	downgrade_write(&dvnode->validate_lock);
38562306a36Sopenharmony_cisuccess:
38662306a36Sopenharmony_ci	return req;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cierror_unlock:
38962306a36Sopenharmony_ci	up_write(&dvnode->validate_lock);
39062306a36Sopenharmony_cierror:
39162306a36Sopenharmony_ci	afs_put_read(req);
39262306a36Sopenharmony_ci	_leave(" = %d", ret);
39362306a36Sopenharmony_ci	return ERR_PTR(ret);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/*
39762306a36Sopenharmony_ci * deal with one block in an AFS directory
39862306a36Sopenharmony_ci */
39962306a36Sopenharmony_cistatic int afs_dir_iterate_block(struct afs_vnode *dvnode,
40062306a36Sopenharmony_ci				 struct dir_context *ctx,
40162306a36Sopenharmony_ci				 union afs_xdr_dir_block *block,
40262306a36Sopenharmony_ci				 unsigned blkoff)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	union afs_xdr_dirent *dire;
40562306a36Sopenharmony_ci	unsigned offset, next, curr, nr_slots;
40662306a36Sopenharmony_ci	size_t nlen;
40762306a36Sopenharmony_ci	int tmp;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	_enter("%llx,%x", ctx->pos, blkoff);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	curr = (ctx->pos - blkoff) / sizeof(union afs_xdr_dirent);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* walk through the block, an entry at a time */
41462306a36Sopenharmony_ci	for (offset = (blkoff == 0 ? AFS_DIR_RESV_BLOCKS0 : AFS_DIR_RESV_BLOCKS);
41562306a36Sopenharmony_ci	     offset < AFS_DIR_SLOTS_PER_BLOCK;
41662306a36Sopenharmony_ci	     offset = next
41762306a36Sopenharmony_ci	     ) {
41862306a36Sopenharmony_ci		/* skip entries marked unused in the bitmap */
41962306a36Sopenharmony_ci		if (!(block->hdr.bitmap[offset / 8] &
42062306a36Sopenharmony_ci		      (1 << (offset % 8)))) {
42162306a36Sopenharmony_ci			_debug("ENT[%zu.%u]: unused",
42262306a36Sopenharmony_ci			       blkoff / sizeof(union afs_xdr_dir_block), offset);
42362306a36Sopenharmony_ci			next = offset + 1;
42462306a36Sopenharmony_ci			if (offset >= curr)
42562306a36Sopenharmony_ci				ctx->pos = blkoff +
42662306a36Sopenharmony_ci					next * sizeof(union afs_xdr_dirent);
42762306a36Sopenharmony_ci			continue;
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		/* got a valid entry */
43162306a36Sopenharmony_ci		dire = &block->dirents[offset];
43262306a36Sopenharmony_ci		nlen = strnlen(dire->u.name,
43362306a36Sopenharmony_ci			       sizeof(*block) -
43462306a36Sopenharmony_ci			       offset * sizeof(union afs_xdr_dirent));
43562306a36Sopenharmony_ci		if (nlen > AFSNAMEMAX - 1) {
43662306a36Sopenharmony_ci			_debug("ENT[%zu]: name too long (len %u/%zu)",
43762306a36Sopenharmony_ci			       blkoff / sizeof(union afs_xdr_dir_block),
43862306a36Sopenharmony_ci			       offset, nlen);
43962306a36Sopenharmony_ci			return afs_bad(dvnode, afs_file_error_dir_name_too_long);
44062306a36Sopenharmony_ci		}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		_debug("ENT[%zu.%u]: %s %zu \"%s\"",
44362306a36Sopenharmony_ci		       blkoff / sizeof(union afs_xdr_dir_block), offset,
44462306a36Sopenharmony_ci		       (offset < curr ? "skip" : "fill"),
44562306a36Sopenharmony_ci		       nlen, dire->u.name);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		nr_slots = afs_dir_calc_slots(nlen);
44862306a36Sopenharmony_ci		next = offset + nr_slots;
44962306a36Sopenharmony_ci		if (next > AFS_DIR_SLOTS_PER_BLOCK) {
45062306a36Sopenharmony_ci			_debug("ENT[%zu.%u]:"
45162306a36Sopenharmony_ci			       " %u extends beyond end dir block"
45262306a36Sopenharmony_ci			       " (len %zu)",
45362306a36Sopenharmony_ci			       blkoff / sizeof(union afs_xdr_dir_block),
45462306a36Sopenharmony_ci			       offset, next, nlen);
45562306a36Sopenharmony_ci			return afs_bad(dvnode, afs_file_error_dir_over_end);
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		/* Check that the name-extension dirents are all allocated */
45962306a36Sopenharmony_ci		for (tmp = 1; tmp < nr_slots; tmp++) {
46062306a36Sopenharmony_ci			unsigned int ix = offset + tmp;
46162306a36Sopenharmony_ci			if (!(block->hdr.bitmap[ix / 8] & (1 << (ix % 8)))) {
46262306a36Sopenharmony_ci				_debug("ENT[%zu.u]:"
46362306a36Sopenharmony_ci				       " %u unmarked extension (%u/%u)",
46462306a36Sopenharmony_ci				       blkoff / sizeof(union afs_xdr_dir_block),
46562306a36Sopenharmony_ci				       offset, tmp, nr_slots);
46662306a36Sopenharmony_ci				return afs_bad(dvnode, afs_file_error_dir_unmarked_ext);
46762306a36Sopenharmony_ci			}
46862306a36Sopenharmony_ci		}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		/* skip if starts before the current position */
47162306a36Sopenharmony_ci		if (offset < curr) {
47262306a36Sopenharmony_ci			if (next > curr)
47362306a36Sopenharmony_ci				ctx->pos = blkoff + next * sizeof(union afs_xdr_dirent);
47462306a36Sopenharmony_ci			continue;
47562306a36Sopenharmony_ci		}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		/* found the next entry */
47862306a36Sopenharmony_ci		if (!dir_emit(ctx, dire->u.name, nlen,
47962306a36Sopenharmony_ci			      ntohl(dire->u.vnode),
48062306a36Sopenharmony_ci			      (ctx->actor == afs_lookup_filldir ||
48162306a36Sopenharmony_ci			       ctx->actor == afs_lookup_one_filldir)?
48262306a36Sopenharmony_ci			      ntohl(dire->u.unique) : DT_UNKNOWN)) {
48362306a36Sopenharmony_ci			_leave(" = 0 [full]");
48462306a36Sopenharmony_ci			return 0;
48562306a36Sopenharmony_ci		}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		ctx->pos = blkoff + next * sizeof(union afs_xdr_dirent);
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	_leave(" = 1 [more]");
49162306a36Sopenharmony_ci	return 1;
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci/*
49562306a36Sopenharmony_ci * iterate through the data blob that lists the contents of an AFS directory
49662306a36Sopenharmony_ci */
49762306a36Sopenharmony_cistatic int afs_dir_iterate(struct inode *dir, struct dir_context *ctx,
49862306a36Sopenharmony_ci			   struct key *key, afs_dataversion_t *_dir_version)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct afs_vnode *dvnode = AFS_FS_I(dir);
50162306a36Sopenharmony_ci	union afs_xdr_dir_block *dblock;
50262306a36Sopenharmony_ci	struct afs_read *req;
50362306a36Sopenharmony_ci	struct folio *folio;
50462306a36Sopenharmony_ci	unsigned offset, size;
50562306a36Sopenharmony_ci	int ret;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	_enter("{%lu},%u,,", dir->i_ino, (unsigned)ctx->pos);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) {
51062306a36Sopenharmony_ci		_leave(" = -ESTALE");
51162306a36Sopenharmony_ci		return -ESTALE;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	req = afs_read_dir(dvnode, key);
51562306a36Sopenharmony_ci	if (IS_ERR(req))
51662306a36Sopenharmony_ci		return PTR_ERR(req);
51762306a36Sopenharmony_ci	*_dir_version = req->data_version;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	/* round the file position up to the next entry boundary */
52062306a36Sopenharmony_ci	ctx->pos += sizeof(union afs_xdr_dirent) - 1;
52162306a36Sopenharmony_ci	ctx->pos &= ~(sizeof(union afs_xdr_dirent) - 1);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* walk through the blocks in sequence */
52462306a36Sopenharmony_ci	ret = 0;
52562306a36Sopenharmony_ci	while (ctx->pos < req->actual_len) {
52662306a36Sopenharmony_ci		/* Fetch the appropriate folio from the directory and re-add it
52762306a36Sopenharmony_ci		 * to the LRU.  We have all the pages pinned with an extra ref.
52862306a36Sopenharmony_ci		 */
52962306a36Sopenharmony_ci		folio = __filemap_get_folio(dir->i_mapping, ctx->pos / PAGE_SIZE,
53062306a36Sopenharmony_ci					    FGP_ACCESSED, 0);
53162306a36Sopenharmony_ci		if (IS_ERR(folio)) {
53262306a36Sopenharmony_ci			ret = afs_bad(dvnode, afs_file_error_dir_missing_page);
53362306a36Sopenharmony_ci			break;
53462306a36Sopenharmony_ci		}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		offset = round_down(ctx->pos, sizeof(*dblock)) - folio_file_pos(folio);
53762306a36Sopenharmony_ci		size = min_t(loff_t, folio_size(folio),
53862306a36Sopenharmony_ci			     req->actual_len - folio_file_pos(folio));
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		do {
54162306a36Sopenharmony_ci			dblock = kmap_local_folio(folio, offset);
54262306a36Sopenharmony_ci			ret = afs_dir_iterate_block(dvnode, ctx, dblock,
54362306a36Sopenharmony_ci						    folio_file_pos(folio) + offset);
54462306a36Sopenharmony_ci			kunmap_local(dblock);
54562306a36Sopenharmony_ci			if (ret != 1)
54662306a36Sopenharmony_ci				goto out;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		} while (offset += sizeof(*dblock), offset < size);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		ret = 0;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ciout:
55462306a36Sopenharmony_ci	up_read(&dvnode->validate_lock);
55562306a36Sopenharmony_ci	afs_put_read(req);
55662306a36Sopenharmony_ci	_leave(" = %d", ret);
55762306a36Sopenharmony_ci	return ret;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci/*
56162306a36Sopenharmony_ci * read an AFS directory
56262306a36Sopenharmony_ci */
56362306a36Sopenharmony_cistatic int afs_readdir(struct file *file, struct dir_context *ctx)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	afs_dataversion_t dir_version;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	return afs_dir_iterate(file_inode(file), ctx, afs_file_key(file),
56862306a36Sopenharmony_ci			       &dir_version);
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci/*
57262306a36Sopenharmony_ci * Search the directory for a single name
57362306a36Sopenharmony_ci * - if afs_dir_iterate_block() spots this function, it'll pass the FID
57462306a36Sopenharmony_ci *   uniquifier through dtype
57562306a36Sopenharmony_ci */
57662306a36Sopenharmony_cistatic bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name,
57762306a36Sopenharmony_ci				  int nlen, loff_t fpos, u64 ino, unsigned dtype)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct afs_lookup_one_cookie *cookie =
58062306a36Sopenharmony_ci		container_of(ctx, struct afs_lookup_one_cookie, ctx);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	_enter("{%s,%u},%s,%u,,%llu,%u",
58362306a36Sopenharmony_ci	       cookie->name.name, cookie->name.len, name, nlen,
58462306a36Sopenharmony_ci	       (unsigned long long) ino, dtype);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	/* insanity checks first */
58762306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048);
58862306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(union afs_xdr_dirent) != 32);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (cookie->name.len != nlen ||
59162306a36Sopenharmony_ci	    memcmp(cookie->name.name, name, nlen) != 0) {
59262306a36Sopenharmony_ci		_leave(" = true [keep looking]");
59362306a36Sopenharmony_ci		return true;
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	cookie->fid.vnode = ino;
59762306a36Sopenharmony_ci	cookie->fid.unique = dtype;
59862306a36Sopenharmony_ci	cookie->found = 1;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	_leave(" = false [found]");
60162306a36Sopenharmony_ci	return false;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci/*
60562306a36Sopenharmony_ci * Do a lookup of a single name in a directory
60662306a36Sopenharmony_ci * - just returns the FID the dentry name maps to if found
60762306a36Sopenharmony_ci */
60862306a36Sopenharmony_cistatic int afs_do_lookup_one(struct inode *dir, struct dentry *dentry,
60962306a36Sopenharmony_ci			     struct afs_fid *fid, struct key *key,
61062306a36Sopenharmony_ci			     afs_dataversion_t *_dir_version)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct afs_super_info *as = dir->i_sb->s_fs_info;
61362306a36Sopenharmony_ci	struct afs_lookup_one_cookie cookie = {
61462306a36Sopenharmony_ci		.ctx.actor = afs_lookup_one_filldir,
61562306a36Sopenharmony_ci		.name = dentry->d_name,
61662306a36Sopenharmony_ci		.fid.vid = as->volume->vid
61762306a36Sopenharmony_ci	};
61862306a36Sopenharmony_ci	int ret;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	_enter("{%lu},%p{%pd},", dir->i_ino, dentry, dentry);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	/* search the directory */
62362306a36Sopenharmony_ci	ret = afs_dir_iterate(dir, &cookie.ctx, key, _dir_version);
62462306a36Sopenharmony_ci	if (ret < 0) {
62562306a36Sopenharmony_ci		_leave(" = %d [iter]", ret);
62662306a36Sopenharmony_ci		return ret;
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	if (!cookie.found) {
63062306a36Sopenharmony_ci		_leave(" = -ENOENT [not found]");
63162306a36Sopenharmony_ci		return -ENOENT;
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	*fid = cookie.fid;
63562306a36Sopenharmony_ci	_leave(" = 0 { vn=%llu u=%u }", fid->vnode, fid->unique);
63662306a36Sopenharmony_ci	return 0;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci/*
64062306a36Sopenharmony_ci * search the directory for a name
64162306a36Sopenharmony_ci * - if afs_dir_iterate_block() spots this function, it'll pass the FID
64262306a36Sopenharmony_ci *   uniquifier through dtype
64362306a36Sopenharmony_ci */
64462306a36Sopenharmony_cistatic bool afs_lookup_filldir(struct dir_context *ctx, const char *name,
64562306a36Sopenharmony_ci			      int nlen, loff_t fpos, u64 ino, unsigned dtype)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	struct afs_lookup_cookie *cookie =
64862306a36Sopenharmony_ci		container_of(ctx, struct afs_lookup_cookie, ctx);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	_enter("{%s,%u},%s,%u,,%llu,%u",
65162306a36Sopenharmony_ci	       cookie->name.name, cookie->name.len, name, nlen,
65262306a36Sopenharmony_ci	       (unsigned long long) ino, dtype);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	/* insanity checks first */
65562306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048);
65662306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(union afs_xdr_dirent) != 32);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if (cookie->found) {
65962306a36Sopenharmony_ci		if (cookie->nr_fids < 50) {
66062306a36Sopenharmony_ci			cookie->fids[cookie->nr_fids].vnode	= ino;
66162306a36Sopenharmony_ci			cookie->fids[cookie->nr_fids].unique	= dtype;
66262306a36Sopenharmony_ci			cookie->nr_fids++;
66362306a36Sopenharmony_ci		}
66462306a36Sopenharmony_ci	} else if (cookie->name.len == nlen &&
66562306a36Sopenharmony_ci		   memcmp(cookie->name.name, name, nlen) == 0) {
66662306a36Sopenharmony_ci		cookie->fids[1].vnode	= ino;
66762306a36Sopenharmony_ci		cookie->fids[1].unique	= dtype;
66862306a36Sopenharmony_ci		cookie->found = 1;
66962306a36Sopenharmony_ci		if (cookie->one_only)
67062306a36Sopenharmony_ci			return false;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	return cookie->nr_fids < 50;
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci/*
67762306a36Sopenharmony_ci * Deal with the result of a successful lookup operation.  Turn all the files
67862306a36Sopenharmony_ci * into inodes and save the first one - which is the one we actually want.
67962306a36Sopenharmony_ci */
68062306a36Sopenharmony_cistatic void afs_do_lookup_success(struct afs_operation *op)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	struct afs_vnode_param *vp;
68362306a36Sopenharmony_ci	struct afs_vnode *vnode;
68462306a36Sopenharmony_ci	struct inode *inode;
68562306a36Sopenharmony_ci	u32 abort_code;
68662306a36Sopenharmony_ci	int i;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	_enter("");
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	for (i = 0; i < op->nr_files; i++) {
69162306a36Sopenharmony_ci		switch (i) {
69262306a36Sopenharmony_ci		case 0:
69362306a36Sopenharmony_ci			vp = &op->file[0];
69462306a36Sopenharmony_ci			abort_code = vp->scb.status.abort_code;
69562306a36Sopenharmony_ci			if (abort_code != 0) {
69662306a36Sopenharmony_ci				op->ac.abort_code = abort_code;
69762306a36Sopenharmony_ci				op->error = afs_abort_to_error(abort_code);
69862306a36Sopenharmony_ci			}
69962306a36Sopenharmony_ci			break;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci		case 1:
70262306a36Sopenharmony_ci			vp = &op->file[1];
70362306a36Sopenharmony_ci			break;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		default:
70662306a36Sopenharmony_ci			vp = &op->more_files[i - 2];
70762306a36Sopenharmony_ci			break;
70862306a36Sopenharmony_ci		}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci		if (!vp->scb.have_status && !vp->scb.have_error)
71162306a36Sopenharmony_ci			continue;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci		_debug("do [%u]", i);
71462306a36Sopenharmony_ci		if (vp->vnode) {
71562306a36Sopenharmony_ci			if (!test_bit(AFS_VNODE_UNSET, &vp->vnode->flags))
71662306a36Sopenharmony_ci				afs_vnode_commit_status(op, vp);
71762306a36Sopenharmony_ci		} else if (vp->scb.status.abort_code == 0) {
71862306a36Sopenharmony_ci			inode = afs_iget(op, vp);
71962306a36Sopenharmony_ci			if (!IS_ERR(inode)) {
72062306a36Sopenharmony_ci				vnode = AFS_FS_I(inode);
72162306a36Sopenharmony_ci				afs_cache_permit(vnode, op->key,
72262306a36Sopenharmony_ci						 0 /* Assume vnode->cb_break is 0 */ +
72362306a36Sopenharmony_ci						 op->cb_v_break,
72462306a36Sopenharmony_ci						 &vp->scb);
72562306a36Sopenharmony_ci				vp->vnode = vnode;
72662306a36Sopenharmony_ci				vp->put_vnode = true;
72762306a36Sopenharmony_ci			}
72862306a36Sopenharmony_ci		} else {
72962306a36Sopenharmony_ci			_debug("- abort %d %llx:%llx.%x",
73062306a36Sopenharmony_ci			       vp->scb.status.abort_code,
73162306a36Sopenharmony_ci			       vp->fid.vid, vp->fid.vnode, vp->fid.unique);
73262306a36Sopenharmony_ci		}
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	_leave("");
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic const struct afs_operation_ops afs_inline_bulk_status_operation = {
73962306a36Sopenharmony_ci	.issue_afs_rpc	= afs_fs_inline_bulk_status,
74062306a36Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_inline_bulk_status,
74162306a36Sopenharmony_ci	.success	= afs_do_lookup_success,
74262306a36Sopenharmony_ci};
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cistatic const struct afs_operation_ops afs_lookup_fetch_status_operation = {
74562306a36Sopenharmony_ci	.issue_afs_rpc	= afs_fs_fetch_status,
74662306a36Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_fetch_status,
74762306a36Sopenharmony_ci	.success	= afs_do_lookup_success,
74862306a36Sopenharmony_ci	.aborted	= afs_check_for_remote_deletion,
74962306a36Sopenharmony_ci};
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci/*
75262306a36Sopenharmony_ci * See if we know that the server we expect to use doesn't support
75362306a36Sopenharmony_ci * FS.InlineBulkStatus.
75462306a36Sopenharmony_ci */
75562306a36Sopenharmony_cistatic bool afs_server_supports_ibulk(struct afs_vnode *dvnode)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	struct afs_server_list *slist;
75862306a36Sopenharmony_ci	struct afs_volume *volume = dvnode->volume;
75962306a36Sopenharmony_ci	struct afs_server *server;
76062306a36Sopenharmony_ci	bool ret = true;
76162306a36Sopenharmony_ci	int i;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	if (!test_bit(AFS_VOLUME_MAYBE_NO_IBULK, &volume->flags))
76462306a36Sopenharmony_ci		return true;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	rcu_read_lock();
76762306a36Sopenharmony_ci	slist = rcu_dereference(volume->servers);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	for (i = 0; i < slist->nr_servers; i++) {
77062306a36Sopenharmony_ci		server = slist->servers[i].server;
77162306a36Sopenharmony_ci		if (server == dvnode->cb_server) {
77262306a36Sopenharmony_ci			if (test_bit(AFS_SERVER_FL_NO_IBULK, &server->flags))
77362306a36Sopenharmony_ci				ret = false;
77462306a36Sopenharmony_ci			break;
77562306a36Sopenharmony_ci		}
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	rcu_read_unlock();
77962306a36Sopenharmony_ci	return ret;
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci/*
78362306a36Sopenharmony_ci * Do a lookup in a directory.  We make use of bulk lookup to query a slew of
78462306a36Sopenharmony_ci * files in one go and create inodes for them.  The inode of the file we were
78562306a36Sopenharmony_ci * asked for is returned.
78662306a36Sopenharmony_ci */
78762306a36Sopenharmony_cistatic struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,
78862306a36Sopenharmony_ci				   struct key *key)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	struct afs_lookup_cookie *cookie;
79162306a36Sopenharmony_ci	struct afs_vnode_param *vp;
79262306a36Sopenharmony_ci	struct afs_operation *op;
79362306a36Sopenharmony_ci	struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode;
79462306a36Sopenharmony_ci	struct inode *inode = NULL, *ti;
79562306a36Sopenharmony_ci	afs_dataversion_t data_version = READ_ONCE(dvnode->status.data_version);
79662306a36Sopenharmony_ci	long ret;
79762306a36Sopenharmony_ci	int i;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	_enter("{%lu},%p{%pd},", dir->i_ino, dentry, dentry);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	cookie = kzalloc(sizeof(struct afs_lookup_cookie), GFP_KERNEL);
80262306a36Sopenharmony_ci	if (!cookie)
80362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cookie->fids); i++)
80662306a36Sopenharmony_ci		cookie->fids[i].vid = dvnode->fid.vid;
80762306a36Sopenharmony_ci	cookie->ctx.actor = afs_lookup_filldir;
80862306a36Sopenharmony_ci	cookie->name = dentry->d_name;
80962306a36Sopenharmony_ci	cookie->nr_fids = 2; /* slot 0 is saved for the fid we actually want
81062306a36Sopenharmony_ci			      * and slot 1 for the directory */
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	if (!afs_server_supports_ibulk(dvnode))
81362306a36Sopenharmony_ci		cookie->one_only = true;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	/* search the directory */
81662306a36Sopenharmony_ci	ret = afs_dir_iterate(dir, &cookie->ctx, key, &data_version);
81762306a36Sopenharmony_ci	if (ret < 0)
81862306a36Sopenharmony_ci		goto out;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	dentry->d_fsdata = (void *)(unsigned long)data_version;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	ret = -ENOENT;
82362306a36Sopenharmony_ci	if (!cookie->found)
82462306a36Sopenharmony_ci		goto out;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/* Check to see if we already have an inode for the primary fid. */
82762306a36Sopenharmony_ci	inode = ilookup5(dir->i_sb, cookie->fids[1].vnode,
82862306a36Sopenharmony_ci			 afs_ilookup5_test_by_fid, &cookie->fids[1]);
82962306a36Sopenharmony_ci	if (inode)
83062306a36Sopenharmony_ci		goto out; /* We do */
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	/* Okay, we didn't find it.  We need to query the server - and whilst
83362306a36Sopenharmony_ci	 * we're doing that, we're going to attempt to look up a bunch of other
83462306a36Sopenharmony_ci	 * vnodes also.
83562306a36Sopenharmony_ci	 */
83662306a36Sopenharmony_ci	op = afs_alloc_operation(NULL, dvnode->volume);
83762306a36Sopenharmony_ci	if (IS_ERR(op)) {
83862306a36Sopenharmony_ci		ret = PTR_ERR(op);
83962306a36Sopenharmony_ci		goto out;
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	afs_op_set_vnode(op, 0, dvnode);
84362306a36Sopenharmony_ci	afs_op_set_fid(op, 1, &cookie->fids[1]);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	op->nr_files = cookie->nr_fids;
84662306a36Sopenharmony_ci	_debug("nr_files %u", op->nr_files);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	/* Need space for examining all the selected files */
84962306a36Sopenharmony_ci	op->error = -ENOMEM;
85062306a36Sopenharmony_ci	if (op->nr_files > 2) {
85162306a36Sopenharmony_ci		op->more_files = kvcalloc(op->nr_files - 2,
85262306a36Sopenharmony_ci					  sizeof(struct afs_vnode_param),
85362306a36Sopenharmony_ci					  GFP_KERNEL);
85462306a36Sopenharmony_ci		if (!op->more_files)
85562306a36Sopenharmony_ci			goto out_op;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci		for (i = 2; i < op->nr_files; i++) {
85862306a36Sopenharmony_ci			vp = &op->more_files[i - 2];
85962306a36Sopenharmony_ci			vp->fid = cookie->fids[i];
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci			/* Find any inodes that already exist and get their
86262306a36Sopenharmony_ci			 * callback counters.
86362306a36Sopenharmony_ci			 */
86462306a36Sopenharmony_ci			ti = ilookup5_nowait(dir->i_sb, vp->fid.vnode,
86562306a36Sopenharmony_ci					     afs_ilookup5_test_by_fid, &vp->fid);
86662306a36Sopenharmony_ci			if (!IS_ERR_OR_NULL(ti)) {
86762306a36Sopenharmony_ci				vnode = AFS_FS_I(ti);
86862306a36Sopenharmony_ci				vp->dv_before = vnode->status.data_version;
86962306a36Sopenharmony_ci				vp->cb_break_before = afs_calc_vnode_cb_break(vnode);
87062306a36Sopenharmony_ci				vp->vnode = vnode;
87162306a36Sopenharmony_ci				vp->put_vnode = true;
87262306a36Sopenharmony_ci				vp->speculative = true; /* vnode not locked */
87362306a36Sopenharmony_ci			}
87462306a36Sopenharmony_ci		}
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	/* Try FS.InlineBulkStatus first.  Abort codes for the individual
87862306a36Sopenharmony_ci	 * lookups contained therein are stored in the reply without aborting
87962306a36Sopenharmony_ci	 * the whole operation.
88062306a36Sopenharmony_ci	 */
88162306a36Sopenharmony_ci	op->error = -ENOTSUPP;
88262306a36Sopenharmony_ci	if (!cookie->one_only) {
88362306a36Sopenharmony_ci		op->ops = &afs_inline_bulk_status_operation;
88462306a36Sopenharmony_ci		afs_begin_vnode_operation(op);
88562306a36Sopenharmony_ci		afs_wait_for_operation(op);
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	if (op->error == -ENOTSUPP) {
88962306a36Sopenharmony_ci		/* We could try FS.BulkStatus next, but this aborts the entire
89062306a36Sopenharmony_ci		 * op if any of the lookups fails - so, for the moment, revert
89162306a36Sopenharmony_ci		 * to FS.FetchStatus for op->file[1].
89262306a36Sopenharmony_ci		 */
89362306a36Sopenharmony_ci		op->fetch_status.which = 1;
89462306a36Sopenharmony_ci		op->ops = &afs_lookup_fetch_status_operation;
89562306a36Sopenharmony_ci		afs_begin_vnode_operation(op);
89662306a36Sopenharmony_ci		afs_wait_for_operation(op);
89762306a36Sopenharmony_ci	}
89862306a36Sopenharmony_ci	inode = ERR_PTR(op->error);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ciout_op:
90162306a36Sopenharmony_ci	if (op->error == 0) {
90262306a36Sopenharmony_ci		inode = &op->file[1].vnode->netfs.inode;
90362306a36Sopenharmony_ci		op->file[1].vnode = NULL;
90462306a36Sopenharmony_ci	}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	if (op->file[0].scb.have_status)
90762306a36Sopenharmony_ci		dentry->d_fsdata = (void *)(unsigned long)op->file[0].scb.status.data_version;
90862306a36Sopenharmony_ci	else
90962306a36Sopenharmony_ci		dentry->d_fsdata = (void *)(unsigned long)op->file[0].dv_before;
91062306a36Sopenharmony_ci	ret = afs_put_operation(op);
91162306a36Sopenharmony_ciout:
91262306a36Sopenharmony_ci	kfree(cookie);
91362306a36Sopenharmony_ci	_leave("");
91462306a36Sopenharmony_ci	return inode ?: ERR_PTR(ret);
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci/*
91862306a36Sopenharmony_ci * Look up an entry in a directory with @sys substitution.
91962306a36Sopenharmony_ci */
92062306a36Sopenharmony_cistatic struct dentry *afs_lookup_atsys(struct inode *dir, struct dentry *dentry,
92162306a36Sopenharmony_ci				       struct key *key)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	struct afs_sysnames *subs;
92462306a36Sopenharmony_ci	struct afs_net *net = afs_i2net(dir);
92562306a36Sopenharmony_ci	struct dentry *ret;
92662306a36Sopenharmony_ci	char *buf, *p, *name;
92762306a36Sopenharmony_ci	int len, i;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	_enter("");
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	ret = ERR_PTR(-ENOMEM);
93262306a36Sopenharmony_ci	p = buf = kmalloc(AFSNAMEMAX, GFP_KERNEL);
93362306a36Sopenharmony_ci	if (!buf)
93462306a36Sopenharmony_ci		goto out_p;
93562306a36Sopenharmony_ci	if (dentry->d_name.len > 4) {
93662306a36Sopenharmony_ci		memcpy(p, dentry->d_name.name, dentry->d_name.len - 4);
93762306a36Sopenharmony_ci		p += dentry->d_name.len - 4;
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	/* There is an ordered list of substitutes that we have to try. */
94162306a36Sopenharmony_ci	read_lock(&net->sysnames_lock);
94262306a36Sopenharmony_ci	subs = net->sysnames;
94362306a36Sopenharmony_ci	refcount_inc(&subs->usage);
94462306a36Sopenharmony_ci	read_unlock(&net->sysnames_lock);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	for (i = 0; i < subs->nr; i++) {
94762306a36Sopenharmony_ci		name = subs->subs[i];
94862306a36Sopenharmony_ci		len = dentry->d_name.len - 4 + strlen(name);
94962306a36Sopenharmony_ci		if (len >= AFSNAMEMAX) {
95062306a36Sopenharmony_ci			ret = ERR_PTR(-ENAMETOOLONG);
95162306a36Sopenharmony_ci			goto out_s;
95262306a36Sopenharmony_ci		}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		strcpy(p, name);
95562306a36Sopenharmony_ci		ret = lookup_one_len(buf, dentry->d_parent, len);
95662306a36Sopenharmony_ci		if (IS_ERR(ret) || d_is_positive(ret))
95762306a36Sopenharmony_ci			goto out_s;
95862306a36Sopenharmony_ci		dput(ret);
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	/* We don't want to d_add() the @sys dentry here as we don't want to
96262306a36Sopenharmony_ci	 * the cached dentry to hide changes to the sysnames list.
96362306a36Sopenharmony_ci	 */
96462306a36Sopenharmony_ci	ret = NULL;
96562306a36Sopenharmony_ciout_s:
96662306a36Sopenharmony_ci	afs_put_sysnames(subs);
96762306a36Sopenharmony_ci	kfree(buf);
96862306a36Sopenharmony_ciout_p:
96962306a36Sopenharmony_ci	key_put(key);
97062306a36Sopenharmony_ci	return ret;
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci/*
97462306a36Sopenharmony_ci * look up an entry in a directory
97562306a36Sopenharmony_ci */
97662306a36Sopenharmony_cistatic struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
97762306a36Sopenharmony_ci				 unsigned int flags)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	struct afs_vnode *dvnode = AFS_FS_I(dir);
98062306a36Sopenharmony_ci	struct afs_fid fid = {};
98162306a36Sopenharmony_ci	struct inode *inode;
98262306a36Sopenharmony_ci	struct dentry *d;
98362306a36Sopenharmony_ci	struct key *key;
98462306a36Sopenharmony_ci	int ret;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	_enter("{%llx:%llu},%p{%pd},",
98762306a36Sopenharmony_ci	       dvnode->fid.vid, dvnode->fid.vnode, dentry, dentry);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	ASSERTCMP(d_inode(dentry), ==, NULL);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	if (dentry->d_name.len >= AFSNAMEMAX) {
99262306a36Sopenharmony_ci		_leave(" = -ENAMETOOLONG");
99362306a36Sopenharmony_ci		return ERR_PTR(-ENAMETOOLONG);
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DELETED, &dvnode->flags)) {
99762306a36Sopenharmony_ci		_leave(" = -ESTALE");
99862306a36Sopenharmony_ci		return ERR_PTR(-ESTALE);
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	key = afs_request_key(dvnode->volume->cell);
100262306a36Sopenharmony_ci	if (IS_ERR(key)) {
100362306a36Sopenharmony_ci		_leave(" = %ld [key]", PTR_ERR(key));
100462306a36Sopenharmony_ci		return ERR_CAST(key);
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	ret = afs_validate(dvnode, key);
100862306a36Sopenharmony_ci	if (ret < 0) {
100962306a36Sopenharmony_ci		key_put(key);
101062306a36Sopenharmony_ci		_leave(" = %d [val]", ret);
101162306a36Sopenharmony_ci		return ERR_PTR(ret);
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	if (dentry->d_name.len >= 4 &&
101562306a36Sopenharmony_ci	    dentry->d_name.name[dentry->d_name.len - 4] == '@' &&
101662306a36Sopenharmony_ci	    dentry->d_name.name[dentry->d_name.len - 3] == 's' &&
101762306a36Sopenharmony_ci	    dentry->d_name.name[dentry->d_name.len - 2] == 'y' &&
101862306a36Sopenharmony_ci	    dentry->d_name.name[dentry->d_name.len - 1] == 's')
101962306a36Sopenharmony_ci		return afs_lookup_atsys(dir, dentry, key);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	afs_stat_v(dvnode, n_lookup);
102262306a36Sopenharmony_ci	inode = afs_do_lookup(dir, dentry, key);
102362306a36Sopenharmony_ci	key_put(key);
102462306a36Sopenharmony_ci	if (inode == ERR_PTR(-ENOENT))
102562306a36Sopenharmony_ci		inode = afs_try_auto_mntpt(dentry, dir);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(inode))
102862306a36Sopenharmony_ci		fid = AFS_FS_I(inode)->fid;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	_debug("splice %p", dentry->d_inode);
103162306a36Sopenharmony_ci	d = d_splice_alias(inode, dentry);
103262306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(d)) {
103362306a36Sopenharmony_ci		d->d_fsdata = dentry->d_fsdata;
103462306a36Sopenharmony_ci		trace_afs_lookup(dvnode, &d->d_name, &fid);
103562306a36Sopenharmony_ci	} else {
103662306a36Sopenharmony_ci		trace_afs_lookup(dvnode, &dentry->d_name, &fid);
103762306a36Sopenharmony_ci	}
103862306a36Sopenharmony_ci	_leave("");
103962306a36Sopenharmony_ci	return d;
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci/*
104362306a36Sopenharmony_ci * Check the validity of a dentry under RCU conditions.
104462306a36Sopenharmony_ci */
104562306a36Sopenharmony_cistatic int afs_d_revalidate_rcu(struct dentry *dentry)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	struct afs_vnode *dvnode;
104862306a36Sopenharmony_ci	struct dentry *parent;
104962306a36Sopenharmony_ci	struct inode *dir;
105062306a36Sopenharmony_ci	long dir_version, de_version;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	_enter("%p", dentry);
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	/* Check the parent directory is still valid first. */
105562306a36Sopenharmony_ci	parent = READ_ONCE(dentry->d_parent);
105662306a36Sopenharmony_ci	dir = d_inode_rcu(parent);
105762306a36Sopenharmony_ci	if (!dir)
105862306a36Sopenharmony_ci		return -ECHILD;
105962306a36Sopenharmony_ci	dvnode = AFS_FS_I(dir);
106062306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DELETED, &dvnode->flags))
106162306a36Sopenharmony_ci		return -ECHILD;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	if (!afs_check_validity(dvnode))
106462306a36Sopenharmony_ci		return -ECHILD;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	/* We only need to invalidate a dentry if the server's copy changed
106762306a36Sopenharmony_ci	 * behind our back.  If we made the change, it's no problem.  Note that
106862306a36Sopenharmony_ci	 * on a 32-bit system, we only have 32 bits in the dentry to store the
106962306a36Sopenharmony_ci	 * version.
107062306a36Sopenharmony_ci	 */
107162306a36Sopenharmony_ci	dir_version = (long)READ_ONCE(dvnode->status.data_version);
107262306a36Sopenharmony_ci	de_version = (long)READ_ONCE(dentry->d_fsdata);
107362306a36Sopenharmony_ci	if (de_version != dir_version) {
107462306a36Sopenharmony_ci		dir_version = (long)READ_ONCE(dvnode->invalid_before);
107562306a36Sopenharmony_ci		if (de_version - dir_version < 0)
107662306a36Sopenharmony_ci			return -ECHILD;
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	return 1; /* Still valid */
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci/*
108362306a36Sopenharmony_ci * check that a dentry lookup hit has found a valid entry
108462306a36Sopenharmony_ci * - NOTE! the hit can be a negative hit too, so we can't assume we have an
108562306a36Sopenharmony_ci *   inode
108662306a36Sopenharmony_ci */
108762306a36Sopenharmony_cistatic int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	struct afs_vnode *vnode, *dir;
109062306a36Sopenharmony_ci	struct afs_fid fid;
109162306a36Sopenharmony_ci	struct dentry *parent;
109262306a36Sopenharmony_ci	struct inode *inode;
109362306a36Sopenharmony_ci	struct key *key;
109462306a36Sopenharmony_ci	afs_dataversion_t dir_version, invalid_before;
109562306a36Sopenharmony_ci	long de_version;
109662306a36Sopenharmony_ci	int ret;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (flags & LOOKUP_RCU)
109962306a36Sopenharmony_ci		return afs_d_revalidate_rcu(dentry);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	if (d_really_is_positive(dentry)) {
110262306a36Sopenharmony_ci		vnode = AFS_FS_I(d_inode(dentry));
110362306a36Sopenharmony_ci		_enter("{v={%llx:%llu} n=%pd fl=%lx},",
110462306a36Sopenharmony_ci		       vnode->fid.vid, vnode->fid.vnode, dentry,
110562306a36Sopenharmony_ci		       vnode->flags);
110662306a36Sopenharmony_ci	} else {
110762306a36Sopenharmony_ci		_enter("{neg n=%pd}", dentry);
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	key = afs_request_key(AFS_FS_S(dentry->d_sb)->volume->cell);
111162306a36Sopenharmony_ci	if (IS_ERR(key))
111262306a36Sopenharmony_ci		key = NULL;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	/* Hold the parent dentry so we can peer at it */
111562306a36Sopenharmony_ci	parent = dget_parent(dentry);
111662306a36Sopenharmony_ci	dir = AFS_FS_I(d_inode(parent));
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	/* validate the parent directory */
111962306a36Sopenharmony_ci	afs_validate(dir, key);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DELETED, &dir->flags)) {
112262306a36Sopenharmony_ci		_debug("%pd: parent dir deleted", dentry);
112362306a36Sopenharmony_ci		goto not_found;
112462306a36Sopenharmony_ci	}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	/* We only need to invalidate a dentry if the server's copy changed
112762306a36Sopenharmony_ci	 * behind our back.  If we made the change, it's no problem.  Note that
112862306a36Sopenharmony_ci	 * on a 32-bit system, we only have 32 bits in the dentry to store the
112962306a36Sopenharmony_ci	 * version.
113062306a36Sopenharmony_ci	 */
113162306a36Sopenharmony_ci	dir_version = dir->status.data_version;
113262306a36Sopenharmony_ci	de_version = (long)dentry->d_fsdata;
113362306a36Sopenharmony_ci	if (de_version == (long)dir_version)
113462306a36Sopenharmony_ci		goto out_valid_noupdate;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	invalid_before = dir->invalid_before;
113762306a36Sopenharmony_ci	if (de_version - (long)invalid_before >= 0)
113862306a36Sopenharmony_ci		goto out_valid;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	_debug("dir modified");
114162306a36Sopenharmony_ci	afs_stat_v(dir, n_reval);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	/* search the directory for this vnode */
114462306a36Sopenharmony_ci	ret = afs_do_lookup_one(&dir->netfs.inode, dentry, &fid, key, &dir_version);
114562306a36Sopenharmony_ci	switch (ret) {
114662306a36Sopenharmony_ci	case 0:
114762306a36Sopenharmony_ci		/* the filename maps to something */
114862306a36Sopenharmony_ci		if (d_really_is_negative(dentry))
114962306a36Sopenharmony_ci			goto not_found;
115062306a36Sopenharmony_ci		inode = d_inode(dentry);
115162306a36Sopenharmony_ci		if (is_bad_inode(inode)) {
115262306a36Sopenharmony_ci			printk("kAFS: afs_d_revalidate: %pd2 has bad inode\n",
115362306a36Sopenharmony_ci			       dentry);
115462306a36Sopenharmony_ci			goto not_found;
115562306a36Sopenharmony_ci		}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci		vnode = AFS_FS_I(inode);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci		/* if the vnode ID has changed, then the dirent points to a
116062306a36Sopenharmony_ci		 * different file */
116162306a36Sopenharmony_ci		if (fid.vnode != vnode->fid.vnode) {
116262306a36Sopenharmony_ci			_debug("%pd: dirent changed [%llu != %llu]",
116362306a36Sopenharmony_ci			       dentry, fid.vnode,
116462306a36Sopenharmony_ci			       vnode->fid.vnode);
116562306a36Sopenharmony_ci			goto not_found;
116662306a36Sopenharmony_ci		}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci		/* if the vnode ID uniqifier has changed, then the file has
116962306a36Sopenharmony_ci		 * been deleted and replaced, and the original vnode ID has
117062306a36Sopenharmony_ci		 * been reused */
117162306a36Sopenharmony_ci		if (fid.unique != vnode->fid.unique) {
117262306a36Sopenharmony_ci			_debug("%pd: file deleted (uq %u -> %u I:%u)",
117362306a36Sopenharmony_ci			       dentry, fid.unique,
117462306a36Sopenharmony_ci			       vnode->fid.unique,
117562306a36Sopenharmony_ci			       vnode->netfs.inode.i_generation);
117662306a36Sopenharmony_ci			goto not_found;
117762306a36Sopenharmony_ci		}
117862306a36Sopenharmony_ci		goto out_valid;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	case -ENOENT:
118162306a36Sopenharmony_ci		/* the filename is unknown */
118262306a36Sopenharmony_ci		_debug("%pd: dirent not found", dentry);
118362306a36Sopenharmony_ci		if (d_really_is_positive(dentry))
118462306a36Sopenharmony_ci			goto not_found;
118562306a36Sopenharmony_ci		goto out_valid;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	default:
118862306a36Sopenharmony_ci		_debug("failed to iterate dir %pd: %d",
118962306a36Sopenharmony_ci		       parent, ret);
119062306a36Sopenharmony_ci		goto not_found;
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ciout_valid:
119462306a36Sopenharmony_ci	dentry->d_fsdata = (void *)(unsigned long)dir_version;
119562306a36Sopenharmony_ciout_valid_noupdate:
119662306a36Sopenharmony_ci	dput(parent);
119762306a36Sopenharmony_ci	key_put(key);
119862306a36Sopenharmony_ci	_leave(" = 1 [valid]");
119962306a36Sopenharmony_ci	return 1;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_cinot_found:
120262306a36Sopenharmony_ci	_debug("dropping dentry %pd2", dentry);
120362306a36Sopenharmony_ci	dput(parent);
120462306a36Sopenharmony_ci	key_put(key);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	_leave(" = 0 [bad]");
120762306a36Sopenharmony_ci	return 0;
120862306a36Sopenharmony_ci}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci/*
121162306a36Sopenharmony_ci * allow the VFS to enquire as to whether a dentry should be unhashed (mustn't
121262306a36Sopenharmony_ci * sleep)
121362306a36Sopenharmony_ci * - called from dput() when d_count is going to 0.
121462306a36Sopenharmony_ci * - return 1 to request dentry be unhashed, 0 otherwise
121562306a36Sopenharmony_ci */
121662306a36Sopenharmony_cistatic int afs_d_delete(const struct dentry *dentry)
121762306a36Sopenharmony_ci{
121862306a36Sopenharmony_ci	_enter("%pd", dentry);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
122162306a36Sopenharmony_ci		goto zap;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	if (d_really_is_positive(dentry) &&
122462306a36Sopenharmony_ci	    (test_bit(AFS_VNODE_DELETED,   &AFS_FS_I(d_inode(dentry))->flags) ||
122562306a36Sopenharmony_ci	     test_bit(AFS_VNODE_PSEUDODIR, &AFS_FS_I(d_inode(dentry))->flags)))
122662306a36Sopenharmony_ci		goto zap;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	_leave(" = 0 [keep]");
122962306a36Sopenharmony_ci	return 0;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_cizap:
123262306a36Sopenharmony_ci	_leave(" = 1 [zap]");
123362306a36Sopenharmony_ci	return 1;
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci/*
123762306a36Sopenharmony_ci * Clean up sillyrename files on dentry removal.
123862306a36Sopenharmony_ci */
123962306a36Sopenharmony_cistatic void afs_d_iput(struct dentry *dentry, struct inode *inode)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
124262306a36Sopenharmony_ci		afs_silly_iput(dentry, inode);
124362306a36Sopenharmony_ci	iput(inode);
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci/*
124762306a36Sopenharmony_ci * handle dentry release
124862306a36Sopenharmony_ci */
124962306a36Sopenharmony_civoid afs_d_release(struct dentry *dentry)
125062306a36Sopenharmony_ci{
125162306a36Sopenharmony_ci	_enter("%pd", dentry);
125262306a36Sopenharmony_ci}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_civoid afs_check_for_remote_deletion(struct afs_operation *op)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct afs_vnode *vnode = op->file[0].vnode;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	switch (op->ac.abort_code) {
125962306a36Sopenharmony_ci	case VNOVNODE:
126062306a36Sopenharmony_ci		set_bit(AFS_VNODE_DELETED, &vnode->flags);
126162306a36Sopenharmony_ci		afs_break_callback(vnode, afs_cb_break_for_deleted);
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci/*
126662306a36Sopenharmony_ci * Create a new inode for create/mkdir/symlink
126762306a36Sopenharmony_ci */
126862306a36Sopenharmony_cistatic void afs_vnode_new_inode(struct afs_operation *op)
126962306a36Sopenharmony_ci{
127062306a36Sopenharmony_ci	struct afs_vnode_param *vp = &op->file[1];
127162306a36Sopenharmony_ci	struct afs_vnode *vnode;
127262306a36Sopenharmony_ci	struct inode *inode;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	_enter("");
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	ASSERTCMP(op->error, ==, 0);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	inode = afs_iget(op, vp);
127962306a36Sopenharmony_ci	if (IS_ERR(inode)) {
128062306a36Sopenharmony_ci		/* ENOMEM or EINTR at a really inconvenient time - just abandon
128162306a36Sopenharmony_ci		 * the new directory on the server.
128262306a36Sopenharmony_ci		 */
128362306a36Sopenharmony_ci		op->error = PTR_ERR(inode);
128462306a36Sopenharmony_ci		return;
128562306a36Sopenharmony_ci	}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	vnode = AFS_FS_I(inode);
128862306a36Sopenharmony_ci	set_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
128962306a36Sopenharmony_ci	if (!op->error)
129062306a36Sopenharmony_ci		afs_cache_permit(vnode, op->key, vnode->cb_break, &vp->scb);
129162306a36Sopenharmony_ci	d_instantiate(op->dentry, inode);
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_cistatic void afs_create_success(struct afs_operation *op)
129562306a36Sopenharmony_ci{
129662306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
129762306a36Sopenharmony_ci	op->ctime = op->file[0].scb.status.mtime_client;
129862306a36Sopenharmony_ci	afs_vnode_commit_status(op, &op->file[0]);
129962306a36Sopenharmony_ci	afs_update_dentry_version(op, &op->file[0], op->dentry);
130062306a36Sopenharmony_ci	afs_vnode_new_inode(op);
130162306a36Sopenharmony_ci}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_cistatic void afs_create_edit_dir(struct afs_operation *op)
130462306a36Sopenharmony_ci{
130562306a36Sopenharmony_ci	struct afs_vnode_param *dvp = &op->file[0];
130662306a36Sopenharmony_ci	struct afs_vnode_param *vp = &op->file[1];
130762306a36Sopenharmony_ci	struct afs_vnode *dvnode = dvp->vnode;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	down_write(&dvnode->validate_lock);
131262306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
131362306a36Sopenharmony_ci	    dvnode->status.data_version == dvp->dv_before + dvp->dv_delta)
131462306a36Sopenharmony_ci		afs_edit_dir_add(dvnode, &op->dentry->d_name, &vp->fid,
131562306a36Sopenharmony_ci				 op->create.reason);
131662306a36Sopenharmony_ci	up_write(&dvnode->validate_lock);
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_cistatic void afs_create_put(struct afs_operation *op)
132062306a36Sopenharmony_ci{
132162306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	if (op->error)
132462306a36Sopenharmony_ci		d_drop(op->dentry);
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_cistatic const struct afs_operation_ops afs_mkdir_operation = {
132862306a36Sopenharmony_ci	.issue_afs_rpc	= afs_fs_make_dir,
132962306a36Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_make_dir,
133062306a36Sopenharmony_ci	.success	= afs_create_success,
133162306a36Sopenharmony_ci	.aborted	= afs_check_for_remote_deletion,
133262306a36Sopenharmony_ci	.edit_dir	= afs_create_edit_dir,
133362306a36Sopenharmony_ci	.put		= afs_create_put,
133462306a36Sopenharmony_ci};
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci/*
133762306a36Sopenharmony_ci * create a directory on an AFS filesystem
133862306a36Sopenharmony_ci */
133962306a36Sopenharmony_cistatic int afs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
134062306a36Sopenharmony_ci		     struct dentry *dentry, umode_t mode)
134162306a36Sopenharmony_ci{
134262306a36Sopenharmony_ci	struct afs_operation *op;
134362306a36Sopenharmony_ci	struct afs_vnode *dvnode = AFS_FS_I(dir);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	_enter("{%llx:%llu},{%pd},%ho",
134662306a36Sopenharmony_ci	       dvnode->fid.vid, dvnode->fid.vnode, dentry, mode);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	op = afs_alloc_operation(NULL, dvnode->volume);
134962306a36Sopenharmony_ci	if (IS_ERR(op)) {
135062306a36Sopenharmony_ci		d_drop(dentry);
135162306a36Sopenharmony_ci		return PTR_ERR(op);
135262306a36Sopenharmony_ci	}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	afs_op_set_vnode(op, 0, dvnode);
135562306a36Sopenharmony_ci	op->file[0].dv_delta = 1;
135662306a36Sopenharmony_ci	op->file[0].modification = true;
135762306a36Sopenharmony_ci	op->file[0].update_ctime = true;
135862306a36Sopenharmony_ci	op->dentry	= dentry;
135962306a36Sopenharmony_ci	op->create.mode	= S_IFDIR | mode;
136062306a36Sopenharmony_ci	op->create.reason = afs_edit_dir_for_mkdir;
136162306a36Sopenharmony_ci	op->mtime	= current_time(dir);
136262306a36Sopenharmony_ci	op->ops		= &afs_mkdir_operation;
136362306a36Sopenharmony_ci	return afs_do_sync_operation(op);
136462306a36Sopenharmony_ci}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci/*
136762306a36Sopenharmony_ci * Remove a subdir from a directory.
136862306a36Sopenharmony_ci */
136962306a36Sopenharmony_cistatic void afs_dir_remove_subdir(struct dentry *dentry)
137062306a36Sopenharmony_ci{
137162306a36Sopenharmony_ci	if (d_really_is_positive(dentry)) {
137262306a36Sopenharmony_ci		struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci		clear_nlink(&vnode->netfs.inode);
137562306a36Sopenharmony_ci		set_bit(AFS_VNODE_DELETED, &vnode->flags);
137662306a36Sopenharmony_ci		clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
137762306a36Sopenharmony_ci		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
137862306a36Sopenharmony_ci	}
137962306a36Sopenharmony_ci}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_cistatic void afs_rmdir_success(struct afs_operation *op)
138262306a36Sopenharmony_ci{
138362306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
138462306a36Sopenharmony_ci	op->ctime = op->file[0].scb.status.mtime_client;
138562306a36Sopenharmony_ci	afs_vnode_commit_status(op, &op->file[0]);
138662306a36Sopenharmony_ci	afs_update_dentry_version(op, &op->file[0], op->dentry);
138762306a36Sopenharmony_ci}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_cistatic void afs_rmdir_edit_dir(struct afs_operation *op)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci	struct afs_vnode_param *dvp = &op->file[0];
139262306a36Sopenharmony_ci	struct afs_vnode *dvnode = dvp->vnode;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
139562306a36Sopenharmony_ci	afs_dir_remove_subdir(op->dentry);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	down_write(&dvnode->validate_lock);
139862306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
139962306a36Sopenharmony_ci	    dvnode->status.data_version == dvp->dv_before + dvp->dv_delta)
140062306a36Sopenharmony_ci		afs_edit_dir_remove(dvnode, &op->dentry->d_name,
140162306a36Sopenharmony_ci				    afs_edit_dir_for_rmdir);
140262306a36Sopenharmony_ci	up_write(&dvnode->validate_lock);
140362306a36Sopenharmony_ci}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_cistatic void afs_rmdir_put(struct afs_operation *op)
140662306a36Sopenharmony_ci{
140762306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
140862306a36Sopenharmony_ci	if (op->file[1].vnode)
140962306a36Sopenharmony_ci		up_write(&op->file[1].vnode->rmdir_lock);
141062306a36Sopenharmony_ci}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_cistatic const struct afs_operation_ops afs_rmdir_operation = {
141362306a36Sopenharmony_ci	.issue_afs_rpc	= afs_fs_remove_dir,
141462306a36Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_remove_dir,
141562306a36Sopenharmony_ci	.success	= afs_rmdir_success,
141662306a36Sopenharmony_ci	.aborted	= afs_check_for_remote_deletion,
141762306a36Sopenharmony_ci	.edit_dir	= afs_rmdir_edit_dir,
141862306a36Sopenharmony_ci	.put		= afs_rmdir_put,
141962306a36Sopenharmony_ci};
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci/*
142262306a36Sopenharmony_ci * remove a directory from an AFS filesystem
142362306a36Sopenharmony_ci */
142462306a36Sopenharmony_cistatic int afs_rmdir(struct inode *dir, struct dentry *dentry)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	struct afs_operation *op;
142762306a36Sopenharmony_ci	struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL;
142862306a36Sopenharmony_ci	int ret;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	_enter("{%llx:%llu},{%pd}",
143162306a36Sopenharmony_ci	       dvnode->fid.vid, dvnode->fid.vnode, dentry);
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	op = afs_alloc_operation(NULL, dvnode->volume);
143462306a36Sopenharmony_ci	if (IS_ERR(op))
143562306a36Sopenharmony_ci		return PTR_ERR(op);
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	afs_op_set_vnode(op, 0, dvnode);
143862306a36Sopenharmony_ci	op->file[0].dv_delta = 1;
143962306a36Sopenharmony_ci	op->file[0].modification = true;
144062306a36Sopenharmony_ci	op->file[0].update_ctime = true;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	op->dentry	= dentry;
144362306a36Sopenharmony_ci	op->ops		= &afs_rmdir_operation;
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	/* Try to make sure we have a callback promise on the victim. */
144662306a36Sopenharmony_ci	if (d_really_is_positive(dentry)) {
144762306a36Sopenharmony_ci		vnode = AFS_FS_I(d_inode(dentry));
144862306a36Sopenharmony_ci		ret = afs_validate(vnode, op->key);
144962306a36Sopenharmony_ci		if (ret < 0)
145062306a36Sopenharmony_ci			goto error;
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	if (vnode) {
145462306a36Sopenharmony_ci		ret = down_write_killable(&vnode->rmdir_lock);
145562306a36Sopenharmony_ci		if (ret < 0)
145662306a36Sopenharmony_ci			goto error;
145762306a36Sopenharmony_ci		op->file[1].vnode = vnode;
145862306a36Sopenharmony_ci	}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	return afs_do_sync_operation(op);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_cierror:
146362306a36Sopenharmony_ci	return afs_put_operation(op);
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci/*
146762306a36Sopenharmony_ci * Remove a link to a file or symlink from a directory.
146862306a36Sopenharmony_ci *
146962306a36Sopenharmony_ci * If the file was not deleted due to excess hard links, the fileserver will
147062306a36Sopenharmony_ci * break the callback promise on the file - if it had one - before it returns
147162306a36Sopenharmony_ci * to us, and if it was deleted, it won't
147262306a36Sopenharmony_ci *
147362306a36Sopenharmony_ci * However, if we didn't have a callback promise outstanding, or it was
147462306a36Sopenharmony_ci * outstanding on a different server, then it won't break it either...
147562306a36Sopenharmony_ci */
147662306a36Sopenharmony_cistatic void afs_dir_remove_link(struct afs_operation *op)
147762306a36Sopenharmony_ci{
147862306a36Sopenharmony_ci	struct afs_vnode *dvnode = op->file[0].vnode;
147962306a36Sopenharmony_ci	struct afs_vnode *vnode = op->file[1].vnode;
148062306a36Sopenharmony_ci	struct dentry *dentry = op->dentry;
148162306a36Sopenharmony_ci	int ret;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	if (op->error != 0 ||
148462306a36Sopenharmony_ci	    (op->file[1].scb.have_status && op->file[1].scb.have_error))
148562306a36Sopenharmony_ci		return;
148662306a36Sopenharmony_ci	if (d_really_is_positive(dentry))
148762306a36Sopenharmony_ci		return;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
149062306a36Sopenharmony_ci		/* Already done */
149162306a36Sopenharmony_ci	} else if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
149262306a36Sopenharmony_ci		write_seqlock(&vnode->cb_lock);
149362306a36Sopenharmony_ci		drop_nlink(&vnode->netfs.inode);
149462306a36Sopenharmony_ci		if (vnode->netfs.inode.i_nlink == 0) {
149562306a36Sopenharmony_ci			set_bit(AFS_VNODE_DELETED, &vnode->flags);
149662306a36Sopenharmony_ci			__afs_break_callback(vnode, afs_cb_break_for_unlink);
149762306a36Sopenharmony_ci		}
149862306a36Sopenharmony_ci		write_sequnlock(&vnode->cb_lock);
149962306a36Sopenharmony_ci	} else {
150062306a36Sopenharmony_ci		afs_break_callback(vnode, afs_cb_break_for_unlink);
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci		if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
150362306a36Sopenharmony_ci			_debug("AFS_VNODE_DELETED");
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci		ret = afs_validate(vnode, op->key);
150662306a36Sopenharmony_ci		if (ret != -ESTALE)
150762306a36Sopenharmony_ci			op->error = ret;
150862306a36Sopenharmony_ci	}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	_debug("nlink %d [val %d]", vnode->netfs.inode.i_nlink, op->error);
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic void afs_unlink_success(struct afs_operation *op)
151462306a36Sopenharmony_ci{
151562306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
151662306a36Sopenharmony_ci	op->ctime = op->file[0].scb.status.mtime_client;
151762306a36Sopenharmony_ci	afs_check_dir_conflict(op, &op->file[0]);
151862306a36Sopenharmony_ci	afs_vnode_commit_status(op, &op->file[0]);
151962306a36Sopenharmony_ci	afs_vnode_commit_status(op, &op->file[1]);
152062306a36Sopenharmony_ci	afs_update_dentry_version(op, &op->file[0], op->dentry);
152162306a36Sopenharmony_ci	afs_dir_remove_link(op);
152262306a36Sopenharmony_ci}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_cistatic void afs_unlink_edit_dir(struct afs_operation *op)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	struct afs_vnode_param *dvp = &op->file[0];
152762306a36Sopenharmony_ci	struct afs_vnode *dvnode = dvp->vnode;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
153062306a36Sopenharmony_ci	down_write(&dvnode->validate_lock);
153162306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
153262306a36Sopenharmony_ci	    dvnode->status.data_version == dvp->dv_before + dvp->dv_delta)
153362306a36Sopenharmony_ci		afs_edit_dir_remove(dvnode, &op->dentry->d_name,
153462306a36Sopenharmony_ci				    afs_edit_dir_for_unlink);
153562306a36Sopenharmony_ci	up_write(&dvnode->validate_lock);
153662306a36Sopenharmony_ci}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_cistatic void afs_unlink_put(struct afs_operation *op)
153962306a36Sopenharmony_ci{
154062306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
154162306a36Sopenharmony_ci	if (op->unlink.need_rehash && op->error < 0 && op->error != -ENOENT)
154262306a36Sopenharmony_ci		d_rehash(op->dentry);
154362306a36Sopenharmony_ci}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_cistatic const struct afs_operation_ops afs_unlink_operation = {
154662306a36Sopenharmony_ci	.issue_afs_rpc	= afs_fs_remove_file,
154762306a36Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_remove_file,
154862306a36Sopenharmony_ci	.success	= afs_unlink_success,
154962306a36Sopenharmony_ci	.aborted	= afs_check_for_remote_deletion,
155062306a36Sopenharmony_ci	.edit_dir	= afs_unlink_edit_dir,
155162306a36Sopenharmony_ci	.put		= afs_unlink_put,
155262306a36Sopenharmony_ci};
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci/*
155562306a36Sopenharmony_ci * Remove a file or symlink from an AFS filesystem.
155662306a36Sopenharmony_ci */
155762306a36Sopenharmony_cistatic int afs_unlink(struct inode *dir, struct dentry *dentry)
155862306a36Sopenharmony_ci{
155962306a36Sopenharmony_ci	struct afs_operation *op;
156062306a36Sopenharmony_ci	struct afs_vnode *dvnode = AFS_FS_I(dir);
156162306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
156262306a36Sopenharmony_ci	int ret;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	_enter("{%llx:%llu},{%pd}",
156562306a36Sopenharmony_ci	       dvnode->fid.vid, dvnode->fid.vnode, dentry);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	if (dentry->d_name.len >= AFSNAMEMAX)
156862306a36Sopenharmony_ci		return -ENAMETOOLONG;
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	op = afs_alloc_operation(NULL, dvnode->volume);
157162306a36Sopenharmony_ci	if (IS_ERR(op))
157262306a36Sopenharmony_ci		return PTR_ERR(op);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	afs_op_set_vnode(op, 0, dvnode);
157562306a36Sopenharmony_ci	op->file[0].dv_delta = 1;
157662306a36Sopenharmony_ci	op->file[0].modification = true;
157762306a36Sopenharmony_ci	op->file[0].update_ctime = true;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	/* Try to make sure we have a callback promise on the victim. */
158062306a36Sopenharmony_ci	ret = afs_validate(vnode, op->key);
158162306a36Sopenharmony_ci	if (ret < 0) {
158262306a36Sopenharmony_ci		op->error = ret;
158362306a36Sopenharmony_ci		goto error;
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	spin_lock(&dentry->d_lock);
158762306a36Sopenharmony_ci	if (d_count(dentry) > 1) {
158862306a36Sopenharmony_ci		spin_unlock(&dentry->d_lock);
158962306a36Sopenharmony_ci		/* Start asynchronous writeout of the inode */
159062306a36Sopenharmony_ci		write_inode_now(d_inode(dentry), 0);
159162306a36Sopenharmony_ci		op->error = afs_sillyrename(dvnode, vnode, dentry, op->key);
159262306a36Sopenharmony_ci		goto error;
159362306a36Sopenharmony_ci	}
159462306a36Sopenharmony_ci	if (!d_unhashed(dentry)) {
159562306a36Sopenharmony_ci		/* Prevent a race with RCU lookup. */
159662306a36Sopenharmony_ci		__d_drop(dentry);
159762306a36Sopenharmony_ci		op->unlink.need_rehash = true;
159862306a36Sopenharmony_ci	}
159962306a36Sopenharmony_ci	spin_unlock(&dentry->d_lock);
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	op->file[1].vnode = vnode;
160262306a36Sopenharmony_ci	op->file[1].update_ctime = true;
160362306a36Sopenharmony_ci	op->file[1].op_unlinked = true;
160462306a36Sopenharmony_ci	op->dentry	= dentry;
160562306a36Sopenharmony_ci	op->ops		= &afs_unlink_operation;
160662306a36Sopenharmony_ci	afs_begin_vnode_operation(op);
160762306a36Sopenharmony_ci	afs_wait_for_operation(op);
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	/* If there was a conflict with a third party, check the status of the
161062306a36Sopenharmony_ci	 * unlinked vnode.
161162306a36Sopenharmony_ci	 */
161262306a36Sopenharmony_ci	if (op->error == 0 && (op->flags & AFS_OPERATION_DIR_CONFLICT)) {
161362306a36Sopenharmony_ci		op->file[1].update_ctime = false;
161462306a36Sopenharmony_ci		op->fetch_status.which = 1;
161562306a36Sopenharmony_ci		op->ops = &afs_fetch_status_operation;
161662306a36Sopenharmony_ci		afs_begin_vnode_operation(op);
161762306a36Sopenharmony_ci		afs_wait_for_operation(op);
161862306a36Sopenharmony_ci	}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	return afs_put_operation(op);
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_cierror:
162362306a36Sopenharmony_ci	return afs_put_operation(op);
162462306a36Sopenharmony_ci}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_cistatic const struct afs_operation_ops afs_create_operation = {
162762306a36Sopenharmony_ci	.issue_afs_rpc	= afs_fs_create_file,
162862306a36Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_create_file,
162962306a36Sopenharmony_ci	.success	= afs_create_success,
163062306a36Sopenharmony_ci	.aborted	= afs_check_for_remote_deletion,
163162306a36Sopenharmony_ci	.edit_dir	= afs_create_edit_dir,
163262306a36Sopenharmony_ci	.put		= afs_create_put,
163362306a36Sopenharmony_ci};
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci/*
163662306a36Sopenharmony_ci * create a regular file on an AFS filesystem
163762306a36Sopenharmony_ci */
163862306a36Sopenharmony_cistatic int afs_create(struct mnt_idmap *idmap, struct inode *dir,
163962306a36Sopenharmony_ci		      struct dentry *dentry, umode_t mode, bool excl)
164062306a36Sopenharmony_ci{
164162306a36Sopenharmony_ci	struct afs_operation *op;
164262306a36Sopenharmony_ci	struct afs_vnode *dvnode = AFS_FS_I(dir);
164362306a36Sopenharmony_ci	int ret = -ENAMETOOLONG;
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	_enter("{%llx:%llu},{%pd},%ho",
164662306a36Sopenharmony_ci	       dvnode->fid.vid, dvnode->fid.vnode, dentry, mode);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	if (dentry->d_name.len >= AFSNAMEMAX)
164962306a36Sopenharmony_ci		goto error;
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	op = afs_alloc_operation(NULL, dvnode->volume);
165262306a36Sopenharmony_ci	if (IS_ERR(op)) {
165362306a36Sopenharmony_ci		ret = PTR_ERR(op);
165462306a36Sopenharmony_ci		goto error;
165562306a36Sopenharmony_ci	}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	afs_op_set_vnode(op, 0, dvnode);
165862306a36Sopenharmony_ci	op->file[0].dv_delta = 1;
165962306a36Sopenharmony_ci	op->file[0].modification = true;
166062306a36Sopenharmony_ci	op->file[0].update_ctime = true;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	op->dentry	= dentry;
166362306a36Sopenharmony_ci	op->create.mode	= S_IFREG | mode;
166462306a36Sopenharmony_ci	op->create.reason = afs_edit_dir_for_create;
166562306a36Sopenharmony_ci	op->mtime	= current_time(dir);
166662306a36Sopenharmony_ci	op->ops		= &afs_create_operation;
166762306a36Sopenharmony_ci	return afs_do_sync_operation(op);
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_cierror:
167062306a36Sopenharmony_ci	d_drop(dentry);
167162306a36Sopenharmony_ci	_leave(" = %d", ret);
167262306a36Sopenharmony_ci	return ret;
167362306a36Sopenharmony_ci}
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_cistatic void afs_link_success(struct afs_operation *op)
167662306a36Sopenharmony_ci{
167762306a36Sopenharmony_ci	struct afs_vnode_param *dvp = &op->file[0];
167862306a36Sopenharmony_ci	struct afs_vnode_param *vp = &op->file[1];
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
168162306a36Sopenharmony_ci	op->ctime = dvp->scb.status.mtime_client;
168262306a36Sopenharmony_ci	afs_vnode_commit_status(op, dvp);
168362306a36Sopenharmony_ci	afs_vnode_commit_status(op, vp);
168462306a36Sopenharmony_ci	afs_update_dentry_version(op, dvp, op->dentry);
168562306a36Sopenharmony_ci	if (op->dentry_2->d_parent == op->dentry->d_parent)
168662306a36Sopenharmony_ci		afs_update_dentry_version(op, dvp, op->dentry_2);
168762306a36Sopenharmony_ci	ihold(&vp->vnode->netfs.inode);
168862306a36Sopenharmony_ci	d_instantiate(op->dentry, &vp->vnode->netfs.inode);
168962306a36Sopenharmony_ci}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_cistatic void afs_link_put(struct afs_operation *op)
169262306a36Sopenharmony_ci{
169362306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
169462306a36Sopenharmony_ci	if (op->error)
169562306a36Sopenharmony_ci		d_drop(op->dentry);
169662306a36Sopenharmony_ci}
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_cistatic const struct afs_operation_ops afs_link_operation = {
169962306a36Sopenharmony_ci	.issue_afs_rpc	= afs_fs_link,
170062306a36Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_link,
170162306a36Sopenharmony_ci	.success	= afs_link_success,
170262306a36Sopenharmony_ci	.aborted	= afs_check_for_remote_deletion,
170362306a36Sopenharmony_ci	.edit_dir	= afs_create_edit_dir,
170462306a36Sopenharmony_ci	.put		= afs_link_put,
170562306a36Sopenharmony_ci};
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci/*
170862306a36Sopenharmony_ci * create a hard link between files in an AFS filesystem
170962306a36Sopenharmony_ci */
171062306a36Sopenharmony_cistatic int afs_link(struct dentry *from, struct inode *dir,
171162306a36Sopenharmony_ci		    struct dentry *dentry)
171262306a36Sopenharmony_ci{
171362306a36Sopenharmony_ci	struct afs_operation *op;
171462306a36Sopenharmony_ci	struct afs_vnode *dvnode = AFS_FS_I(dir);
171562306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(d_inode(from));
171662306a36Sopenharmony_ci	int ret = -ENAMETOOLONG;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	_enter("{%llx:%llu},{%llx:%llu},{%pd}",
171962306a36Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode,
172062306a36Sopenharmony_ci	       dvnode->fid.vid, dvnode->fid.vnode,
172162306a36Sopenharmony_ci	       dentry);
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	if (dentry->d_name.len >= AFSNAMEMAX)
172462306a36Sopenharmony_ci		goto error;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	op = afs_alloc_operation(NULL, dvnode->volume);
172762306a36Sopenharmony_ci	if (IS_ERR(op)) {
172862306a36Sopenharmony_ci		ret = PTR_ERR(op);
172962306a36Sopenharmony_ci		goto error;
173062306a36Sopenharmony_ci	}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	ret = afs_validate(vnode, op->key);
173362306a36Sopenharmony_ci	if (ret < 0)
173462306a36Sopenharmony_ci		goto error_op;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	afs_op_set_vnode(op, 0, dvnode);
173762306a36Sopenharmony_ci	afs_op_set_vnode(op, 1, vnode);
173862306a36Sopenharmony_ci	op->file[0].dv_delta = 1;
173962306a36Sopenharmony_ci	op->file[0].modification = true;
174062306a36Sopenharmony_ci	op->file[0].update_ctime = true;
174162306a36Sopenharmony_ci	op->file[1].update_ctime = true;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	op->dentry		= dentry;
174462306a36Sopenharmony_ci	op->dentry_2		= from;
174562306a36Sopenharmony_ci	op->ops			= &afs_link_operation;
174662306a36Sopenharmony_ci	op->create.reason	= afs_edit_dir_for_link;
174762306a36Sopenharmony_ci	return afs_do_sync_operation(op);
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_cierror_op:
175062306a36Sopenharmony_ci	afs_put_operation(op);
175162306a36Sopenharmony_cierror:
175262306a36Sopenharmony_ci	d_drop(dentry);
175362306a36Sopenharmony_ci	_leave(" = %d", ret);
175462306a36Sopenharmony_ci	return ret;
175562306a36Sopenharmony_ci}
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_cistatic const struct afs_operation_ops afs_symlink_operation = {
175862306a36Sopenharmony_ci	.issue_afs_rpc	= afs_fs_symlink,
175962306a36Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_symlink,
176062306a36Sopenharmony_ci	.success	= afs_create_success,
176162306a36Sopenharmony_ci	.aborted	= afs_check_for_remote_deletion,
176262306a36Sopenharmony_ci	.edit_dir	= afs_create_edit_dir,
176362306a36Sopenharmony_ci	.put		= afs_create_put,
176462306a36Sopenharmony_ci};
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci/*
176762306a36Sopenharmony_ci * create a symlink in an AFS filesystem
176862306a36Sopenharmony_ci */
176962306a36Sopenharmony_cistatic int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,
177062306a36Sopenharmony_ci		       struct dentry *dentry, const char *content)
177162306a36Sopenharmony_ci{
177262306a36Sopenharmony_ci	struct afs_operation *op;
177362306a36Sopenharmony_ci	struct afs_vnode *dvnode = AFS_FS_I(dir);
177462306a36Sopenharmony_ci	int ret;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	_enter("{%llx:%llu},{%pd},%s",
177762306a36Sopenharmony_ci	       dvnode->fid.vid, dvnode->fid.vnode, dentry,
177862306a36Sopenharmony_ci	       content);
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	ret = -ENAMETOOLONG;
178162306a36Sopenharmony_ci	if (dentry->d_name.len >= AFSNAMEMAX)
178262306a36Sopenharmony_ci		goto error;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	ret = -EINVAL;
178562306a36Sopenharmony_ci	if (strlen(content) >= AFSPATHMAX)
178662306a36Sopenharmony_ci		goto error;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	op = afs_alloc_operation(NULL, dvnode->volume);
178962306a36Sopenharmony_ci	if (IS_ERR(op)) {
179062306a36Sopenharmony_ci		ret = PTR_ERR(op);
179162306a36Sopenharmony_ci		goto error;
179262306a36Sopenharmony_ci	}
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	afs_op_set_vnode(op, 0, dvnode);
179562306a36Sopenharmony_ci	op->file[0].dv_delta = 1;
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	op->dentry		= dentry;
179862306a36Sopenharmony_ci	op->ops			= &afs_symlink_operation;
179962306a36Sopenharmony_ci	op->create.reason	= afs_edit_dir_for_symlink;
180062306a36Sopenharmony_ci	op->create.symlink	= content;
180162306a36Sopenharmony_ci	op->mtime		= current_time(dir);
180262306a36Sopenharmony_ci	return afs_do_sync_operation(op);
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_cierror:
180562306a36Sopenharmony_ci	d_drop(dentry);
180662306a36Sopenharmony_ci	_leave(" = %d", ret);
180762306a36Sopenharmony_ci	return ret;
180862306a36Sopenharmony_ci}
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_cistatic void afs_rename_success(struct afs_operation *op)
181162306a36Sopenharmony_ci{
181262306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	op->ctime = op->file[0].scb.status.mtime_client;
181562306a36Sopenharmony_ci	afs_check_dir_conflict(op, &op->file[1]);
181662306a36Sopenharmony_ci	afs_vnode_commit_status(op, &op->file[0]);
181762306a36Sopenharmony_ci	if (op->file[1].vnode != op->file[0].vnode) {
181862306a36Sopenharmony_ci		op->ctime = op->file[1].scb.status.mtime_client;
181962306a36Sopenharmony_ci		afs_vnode_commit_status(op, &op->file[1]);
182062306a36Sopenharmony_ci	}
182162306a36Sopenharmony_ci}
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_cistatic void afs_rename_edit_dir(struct afs_operation *op)
182462306a36Sopenharmony_ci{
182562306a36Sopenharmony_ci	struct afs_vnode_param *orig_dvp = &op->file[0];
182662306a36Sopenharmony_ci	struct afs_vnode_param *new_dvp = &op->file[1];
182762306a36Sopenharmony_ci	struct afs_vnode *orig_dvnode = orig_dvp->vnode;
182862306a36Sopenharmony_ci	struct afs_vnode *new_dvnode = new_dvp->vnode;
182962306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(d_inode(op->dentry));
183062306a36Sopenharmony_ci	struct dentry *old_dentry = op->dentry;
183162306a36Sopenharmony_ci	struct dentry *new_dentry = op->dentry_2;
183262306a36Sopenharmony_ci	struct inode *new_inode;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	if (op->rename.rehash) {
183762306a36Sopenharmony_ci		d_rehash(op->rename.rehash);
183862306a36Sopenharmony_ci		op->rename.rehash = NULL;
183962306a36Sopenharmony_ci	}
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	down_write(&orig_dvnode->validate_lock);
184262306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags) &&
184362306a36Sopenharmony_ci	    orig_dvnode->status.data_version == orig_dvp->dv_before + orig_dvp->dv_delta)
184462306a36Sopenharmony_ci		afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name,
184562306a36Sopenharmony_ci				    afs_edit_dir_for_rename_0);
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	if (new_dvnode != orig_dvnode) {
184862306a36Sopenharmony_ci		up_write(&orig_dvnode->validate_lock);
184962306a36Sopenharmony_ci		down_write(&new_dvnode->validate_lock);
185062306a36Sopenharmony_ci	}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags) &&
185362306a36Sopenharmony_ci	    new_dvnode->status.data_version == new_dvp->dv_before + new_dvp->dv_delta) {
185462306a36Sopenharmony_ci		if (!op->rename.new_negative)
185562306a36Sopenharmony_ci			afs_edit_dir_remove(new_dvnode, &new_dentry->d_name,
185662306a36Sopenharmony_ci					    afs_edit_dir_for_rename_1);
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci		afs_edit_dir_add(new_dvnode, &new_dentry->d_name,
185962306a36Sopenharmony_ci				 &vnode->fid, afs_edit_dir_for_rename_2);
186062306a36Sopenharmony_ci	}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	new_inode = d_inode(new_dentry);
186362306a36Sopenharmony_ci	if (new_inode) {
186462306a36Sopenharmony_ci		spin_lock(&new_inode->i_lock);
186562306a36Sopenharmony_ci		if (S_ISDIR(new_inode->i_mode))
186662306a36Sopenharmony_ci			clear_nlink(new_inode);
186762306a36Sopenharmony_ci		else if (new_inode->i_nlink > 0)
186862306a36Sopenharmony_ci			drop_nlink(new_inode);
186962306a36Sopenharmony_ci		spin_unlock(&new_inode->i_lock);
187062306a36Sopenharmony_ci	}
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	/* Now we can update d_fsdata on the dentries to reflect their
187362306a36Sopenharmony_ci	 * new parent's data_version.
187462306a36Sopenharmony_ci	 *
187562306a36Sopenharmony_ci	 * Note that if we ever implement RENAME_EXCHANGE, we'll have
187662306a36Sopenharmony_ci	 * to update both dentries with opposing dir versions.
187762306a36Sopenharmony_ci	 */
187862306a36Sopenharmony_ci	afs_update_dentry_version(op, new_dvp, op->dentry);
187962306a36Sopenharmony_ci	afs_update_dentry_version(op, new_dvp, op->dentry_2);
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	d_move(old_dentry, new_dentry);
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	up_write(&new_dvnode->validate_lock);
188462306a36Sopenharmony_ci}
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_cistatic void afs_rename_put(struct afs_operation *op)
188762306a36Sopenharmony_ci{
188862306a36Sopenharmony_ci	_enter("op=%08x", op->debug_id);
188962306a36Sopenharmony_ci	if (op->rename.rehash)
189062306a36Sopenharmony_ci		d_rehash(op->rename.rehash);
189162306a36Sopenharmony_ci	dput(op->rename.tmp);
189262306a36Sopenharmony_ci	if (op->error)
189362306a36Sopenharmony_ci		d_rehash(op->dentry);
189462306a36Sopenharmony_ci}
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_cistatic const struct afs_operation_ops afs_rename_operation = {
189762306a36Sopenharmony_ci	.issue_afs_rpc	= afs_fs_rename,
189862306a36Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_rename,
189962306a36Sopenharmony_ci	.success	= afs_rename_success,
190062306a36Sopenharmony_ci	.edit_dir	= afs_rename_edit_dir,
190162306a36Sopenharmony_ci	.put		= afs_rename_put,
190262306a36Sopenharmony_ci};
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci/*
190562306a36Sopenharmony_ci * rename a file in an AFS filesystem and/or move it between directories
190662306a36Sopenharmony_ci */
190762306a36Sopenharmony_cistatic int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
190862306a36Sopenharmony_ci		      struct dentry *old_dentry, struct inode *new_dir,
190962306a36Sopenharmony_ci		      struct dentry *new_dentry, unsigned int flags)
191062306a36Sopenharmony_ci{
191162306a36Sopenharmony_ci	struct afs_operation *op;
191262306a36Sopenharmony_ci	struct afs_vnode *orig_dvnode, *new_dvnode, *vnode;
191362306a36Sopenharmony_ci	int ret;
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	if (flags)
191662306a36Sopenharmony_ci		return -EINVAL;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	/* Don't allow silly-rename files be moved around. */
191962306a36Sopenharmony_ci	if (old_dentry->d_flags & DCACHE_NFSFS_RENAMED)
192062306a36Sopenharmony_ci		return -EINVAL;
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	vnode = AFS_FS_I(d_inode(old_dentry));
192362306a36Sopenharmony_ci	orig_dvnode = AFS_FS_I(old_dir);
192462306a36Sopenharmony_ci	new_dvnode = AFS_FS_I(new_dir);
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	_enter("{%llx:%llu},{%llx:%llu},{%llx:%llu},{%pd}",
192762306a36Sopenharmony_ci	       orig_dvnode->fid.vid, orig_dvnode->fid.vnode,
192862306a36Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode,
192962306a36Sopenharmony_ci	       new_dvnode->fid.vid, new_dvnode->fid.vnode,
193062306a36Sopenharmony_ci	       new_dentry);
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	op = afs_alloc_operation(NULL, orig_dvnode->volume);
193362306a36Sopenharmony_ci	if (IS_ERR(op))
193462306a36Sopenharmony_ci		return PTR_ERR(op);
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	ret = afs_validate(vnode, op->key);
193762306a36Sopenharmony_ci	op->error = ret;
193862306a36Sopenharmony_ci	if (ret < 0)
193962306a36Sopenharmony_ci		goto error;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	afs_op_set_vnode(op, 0, orig_dvnode);
194262306a36Sopenharmony_ci	afs_op_set_vnode(op, 1, new_dvnode); /* May be same as orig_dvnode */
194362306a36Sopenharmony_ci	op->file[0].dv_delta = 1;
194462306a36Sopenharmony_ci	op->file[1].dv_delta = 1;
194562306a36Sopenharmony_ci	op->file[0].modification = true;
194662306a36Sopenharmony_ci	op->file[1].modification = true;
194762306a36Sopenharmony_ci	op->file[0].update_ctime = true;
194862306a36Sopenharmony_ci	op->file[1].update_ctime = true;
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	op->dentry		= old_dentry;
195162306a36Sopenharmony_ci	op->dentry_2		= new_dentry;
195262306a36Sopenharmony_ci	op->rename.new_negative	= d_is_negative(new_dentry);
195362306a36Sopenharmony_ci	op->ops			= &afs_rename_operation;
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	/* For non-directories, check whether the target is busy and if so,
195662306a36Sopenharmony_ci	 * make a copy of the dentry and then do a silly-rename.  If the
195762306a36Sopenharmony_ci	 * silly-rename succeeds, the copied dentry is hashed and becomes the
195862306a36Sopenharmony_ci	 * new target.
195962306a36Sopenharmony_ci	 */
196062306a36Sopenharmony_ci	if (d_is_positive(new_dentry) && !d_is_dir(new_dentry)) {
196162306a36Sopenharmony_ci		/* To prevent any new references to the target during the
196262306a36Sopenharmony_ci		 * rename, we unhash the dentry in advance.
196362306a36Sopenharmony_ci		 */
196462306a36Sopenharmony_ci		if (!d_unhashed(new_dentry)) {
196562306a36Sopenharmony_ci			d_drop(new_dentry);
196662306a36Sopenharmony_ci			op->rename.rehash = new_dentry;
196762306a36Sopenharmony_ci		}
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci		if (d_count(new_dentry) > 2) {
197062306a36Sopenharmony_ci			/* copy the target dentry's name */
197162306a36Sopenharmony_ci			op->rename.tmp = d_alloc(new_dentry->d_parent,
197262306a36Sopenharmony_ci						 &new_dentry->d_name);
197362306a36Sopenharmony_ci			if (!op->rename.tmp) {
197462306a36Sopenharmony_ci				op->error = -ENOMEM;
197562306a36Sopenharmony_ci				goto error;
197662306a36Sopenharmony_ci			}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci			ret = afs_sillyrename(new_dvnode,
197962306a36Sopenharmony_ci					      AFS_FS_I(d_inode(new_dentry)),
198062306a36Sopenharmony_ci					      new_dentry, op->key);
198162306a36Sopenharmony_ci			if (ret) {
198262306a36Sopenharmony_ci				op->error = ret;
198362306a36Sopenharmony_ci				goto error;
198462306a36Sopenharmony_ci			}
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci			op->dentry_2 = op->rename.tmp;
198762306a36Sopenharmony_ci			op->rename.rehash = NULL;
198862306a36Sopenharmony_ci			op->rename.new_negative = true;
198962306a36Sopenharmony_ci		}
199062306a36Sopenharmony_ci	}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	/* This bit is potentially nasty as there's a potential race with
199362306a36Sopenharmony_ci	 * afs_d_revalidate{,_rcu}().  We have to change d_fsdata on the dentry
199462306a36Sopenharmony_ci	 * to reflect it's new parent's new data_version after the op, but
199562306a36Sopenharmony_ci	 * d_revalidate may see old_dentry between the op having taken place
199662306a36Sopenharmony_ci	 * and the version being updated.
199762306a36Sopenharmony_ci	 *
199862306a36Sopenharmony_ci	 * So drop the old_dentry for now to make other threads go through
199962306a36Sopenharmony_ci	 * lookup instead - which we hold a lock against.
200062306a36Sopenharmony_ci	 */
200162306a36Sopenharmony_ci	d_drop(old_dentry);
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	return afs_do_sync_operation(op);
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_cierror:
200662306a36Sopenharmony_ci	return afs_put_operation(op);
200762306a36Sopenharmony_ci}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci/*
201062306a36Sopenharmony_ci * Release a directory folio and clean up its private state if it's not busy
201162306a36Sopenharmony_ci * - return true if the folio can now be released, false if not
201262306a36Sopenharmony_ci */
201362306a36Sopenharmony_cistatic bool afs_dir_release_folio(struct folio *folio, gfp_t gfp_flags)
201462306a36Sopenharmony_ci{
201562306a36Sopenharmony_ci	struct afs_vnode *dvnode = AFS_FS_I(folio_inode(folio));
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	_enter("{{%llx:%llu}[%lu]}", dvnode->fid.vid, dvnode->fid.vnode, folio_index(folio));
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	folio_detach_private(folio);
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	/* The directory will need reloading. */
202262306a36Sopenharmony_ci	if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
202362306a36Sopenharmony_ci		afs_stat_v(dvnode, n_relpg);
202462306a36Sopenharmony_ci	return true;
202562306a36Sopenharmony_ci}
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci/*
202862306a36Sopenharmony_ci * Invalidate part or all of a folio.
202962306a36Sopenharmony_ci */
203062306a36Sopenharmony_cistatic void afs_dir_invalidate_folio(struct folio *folio, size_t offset,
203162306a36Sopenharmony_ci				   size_t length)
203262306a36Sopenharmony_ci{
203362306a36Sopenharmony_ci	struct afs_vnode *dvnode = AFS_FS_I(folio_inode(folio));
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	_enter("{%lu},%zu,%zu", folio->index, offset, length);
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci	BUG_ON(!folio_test_locked(folio));
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	/* The directory will need reloading. */
204062306a36Sopenharmony_ci	if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
204162306a36Sopenharmony_ci		afs_stat_v(dvnode, n_inval);
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	/* we clean up only if the entire folio is being invalidated */
204462306a36Sopenharmony_ci	if (offset == 0 && length == folio_size(folio))
204562306a36Sopenharmony_ci		folio_detach_private(folio);
204662306a36Sopenharmony_ci}
2047