162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2002 Red Hat, Inc. All rights reserved.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This software may be freely redistributed under the terms of the
562306a36Sopenharmony_ci * GNU General Public License.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License
862306a36Sopenharmony_ci * along with this program; if not, write to the Free Software
962306a36Sopenharmony_ci * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Authors: David Woodhouse <dwmw2@infradead.org>
1262306a36Sopenharmony_ci *          David Howells <dhowells@redhat.com>
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/init.h>
1962306a36Sopenharmony_ci#include <linux/fs.h>
2062306a36Sopenharmony_ci#include <linux/pagemap.h>
2162306a36Sopenharmony_ci#include <linux/sched.h>
2262306a36Sopenharmony_ci#include <linux/mount.h>
2362306a36Sopenharmony_ci#include <linux/namei.h>
2462306a36Sopenharmony_ci#include <linux/iversion.h>
2562306a36Sopenharmony_ci#include "internal.h"
2662306a36Sopenharmony_ci#include "afs_fs.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic const struct inode_operations afs_symlink_inode_operations = {
2962306a36Sopenharmony_ci	.get_link	= page_get_link,
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic noinline void dump_vnode(struct afs_vnode *vnode, struct afs_vnode *parent_vnode)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	static unsigned long once_only;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	pr_warn("kAFS: AFS vnode with undefined type %u\n", vnode->status.type);
3762306a36Sopenharmony_ci	pr_warn("kAFS: A=%d m=%o s=%llx v=%llx\n",
3862306a36Sopenharmony_ci		vnode->status.abort_code,
3962306a36Sopenharmony_ci		vnode->status.mode,
4062306a36Sopenharmony_ci		vnode->status.size,
4162306a36Sopenharmony_ci		vnode->status.data_version);
4262306a36Sopenharmony_ci	pr_warn("kAFS: vnode %llx:%llx:%x\n",
4362306a36Sopenharmony_ci		vnode->fid.vid,
4462306a36Sopenharmony_ci		vnode->fid.vnode,
4562306a36Sopenharmony_ci		vnode->fid.unique);
4662306a36Sopenharmony_ci	if (parent_vnode)
4762306a36Sopenharmony_ci		pr_warn("kAFS: dir %llx:%llx:%x\n",
4862306a36Sopenharmony_ci			parent_vnode->fid.vid,
4962306a36Sopenharmony_ci			parent_vnode->fid.vnode,
5062306a36Sopenharmony_ci			parent_vnode->fid.unique);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (!test_and_set_bit(0, &once_only))
5362306a36Sopenharmony_ci		dump_stack();
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/*
5762306a36Sopenharmony_ci * Set parameters for the netfs library
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_cistatic void afs_set_netfs_context(struct afs_vnode *vnode)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	netfs_inode_init(&vnode->netfs, &afs_req_ops);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/*
6562306a36Sopenharmony_ci * Initialise an inode from the vnode status.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistatic int afs_inode_init_from_status(struct afs_operation *op,
6862306a36Sopenharmony_ci				      struct afs_vnode_param *vp,
6962306a36Sopenharmony_ci				      struct afs_vnode *vnode)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct afs_file_status *status = &vp->scb.status;
7262306a36Sopenharmony_ci	struct inode *inode = AFS_VNODE_TO_I(vnode);
7362306a36Sopenharmony_ci	struct timespec64 t;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	_enter("{%llx:%llu.%u} %s",
7662306a36Sopenharmony_ci	       vp->fid.vid, vp->fid.vnode, vp->fid.unique,
7762306a36Sopenharmony_ci	       op->type ? op->type->name : "???");
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	_debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu",
8062306a36Sopenharmony_ci	       status->type,
8162306a36Sopenharmony_ci	       status->nlink,
8262306a36Sopenharmony_ci	       (unsigned long long) status->size,
8362306a36Sopenharmony_ci	       status->data_version,
8462306a36Sopenharmony_ci	       status->mode);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	write_seqlock(&vnode->cb_lock);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	vnode->cb_v_break = op->cb_v_break;
8962306a36Sopenharmony_ci	vnode->cb_s_break = op->cb_s_break;
9062306a36Sopenharmony_ci	vnode->status = *status;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	t = status->mtime_client;
9362306a36Sopenharmony_ci	inode_set_ctime_to_ts(inode, t);
9462306a36Sopenharmony_ci	inode->i_mtime = t;
9562306a36Sopenharmony_ci	inode->i_atime = t;
9662306a36Sopenharmony_ci	inode->i_flags |= S_NOATIME;
9762306a36Sopenharmony_ci	inode->i_uid = make_kuid(&init_user_ns, status->owner);
9862306a36Sopenharmony_ci	inode->i_gid = make_kgid(&init_user_ns, status->group);
9962306a36Sopenharmony_ci	set_nlink(&vnode->netfs.inode, status->nlink);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	switch (status->type) {
10262306a36Sopenharmony_ci	case AFS_FTYPE_FILE:
10362306a36Sopenharmony_ci		inode->i_mode	= S_IFREG | (status->mode & S_IALLUGO);
10462306a36Sopenharmony_ci		inode->i_op	= &afs_file_inode_operations;
10562306a36Sopenharmony_ci		inode->i_fop	= &afs_file_operations;
10662306a36Sopenharmony_ci		inode->i_mapping->a_ops	= &afs_file_aops;
10762306a36Sopenharmony_ci		mapping_set_large_folios(inode->i_mapping);
10862306a36Sopenharmony_ci		break;
10962306a36Sopenharmony_ci	case AFS_FTYPE_DIR:
11062306a36Sopenharmony_ci		inode->i_mode	= S_IFDIR |  (status->mode & S_IALLUGO);
11162306a36Sopenharmony_ci		inode->i_op	= &afs_dir_inode_operations;
11262306a36Sopenharmony_ci		inode->i_fop	= &afs_dir_file_operations;
11362306a36Sopenharmony_ci		inode->i_mapping->a_ops	= &afs_dir_aops;
11462306a36Sopenharmony_ci		mapping_set_large_folios(inode->i_mapping);
11562306a36Sopenharmony_ci		break;
11662306a36Sopenharmony_ci	case AFS_FTYPE_SYMLINK:
11762306a36Sopenharmony_ci		/* Symlinks with a mode of 0644 are actually mountpoints. */
11862306a36Sopenharmony_ci		if ((status->mode & 0777) == 0644) {
11962306a36Sopenharmony_ci			inode->i_flags |= S_AUTOMOUNT;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci			set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci			inode->i_mode	= S_IFDIR | 0555;
12462306a36Sopenharmony_ci			inode->i_op	= &afs_mntpt_inode_operations;
12562306a36Sopenharmony_ci			inode->i_fop	= &afs_mntpt_file_operations;
12662306a36Sopenharmony_ci			inode->i_mapping->a_ops	= &afs_symlink_aops;
12762306a36Sopenharmony_ci		} else {
12862306a36Sopenharmony_ci			inode->i_mode	= S_IFLNK | status->mode;
12962306a36Sopenharmony_ci			inode->i_op	= &afs_symlink_inode_operations;
13062306a36Sopenharmony_ci			inode->i_mapping->a_ops	= &afs_symlink_aops;
13162306a36Sopenharmony_ci		}
13262306a36Sopenharmony_ci		inode_nohighmem(inode);
13362306a36Sopenharmony_ci		break;
13462306a36Sopenharmony_ci	default:
13562306a36Sopenharmony_ci		dump_vnode(vnode, op->file[0].vnode != vnode ? op->file[0].vnode : NULL);
13662306a36Sopenharmony_ci		write_sequnlock(&vnode->cb_lock);
13762306a36Sopenharmony_ci		return afs_protocol_error(NULL, afs_eproto_file_type);
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	afs_set_i_size(vnode, status->size);
14162306a36Sopenharmony_ci	afs_set_netfs_context(vnode);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	vnode->invalid_before	= status->data_version;
14462306a36Sopenharmony_ci	inode_set_iversion_raw(&vnode->netfs.inode, status->data_version);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (!vp->scb.have_cb) {
14762306a36Sopenharmony_ci		/* it's a symlink we just created (the fileserver
14862306a36Sopenharmony_ci		 * didn't give us a callback) */
14962306a36Sopenharmony_ci		vnode->cb_expires_at = ktime_get_real_seconds();
15062306a36Sopenharmony_ci	} else {
15162306a36Sopenharmony_ci		vnode->cb_expires_at = vp->scb.callback.expires_at;
15262306a36Sopenharmony_ci		vnode->cb_server = op->server;
15362306a36Sopenharmony_ci		set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	write_sequnlock(&vnode->cb_lock);
15762306a36Sopenharmony_ci	return 0;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/*
16162306a36Sopenharmony_ci * Update the core inode struct from a returned status record.
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_cistatic void afs_apply_status(struct afs_operation *op,
16462306a36Sopenharmony_ci			     struct afs_vnode_param *vp)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct afs_file_status *status = &vp->scb.status;
16762306a36Sopenharmony_ci	struct afs_vnode *vnode = vp->vnode;
16862306a36Sopenharmony_ci	struct inode *inode = &vnode->netfs.inode;
16962306a36Sopenharmony_ci	struct timespec64 t;
17062306a36Sopenharmony_ci	umode_t mode;
17162306a36Sopenharmony_ci	bool data_changed = false;
17262306a36Sopenharmony_ci	bool change_size = vp->set_size;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	_enter("{%llx:%llu.%u} %s",
17562306a36Sopenharmony_ci	       vp->fid.vid, vp->fid.vnode, vp->fid.unique,
17662306a36Sopenharmony_ci	       op->type ? op->type->name : "???");
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	BUG_ON(test_bit(AFS_VNODE_UNSET, &vnode->flags));
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (status->type != vnode->status.type) {
18162306a36Sopenharmony_ci		pr_warn("Vnode %llx:%llx:%x changed type %u to %u\n",
18262306a36Sopenharmony_ci			vnode->fid.vid,
18362306a36Sopenharmony_ci			vnode->fid.vnode,
18462306a36Sopenharmony_ci			vnode->fid.unique,
18562306a36Sopenharmony_ci			status->type, vnode->status.type);
18662306a36Sopenharmony_ci		afs_protocol_error(NULL, afs_eproto_bad_status);
18762306a36Sopenharmony_ci		return;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (status->nlink != vnode->status.nlink)
19162306a36Sopenharmony_ci		set_nlink(inode, status->nlink);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (status->owner != vnode->status.owner)
19462306a36Sopenharmony_ci		inode->i_uid = make_kuid(&init_user_ns, status->owner);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (status->group != vnode->status.group)
19762306a36Sopenharmony_ci		inode->i_gid = make_kgid(&init_user_ns, status->group);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (status->mode != vnode->status.mode) {
20062306a36Sopenharmony_ci		mode = inode->i_mode;
20162306a36Sopenharmony_ci		mode &= ~S_IALLUGO;
20262306a36Sopenharmony_ci		mode |= status->mode & S_IALLUGO;
20362306a36Sopenharmony_ci		WRITE_ONCE(inode->i_mode, mode);
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	t = status->mtime_client;
20762306a36Sopenharmony_ci	inode->i_mtime = t;
20862306a36Sopenharmony_ci	if (vp->update_ctime)
20962306a36Sopenharmony_ci		inode_set_ctime_to_ts(inode, op->ctime);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (vnode->status.data_version != status->data_version)
21262306a36Sopenharmony_ci		data_changed = true;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	vnode->status = *status;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (vp->dv_before + vp->dv_delta != status->data_version) {
21762306a36Sopenharmony_ci		if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags))
21862306a36Sopenharmony_ci			pr_warn("kAFS: vnode modified {%llx:%llu} %llx->%llx %s (op=%x)\n",
21962306a36Sopenharmony_ci				vnode->fid.vid, vnode->fid.vnode,
22062306a36Sopenharmony_ci				(unsigned long long)vp->dv_before + vp->dv_delta,
22162306a36Sopenharmony_ci				(unsigned long long)status->data_version,
22262306a36Sopenharmony_ci				op->type ? op->type->name : "???",
22362306a36Sopenharmony_ci				op->debug_id);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		vnode->invalid_before = status->data_version;
22662306a36Sopenharmony_ci		if (vnode->status.type == AFS_FTYPE_DIR) {
22762306a36Sopenharmony_ci			if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
22862306a36Sopenharmony_ci				afs_stat_v(vnode, n_inval);
22962306a36Sopenharmony_ci		} else {
23062306a36Sopenharmony_ci			set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
23162306a36Sopenharmony_ci		}
23262306a36Sopenharmony_ci		change_size = true;
23362306a36Sopenharmony_ci		data_changed = true;
23462306a36Sopenharmony_ci	} else if (vnode->status.type == AFS_FTYPE_DIR) {
23562306a36Sopenharmony_ci		/* Expected directory change is handled elsewhere so
23662306a36Sopenharmony_ci		 * that we can locally edit the directory and save on a
23762306a36Sopenharmony_ci		 * download.
23862306a36Sopenharmony_ci		 */
23962306a36Sopenharmony_ci		if (test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
24062306a36Sopenharmony_ci			data_changed = false;
24162306a36Sopenharmony_ci		change_size = true;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (data_changed) {
24562306a36Sopenharmony_ci		inode_set_iversion_raw(inode, status->data_version);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		/* Only update the size if the data version jumped.  If the
24862306a36Sopenharmony_ci		 * file is being modified locally, then we might have our own
24962306a36Sopenharmony_ci		 * idea of what the size should be that's not the same as
25062306a36Sopenharmony_ci		 * what's on the server.
25162306a36Sopenharmony_ci		 */
25262306a36Sopenharmony_ci		vnode->netfs.remote_i_size = status->size;
25362306a36Sopenharmony_ci		if (change_size) {
25462306a36Sopenharmony_ci			afs_set_i_size(vnode, status->size);
25562306a36Sopenharmony_ci			inode_set_ctime_to_ts(inode, t);
25662306a36Sopenharmony_ci			inode->i_atime = t;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci/*
26262306a36Sopenharmony_ci * Apply a callback to a vnode.
26362306a36Sopenharmony_ci */
26462306a36Sopenharmony_cistatic void afs_apply_callback(struct afs_operation *op,
26562306a36Sopenharmony_ci			       struct afs_vnode_param *vp)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct afs_callback *cb = &vp->scb.callback;
26862306a36Sopenharmony_ci	struct afs_vnode *vnode = vp->vnode;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (!afs_cb_is_broken(vp->cb_break_before, vnode)) {
27162306a36Sopenharmony_ci		vnode->cb_expires_at	= cb->expires_at;
27262306a36Sopenharmony_ci		vnode->cb_server	= op->server;
27362306a36Sopenharmony_ci		set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/*
27862306a36Sopenharmony_ci * Apply the received status and callback to an inode all in the same critical
27962306a36Sopenharmony_ci * section to avoid races with afs_validate().
28062306a36Sopenharmony_ci */
28162306a36Sopenharmony_civoid afs_vnode_commit_status(struct afs_operation *op, struct afs_vnode_param *vp)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct afs_vnode *vnode = vp->vnode;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	_enter("");
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	write_seqlock(&vnode->cb_lock);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (vp->scb.have_error) {
29062306a36Sopenharmony_ci		/* A YFS server will return this from RemoveFile2 and AFS and
29162306a36Sopenharmony_ci		 * YFS will return this from InlineBulkStatus.
29262306a36Sopenharmony_ci		 */
29362306a36Sopenharmony_ci		if (vp->scb.status.abort_code == VNOVNODE) {
29462306a36Sopenharmony_ci			set_bit(AFS_VNODE_DELETED, &vnode->flags);
29562306a36Sopenharmony_ci			clear_nlink(&vnode->netfs.inode);
29662306a36Sopenharmony_ci			__afs_break_callback(vnode, afs_cb_break_for_deleted);
29762306a36Sopenharmony_ci			op->flags &= ~AFS_OPERATION_DIR_CONFLICT;
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci	} else if (vp->scb.have_status) {
30062306a36Sopenharmony_ci		if (vp->speculative &&
30162306a36Sopenharmony_ci		    (test_bit(AFS_VNODE_MODIFYING, &vnode->flags) ||
30262306a36Sopenharmony_ci		     vp->dv_before != vnode->status.data_version))
30362306a36Sopenharmony_ci			/* Ignore the result of a speculative bulk status fetch
30462306a36Sopenharmony_ci			 * if it splits around a modification op, thereby
30562306a36Sopenharmony_ci			 * appearing to regress the data version.
30662306a36Sopenharmony_ci			 */
30762306a36Sopenharmony_ci			goto out;
30862306a36Sopenharmony_ci		afs_apply_status(op, vp);
30962306a36Sopenharmony_ci		if (vp->scb.have_cb)
31062306a36Sopenharmony_ci			afs_apply_callback(op, vp);
31162306a36Sopenharmony_ci	} else if (vp->op_unlinked && !(op->flags & AFS_OPERATION_DIR_CONFLICT)) {
31262306a36Sopenharmony_ci		drop_nlink(&vnode->netfs.inode);
31362306a36Sopenharmony_ci		if (vnode->netfs.inode.i_nlink == 0) {
31462306a36Sopenharmony_ci			set_bit(AFS_VNODE_DELETED, &vnode->flags);
31562306a36Sopenharmony_ci			__afs_break_callback(vnode, afs_cb_break_for_deleted);
31662306a36Sopenharmony_ci		}
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ciout:
32062306a36Sopenharmony_ci	write_sequnlock(&vnode->cb_lock);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (vp->scb.have_status)
32362306a36Sopenharmony_ci		afs_cache_permit(vnode, op->key, vp->cb_break_before, &vp->scb);
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic void afs_fetch_status_success(struct afs_operation *op)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct afs_vnode_param *vp = &op->file[op->fetch_status.which];
32962306a36Sopenharmony_ci	struct afs_vnode *vnode = vp->vnode;
33062306a36Sopenharmony_ci	int ret;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (vnode->netfs.inode.i_state & I_NEW) {
33362306a36Sopenharmony_ci		ret = afs_inode_init_from_status(op, vp, vnode);
33462306a36Sopenharmony_ci		op->error = ret;
33562306a36Sopenharmony_ci		if (ret == 0)
33662306a36Sopenharmony_ci			afs_cache_permit(vnode, op->key, vp->cb_break_before, &vp->scb);
33762306a36Sopenharmony_ci	} else {
33862306a36Sopenharmony_ci		afs_vnode_commit_status(op, vp);
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ciconst struct afs_operation_ops afs_fetch_status_operation = {
34362306a36Sopenharmony_ci	.issue_afs_rpc	= afs_fs_fetch_status,
34462306a36Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_fetch_status,
34562306a36Sopenharmony_ci	.success	= afs_fetch_status_success,
34662306a36Sopenharmony_ci	.aborted	= afs_check_for_remote_deletion,
34762306a36Sopenharmony_ci};
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci/*
35062306a36Sopenharmony_ci * Fetch file status from the volume.
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_ciint afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool is_new,
35362306a36Sopenharmony_ci		     afs_access_t *_caller_access)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct afs_operation *op;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	_enter("%s,{%llx:%llu.%u,S=%lx}",
35862306a36Sopenharmony_ci	       vnode->volume->name,
35962306a36Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique,
36062306a36Sopenharmony_ci	       vnode->flags);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	op = afs_alloc_operation(key, vnode->volume);
36362306a36Sopenharmony_ci	if (IS_ERR(op))
36462306a36Sopenharmony_ci		return PTR_ERR(op);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	afs_op_set_vnode(op, 0, vnode);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	op->nr_files	= 1;
36962306a36Sopenharmony_ci	op->ops		= &afs_fetch_status_operation;
37062306a36Sopenharmony_ci	afs_begin_vnode_operation(op);
37162306a36Sopenharmony_ci	afs_wait_for_operation(op);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (_caller_access)
37462306a36Sopenharmony_ci		*_caller_access = op->file[0].scb.status.caller_access;
37562306a36Sopenharmony_ci	return afs_put_operation(op);
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/*
37962306a36Sopenharmony_ci * ilookup() comparator
38062306a36Sopenharmony_ci */
38162306a36Sopenharmony_ciint afs_ilookup5_test_by_fid(struct inode *inode, void *opaque)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(inode);
38462306a36Sopenharmony_ci	struct afs_fid *fid = opaque;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return (fid->vnode == vnode->fid.vnode &&
38762306a36Sopenharmony_ci		fid->vnode_hi == vnode->fid.vnode_hi &&
38862306a36Sopenharmony_ci		fid->unique == vnode->fid.unique);
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci/*
39262306a36Sopenharmony_ci * iget5() comparator
39362306a36Sopenharmony_ci */
39462306a36Sopenharmony_cistatic int afs_iget5_test(struct inode *inode, void *opaque)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct afs_vnode_param *vp = opaque;
39762306a36Sopenharmony_ci	//struct afs_vnode *vnode = AFS_FS_I(inode);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return afs_ilookup5_test_by_fid(inode, &vp->fid);
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci/*
40362306a36Sopenharmony_ci * iget5() inode initialiser
40462306a36Sopenharmony_ci */
40562306a36Sopenharmony_cistatic int afs_iget5_set(struct inode *inode, void *opaque)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct afs_vnode_param *vp = opaque;
40862306a36Sopenharmony_ci	struct afs_super_info *as = AFS_FS_S(inode->i_sb);
40962306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(inode);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	vnode->volume		= as->volume;
41262306a36Sopenharmony_ci	vnode->fid		= vp->fid;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/* YFS supports 96-bit vnode IDs, but Linux only supports
41562306a36Sopenharmony_ci	 * 64-bit inode numbers.
41662306a36Sopenharmony_ci	 */
41762306a36Sopenharmony_ci	inode->i_ino		= vnode->fid.vnode;
41862306a36Sopenharmony_ci	inode->i_generation	= vnode->fid.unique;
41962306a36Sopenharmony_ci	return 0;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci/*
42362306a36Sopenharmony_ci * Get a cache cookie for an inode.
42462306a36Sopenharmony_ci */
42562306a36Sopenharmony_cistatic void afs_get_inode_cache(struct afs_vnode *vnode)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci#ifdef CONFIG_AFS_FSCACHE
42862306a36Sopenharmony_ci	struct {
42962306a36Sopenharmony_ci		__be32 vnode_id;
43062306a36Sopenharmony_ci		__be32 unique;
43162306a36Sopenharmony_ci		__be32 vnode_id_ext[2];	/* Allow for a 96-bit key */
43262306a36Sopenharmony_ci	} __packed key;
43362306a36Sopenharmony_ci	struct afs_vnode_cache_aux aux;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (vnode->status.type != AFS_FTYPE_FILE) {
43662306a36Sopenharmony_ci		vnode->netfs.cache = NULL;
43762306a36Sopenharmony_ci		return;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	key.vnode_id		= htonl(vnode->fid.vnode);
44162306a36Sopenharmony_ci	key.unique		= htonl(vnode->fid.unique);
44262306a36Sopenharmony_ci	key.vnode_id_ext[0]	= htonl(vnode->fid.vnode >> 32);
44362306a36Sopenharmony_ci	key.vnode_id_ext[1]	= htonl(vnode->fid.vnode_hi);
44462306a36Sopenharmony_ci	afs_set_cache_aux(vnode, &aux);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	afs_vnode_set_cache(vnode,
44762306a36Sopenharmony_ci			    fscache_acquire_cookie(
44862306a36Sopenharmony_ci				    vnode->volume->cache,
44962306a36Sopenharmony_ci				    vnode->status.type == AFS_FTYPE_FILE ?
45062306a36Sopenharmony_ci				    0 : FSCACHE_ADV_SINGLE_CHUNK,
45162306a36Sopenharmony_ci				    &key, sizeof(key),
45262306a36Sopenharmony_ci				    &aux, sizeof(aux),
45362306a36Sopenharmony_ci				    i_size_read(&vnode->netfs.inode)));
45462306a36Sopenharmony_ci#endif
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci/*
45862306a36Sopenharmony_ci * inode retrieval
45962306a36Sopenharmony_ci */
46062306a36Sopenharmony_cistruct inode *afs_iget(struct afs_operation *op, struct afs_vnode_param *vp)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct afs_vnode_param *dvp = &op->file[0];
46362306a36Sopenharmony_ci	struct super_block *sb = dvp->vnode->netfs.inode.i_sb;
46462306a36Sopenharmony_ci	struct afs_vnode *vnode;
46562306a36Sopenharmony_ci	struct inode *inode;
46662306a36Sopenharmony_ci	int ret;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	_enter(",{%llx:%llu.%u},,", vp->fid.vid, vp->fid.vnode, vp->fid.unique);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	inode = iget5_locked(sb, vp->fid.vnode, afs_iget5_test, afs_iget5_set, vp);
47162306a36Sopenharmony_ci	if (!inode) {
47262306a36Sopenharmony_ci		_leave(" = -ENOMEM");
47362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	vnode = AFS_FS_I(inode);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	_debug("GOT INODE %p { vl=%llx vn=%llx, u=%x }",
47962306a36Sopenharmony_ci	       inode, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* deal with an existing inode */
48262306a36Sopenharmony_ci	if (!(inode->i_state & I_NEW)) {
48362306a36Sopenharmony_ci		_leave(" = %p", inode);
48462306a36Sopenharmony_ci		return inode;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	ret = afs_inode_init_from_status(op, vp, vnode);
48862306a36Sopenharmony_ci	if (ret < 0)
48962306a36Sopenharmony_ci		goto bad_inode;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	afs_get_inode_cache(vnode);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* success */
49462306a36Sopenharmony_ci	clear_bit(AFS_VNODE_UNSET, &vnode->flags);
49562306a36Sopenharmony_ci	unlock_new_inode(inode);
49662306a36Sopenharmony_ci	_leave(" = %p", inode);
49762306a36Sopenharmony_ci	return inode;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	/* failure */
50062306a36Sopenharmony_cibad_inode:
50162306a36Sopenharmony_ci	iget_failed(inode);
50262306a36Sopenharmony_ci	_leave(" = %d [bad]", ret);
50362306a36Sopenharmony_ci	return ERR_PTR(ret);
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic int afs_iget5_set_root(struct inode *inode, void *opaque)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	struct afs_super_info *as = AFS_FS_S(inode->i_sb);
50962306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(inode);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	vnode->volume		= as->volume;
51262306a36Sopenharmony_ci	vnode->fid.vid		= as->volume->vid,
51362306a36Sopenharmony_ci	vnode->fid.vnode	= 1;
51462306a36Sopenharmony_ci	vnode->fid.unique	= 1;
51562306a36Sopenharmony_ci	inode->i_ino		= 1;
51662306a36Sopenharmony_ci	inode->i_generation	= 1;
51762306a36Sopenharmony_ci	return 0;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci/*
52162306a36Sopenharmony_ci * Set up the root inode for a volume.  This is always vnode 1, unique 1 within
52262306a36Sopenharmony_ci * the volume.
52362306a36Sopenharmony_ci */
52462306a36Sopenharmony_cistruct inode *afs_root_iget(struct super_block *sb, struct key *key)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct afs_super_info *as = AFS_FS_S(sb);
52762306a36Sopenharmony_ci	struct afs_operation *op;
52862306a36Sopenharmony_ci	struct afs_vnode *vnode;
52962306a36Sopenharmony_ci	struct inode *inode;
53062306a36Sopenharmony_ci	int ret;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	_enter(",{%llx},,", as->volume->vid);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	inode = iget5_locked(sb, 1, NULL, afs_iget5_set_root, NULL);
53562306a36Sopenharmony_ci	if (!inode) {
53662306a36Sopenharmony_ci		_leave(" = -ENOMEM");
53762306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	_debug("GOT ROOT INODE %p { vl=%llx }", inode, as->volume->vid);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	BUG_ON(!(inode->i_state & I_NEW));
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	vnode = AFS_FS_I(inode);
54562306a36Sopenharmony_ci	vnode->cb_v_break = as->volume->cb_v_break,
54662306a36Sopenharmony_ci	afs_set_netfs_context(vnode);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	op = afs_alloc_operation(key, as->volume);
54962306a36Sopenharmony_ci	if (IS_ERR(op)) {
55062306a36Sopenharmony_ci		ret = PTR_ERR(op);
55162306a36Sopenharmony_ci		goto error;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	afs_op_set_vnode(op, 0, vnode);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	op->nr_files	= 1;
55762306a36Sopenharmony_ci	op->ops		= &afs_fetch_status_operation;
55862306a36Sopenharmony_ci	ret = afs_do_sync_operation(op);
55962306a36Sopenharmony_ci	if (ret < 0)
56062306a36Sopenharmony_ci		goto error;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	afs_get_inode_cache(vnode);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	clear_bit(AFS_VNODE_UNSET, &vnode->flags);
56562306a36Sopenharmony_ci	unlock_new_inode(inode);
56662306a36Sopenharmony_ci	_leave(" = %p", inode);
56762306a36Sopenharmony_ci	return inode;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cierror:
57062306a36Sopenharmony_ci	iget_failed(inode);
57162306a36Sopenharmony_ci	_leave(" = %d [bad]", ret);
57262306a36Sopenharmony_ci	return ERR_PTR(ret);
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci/*
57662306a36Sopenharmony_ci * mark the data attached to an inode as obsolete due to a write on the server
57762306a36Sopenharmony_ci * - might also want to ditch all the outstanding writes and dirty pages
57862306a36Sopenharmony_ci */
57962306a36Sopenharmony_cistatic void afs_zap_data(struct afs_vnode *vnode)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	_enter("{%llx:%llu}", vnode->fid.vid, vnode->fid.vnode);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	afs_invalidate_cache(vnode, 0);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/* nuke all the non-dirty pages that aren't locked, mapped or being
58662306a36Sopenharmony_ci	 * written back in a regular file and completely discard the pages in a
58762306a36Sopenharmony_ci	 * directory or symlink */
58862306a36Sopenharmony_ci	if (S_ISREG(vnode->netfs.inode.i_mode))
58962306a36Sopenharmony_ci		invalidate_remote_inode(&vnode->netfs.inode);
59062306a36Sopenharmony_ci	else
59162306a36Sopenharmony_ci		invalidate_inode_pages2(vnode->netfs.inode.i_mapping);
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci/*
59562306a36Sopenharmony_ci * Check to see if we have a server currently serving this volume and that it
59662306a36Sopenharmony_ci * hasn't been reinitialised or dropped from the list.
59762306a36Sopenharmony_ci */
59862306a36Sopenharmony_cistatic bool afs_check_server_good(struct afs_vnode *vnode)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct afs_server_list *slist;
60162306a36Sopenharmony_ci	struct afs_server *server;
60262306a36Sopenharmony_ci	bool good;
60362306a36Sopenharmony_ci	int i;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (vnode->cb_fs_s_break == atomic_read(&vnode->volume->cell->fs_s_break))
60662306a36Sopenharmony_ci		return true;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	rcu_read_lock();
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	slist = rcu_dereference(vnode->volume->servers);
61162306a36Sopenharmony_ci	for (i = 0; i < slist->nr_servers; i++) {
61262306a36Sopenharmony_ci		server = slist->servers[i].server;
61362306a36Sopenharmony_ci		if (server == vnode->cb_server) {
61462306a36Sopenharmony_ci			good = (vnode->cb_s_break == server->cb_s_break);
61562306a36Sopenharmony_ci			rcu_read_unlock();
61662306a36Sopenharmony_ci			return good;
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	rcu_read_unlock();
62162306a36Sopenharmony_ci	return false;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci/*
62562306a36Sopenharmony_ci * Check the validity of a vnode/inode.
62662306a36Sopenharmony_ci */
62762306a36Sopenharmony_cibool afs_check_validity(struct afs_vnode *vnode)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	enum afs_cb_break_reason need_clear = afs_cb_break_no_break;
63062306a36Sopenharmony_ci	time64_t now = ktime_get_real_seconds();
63162306a36Sopenharmony_ci	unsigned int cb_break;
63262306a36Sopenharmony_ci	int seq = 0;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	do {
63562306a36Sopenharmony_ci		read_seqbegin_or_lock(&vnode->cb_lock, &seq);
63662306a36Sopenharmony_ci		cb_break = vnode->cb_break;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
63962306a36Sopenharmony_ci			if (vnode->cb_v_break != vnode->volume->cb_v_break)
64062306a36Sopenharmony_ci				need_clear = afs_cb_break_for_v_break;
64162306a36Sopenharmony_ci			else if (!afs_check_server_good(vnode))
64262306a36Sopenharmony_ci				need_clear = afs_cb_break_for_s_reinit;
64362306a36Sopenharmony_ci			else if (test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags))
64462306a36Sopenharmony_ci				need_clear = afs_cb_break_for_zap;
64562306a36Sopenharmony_ci			else if (vnode->cb_expires_at - 10 <= now)
64662306a36Sopenharmony_ci				need_clear = afs_cb_break_for_lapsed;
64762306a36Sopenharmony_ci		} else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
64862306a36Sopenharmony_ci			;
64962306a36Sopenharmony_ci		} else {
65062306a36Sopenharmony_ci			need_clear = afs_cb_break_no_promise;
65162306a36Sopenharmony_ci		}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	} while (need_seqretry(&vnode->cb_lock, seq));
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	done_seqretry(&vnode->cb_lock, seq);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (need_clear == afs_cb_break_no_break)
65862306a36Sopenharmony_ci		return true;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	write_seqlock(&vnode->cb_lock);
66162306a36Sopenharmony_ci	if (need_clear == afs_cb_break_no_promise)
66262306a36Sopenharmony_ci		vnode->cb_v_break = vnode->volume->cb_v_break;
66362306a36Sopenharmony_ci	else if (cb_break == vnode->cb_break)
66462306a36Sopenharmony_ci		__afs_break_callback(vnode, need_clear);
66562306a36Sopenharmony_ci	else
66662306a36Sopenharmony_ci		trace_afs_cb_miss(&vnode->fid, need_clear);
66762306a36Sopenharmony_ci	write_sequnlock(&vnode->cb_lock);
66862306a36Sopenharmony_ci	return false;
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci/*
67262306a36Sopenharmony_ci * Returns true if the pagecache is still valid.  Does not sleep.
67362306a36Sopenharmony_ci */
67462306a36Sopenharmony_cibool afs_pagecache_valid(struct afs_vnode *vnode)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	if (unlikely(test_bit(AFS_VNODE_DELETED, &vnode->flags))) {
67762306a36Sopenharmony_ci		if (vnode->netfs.inode.i_nlink)
67862306a36Sopenharmony_ci			clear_nlink(&vnode->netfs.inode);
67962306a36Sopenharmony_ci		return true;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags) &&
68362306a36Sopenharmony_ci	    afs_check_validity(vnode))
68462306a36Sopenharmony_ci		return true;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return false;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci/*
69062306a36Sopenharmony_ci * validate a vnode/inode
69162306a36Sopenharmony_ci * - there are several things we need to check
69262306a36Sopenharmony_ci *   - parent dir data changes (rm, rmdir, rename, mkdir, create, link,
69362306a36Sopenharmony_ci *     symlink)
69462306a36Sopenharmony_ci *   - parent dir metadata changed (security changes)
69562306a36Sopenharmony_ci *   - dentry data changed (write, truncate)
69662306a36Sopenharmony_ci *   - dentry metadata changed (security changes)
69762306a36Sopenharmony_ci */
69862306a36Sopenharmony_ciint afs_validate(struct afs_vnode *vnode, struct key *key)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	int ret;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	_enter("{v={%llx:%llu} fl=%lx},%x",
70362306a36Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode, vnode->flags,
70462306a36Sopenharmony_ci	       key_serial(key));
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (afs_pagecache_valid(vnode))
70762306a36Sopenharmony_ci		goto valid;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	down_write(&vnode->validate_lock);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* if the promise has expired, we need to check the server again to get
71262306a36Sopenharmony_ci	 * a new promise - note that if the (parent) directory's metadata was
71362306a36Sopenharmony_ci	 * changed then the security may be different and we may no longer have
71462306a36Sopenharmony_ci	 * access */
71562306a36Sopenharmony_ci	if (!test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
71662306a36Sopenharmony_ci		_debug("not promised");
71762306a36Sopenharmony_ci		ret = afs_fetch_status(vnode, key, false, NULL);
71862306a36Sopenharmony_ci		if (ret < 0) {
71962306a36Sopenharmony_ci			if (ret == -ENOENT) {
72062306a36Sopenharmony_ci				set_bit(AFS_VNODE_DELETED, &vnode->flags);
72162306a36Sopenharmony_ci				ret = -ESTALE;
72262306a36Sopenharmony_ci			}
72362306a36Sopenharmony_ci			goto error_unlock;
72462306a36Sopenharmony_ci		}
72562306a36Sopenharmony_ci		_debug("new promise [fl=%lx]", vnode->flags);
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
72962306a36Sopenharmony_ci		_debug("file already deleted");
73062306a36Sopenharmony_ci		ret = -ESTALE;
73162306a36Sopenharmony_ci		goto error_unlock;
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	/* if the vnode's data version number changed then its contents are
73562306a36Sopenharmony_ci	 * different */
73662306a36Sopenharmony_ci	if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags))
73762306a36Sopenharmony_ci		afs_zap_data(vnode);
73862306a36Sopenharmony_ci	up_write(&vnode->validate_lock);
73962306a36Sopenharmony_civalid:
74062306a36Sopenharmony_ci	_leave(" = 0");
74162306a36Sopenharmony_ci	return 0;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_cierror_unlock:
74462306a36Sopenharmony_ci	up_write(&vnode->validate_lock);
74562306a36Sopenharmony_ci	_leave(" = %d", ret);
74662306a36Sopenharmony_ci	return ret;
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci/*
75062306a36Sopenharmony_ci * read the attributes of an inode
75162306a36Sopenharmony_ci */
75262306a36Sopenharmony_ciint afs_getattr(struct mnt_idmap *idmap, const struct path *path,
75362306a36Sopenharmony_ci		struct kstat *stat, u32 request_mask, unsigned int query_flags)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	struct inode *inode = d_inode(path->dentry);
75662306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(inode);
75762306a36Sopenharmony_ci	struct key *key;
75862306a36Sopenharmony_ci	int ret, seq = 0;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	_enter("{ ino=%lu v=%u }", inode->i_ino, inode->i_generation);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	if (vnode->volume &&
76362306a36Sopenharmony_ci	    !(query_flags & AT_STATX_DONT_SYNC) &&
76462306a36Sopenharmony_ci	    !test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
76562306a36Sopenharmony_ci		key = afs_request_key(vnode->volume->cell);
76662306a36Sopenharmony_ci		if (IS_ERR(key))
76762306a36Sopenharmony_ci			return PTR_ERR(key);
76862306a36Sopenharmony_ci		ret = afs_validate(vnode, key);
76962306a36Sopenharmony_ci		key_put(key);
77062306a36Sopenharmony_ci		if (ret < 0)
77162306a36Sopenharmony_ci			return ret;
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	do {
77562306a36Sopenharmony_ci		read_seqbegin_or_lock(&vnode->cb_lock, &seq);
77662306a36Sopenharmony_ci		generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
77762306a36Sopenharmony_ci		if (test_bit(AFS_VNODE_SILLY_DELETED, &vnode->flags) &&
77862306a36Sopenharmony_ci		    stat->nlink > 0)
77962306a36Sopenharmony_ci			stat->nlink -= 1;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		/* Lie about the size of directories.  We maintain a locally
78262306a36Sopenharmony_ci		 * edited copy and may make different allocation decisions on
78362306a36Sopenharmony_ci		 * it, but we need to give userspace the server's size.
78462306a36Sopenharmony_ci		 */
78562306a36Sopenharmony_ci		if (S_ISDIR(inode->i_mode))
78662306a36Sopenharmony_ci			stat->size = vnode->netfs.remote_i_size;
78762306a36Sopenharmony_ci	} while (need_seqretry(&vnode->cb_lock, seq));
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	done_seqretry(&vnode->cb_lock, seq);
79062306a36Sopenharmony_ci	return 0;
79162306a36Sopenharmony_ci}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci/*
79462306a36Sopenharmony_ci * discard an AFS inode
79562306a36Sopenharmony_ci */
79662306a36Sopenharmony_ciint afs_drop_inode(struct inode *inode)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	_enter("");
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	if (test_bit(AFS_VNODE_PSEUDODIR, &AFS_FS_I(inode)->flags))
80162306a36Sopenharmony_ci		return generic_delete_inode(inode);
80262306a36Sopenharmony_ci	else
80362306a36Sopenharmony_ci		return generic_drop_inode(inode);
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci/*
80762306a36Sopenharmony_ci * clear an AFS inode
80862306a36Sopenharmony_ci */
80962306a36Sopenharmony_civoid afs_evict_inode(struct inode *inode)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	struct afs_vnode_cache_aux aux;
81262306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(inode);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	_enter("{%llx:%llu.%d}",
81562306a36Sopenharmony_ci	       vnode->fid.vid,
81662306a36Sopenharmony_ci	       vnode->fid.vnode,
81762306a36Sopenharmony_ci	       vnode->fid.unique);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	_debug("CLEAR INODE %p", inode);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	ASSERTCMP(inode->i_ino, ==, vnode->fid.vnode);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	truncate_inode_pages_final(&inode->i_data);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	afs_set_cache_aux(vnode, &aux);
82662306a36Sopenharmony_ci	fscache_clear_inode_writeback(afs_vnode_cache(vnode), inode, &aux);
82762306a36Sopenharmony_ci	clear_inode(inode);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	while (!list_empty(&vnode->wb_keys)) {
83062306a36Sopenharmony_ci		struct afs_wb_key *wbk = list_entry(vnode->wb_keys.next,
83162306a36Sopenharmony_ci						    struct afs_wb_key, vnode_link);
83262306a36Sopenharmony_ci		list_del(&wbk->vnode_link);
83362306a36Sopenharmony_ci		afs_put_wb_key(wbk);
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	fscache_relinquish_cookie(afs_vnode_cache(vnode),
83762306a36Sopenharmony_ci				  test_bit(AFS_VNODE_DELETED, &vnode->flags));
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	afs_prune_wb_keys(vnode);
84062306a36Sopenharmony_ci	afs_put_permits(rcu_access_pointer(vnode->permit_cache));
84162306a36Sopenharmony_ci	key_put(vnode->silly_key);
84262306a36Sopenharmony_ci	vnode->silly_key = NULL;
84362306a36Sopenharmony_ci	key_put(vnode->lock_key);
84462306a36Sopenharmony_ci	vnode->lock_key = NULL;
84562306a36Sopenharmony_ci	_leave("");
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_cistatic void afs_setattr_success(struct afs_operation *op)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	struct afs_vnode_param *vp = &op->file[0];
85162306a36Sopenharmony_ci	struct inode *inode = &vp->vnode->netfs.inode;
85262306a36Sopenharmony_ci	loff_t old_i_size = i_size_read(inode);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	op->setattr.old_i_size = old_i_size;
85562306a36Sopenharmony_ci	afs_vnode_commit_status(op, vp);
85662306a36Sopenharmony_ci	/* inode->i_size has now been changed. */
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	if (op->setattr.attr->ia_valid & ATTR_SIZE) {
85962306a36Sopenharmony_ci		loff_t size = op->setattr.attr->ia_size;
86062306a36Sopenharmony_ci		if (size > old_i_size)
86162306a36Sopenharmony_ci			pagecache_isize_extended(inode, old_i_size, size);
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_cistatic void afs_setattr_edit_file(struct afs_operation *op)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	struct afs_vnode_param *vp = &op->file[0];
86862306a36Sopenharmony_ci	struct inode *inode = &vp->vnode->netfs.inode;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	if (op->setattr.attr->ia_valid & ATTR_SIZE) {
87162306a36Sopenharmony_ci		loff_t size = op->setattr.attr->ia_size;
87262306a36Sopenharmony_ci		loff_t i_size = op->setattr.old_i_size;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci		if (size < i_size)
87562306a36Sopenharmony_ci			truncate_pagecache(inode, size);
87662306a36Sopenharmony_ci		if (size != i_size)
87762306a36Sopenharmony_ci			fscache_resize_cookie(afs_vnode_cache(vp->vnode),
87862306a36Sopenharmony_ci					      vp->scb.status.size);
87962306a36Sopenharmony_ci	}
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistatic const struct afs_operation_ops afs_setattr_operation = {
88362306a36Sopenharmony_ci	.issue_afs_rpc	= afs_fs_setattr,
88462306a36Sopenharmony_ci	.issue_yfs_rpc	= yfs_fs_setattr,
88562306a36Sopenharmony_ci	.success	= afs_setattr_success,
88662306a36Sopenharmony_ci	.edit_dir	= afs_setattr_edit_file,
88762306a36Sopenharmony_ci};
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci/*
89062306a36Sopenharmony_ci * set the attributes of an inode
89162306a36Sopenharmony_ci */
89262306a36Sopenharmony_ciint afs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
89362306a36Sopenharmony_ci		struct iattr *attr)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	const unsigned int supported =
89662306a36Sopenharmony_ci		ATTR_SIZE | ATTR_MODE | ATTR_UID | ATTR_GID |
89762306a36Sopenharmony_ci		ATTR_MTIME | ATTR_MTIME_SET | ATTR_TIMES_SET | ATTR_TOUCH;
89862306a36Sopenharmony_ci	struct afs_operation *op;
89962306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
90062306a36Sopenharmony_ci	struct inode *inode = &vnode->netfs.inode;
90162306a36Sopenharmony_ci	loff_t i_size;
90262306a36Sopenharmony_ci	int ret;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	_enter("{%llx:%llu},{n=%pd},%x",
90562306a36Sopenharmony_ci	       vnode->fid.vid, vnode->fid.vnode, dentry,
90662306a36Sopenharmony_ci	       attr->ia_valid);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	if (!(attr->ia_valid & supported)) {
90962306a36Sopenharmony_ci		_leave(" = 0 [unsupported]");
91062306a36Sopenharmony_ci		return 0;
91162306a36Sopenharmony_ci	}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	i_size = i_size_read(inode);
91462306a36Sopenharmony_ci	if (attr->ia_valid & ATTR_SIZE) {
91562306a36Sopenharmony_ci		if (!S_ISREG(inode->i_mode))
91662306a36Sopenharmony_ci			return -EISDIR;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci		ret = inode_newsize_ok(inode, attr->ia_size);
91962306a36Sopenharmony_ci		if (ret)
92062306a36Sopenharmony_ci			return ret;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		if (attr->ia_size == i_size)
92362306a36Sopenharmony_ci			attr->ia_valid &= ~ATTR_SIZE;
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	fscache_use_cookie(afs_vnode_cache(vnode), true);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	/* Prevent any new writebacks from starting whilst we do this. */
92962306a36Sopenharmony_ci	down_write(&vnode->validate_lock);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) {
93262306a36Sopenharmony_ci		loff_t size = attr->ia_size;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci		/* Wait for any outstanding writes to the server to complete */
93562306a36Sopenharmony_ci		loff_t from = min(size, i_size);
93662306a36Sopenharmony_ci		loff_t to = max(size, i_size);
93762306a36Sopenharmony_ci		ret = filemap_fdatawait_range(inode->i_mapping, from, to);
93862306a36Sopenharmony_ci		if (ret < 0)
93962306a36Sopenharmony_ci			goto out_unlock;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci		/* Don't talk to the server if we're just shortening in-memory
94262306a36Sopenharmony_ci		 * writes that haven't gone to the server yet.
94362306a36Sopenharmony_ci		 */
94462306a36Sopenharmony_ci		if (!(attr->ia_valid & (supported & ~ATTR_SIZE & ~ATTR_MTIME)) &&
94562306a36Sopenharmony_ci		    attr->ia_size < i_size &&
94662306a36Sopenharmony_ci		    attr->ia_size > vnode->status.size) {
94762306a36Sopenharmony_ci			truncate_pagecache(inode, attr->ia_size);
94862306a36Sopenharmony_ci			fscache_resize_cookie(afs_vnode_cache(vnode),
94962306a36Sopenharmony_ci					      attr->ia_size);
95062306a36Sopenharmony_ci			i_size_write(inode, attr->ia_size);
95162306a36Sopenharmony_ci			ret = 0;
95262306a36Sopenharmony_ci			goto out_unlock;
95362306a36Sopenharmony_ci		}
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	op = afs_alloc_operation(((attr->ia_valid & ATTR_FILE) ?
95762306a36Sopenharmony_ci				  afs_file_key(attr->ia_file) : NULL),
95862306a36Sopenharmony_ci				 vnode->volume);
95962306a36Sopenharmony_ci	if (IS_ERR(op)) {
96062306a36Sopenharmony_ci		ret = PTR_ERR(op);
96162306a36Sopenharmony_ci		goto out_unlock;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	afs_op_set_vnode(op, 0, vnode);
96562306a36Sopenharmony_ci	op->setattr.attr = attr;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if (attr->ia_valid & ATTR_SIZE) {
96862306a36Sopenharmony_ci		op->file[0].dv_delta = 1;
96962306a36Sopenharmony_ci		op->file[0].set_size = true;
97062306a36Sopenharmony_ci	}
97162306a36Sopenharmony_ci	op->ctime = attr->ia_ctime;
97262306a36Sopenharmony_ci	op->file[0].update_ctime = 1;
97362306a36Sopenharmony_ci	op->file[0].modification = true;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	op->ops = &afs_setattr_operation;
97662306a36Sopenharmony_ci	ret = afs_do_sync_operation(op);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ciout_unlock:
97962306a36Sopenharmony_ci	up_write(&vnode->validate_lock);
98062306a36Sopenharmony_ci	fscache_unuse_cookie(afs_vnode_cache(vnode), NULL, NULL);
98162306a36Sopenharmony_ci	_leave(" = %d", ret);
98262306a36Sopenharmony_ci	return ret;
98362306a36Sopenharmony_ci}
984