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