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