162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/ceph/ceph_debug.h> 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/spinlock.h> 562306a36Sopenharmony_ci#include <linux/namei.h> 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci#include <linux/sched.h> 862306a36Sopenharmony_ci#include <linux/xattr.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "super.h" 1162306a36Sopenharmony_ci#include "mds_client.h" 1262306a36Sopenharmony_ci#include "crypto.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Directory operations: readdir, lookup, create, link, unlink, 1662306a36Sopenharmony_ci * rename, etc. 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * Ceph MDS operations are specified in terms of a base ino and 2162306a36Sopenharmony_ci * relative path. Thus, the client can specify an operation on a 2262306a36Sopenharmony_ci * specific inode (e.g., a getattr due to fstat(2)), or as a path 2362306a36Sopenharmony_ci * relative to, say, the root directory. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * Normally, we limit ourselves to strict inode ops (no path component) 2662306a36Sopenharmony_ci * or dentry operations (a single path component relative to an ino). The 2762306a36Sopenharmony_ci * exception to this is open_root_dentry(), which will open the mount 2862306a36Sopenharmony_ci * point by name. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciconst struct dentry_operations ceph_dentry_ops; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic bool __dentry_lease_is_valid(struct ceph_dentry_info *di); 3462306a36Sopenharmony_cistatic int __dir_lease_try_check(const struct dentry *dentry); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * Initialize ceph dentry state. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_cistatic int ceph_d_init(struct dentry *dentry) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct ceph_dentry_info *di; 4262306a36Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dentry->d_sb); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci di = kmem_cache_zalloc(ceph_dentry_cachep, GFP_KERNEL); 4562306a36Sopenharmony_ci if (!di) 4662306a36Sopenharmony_ci return -ENOMEM; /* oh well */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci di->dentry = dentry; 4962306a36Sopenharmony_ci di->lease_session = NULL; 5062306a36Sopenharmony_ci di->time = jiffies; 5162306a36Sopenharmony_ci dentry->d_fsdata = di; 5262306a36Sopenharmony_ci INIT_LIST_HEAD(&di->lease_list); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci atomic64_inc(&mdsc->metric.total_dentries); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* 6062306a36Sopenharmony_ci * for f_pos for readdir: 6162306a36Sopenharmony_ci * - hash order: 6262306a36Sopenharmony_ci * (0xff << 52) | ((24 bits hash) << 28) | 6362306a36Sopenharmony_ci * (the nth entry has hash collision); 6462306a36Sopenharmony_ci * - frag+name order; 6562306a36Sopenharmony_ci * ((frag value) << 28) | (the nth entry in frag); 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci#define OFFSET_BITS 28 6862306a36Sopenharmony_ci#define OFFSET_MASK ((1 << OFFSET_BITS) - 1) 6962306a36Sopenharmony_ci#define HASH_ORDER (0xffull << (OFFSET_BITS + 24)) 7062306a36Sopenharmony_ciloff_t ceph_make_fpos(unsigned high, unsigned off, bool hash_order) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci loff_t fpos = ((loff_t)high << 28) | (loff_t)off; 7362306a36Sopenharmony_ci if (hash_order) 7462306a36Sopenharmony_ci fpos |= HASH_ORDER; 7562306a36Sopenharmony_ci return fpos; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic bool is_hash_order(loff_t p) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci return (p & HASH_ORDER) == HASH_ORDER; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic unsigned fpos_frag(loff_t p) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci return p >> OFFSET_BITS; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic unsigned fpos_hash(loff_t p) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci return ceph_frag_value(fpos_frag(p)); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic unsigned fpos_off(loff_t p) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci return p & OFFSET_MASK; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int fpos_cmp(loff_t l, loff_t r) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci int v = ceph_frag_compare(fpos_frag(l), fpos_frag(r)); 10162306a36Sopenharmony_ci if (v) 10262306a36Sopenharmony_ci return v; 10362306a36Sopenharmony_ci return (int)(fpos_off(l) - fpos_off(r)); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * make note of the last dentry we read, so we can 10862306a36Sopenharmony_ci * continue at the same lexicographical point, 10962306a36Sopenharmony_ci * regardless of what dir changes take place on the 11062306a36Sopenharmony_ci * server. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_cistatic int note_last_dentry(struct ceph_dir_file_info *dfi, const char *name, 11362306a36Sopenharmony_ci int len, unsigned next_offset) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci char *buf = kmalloc(len+1, GFP_KERNEL); 11662306a36Sopenharmony_ci if (!buf) 11762306a36Sopenharmony_ci return -ENOMEM; 11862306a36Sopenharmony_ci kfree(dfi->last_name); 11962306a36Sopenharmony_ci dfi->last_name = buf; 12062306a36Sopenharmony_ci memcpy(dfi->last_name, name, len); 12162306a36Sopenharmony_ci dfi->last_name[len] = 0; 12262306a36Sopenharmony_ci dfi->next_offset = next_offset; 12362306a36Sopenharmony_ci dout("note_last_dentry '%s'\n", dfi->last_name); 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic struct dentry * 12962306a36Sopenharmony_ci__dcache_find_get_entry(struct dentry *parent, u64 idx, 13062306a36Sopenharmony_ci struct ceph_readdir_cache_control *cache_ctl) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct inode *dir = d_inode(parent); 13362306a36Sopenharmony_ci struct dentry *dentry; 13462306a36Sopenharmony_ci unsigned idx_mask = (PAGE_SIZE / sizeof(struct dentry *)) - 1; 13562306a36Sopenharmony_ci loff_t ptr_pos = idx * sizeof(struct dentry *); 13662306a36Sopenharmony_ci pgoff_t ptr_pgoff = ptr_pos >> PAGE_SHIFT; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (ptr_pos >= i_size_read(dir)) 13962306a36Sopenharmony_ci return NULL; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (!cache_ctl->page || ptr_pgoff != page_index(cache_ctl->page)) { 14262306a36Sopenharmony_ci ceph_readdir_cache_release(cache_ctl); 14362306a36Sopenharmony_ci cache_ctl->page = find_lock_page(&dir->i_data, ptr_pgoff); 14462306a36Sopenharmony_ci if (!cache_ctl->page) { 14562306a36Sopenharmony_ci dout(" page %lu not found\n", ptr_pgoff); 14662306a36Sopenharmony_ci return ERR_PTR(-EAGAIN); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci /* reading/filling the cache are serialized by 14962306a36Sopenharmony_ci i_rwsem, no need to use page lock */ 15062306a36Sopenharmony_ci unlock_page(cache_ctl->page); 15162306a36Sopenharmony_ci cache_ctl->dentries = kmap(cache_ctl->page); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci cache_ctl->index = idx & idx_mask; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci rcu_read_lock(); 15762306a36Sopenharmony_ci spin_lock(&parent->d_lock); 15862306a36Sopenharmony_ci /* check i_size again here, because empty directory can be 15962306a36Sopenharmony_ci * marked as complete while not holding the i_rwsem. */ 16062306a36Sopenharmony_ci if (ceph_dir_is_complete_ordered(dir) && ptr_pos < i_size_read(dir)) 16162306a36Sopenharmony_ci dentry = cache_ctl->dentries[cache_ctl->index]; 16262306a36Sopenharmony_ci else 16362306a36Sopenharmony_ci dentry = NULL; 16462306a36Sopenharmony_ci spin_unlock(&parent->d_lock); 16562306a36Sopenharmony_ci if (dentry && !lockref_get_not_dead(&dentry->d_lockref)) 16662306a36Sopenharmony_ci dentry = NULL; 16762306a36Sopenharmony_ci rcu_read_unlock(); 16862306a36Sopenharmony_ci return dentry ? : ERR_PTR(-EAGAIN); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* 17262306a36Sopenharmony_ci * When possible, we try to satisfy a readdir by peeking at the 17362306a36Sopenharmony_ci * dcache. We make this work by carefully ordering dentries on 17462306a36Sopenharmony_ci * d_child when we initially get results back from the MDS, and 17562306a36Sopenharmony_ci * falling back to a "normal" sync readdir if any dentries in the dir 17662306a36Sopenharmony_ci * are dropped. 17762306a36Sopenharmony_ci * 17862306a36Sopenharmony_ci * Complete dir indicates that we have all dentries in the dir. It is 17962306a36Sopenharmony_ci * defined IFF we hold CEPH_CAP_FILE_SHARED (which will be revoked by 18062306a36Sopenharmony_ci * the MDS if/when the directory is modified). 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_cistatic int __dcache_readdir(struct file *file, struct dir_context *ctx, 18362306a36Sopenharmony_ci int shared_gen) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct ceph_dir_file_info *dfi = file->private_data; 18662306a36Sopenharmony_ci struct dentry *parent = file->f_path.dentry; 18762306a36Sopenharmony_ci struct inode *dir = d_inode(parent); 18862306a36Sopenharmony_ci struct dentry *dentry, *last = NULL; 18962306a36Sopenharmony_ci struct ceph_dentry_info *di; 19062306a36Sopenharmony_ci struct ceph_readdir_cache_control cache_ctl = {}; 19162306a36Sopenharmony_ci u64 idx = 0; 19262306a36Sopenharmony_ci int err = 0; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci dout("__dcache_readdir %p v%u at %llx\n", dir, (unsigned)shared_gen, ctx->pos); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* search start position */ 19762306a36Sopenharmony_ci if (ctx->pos > 2) { 19862306a36Sopenharmony_ci u64 count = div_u64(i_size_read(dir), sizeof(struct dentry *)); 19962306a36Sopenharmony_ci while (count > 0) { 20062306a36Sopenharmony_ci u64 step = count >> 1; 20162306a36Sopenharmony_ci dentry = __dcache_find_get_entry(parent, idx + step, 20262306a36Sopenharmony_ci &cache_ctl); 20362306a36Sopenharmony_ci if (!dentry) { 20462306a36Sopenharmony_ci /* use linar search */ 20562306a36Sopenharmony_ci idx = 0; 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci if (IS_ERR(dentry)) { 20962306a36Sopenharmony_ci err = PTR_ERR(dentry); 21062306a36Sopenharmony_ci goto out; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci di = ceph_dentry(dentry); 21362306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 21462306a36Sopenharmony_ci if (fpos_cmp(di->offset, ctx->pos) < 0) { 21562306a36Sopenharmony_ci idx += step + 1; 21662306a36Sopenharmony_ci count -= step + 1; 21762306a36Sopenharmony_ci } else { 21862306a36Sopenharmony_ci count = step; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 22162306a36Sopenharmony_ci dput(dentry); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci dout("__dcache_readdir %p cache idx %llu\n", dir, idx); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci for (;;) { 22962306a36Sopenharmony_ci bool emit_dentry = false; 23062306a36Sopenharmony_ci dentry = __dcache_find_get_entry(parent, idx++, &cache_ctl); 23162306a36Sopenharmony_ci if (!dentry) { 23262306a36Sopenharmony_ci dfi->file_info.flags |= CEPH_F_ATEND; 23362306a36Sopenharmony_ci err = 0; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci if (IS_ERR(dentry)) { 23762306a36Sopenharmony_ci err = PTR_ERR(dentry); 23862306a36Sopenharmony_ci goto out; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 24262306a36Sopenharmony_ci di = ceph_dentry(dentry); 24362306a36Sopenharmony_ci if (d_unhashed(dentry) || 24462306a36Sopenharmony_ci d_really_is_negative(dentry) || 24562306a36Sopenharmony_ci di->lease_shared_gen != shared_gen || 24662306a36Sopenharmony_ci ((dentry->d_flags & DCACHE_NOKEY_NAME) && 24762306a36Sopenharmony_ci fscrypt_has_encryption_key(dir))) { 24862306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 24962306a36Sopenharmony_ci dput(dentry); 25062306a36Sopenharmony_ci err = -EAGAIN; 25162306a36Sopenharmony_ci goto out; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci if (fpos_cmp(ctx->pos, di->offset) <= 0) { 25462306a36Sopenharmony_ci __ceph_dentry_dir_lease_touch(di); 25562306a36Sopenharmony_ci emit_dentry = true; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (emit_dentry) { 26062306a36Sopenharmony_ci dout(" %llx dentry %p %pd %p\n", di->offset, 26162306a36Sopenharmony_ci dentry, dentry, d_inode(dentry)); 26262306a36Sopenharmony_ci ctx->pos = di->offset; 26362306a36Sopenharmony_ci if (!dir_emit(ctx, dentry->d_name.name, 26462306a36Sopenharmony_ci dentry->d_name.len, ceph_present_inode(d_inode(dentry)), 26562306a36Sopenharmony_ci d_inode(dentry)->i_mode >> 12)) { 26662306a36Sopenharmony_ci dput(dentry); 26762306a36Sopenharmony_ci err = 0; 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci ctx->pos++; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (last) 27362306a36Sopenharmony_ci dput(last); 27462306a36Sopenharmony_ci last = dentry; 27562306a36Sopenharmony_ci } else { 27662306a36Sopenharmony_ci dput(dentry); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ciout: 28062306a36Sopenharmony_ci ceph_readdir_cache_release(&cache_ctl); 28162306a36Sopenharmony_ci if (last) { 28262306a36Sopenharmony_ci int ret; 28362306a36Sopenharmony_ci di = ceph_dentry(last); 28462306a36Sopenharmony_ci ret = note_last_dentry(dfi, last->d_name.name, last->d_name.len, 28562306a36Sopenharmony_ci fpos_off(di->offset) + 1); 28662306a36Sopenharmony_ci if (ret < 0) 28762306a36Sopenharmony_ci err = ret; 28862306a36Sopenharmony_ci dput(last); 28962306a36Sopenharmony_ci /* last_name no longer match cache index */ 29062306a36Sopenharmony_ci if (dfi->readdir_cache_idx >= 0) { 29162306a36Sopenharmony_ci dfi->readdir_cache_idx = -1; 29262306a36Sopenharmony_ci dfi->dir_release_count = 0; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci return err; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic bool need_send_readdir(struct ceph_dir_file_info *dfi, loff_t pos) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci if (!dfi->last_readdir) 30162306a36Sopenharmony_ci return true; 30262306a36Sopenharmony_ci if (is_hash_order(pos)) 30362306a36Sopenharmony_ci return !ceph_frag_contains_value(dfi->frag, fpos_hash(pos)); 30462306a36Sopenharmony_ci else 30562306a36Sopenharmony_ci return dfi->frag != fpos_frag(pos); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int ceph_readdir(struct file *file, struct dir_context *ctx) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct ceph_dir_file_info *dfi = file->private_data; 31162306a36Sopenharmony_ci struct inode *inode = file_inode(file); 31262306a36Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(inode); 31362306a36Sopenharmony_ci struct ceph_fs_client *fsc = ceph_inode_to_client(inode); 31462306a36Sopenharmony_ci struct ceph_mds_client *mdsc = fsc->mdsc; 31562306a36Sopenharmony_ci int i; 31662306a36Sopenharmony_ci int err; 31762306a36Sopenharmony_ci unsigned frag = -1; 31862306a36Sopenharmony_ci struct ceph_mds_reply_info_parsed *rinfo; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci dout("readdir %p file %p pos %llx\n", inode, file, ctx->pos); 32162306a36Sopenharmony_ci if (dfi->file_info.flags & CEPH_F_ATEND) 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* always start with . and .. */ 32562306a36Sopenharmony_ci if (ctx->pos == 0) { 32662306a36Sopenharmony_ci dout("readdir off 0 -> '.'\n"); 32762306a36Sopenharmony_ci if (!dir_emit(ctx, ".", 1, ceph_present_inode(inode), 32862306a36Sopenharmony_ci inode->i_mode >> 12)) 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci ctx->pos = 1; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci if (ctx->pos == 1) { 33362306a36Sopenharmony_ci u64 ino; 33462306a36Sopenharmony_ci struct dentry *dentry = file->f_path.dentry; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 33762306a36Sopenharmony_ci ino = ceph_present_inode(dentry->d_parent->d_inode); 33862306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci dout("readdir off 1 -> '..'\n"); 34162306a36Sopenharmony_ci if (!dir_emit(ctx, "..", 2, ino, inode->i_mode >> 12)) 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci ctx->pos = 2; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci err = ceph_fscrypt_prepare_readdir(inode); 34762306a36Sopenharmony_ci if (err < 0) 34862306a36Sopenharmony_ci return err; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 35162306a36Sopenharmony_ci /* request Fx cap. if have Fx, we don't need to release Fs cap 35262306a36Sopenharmony_ci * for later create/unlink. */ 35362306a36Sopenharmony_ci __ceph_touch_fmode(ci, mdsc, CEPH_FILE_MODE_WR); 35462306a36Sopenharmony_ci /* can we use the dcache? */ 35562306a36Sopenharmony_ci if (ceph_test_mount_opt(fsc, DCACHE) && 35662306a36Sopenharmony_ci !ceph_test_mount_opt(fsc, NOASYNCREADDIR) && 35762306a36Sopenharmony_ci ceph_snap(inode) != CEPH_SNAPDIR && 35862306a36Sopenharmony_ci __ceph_dir_is_complete_ordered(ci) && 35962306a36Sopenharmony_ci __ceph_caps_issued_mask_metric(ci, CEPH_CAP_FILE_SHARED, 1)) { 36062306a36Sopenharmony_ci int shared_gen = atomic_read(&ci->i_shared_gen); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 36362306a36Sopenharmony_ci err = __dcache_readdir(file, ctx, shared_gen); 36462306a36Sopenharmony_ci if (err != -EAGAIN) 36562306a36Sopenharmony_ci return err; 36662306a36Sopenharmony_ci } else { 36762306a36Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* proceed with a normal readdir */ 37162306a36Sopenharmony_cimore: 37262306a36Sopenharmony_ci /* do we have the correct frag content buffered? */ 37362306a36Sopenharmony_ci if (need_send_readdir(dfi, ctx->pos)) { 37462306a36Sopenharmony_ci struct ceph_mds_request *req; 37562306a36Sopenharmony_ci int op = ceph_snap(inode) == CEPH_SNAPDIR ? 37662306a36Sopenharmony_ci CEPH_MDS_OP_LSSNAP : CEPH_MDS_OP_READDIR; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* discard old result, if any */ 37962306a36Sopenharmony_ci if (dfi->last_readdir) { 38062306a36Sopenharmony_ci ceph_mdsc_put_request(dfi->last_readdir); 38162306a36Sopenharmony_ci dfi->last_readdir = NULL; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (is_hash_order(ctx->pos)) { 38562306a36Sopenharmony_ci /* fragtree isn't always accurate. choose frag 38662306a36Sopenharmony_ci * based on previous reply when possible. */ 38762306a36Sopenharmony_ci if (frag == (unsigned)-1) 38862306a36Sopenharmony_ci frag = ceph_choose_frag(ci, fpos_hash(ctx->pos), 38962306a36Sopenharmony_ci NULL, NULL); 39062306a36Sopenharmony_ci } else { 39162306a36Sopenharmony_ci frag = fpos_frag(ctx->pos); 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci dout("readdir fetching %llx.%llx frag %x offset '%s'\n", 39562306a36Sopenharmony_ci ceph_vinop(inode), frag, dfi->last_name); 39662306a36Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); 39762306a36Sopenharmony_ci if (IS_ERR(req)) 39862306a36Sopenharmony_ci return PTR_ERR(req); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci err = ceph_alloc_readdir_reply_buffer(req, inode); 40162306a36Sopenharmony_ci if (err) { 40262306a36Sopenharmony_ci ceph_mdsc_put_request(req); 40362306a36Sopenharmony_ci return err; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci /* hints to request -> mds selection code */ 40662306a36Sopenharmony_ci req->r_direct_mode = USE_AUTH_MDS; 40762306a36Sopenharmony_ci if (op == CEPH_MDS_OP_READDIR) { 40862306a36Sopenharmony_ci req->r_direct_hash = ceph_frag_value(frag); 40962306a36Sopenharmony_ci __set_bit(CEPH_MDS_R_DIRECT_IS_HASH, &req->r_req_flags); 41062306a36Sopenharmony_ci req->r_inode_drop = CEPH_CAP_FILE_EXCL; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci if (dfi->last_name) { 41362306a36Sopenharmony_ci struct qstr d_name = { .name = dfi->last_name, 41462306a36Sopenharmony_ci .len = strlen(dfi->last_name) }; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci req->r_path2 = kzalloc(NAME_MAX + 1, GFP_KERNEL); 41762306a36Sopenharmony_ci if (!req->r_path2) { 41862306a36Sopenharmony_ci ceph_mdsc_put_request(req); 41962306a36Sopenharmony_ci return -ENOMEM; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci err = ceph_encode_encrypted_dname(inode, &d_name, 42362306a36Sopenharmony_ci req->r_path2); 42462306a36Sopenharmony_ci if (err < 0) { 42562306a36Sopenharmony_ci ceph_mdsc_put_request(req); 42662306a36Sopenharmony_ci return err; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci } else if (is_hash_order(ctx->pos)) { 42962306a36Sopenharmony_ci req->r_args.readdir.offset_hash = 43062306a36Sopenharmony_ci cpu_to_le32(fpos_hash(ctx->pos)); 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci req->r_dir_release_cnt = dfi->dir_release_count; 43462306a36Sopenharmony_ci req->r_dir_ordered_cnt = dfi->dir_ordered_count; 43562306a36Sopenharmony_ci req->r_readdir_cache_idx = dfi->readdir_cache_idx; 43662306a36Sopenharmony_ci req->r_readdir_offset = dfi->next_offset; 43762306a36Sopenharmony_ci req->r_args.readdir.frag = cpu_to_le32(frag); 43862306a36Sopenharmony_ci req->r_args.readdir.flags = 43962306a36Sopenharmony_ci cpu_to_le16(CEPH_READDIR_REPLY_BITFLAGS); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci req->r_inode = inode; 44262306a36Sopenharmony_ci ihold(inode); 44362306a36Sopenharmony_ci req->r_dentry = dget(file->f_path.dentry); 44462306a36Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, NULL, req); 44562306a36Sopenharmony_ci if (err < 0) { 44662306a36Sopenharmony_ci ceph_mdsc_put_request(req); 44762306a36Sopenharmony_ci return err; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci dout("readdir got and parsed readdir result=%d on " 45062306a36Sopenharmony_ci "frag %x, end=%d, complete=%d, hash_order=%d\n", 45162306a36Sopenharmony_ci err, frag, 45262306a36Sopenharmony_ci (int)req->r_reply_info.dir_end, 45362306a36Sopenharmony_ci (int)req->r_reply_info.dir_complete, 45462306a36Sopenharmony_ci (int)req->r_reply_info.hash_order); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci rinfo = &req->r_reply_info; 45762306a36Sopenharmony_ci if (le32_to_cpu(rinfo->dir_dir->frag) != frag) { 45862306a36Sopenharmony_ci frag = le32_to_cpu(rinfo->dir_dir->frag); 45962306a36Sopenharmony_ci if (!rinfo->hash_order) { 46062306a36Sopenharmony_ci dfi->next_offset = req->r_readdir_offset; 46162306a36Sopenharmony_ci /* adjust ctx->pos to beginning of frag */ 46262306a36Sopenharmony_ci ctx->pos = ceph_make_fpos(frag, 46362306a36Sopenharmony_ci dfi->next_offset, 46462306a36Sopenharmony_ci false); 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci dfi->frag = frag; 46962306a36Sopenharmony_ci dfi->last_readdir = req; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (test_bit(CEPH_MDS_R_DID_PREPOPULATE, &req->r_req_flags)) { 47262306a36Sopenharmony_ci dfi->readdir_cache_idx = req->r_readdir_cache_idx; 47362306a36Sopenharmony_ci if (dfi->readdir_cache_idx < 0) { 47462306a36Sopenharmony_ci /* preclude from marking dir ordered */ 47562306a36Sopenharmony_ci dfi->dir_ordered_count = 0; 47662306a36Sopenharmony_ci } else if (ceph_frag_is_leftmost(frag) && 47762306a36Sopenharmony_ci dfi->next_offset == 2) { 47862306a36Sopenharmony_ci /* note dir version at start of readdir so 47962306a36Sopenharmony_ci * we can tell if any dentries get dropped */ 48062306a36Sopenharmony_ci dfi->dir_release_count = req->r_dir_release_cnt; 48162306a36Sopenharmony_ci dfi->dir_ordered_count = req->r_dir_ordered_cnt; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci } else { 48462306a36Sopenharmony_ci dout("readdir !did_prepopulate\n"); 48562306a36Sopenharmony_ci /* disable readdir cache */ 48662306a36Sopenharmony_ci dfi->readdir_cache_idx = -1; 48762306a36Sopenharmony_ci /* preclude from marking dir complete */ 48862306a36Sopenharmony_ci dfi->dir_release_count = 0; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* note next offset and last dentry name */ 49262306a36Sopenharmony_ci if (rinfo->dir_nr > 0) { 49362306a36Sopenharmony_ci struct ceph_mds_reply_dir_entry *rde = 49462306a36Sopenharmony_ci rinfo->dir_entries + (rinfo->dir_nr-1); 49562306a36Sopenharmony_ci unsigned next_offset = req->r_reply_info.dir_end ? 49662306a36Sopenharmony_ci 2 : (fpos_off(rde->offset) + 1); 49762306a36Sopenharmony_ci err = note_last_dentry(dfi, rde->name, rde->name_len, 49862306a36Sopenharmony_ci next_offset); 49962306a36Sopenharmony_ci if (err) { 50062306a36Sopenharmony_ci ceph_mdsc_put_request(dfi->last_readdir); 50162306a36Sopenharmony_ci dfi->last_readdir = NULL; 50262306a36Sopenharmony_ci return err; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci } else if (req->r_reply_info.dir_end) { 50562306a36Sopenharmony_ci dfi->next_offset = 2; 50662306a36Sopenharmony_ci /* keep last name */ 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci rinfo = &dfi->last_readdir->r_reply_info; 51162306a36Sopenharmony_ci dout("readdir frag %x num %d pos %llx chunk first %llx\n", 51262306a36Sopenharmony_ci dfi->frag, rinfo->dir_nr, ctx->pos, 51362306a36Sopenharmony_ci rinfo->dir_nr ? rinfo->dir_entries[0].offset : 0LL); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci i = 0; 51662306a36Sopenharmony_ci /* search start position */ 51762306a36Sopenharmony_ci if (rinfo->dir_nr > 0) { 51862306a36Sopenharmony_ci int step, nr = rinfo->dir_nr; 51962306a36Sopenharmony_ci while (nr > 0) { 52062306a36Sopenharmony_ci step = nr >> 1; 52162306a36Sopenharmony_ci if (rinfo->dir_entries[i + step].offset < ctx->pos) { 52262306a36Sopenharmony_ci i += step + 1; 52362306a36Sopenharmony_ci nr -= step + 1; 52462306a36Sopenharmony_ci } else { 52562306a36Sopenharmony_ci nr = step; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci for (; i < rinfo->dir_nr; i++) { 53062306a36Sopenharmony_ci struct ceph_mds_reply_dir_entry *rde = rinfo->dir_entries + i; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (rde->offset < ctx->pos) { 53362306a36Sopenharmony_ci pr_warn("%s: rde->offset 0x%llx ctx->pos 0x%llx\n", 53462306a36Sopenharmony_ci __func__, rde->offset, ctx->pos); 53562306a36Sopenharmony_ci return -EIO; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (WARN_ON_ONCE(!rde->inode.in)) 53962306a36Sopenharmony_ci return -EIO; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci ctx->pos = rde->offset; 54262306a36Sopenharmony_ci dout("readdir (%d/%d) -> %llx '%.*s' %p\n", 54362306a36Sopenharmony_ci i, rinfo->dir_nr, ctx->pos, 54462306a36Sopenharmony_ci rde->name_len, rde->name, &rde->inode.in); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (!dir_emit(ctx, rde->name, rde->name_len, 54762306a36Sopenharmony_ci ceph_present_ino(inode->i_sb, le64_to_cpu(rde->inode.in->ino)), 54862306a36Sopenharmony_ci le32_to_cpu(rde->inode.in->mode) >> 12)) { 54962306a36Sopenharmony_ci /* 55062306a36Sopenharmony_ci * NOTE: Here no need to put the 'dfi->last_readdir', 55162306a36Sopenharmony_ci * because when dir_emit stops us it's most likely 55262306a36Sopenharmony_ci * doesn't have enough memory, etc. So for next readdir 55362306a36Sopenharmony_ci * it will continue. 55462306a36Sopenharmony_ci */ 55562306a36Sopenharmony_ci dout("filldir stopping us...\n"); 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* Reset the lengths to their original allocated vals */ 56062306a36Sopenharmony_ci ctx->pos++; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ceph_mdsc_put_request(dfi->last_readdir); 56462306a36Sopenharmony_ci dfi->last_readdir = NULL; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (dfi->next_offset > 2) { 56762306a36Sopenharmony_ci frag = dfi->frag; 56862306a36Sopenharmony_ci goto more; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* more frags? */ 57262306a36Sopenharmony_ci if (!ceph_frag_is_rightmost(dfi->frag)) { 57362306a36Sopenharmony_ci frag = ceph_frag_next(dfi->frag); 57462306a36Sopenharmony_ci if (is_hash_order(ctx->pos)) { 57562306a36Sopenharmony_ci loff_t new_pos = ceph_make_fpos(ceph_frag_value(frag), 57662306a36Sopenharmony_ci dfi->next_offset, true); 57762306a36Sopenharmony_ci if (new_pos > ctx->pos) 57862306a36Sopenharmony_ci ctx->pos = new_pos; 57962306a36Sopenharmony_ci /* keep last_name */ 58062306a36Sopenharmony_ci } else { 58162306a36Sopenharmony_ci ctx->pos = ceph_make_fpos(frag, dfi->next_offset, 58262306a36Sopenharmony_ci false); 58362306a36Sopenharmony_ci kfree(dfi->last_name); 58462306a36Sopenharmony_ci dfi->last_name = NULL; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci dout("readdir next frag is %x\n", frag); 58762306a36Sopenharmony_ci goto more; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci dfi->file_info.flags |= CEPH_F_ATEND; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* 59262306a36Sopenharmony_ci * if dir_release_count still matches the dir, no dentries 59362306a36Sopenharmony_ci * were released during the whole readdir, and we should have 59462306a36Sopenharmony_ci * the complete dir contents in our cache. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci if (atomic64_read(&ci->i_release_count) == 59762306a36Sopenharmony_ci dfi->dir_release_count) { 59862306a36Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 59962306a36Sopenharmony_ci if (dfi->dir_ordered_count == 60062306a36Sopenharmony_ci atomic64_read(&ci->i_ordered_count)) { 60162306a36Sopenharmony_ci dout(" marking %p complete and ordered\n", inode); 60262306a36Sopenharmony_ci /* use i_size to track number of entries in 60362306a36Sopenharmony_ci * readdir cache */ 60462306a36Sopenharmony_ci BUG_ON(dfi->readdir_cache_idx < 0); 60562306a36Sopenharmony_ci i_size_write(inode, dfi->readdir_cache_idx * 60662306a36Sopenharmony_ci sizeof(struct dentry*)); 60762306a36Sopenharmony_ci } else { 60862306a36Sopenharmony_ci dout(" marking %p complete\n", inode); 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci __ceph_dir_set_complete(ci, dfi->dir_release_count, 61162306a36Sopenharmony_ci dfi->dir_ordered_count); 61262306a36Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci dout("readdir %p file %p done.\n", inode, file); 61562306a36Sopenharmony_ci return 0; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic void reset_readdir(struct ceph_dir_file_info *dfi) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci if (dfi->last_readdir) { 62162306a36Sopenharmony_ci ceph_mdsc_put_request(dfi->last_readdir); 62262306a36Sopenharmony_ci dfi->last_readdir = NULL; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci kfree(dfi->last_name); 62562306a36Sopenharmony_ci dfi->last_name = NULL; 62662306a36Sopenharmony_ci dfi->dir_release_count = 0; 62762306a36Sopenharmony_ci dfi->readdir_cache_idx = -1; 62862306a36Sopenharmony_ci dfi->next_offset = 2; /* compensate for . and .. */ 62962306a36Sopenharmony_ci dfi->file_info.flags &= ~CEPH_F_ATEND; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci/* 63362306a36Sopenharmony_ci * discard buffered readdir content on seekdir(0), or seek to new frag, 63462306a36Sopenharmony_ci * or seek prior to current chunk 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_cistatic bool need_reset_readdir(struct ceph_dir_file_info *dfi, loff_t new_pos) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct ceph_mds_reply_info_parsed *rinfo; 63962306a36Sopenharmony_ci loff_t chunk_offset; 64062306a36Sopenharmony_ci if (new_pos == 0) 64162306a36Sopenharmony_ci return true; 64262306a36Sopenharmony_ci if (is_hash_order(new_pos)) { 64362306a36Sopenharmony_ci /* no need to reset last_name for a forward seek when 64462306a36Sopenharmony_ci * dentries are sotred in hash order */ 64562306a36Sopenharmony_ci } else if (dfi->frag != fpos_frag(new_pos)) { 64662306a36Sopenharmony_ci return true; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci rinfo = dfi->last_readdir ? &dfi->last_readdir->r_reply_info : NULL; 64962306a36Sopenharmony_ci if (!rinfo || !rinfo->dir_nr) 65062306a36Sopenharmony_ci return true; 65162306a36Sopenharmony_ci chunk_offset = rinfo->dir_entries[0].offset; 65262306a36Sopenharmony_ci return new_pos < chunk_offset || 65362306a36Sopenharmony_ci is_hash_order(new_pos) != is_hash_order(chunk_offset); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct ceph_dir_file_info *dfi = file->private_data; 65962306a36Sopenharmony_ci struct inode *inode = file->f_mapping->host; 66062306a36Sopenharmony_ci loff_t retval; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci inode_lock(inode); 66362306a36Sopenharmony_ci retval = -EINVAL; 66462306a36Sopenharmony_ci switch (whence) { 66562306a36Sopenharmony_ci case SEEK_CUR: 66662306a36Sopenharmony_ci offset += file->f_pos; 66762306a36Sopenharmony_ci break; 66862306a36Sopenharmony_ci case SEEK_SET: 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci case SEEK_END: 67162306a36Sopenharmony_ci retval = -EOPNOTSUPP; 67262306a36Sopenharmony_ci goto out; 67362306a36Sopenharmony_ci default: 67462306a36Sopenharmony_ci goto out; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (offset >= 0) { 67862306a36Sopenharmony_ci if (need_reset_readdir(dfi, offset)) { 67962306a36Sopenharmony_ci dout("dir_llseek dropping %p content\n", file); 68062306a36Sopenharmony_ci reset_readdir(dfi); 68162306a36Sopenharmony_ci } else if (is_hash_order(offset) && offset > file->f_pos) { 68262306a36Sopenharmony_ci /* for hash offset, we don't know if a forward seek 68362306a36Sopenharmony_ci * is within same frag */ 68462306a36Sopenharmony_ci dfi->dir_release_count = 0; 68562306a36Sopenharmony_ci dfi->readdir_cache_idx = -1; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (offset != file->f_pos) { 68962306a36Sopenharmony_ci file->f_pos = offset; 69062306a36Sopenharmony_ci file->f_version = 0; 69162306a36Sopenharmony_ci dfi->file_info.flags &= ~CEPH_F_ATEND; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci retval = offset; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ciout: 69662306a36Sopenharmony_ci inode_unlock(inode); 69762306a36Sopenharmony_ci return retval; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/* 70162306a36Sopenharmony_ci * Handle lookups for the hidden .snap directory. 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_cistruct dentry *ceph_handle_snapdir(struct ceph_mds_request *req, 70462306a36Sopenharmony_ci struct dentry *dentry) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); 70762306a36Sopenharmony_ci struct inode *parent = d_inode(dentry->d_parent); /* we hold i_rwsem */ 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* .snap dir? */ 71062306a36Sopenharmony_ci if (ceph_snap(parent) == CEPH_NOSNAP && 71162306a36Sopenharmony_ci strcmp(dentry->d_name.name, fsc->mount_options->snapdir_name) == 0) { 71262306a36Sopenharmony_ci struct dentry *res; 71362306a36Sopenharmony_ci struct inode *inode = ceph_get_snapdir(parent); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci res = d_splice_alias(inode, dentry); 71662306a36Sopenharmony_ci dout("ENOENT on snapdir %p '%pd', linking to snapdir %p. Spliced dentry %p\n", 71762306a36Sopenharmony_ci dentry, dentry, inode, res); 71862306a36Sopenharmony_ci if (res) 71962306a36Sopenharmony_ci dentry = res; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci return dentry; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci/* 72562306a36Sopenharmony_ci * Figure out final result of a lookup/open request. 72662306a36Sopenharmony_ci * 72762306a36Sopenharmony_ci * Mainly, make sure we return the final req->r_dentry (if it already 72862306a36Sopenharmony_ci * existed) in place of the original VFS-provided dentry when they 72962306a36Sopenharmony_ci * differ. 73062306a36Sopenharmony_ci * 73162306a36Sopenharmony_ci * Gracefully handle the case where the MDS replies with -ENOENT and 73262306a36Sopenharmony_ci * no trace (which it may do, at its discretion, e.g., if it doesn't 73362306a36Sopenharmony_ci * care to issue a lease on the negative dentry). 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_cistruct dentry *ceph_finish_lookup(struct ceph_mds_request *req, 73662306a36Sopenharmony_ci struct dentry *dentry, int err) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci if (err == -ENOENT) { 73962306a36Sopenharmony_ci /* no trace? */ 74062306a36Sopenharmony_ci err = 0; 74162306a36Sopenharmony_ci if (!req->r_reply_info.head->is_dentry) { 74262306a36Sopenharmony_ci dout("ENOENT and no trace, dentry %p inode %p\n", 74362306a36Sopenharmony_ci dentry, d_inode(dentry)); 74462306a36Sopenharmony_ci if (d_really_is_positive(dentry)) { 74562306a36Sopenharmony_ci d_drop(dentry); 74662306a36Sopenharmony_ci err = -ENOENT; 74762306a36Sopenharmony_ci } else { 74862306a36Sopenharmony_ci d_add(dentry, NULL); 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci if (err) 75362306a36Sopenharmony_ci dentry = ERR_PTR(err); 75462306a36Sopenharmony_ci else if (dentry != req->r_dentry) 75562306a36Sopenharmony_ci dentry = dget(req->r_dentry); /* we got spliced */ 75662306a36Sopenharmony_ci else 75762306a36Sopenharmony_ci dentry = NULL; 75862306a36Sopenharmony_ci return dentry; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic bool is_root_ceph_dentry(struct inode *inode, struct dentry *dentry) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci return ceph_ino(inode) == CEPH_INO_ROOT && 76462306a36Sopenharmony_ci strncmp(dentry->d_name.name, ".ceph", 5) == 0; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci/* 76862306a36Sopenharmony_ci * Look up a single dir entry. If there is a lookup intent, inform 76962306a36Sopenharmony_ci * the MDS so that it gets our 'caps wanted' value in a single op. 77062306a36Sopenharmony_ci */ 77162306a36Sopenharmony_cistatic struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry, 77262306a36Sopenharmony_ci unsigned int flags) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); 77562306a36Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); 77662306a36Sopenharmony_ci struct ceph_mds_request *req; 77762306a36Sopenharmony_ci int op; 77862306a36Sopenharmony_ci int mask; 77962306a36Sopenharmony_ci int err; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci dout("lookup %p dentry %p '%pd'\n", 78262306a36Sopenharmony_ci dir, dentry, dentry); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (dentry->d_name.len > NAME_MAX) 78562306a36Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (IS_ENCRYPTED(dir)) { 78862306a36Sopenharmony_ci bool had_key = fscrypt_has_encryption_key(dir); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci err = fscrypt_prepare_lookup_partial(dir, dentry); 79162306a36Sopenharmony_ci if (err < 0) 79262306a36Sopenharmony_ci return ERR_PTR(err); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* mark directory as incomplete if it has been unlocked */ 79562306a36Sopenharmony_ci if (!had_key && fscrypt_has_encryption_key(dir)) 79662306a36Sopenharmony_ci ceph_dir_clear_complete(dir); 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* can we conclude ENOENT locally? */ 80062306a36Sopenharmony_ci if (d_really_is_negative(dentry)) { 80162306a36Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(dir); 80262306a36Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 80562306a36Sopenharmony_ci dout(" dir %p flags are 0x%lx\n", dir, ci->i_ceph_flags); 80662306a36Sopenharmony_ci if (strncmp(dentry->d_name.name, 80762306a36Sopenharmony_ci fsc->mount_options->snapdir_name, 80862306a36Sopenharmony_ci dentry->d_name.len) && 80962306a36Sopenharmony_ci !is_root_ceph_dentry(dir, dentry) && 81062306a36Sopenharmony_ci ceph_test_mount_opt(fsc, DCACHE) && 81162306a36Sopenharmony_ci __ceph_dir_is_complete(ci) && 81262306a36Sopenharmony_ci __ceph_caps_issued_mask_metric(ci, CEPH_CAP_FILE_SHARED, 1)) { 81362306a36Sopenharmony_ci __ceph_touch_fmode(ci, mdsc, CEPH_FILE_MODE_RD); 81462306a36Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 81562306a36Sopenharmony_ci dout(" dir %p complete, -ENOENT\n", dir); 81662306a36Sopenharmony_ci d_add(dentry, NULL); 81762306a36Sopenharmony_ci di->lease_shared_gen = atomic_read(&ci->i_shared_gen); 81862306a36Sopenharmony_ci return NULL; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci op = ceph_snap(dir) == CEPH_SNAPDIR ? 82462306a36Sopenharmony_ci CEPH_MDS_OP_LOOKUPSNAP : CEPH_MDS_OP_LOOKUP; 82562306a36Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, op, USE_ANY_MDS); 82662306a36Sopenharmony_ci if (IS_ERR(req)) 82762306a36Sopenharmony_ci return ERR_CAST(req); 82862306a36Sopenharmony_ci req->r_dentry = dget(dentry); 82962306a36Sopenharmony_ci req->r_num_caps = 2; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci mask = CEPH_STAT_CAP_INODE | CEPH_CAP_AUTH_SHARED; 83262306a36Sopenharmony_ci if (ceph_security_xattr_wanted(dir)) 83362306a36Sopenharmony_ci mask |= CEPH_CAP_XATTR_SHARED; 83462306a36Sopenharmony_ci req->r_args.getattr.mask = cpu_to_le32(mask); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci ihold(dir); 83762306a36Sopenharmony_ci req->r_parent = dir; 83862306a36Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 83962306a36Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, NULL, req); 84062306a36Sopenharmony_ci if (err == -ENOENT) { 84162306a36Sopenharmony_ci struct dentry *res; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci res = ceph_handle_snapdir(req, dentry); 84462306a36Sopenharmony_ci if (IS_ERR(res)) { 84562306a36Sopenharmony_ci err = PTR_ERR(res); 84662306a36Sopenharmony_ci } else { 84762306a36Sopenharmony_ci dentry = res; 84862306a36Sopenharmony_ci err = 0; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci dentry = ceph_finish_lookup(req, dentry, err); 85262306a36Sopenharmony_ci ceph_mdsc_put_request(req); /* will dput(dentry) */ 85362306a36Sopenharmony_ci dout("lookup result=%p\n", dentry); 85462306a36Sopenharmony_ci return dentry; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci/* 85862306a36Sopenharmony_ci * If we do a create but get no trace back from the MDS, follow up with 85962306a36Sopenharmony_ci * a lookup (the VFS expects us to link up the provided dentry). 86062306a36Sopenharmony_ci */ 86162306a36Sopenharmony_ciint ceph_handle_notrace_create(struct inode *dir, struct dentry *dentry) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct dentry *result = ceph_lookup(dir, dentry, 0); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (result && !IS_ERR(result)) { 86662306a36Sopenharmony_ci /* 86762306a36Sopenharmony_ci * We created the item, then did a lookup, and found 86862306a36Sopenharmony_ci * it was already linked to another inode we already 86962306a36Sopenharmony_ci * had in our cache (and thus got spliced). To not 87062306a36Sopenharmony_ci * confuse VFS (especially when inode is a directory), 87162306a36Sopenharmony_ci * we don't link our dentry to that inode, return an 87262306a36Sopenharmony_ci * error instead. 87362306a36Sopenharmony_ci * 87462306a36Sopenharmony_ci * This event should be rare and it happens only when 87562306a36Sopenharmony_ci * we talk to old MDS. Recent MDS does not send traceless 87662306a36Sopenharmony_ci * reply for request that creates new inode. 87762306a36Sopenharmony_ci */ 87862306a36Sopenharmony_ci d_drop(result); 87962306a36Sopenharmony_ci return -ESTALE; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci return PTR_ERR(result); 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic int ceph_mknod(struct mnt_idmap *idmap, struct inode *dir, 88562306a36Sopenharmony_ci struct dentry *dentry, umode_t mode, dev_t rdev) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); 88862306a36Sopenharmony_ci struct ceph_mds_request *req; 88962306a36Sopenharmony_ci struct ceph_acl_sec_ctx as_ctx = {}; 89062306a36Sopenharmony_ci int err; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (ceph_snap(dir) != CEPH_NOSNAP) 89362306a36Sopenharmony_ci return -EROFS; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci err = ceph_wait_on_conflict_unlink(dentry); 89662306a36Sopenharmony_ci if (err) 89762306a36Sopenharmony_ci return err; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (ceph_quota_is_max_files_exceeded(dir)) { 90062306a36Sopenharmony_ci err = -EDQUOT; 90162306a36Sopenharmony_ci goto out; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci dout("mknod in dir %p dentry %p mode 0%ho rdev %d\n", 90562306a36Sopenharmony_ci dir, dentry, mode, rdev); 90662306a36Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_MKNOD, USE_AUTH_MDS); 90762306a36Sopenharmony_ci if (IS_ERR(req)) { 90862306a36Sopenharmony_ci err = PTR_ERR(req); 90962306a36Sopenharmony_ci goto out; 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx); 91362306a36Sopenharmony_ci if (IS_ERR(req->r_new_inode)) { 91462306a36Sopenharmony_ci err = PTR_ERR(req->r_new_inode); 91562306a36Sopenharmony_ci req->r_new_inode = NULL; 91662306a36Sopenharmony_ci goto out_req; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (S_ISREG(mode) && IS_ENCRYPTED(dir)) 92062306a36Sopenharmony_ci set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci req->r_dentry = dget(dentry); 92362306a36Sopenharmony_ci req->r_num_caps = 2; 92462306a36Sopenharmony_ci req->r_parent = dir; 92562306a36Sopenharmony_ci ihold(dir); 92662306a36Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 92762306a36Sopenharmony_ci req->r_args.mknod.mode = cpu_to_le32(mode); 92862306a36Sopenharmony_ci req->r_args.mknod.rdev = cpu_to_le32(rdev); 92962306a36Sopenharmony_ci req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL | 93062306a36Sopenharmony_ci CEPH_CAP_XATTR_EXCL; 93162306a36Sopenharmony_ci req->r_dentry_unless = CEPH_CAP_FILE_EXCL; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci ceph_as_ctx_to_req(req, &as_ctx); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, dir, req); 93662306a36Sopenharmony_ci if (!err && !req->r_reply_info.head->is_dentry) 93762306a36Sopenharmony_ci err = ceph_handle_notrace_create(dir, dentry); 93862306a36Sopenharmony_ciout_req: 93962306a36Sopenharmony_ci ceph_mdsc_put_request(req); 94062306a36Sopenharmony_ciout: 94162306a36Sopenharmony_ci if (!err) 94262306a36Sopenharmony_ci ceph_init_inode_acls(d_inode(dentry), &as_ctx); 94362306a36Sopenharmony_ci else 94462306a36Sopenharmony_ci d_drop(dentry); 94562306a36Sopenharmony_ci ceph_release_acl_sec_ctx(&as_ctx); 94662306a36Sopenharmony_ci return err; 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic int ceph_create(struct mnt_idmap *idmap, struct inode *dir, 95062306a36Sopenharmony_ci struct dentry *dentry, umode_t mode, bool excl) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci return ceph_mknod(idmap, dir, dentry, mode, 0); 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_FS_ENCRYPTION) 95662306a36Sopenharmony_cistatic int prep_encrypted_symlink_target(struct ceph_mds_request *req, 95762306a36Sopenharmony_ci const char *dest) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci int err; 96062306a36Sopenharmony_ci int len = strlen(dest); 96162306a36Sopenharmony_ci struct fscrypt_str osd_link = FSTR_INIT(NULL, 0); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci err = fscrypt_prepare_symlink(req->r_parent, dest, len, PATH_MAX, 96462306a36Sopenharmony_ci &osd_link); 96562306a36Sopenharmony_ci if (err) 96662306a36Sopenharmony_ci goto out; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci err = fscrypt_encrypt_symlink(req->r_new_inode, dest, len, &osd_link); 96962306a36Sopenharmony_ci if (err) 97062306a36Sopenharmony_ci goto out; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci req->r_path2 = kmalloc(CEPH_BASE64_CHARS(osd_link.len) + 1, GFP_KERNEL); 97362306a36Sopenharmony_ci if (!req->r_path2) { 97462306a36Sopenharmony_ci err = -ENOMEM; 97562306a36Sopenharmony_ci goto out; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci len = ceph_base64_encode(osd_link.name, osd_link.len, req->r_path2); 97962306a36Sopenharmony_ci req->r_path2[len] = '\0'; 98062306a36Sopenharmony_ciout: 98162306a36Sopenharmony_ci fscrypt_fname_free_buffer(&osd_link); 98262306a36Sopenharmony_ci return err; 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci#else 98562306a36Sopenharmony_cistatic int prep_encrypted_symlink_target(struct ceph_mds_request *req, 98662306a36Sopenharmony_ci const char *dest) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci return -EOPNOTSUPP; 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci#endif 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic int ceph_symlink(struct mnt_idmap *idmap, struct inode *dir, 99362306a36Sopenharmony_ci struct dentry *dentry, const char *dest) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); 99662306a36Sopenharmony_ci struct ceph_mds_request *req; 99762306a36Sopenharmony_ci struct ceph_acl_sec_ctx as_ctx = {}; 99862306a36Sopenharmony_ci umode_t mode = S_IFLNK | 0777; 99962306a36Sopenharmony_ci int err; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (ceph_snap(dir) != CEPH_NOSNAP) 100262306a36Sopenharmony_ci return -EROFS; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci err = ceph_wait_on_conflict_unlink(dentry); 100562306a36Sopenharmony_ci if (err) 100662306a36Sopenharmony_ci return err; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (ceph_quota_is_max_files_exceeded(dir)) { 100962306a36Sopenharmony_ci err = -EDQUOT; 101062306a36Sopenharmony_ci goto out; 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest); 101462306a36Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS); 101562306a36Sopenharmony_ci if (IS_ERR(req)) { 101662306a36Sopenharmony_ci err = PTR_ERR(req); 101762306a36Sopenharmony_ci goto out; 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx); 102162306a36Sopenharmony_ci if (IS_ERR(req->r_new_inode)) { 102262306a36Sopenharmony_ci err = PTR_ERR(req->r_new_inode); 102362306a36Sopenharmony_ci req->r_new_inode = NULL; 102462306a36Sopenharmony_ci goto out_req; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci req->r_parent = dir; 102862306a36Sopenharmony_ci ihold(dir); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (IS_ENCRYPTED(req->r_new_inode)) { 103162306a36Sopenharmony_ci err = prep_encrypted_symlink_target(req, dest); 103262306a36Sopenharmony_ci if (err) 103362306a36Sopenharmony_ci goto out_req; 103462306a36Sopenharmony_ci } else { 103562306a36Sopenharmony_ci req->r_path2 = kstrdup(dest, GFP_KERNEL); 103662306a36Sopenharmony_ci if (!req->r_path2) { 103762306a36Sopenharmony_ci err = -ENOMEM; 103862306a36Sopenharmony_ci goto out_req; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 104362306a36Sopenharmony_ci req->r_dentry = dget(dentry); 104462306a36Sopenharmony_ci req->r_num_caps = 2; 104562306a36Sopenharmony_ci req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL | 104662306a36Sopenharmony_ci CEPH_CAP_XATTR_EXCL; 104762306a36Sopenharmony_ci req->r_dentry_unless = CEPH_CAP_FILE_EXCL; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci ceph_as_ctx_to_req(req, &as_ctx); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, dir, req); 105262306a36Sopenharmony_ci if (!err && !req->r_reply_info.head->is_dentry) 105362306a36Sopenharmony_ci err = ceph_handle_notrace_create(dir, dentry); 105462306a36Sopenharmony_ciout_req: 105562306a36Sopenharmony_ci ceph_mdsc_put_request(req); 105662306a36Sopenharmony_ciout: 105762306a36Sopenharmony_ci if (err) 105862306a36Sopenharmony_ci d_drop(dentry); 105962306a36Sopenharmony_ci ceph_release_acl_sec_ctx(&as_ctx); 106062306a36Sopenharmony_ci return err; 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_cistatic int ceph_mkdir(struct mnt_idmap *idmap, struct inode *dir, 106462306a36Sopenharmony_ci struct dentry *dentry, umode_t mode) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); 106762306a36Sopenharmony_ci struct ceph_mds_request *req; 106862306a36Sopenharmony_ci struct ceph_acl_sec_ctx as_ctx = {}; 106962306a36Sopenharmony_ci int err; 107062306a36Sopenharmony_ci int op; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci err = ceph_wait_on_conflict_unlink(dentry); 107362306a36Sopenharmony_ci if (err) 107462306a36Sopenharmony_ci return err; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (ceph_snap(dir) == CEPH_SNAPDIR) { 107762306a36Sopenharmony_ci /* mkdir .snap/foo is a MKSNAP */ 107862306a36Sopenharmony_ci op = CEPH_MDS_OP_MKSNAP; 107962306a36Sopenharmony_ci dout("mksnap dir %p snap '%pd' dn %p\n", dir, 108062306a36Sopenharmony_ci dentry, dentry); 108162306a36Sopenharmony_ci } else if (ceph_snap(dir) == CEPH_NOSNAP) { 108262306a36Sopenharmony_ci dout("mkdir dir %p dn %p mode 0%ho\n", dir, dentry, mode); 108362306a36Sopenharmony_ci op = CEPH_MDS_OP_MKDIR; 108462306a36Sopenharmony_ci } else { 108562306a36Sopenharmony_ci err = -EROFS; 108662306a36Sopenharmony_ci goto out; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (op == CEPH_MDS_OP_MKDIR && 109062306a36Sopenharmony_ci ceph_quota_is_max_files_exceeded(dir)) { 109162306a36Sopenharmony_ci err = -EDQUOT; 109262306a36Sopenharmony_ci goto out; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci if ((op == CEPH_MDS_OP_MKSNAP) && IS_ENCRYPTED(dir) && 109562306a36Sopenharmony_ci !fscrypt_has_encryption_key(dir)) { 109662306a36Sopenharmony_ci err = -ENOKEY; 109762306a36Sopenharmony_ci goto out; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); 110262306a36Sopenharmony_ci if (IS_ERR(req)) { 110362306a36Sopenharmony_ci err = PTR_ERR(req); 110462306a36Sopenharmony_ci goto out; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci mode |= S_IFDIR; 110862306a36Sopenharmony_ci req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx); 110962306a36Sopenharmony_ci if (IS_ERR(req->r_new_inode)) { 111062306a36Sopenharmony_ci err = PTR_ERR(req->r_new_inode); 111162306a36Sopenharmony_ci req->r_new_inode = NULL; 111262306a36Sopenharmony_ci goto out_req; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci req->r_dentry = dget(dentry); 111662306a36Sopenharmony_ci req->r_num_caps = 2; 111762306a36Sopenharmony_ci req->r_parent = dir; 111862306a36Sopenharmony_ci ihold(dir); 111962306a36Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 112062306a36Sopenharmony_ci req->r_args.mkdir.mode = cpu_to_le32(mode); 112162306a36Sopenharmony_ci req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL | 112262306a36Sopenharmony_ci CEPH_CAP_XATTR_EXCL; 112362306a36Sopenharmony_ci req->r_dentry_unless = CEPH_CAP_FILE_EXCL; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci ceph_as_ctx_to_req(req, &as_ctx); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, dir, req); 112862306a36Sopenharmony_ci if (!err && 112962306a36Sopenharmony_ci !req->r_reply_info.head->is_target && 113062306a36Sopenharmony_ci !req->r_reply_info.head->is_dentry) 113162306a36Sopenharmony_ci err = ceph_handle_notrace_create(dir, dentry); 113262306a36Sopenharmony_ciout_req: 113362306a36Sopenharmony_ci ceph_mdsc_put_request(req); 113462306a36Sopenharmony_ciout: 113562306a36Sopenharmony_ci if (!err) 113662306a36Sopenharmony_ci ceph_init_inode_acls(d_inode(dentry), &as_ctx); 113762306a36Sopenharmony_ci else 113862306a36Sopenharmony_ci d_drop(dentry); 113962306a36Sopenharmony_ci ceph_release_acl_sec_ctx(&as_ctx); 114062306a36Sopenharmony_ci return err; 114162306a36Sopenharmony_ci} 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistatic int ceph_link(struct dentry *old_dentry, struct inode *dir, 114462306a36Sopenharmony_ci struct dentry *dentry) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); 114762306a36Sopenharmony_ci struct ceph_mds_request *req; 114862306a36Sopenharmony_ci int err; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (dentry->d_flags & DCACHE_DISCONNECTED) 115162306a36Sopenharmony_ci return -EINVAL; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci err = ceph_wait_on_conflict_unlink(dentry); 115462306a36Sopenharmony_ci if (err) 115562306a36Sopenharmony_ci return err; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (ceph_snap(dir) != CEPH_NOSNAP) 115862306a36Sopenharmony_ci return -EROFS; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci err = fscrypt_prepare_link(old_dentry, dir, dentry); 116162306a36Sopenharmony_ci if (err) 116262306a36Sopenharmony_ci return err; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci dout("link in dir %p %llx.%llx old_dentry %p:'%pd' dentry %p:'%pd'\n", 116562306a36Sopenharmony_ci dir, ceph_vinop(dir), old_dentry, old_dentry, dentry, dentry); 116662306a36Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LINK, USE_AUTH_MDS); 116762306a36Sopenharmony_ci if (IS_ERR(req)) { 116862306a36Sopenharmony_ci d_drop(dentry); 116962306a36Sopenharmony_ci return PTR_ERR(req); 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci req->r_dentry = dget(dentry); 117262306a36Sopenharmony_ci req->r_num_caps = 2; 117362306a36Sopenharmony_ci req->r_old_dentry = dget(old_dentry); 117462306a36Sopenharmony_ci /* 117562306a36Sopenharmony_ci * The old_dentry maybe a DCACHE_DISCONNECTED dentry, then we 117662306a36Sopenharmony_ci * will just pass the ino# to MDSs. 117762306a36Sopenharmony_ci */ 117862306a36Sopenharmony_ci if (old_dentry->d_flags & DCACHE_DISCONNECTED) 117962306a36Sopenharmony_ci req->r_ino2 = ceph_vino(d_inode(old_dentry)); 118062306a36Sopenharmony_ci req->r_parent = dir; 118162306a36Sopenharmony_ci ihold(dir); 118262306a36Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 118362306a36Sopenharmony_ci req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_XATTR_EXCL; 118462306a36Sopenharmony_ci req->r_dentry_unless = CEPH_CAP_FILE_EXCL; 118562306a36Sopenharmony_ci /* release LINK_SHARED on source inode (mds will lock it) */ 118662306a36Sopenharmony_ci req->r_old_inode_drop = CEPH_CAP_LINK_SHARED | CEPH_CAP_LINK_EXCL; 118762306a36Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, dir, req); 118862306a36Sopenharmony_ci if (err) { 118962306a36Sopenharmony_ci d_drop(dentry); 119062306a36Sopenharmony_ci } else if (!req->r_reply_info.head->is_dentry) { 119162306a36Sopenharmony_ci ihold(d_inode(old_dentry)); 119262306a36Sopenharmony_ci d_instantiate(dentry, d_inode(old_dentry)); 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci ceph_mdsc_put_request(req); 119562306a36Sopenharmony_ci return err; 119662306a36Sopenharmony_ci} 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cistatic void ceph_async_unlink_cb(struct ceph_mds_client *mdsc, 119962306a36Sopenharmony_ci struct ceph_mds_request *req) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci struct dentry *dentry = req->r_dentry; 120262306a36Sopenharmony_ci struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); 120362306a36Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 120462306a36Sopenharmony_ci int result = req->r_err ? req->r_err : 120562306a36Sopenharmony_ci le32_to_cpu(req->r_reply_info.head->result); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (!test_bit(CEPH_DENTRY_ASYNC_UNLINK_BIT, &di->flags)) 120862306a36Sopenharmony_ci pr_warn("%s dentry %p:%pd async unlink bit is not set\n", 120962306a36Sopenharmony_ci __func__, dentry, dentry); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci spin_lock(&fsc->async_unlink_conflict_lock); 121262306a36Sopenharmony_ci hash_del_rcu(&di->hnode); 121362306a36Sopenharmony_ci spin_unlock(&fsc->async_unlink_conflict_lock); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 121662306a36Sopenharmony_ci di->flags &= ~CEPH_DENTRY_ASYNC_UNLINK; 121762306a36Sopenharmony_ci wake_up_bit(&di->flags, CEPH_DENTRY_ASYNC_UNLINK_BIT); 121862306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci synchronize_rcu(); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (result == -EJUKEBOX) 122362306a36Sopenharmony_ci goto out; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci /* If op failed, mark everyone involved for errors */ 122662306a36Sopenharmony_ci if (result) { 122762306a36Sopenharmony_ci int pathlen = 0; 122862306a36Sopenharmony_ci u64 base = 0; 122962306a36Sopenharmony_ci char *path = ceph_mdsc_build_path(dentry, &pathlen, 123062306a36Sopenharmony_ci &base, 0); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci /* mark error on parent + clear complete */ 123362306a36Sopenharmony_ci mapping_set_error(req->r_parent->i_mapping, result); 123462306a36Sopenharmony_ci ceph_dir_clear_complete(req->r_parent); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci /* drop the dentry -- we don't know its status */ 123762306a36Sopenharmony_ci if (!d_unhashed(dentry)) 123862306a36Sopenharmony_ci d_drop(dentry); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* mark inode itself for an error (since metadata is bogus) */ 124162306a36Sopenharmony_ci mapping_set_error(req->r_old_inode->i_mapping, result); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci pr_warn("async unlink failure path=(%llx)%s result=%d!\n", 124462306a36Sopenharmony_ci base, IS_ERR(path) ? "<<bad>>" : path, result); 124562306a36Sopenharmony_ci ceph_mdsc_free_path(path, pathlen); 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ciout: 124862306a36Sopenharmony_ci iput(req->r_old_inode); 124962306a36Sopenharmony_ci ceph_mdsc_release_dir_caps(req); 125062306a36Sopenharmony_ci} 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_cistatic int get_caps_for_async_unlink(struct inode *dir, struct dentry *dentry) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(dir); 125562306a36Sopenharmony_ci struct ceph_dentry_info *di; 125662306a36Sopenharmony_ci int got = 0, want = CEPH_CAP_FILE_EXCL | CEPH_CAP_DIR_UNLINK; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 125962306a36Sopenharmony_ci if ((__ceph_caps_issued(ci, NULL) & want) == want) { 126062306a36Sopenharmony_ci ceph_take_cap_refs(ci, want, false); 126162306a36Sopenharmony_ci got = want; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci /* If we didn't get anything, return 0 */ 126662306a36Sopenharmony_ci if (!got) 126762306a36Sopenharmony_ci return 0; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 127062306a36Sopenharmony_ci di = ceph_dentry(dentry); 127162306a36Sopenharmony_ci /* 127262306a36Sopenharmony_ci * - We are holding Fx, which implies Fs caps. 127362306a36Sopenharmony_ci * - Only support async unlink for primary linkage 127462306a36Sopenharmony_ci */ 127562306a36Sopenharmony_ci if (atomic_read(&ci->i_shared_gen) != di->lease_shared_gen || 127662306a36Sopenharmony_ci !(di->flags & CEPH_DENTRY_PRIMARY_LINK)) 127762306a36Sopenharmony_ci want = 0; 127862306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci /* Do we still want what we've got? */ 128162306a36Sopenharmony_ci if (want == got) 128262306a36Sopenharmony_ci return got; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci ceph_put_cap_refs(ci, got); 128562306a36Sopenharmony_ci return 0; 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci/* 128962306a36Sopenharmony_ci * rmdir and unlink are differ only by the metadata op code 129062306a36Sopenharmony_ci */ 129162306a36Sopenharmony_cistatic int ceph_unlink(struct inode *dir, struct dentry *dentry) 129262306a36Sopenharmony_ci{ 129362306a36Sopenharmony_ci struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); 129462306a36Sopenharmony_ci struct ceph_mds_client *mdsc = fsc->mdsc; 129562306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 129662306a36Sopenharmony_ci struct ceph_mds_request *req; 129762306a36Sopenharmony_ci bool try_async = ceph_test_mount_opt(fsc, ASYNC_DIROPS); 129862306a36Sopenharmony_ci int err = -EROFS; 129962306a36Sopenharmony_ci int op; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (ceph_snap(dir) == CEPH_SNAPDIR) { 130262306a36Sopenharmony_ci /* rmdir .snap/foo is RMSNAP */ 130362306a36Sopenharmony_ci dout("rmsnap dir %p '%pd' dn %p\n", dir, dentry, dentry); 130462306a36Sopenharmony_ci op = CEPH_MDS_OP_RMSNAP; 130562306a36Sopenharmony_ci } else if (ceph_snap(dir) == CEPH_NOSNAP) { 130662306a36Sopenharmony_ci dout("unlink/rmdir dir %p dn %p inode %p\n", 130762306a36Sopenharmony_ci dir, dentry, inode); 130862306a36Sopenharmony_ci op = d_is_dir(dentry) ? 130962306a36Sopenharmony_ci CEPH_MDS_OP_RMDIR : CEPH_MDS_OP_UNLINK; 131062306a36Sopenharmony_ci } else 131162306a36Sopenharmony_ci goto out; 131262306a36Sopenharmony_ciretry: 131362306a36Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); 131462306a36Sopenharmony_ci if (IS_ERR(req)) { 131562306a36Sopenharmony_ci err = PTR_ERR(req); 131662306a36Sopenharmony_ci goto out; 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci req->r_dentry = dget(dentry); 131962306a36Sopenharmony_ci req->r_num_caps = 2; 132062306a36Sopenharmony_ci req->r_parent = dir; 132162306a36Sopenharmony_ci ihold(dir); 132262306a36Sopenharmony_ci req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_XATTR_EXCL; 132362306a36Sopenharmony_ci req->r_dentry_unless = CEPH_CAP_FILE_EXCL; 132462306a36Sopenharmony_ci req->r_inode_drop = ceph_drop_caps_for_unlink(inode); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci if (try_async && op == CEPH_MDS_OP_UNLINK && 132762306a36Sopenharmony_ci (req->r_dir_caps = get_caps_for_async_unlink(dir, dentry))) { 132862306a36Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci dout("async unlink on %llu/%.*s caps=%s", ceph_ino(dir), 133162306a36Sopenharmony_ci dentry->d_name.len, dentry->d_name.name, 133262306a36Sopenharmony_ci ceph_cap_string(req->r_dir_caps)); 133362306a36Sopenharmony_ci set_bit(CEPH_MDS_R_ASYNC, &req->r_req_flags); 133462306a36Sopenharmony_ci req->r_callback = ceph_async_unlink_cb; 133562306a36Sopenharmony_ci req->r_old_inode = d_inode(dentry); 133662306a36Sopenharmony_ci ihold(req->r_old_inode); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 133962306a36Sopenharmony_ci di->flags |= CEPH_DENTRY_ASYNC_UNLINK; 134062306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci spin_lock(&fsc->async_unlink_conflict_lock); 134362306a36Sopenharmony_ci hash_add_rcu(fsc->async_unlink_conflict, &di->hnode, 134462306a36Sopenharmony_ci dentry->d_name.hash); 134562306a36Sopenharmony_ci spin_unlock(&fsc->async_unlink_conflict_lock); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci err = ceph_mdsc_submit_request(mdsc, dir, req); 134862306a36Sopenharmony_ci if (!err) { 134962306a36Sopenharmony_ci /* 135062306a36Sopenharmony_ci * We have enough caps, so we assume that the unlink 135162306a36Sopenharmony_ci * will succeed. Fix up the target inode and dcache. 135262306a36Sopenharmony_ci */ 135362306a36Sopenharmony_ci drop_nlink(inode); 135462306a36Sopenharmony_ci d_delete(dentry); 135562306a36Sopenharmony_ci } else { 135662306a36Sopenharmony_ci spin_lock(&fsc->async_unlink_conflict_lock); 135762306a36Sopenharmony_ci hash_del_rcu(&di->hnode); 135862306a36Sopenharmony_ci spin_unlock(&fsc->async_unlink_conflict_lock); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 136162306a36Sopenharmony_ci di->flags &= ~CEPH_DENTRY_ASYNC_UNLINK; 136262306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci if (err == -EJUKEBOX) { 136562306a36Sopenharmony_ci try_async = false; 136662306a36Sopenharmony_ci ceph_mdsc_put_request(req); 136762306a36Sopenharmony_ci goto retry; 136862306a36Sopenharmony_ci } 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci } else { 137162306a36Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 137262306a36Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, dir, req); 137362306a36Sopenharmony_ci if (!err && !req->r_reply_info.head->is_dentry) 137462306a36Sopenharmony_ci d_delete(dentry); 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci ceph_mdsc_put_request(req); 137862306a36Sopenharmony_ciout: 137962306a36Sopenharmony_ci return err; 138062306a36Sopenharmony_ci} 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_cistatic int ceph_rename(struct mnt_idmap *idmap, struct inode *old_dir, 138362306a36Sopenharmony_ci struct dentry *old_dentry, struct inode *new_dir, 138462306a36Sopenharmony_ci struct dentry *new_dentry, unsigned int flags) 138562306a36Sopenharmony_ci{ 138662306a36Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(old_dir->i_sb); 138762306a36Sopenharmony_ci struct ceph_mds_request *req; 138862306a36Sopenharmony_ci int op = CEPH_MDS_OP_RENAME; 138962306a36Sopenharmony_ci int err; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci if (flags) 139262306a36Sopenharmony_ci return -EINVAL; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci if (ceph_snap(old_dir) != ceph_snap(new_dir)) 139562306a36Sopenharmony_ci return -EXDEV; 139662306a36Sopenharmony_ci if (ceph_snap(old_dir) != CEPH_NOSNAP) { 139762306a36Sopenharmony_ci if (old_dir == new_dir && ceph_snap(old_dir) == CEPH_SNAPDIR) 139862306a36Sopenharmony_ci op = CEPH_MDS_OP_RENAMESNAP; 139962306a36Sopenharmony_ci else 140062306a36Sopenharmony_ci return -EROFS; 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci /* don't allow cross-quota renames */ 140362306a36Sopenharmony_ci if ((old_dir != new_dir) && 140462306a36Sopenharmony_ci (!ceph_quota_is_same_realm(old_dir, new_dir))) 140562306a36Sopenharmony_ci return -EXDEV; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci err = ceph_wait_on_conflict_unlink(new_dentry); 140862306a36Sopenharmony_ci if (err) 140962306a36Sopenharmony_ci return err; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci err = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry, 141262306a36Sopenharmony_ci flags); 141362306a36Sopenharmony_ci if (err) 141462306a36Sopenharmony_ci return err; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci dout("rename dir %p dentry %p to dir %p dentry %p\n", 141762306a36Sopenharmony_ci old_dir, old_dentry, new_dir, new_dentry); 141862306a36Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); 141962306a36Sopenharmony_ci if (IS_ERR(req)) 142062306a36Sopenharmony_ci return PTR_ERR(req); 142162306a36Sopenharmony_ci ihold(old_dir); 142262306a36Sopenharmony_ci req->r_dentry = dget(new_dentry); 142362306a36Sopenharmony_ci req->r_num_caps = 2; 142462306a36Sopenharmony_ci req->r_old_dentry = dget(old_dentry); 142562306a36Sopenharmony_ci req->r_old_dentry_dir = old_dir; 142662306a36Sopenharmony_ci req->r_parent = new_dir; 142762306a36Sopenharmony_ci ihold(new_dir); 142862306a36Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 142962306a36Sopenharmony_ci req->r_old_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_XATTR_EXCL; 143062306a36Sopenharmony_ci req->r_old_dentry_unless = CEPH_CAP_FILE_EXCL; 143162306a36Sopenharmony_ci req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_XATTR_EXCL; 143262306a36Sopenharmony_ci req->r_dentry_unless = CEPH_CAP_FILE_EXCL; 143362306a36Sopenharmony_ci /* release LINK_RDCACHE on source inode (mds will lock it) */ 143462306a36Sopenharmony_ci req->r_old_inode_drop = CEPH_CAP_LINK_SHARED | CEPH_CAP_LINK_EXCL; 143562306a36Sopenharmony_ci if (d_really_is_positive(new_dentry)) { 143662306a36Sopenharmony_ci req->r_inode_drop = 143762306a36Sopenharmony_ci ceph_drop_caps_for_unlink(d_inode(new_dentry)); 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, old_dir, req); 144062306a36Sopenharmony_ci if (!err && !req->r_reply_info.head->is_dentry) { 144162306a36Sopenharmony_ci /* 144262306a36Sopenharmony_ci * Normally d_move() is done by fill_trace (called by 144362306a36Sopenharmony_ci * do_request, above). If there is no trace, we need 144462306a36Sopenharmony_ci * to do it here. 144562306a36Sopenharmony_ci */ 144662306a36Sopenharmony_ci d_move(old_dentry, new_dentry); 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci ceph_mdsc_put_request(req); 144962306a36Sopenharmony_ci return err; 145062306a36Sopenharmony_ci} 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci/* 145362306a36Sopenharmony_ci * Move dentry to tail of mdsc->dentry_leases list when lease is updated. 145462306a36Sopenharmony_ci * Leases at front of the list will expire first. (Assume all leases have 145562306a36Sopenharmony_ci * similar duration) 145662306a36Sopenharmony_ci * 145762306a36Sopenharmony_ci * Called under dentry->d_lock. 145862306a36Sopenharmony_ci */ 145962306a36Sopenharmony_civoid __ceph_dentry_lease_touch(struct ceph_dentry_info *di) 146062306a36Sopenharmony_ci{ 146162306a36Sopenharmony_ci struct dentry *dn = di->dentry; 146262306a36Sopenharmony_ci struct ceph_mds_client *mdsc; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci dout("dentry_lease_touch %p %p '%pd'\n", di, dn, dn); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci di->flags |= CEPH_DENTRY_LEASE_LIST; 146762306a36Sopenharmony_ci if (di->flags & CEPH_DENTRY_SHRINK_LIST) { 146862306a36Sopenharmony_ci di->flags |= CEPH_DENTRY_REFERENCED; 146962306a36Sopenharmony_ci return; 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci mdsc = ceph_sb_to_client(dn->d_sb)->mdsc; 147362306a36Sopenharmony_ci spin_lock(&mdsc->dentry_list_lock); 147462306a36Sopenharmony_ci list_move_tail(&di->lease_list, &mdsc->dentry_leases); 147562306a36Sopenharmony_ci spin_unlock(&mdsc->dentry_list_lock); 147662306a36Sopenharmony_ci} 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_cistatic void __dentry_dir_lease_touch(struct ceph_mds_client* mdsc, 147962306a36Sopenharmony_ci struct ceph_dentry_info *di) 148062306a36Sopenharmony_ci{ 148162306a36Sopenharmony_ci di->flags &= ~(CEPH_DENTRY_LEASE_LIST | CEPH_DENTRY_REFERENCED); 148262306a36Sopenharmony_ci di->lease_gen = 0; 148362306a36Sopenharmony_ci di->time = jiffies; 148462306a36Sopenharmony_ci list_move_tail(&di->lease_list, &mdsc->dentry_dir_leases); 148562306a36Sopenharmony_ci} 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci/* 148862306a36Sopenharmony_ci * When dir lease is used, add dentry to tail of mdsc->dentry_dir_leases 148962306a36Sopenharmony_ci * list if it's not in the list, otherwise set 'referenced' flag. 149062306a36Sopenharmony_ci * 149162306a36Sopenharmony_ci * Called under dentry->d_lock. 149262306a36Sopenharmony_ci */ 149362306a36Sopenharmony_civoid __ceph_dentry_dir_lease_touch(struct ceph_dentry_info *di) 149462306a36Sopenharmony_ci{ 149562306a36Sopenharmony_ci struct dentry *dn = di->dentry; 149662306a36Sopenharmony_ci struct ceph_mds_client *mdsc; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci dout("dentry_dir_lease_touch %p %p '%pd' (offset 0x%llx)\n", 149962306a36Sopenharmony_ci di, dn, dn, di->offset); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (!list_empty(&di->lease_list)) { 150262306a36Sopenharmony_ci if (di->flags & CEPH_DENTRY_LEASE_LIST) { 150362306a36Sopenharmony_ci /* don't remove dentry from dentry lease list 150462306a36Sopenharmony_ci * if its lease is valid */ 150562306a36Sopenharmony_ci if (__dentry_lease_is_valid(di)) 150662306a36Sopenharmony_ci return; 150762306a36Sopenharmony_ci } else { 150862306a36Sopenharmony_ci di->flags |= CEPH_DENTRY_REFERENCED; 150962306a36Sopenharmony_ci return; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci if (di->flags & CEPH_DENTRY_SHRINK_LIST) { 151462306a36Sopenharmony_ci di->flags |= CEPH_DENTRY_REFERENCED; 151562306a36Sopenharmony_ci di->flags &= ~CEPH_DENTRY_LEASE_LIST; 151662306a36Sopenharmony_ci return; 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci mdsc = ceph_sb_to_client(dn->d_sb)->mdsc; 152062306a36Sopenharmony_ci spin_lock(&mdsc->dentry_list_lock); 152162306a36Sopenharmony_ci __dentry_dir_lease_touch(mdsc, di), 152262306a36Sopenharmony_ci spin_unlock(&mdsc->dentry_list_lock); 152362306a36Sopenharmony_ci} 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_cistatic void __dentry_lease_unlist(struct ceph_dentry_info *di) 152662306a36Sopenharmony_ci{ 152762306a36Sopenharmony_ci struct ceph_mds_client *mdsc; 152862306a36Sopenharmony_ci if (di->flags & CEPH_DENTRY_SHRINK_LIST) 152962306a36Sopenharmony_ci return; 153062306a36Sopenharmony_ci if (list_empty(&di->lease_list)) 153162306a36Sopenharmony_ci return; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci mdsc = ceph_sb_to_client(di->dentry->d_sb)->mdsc; 153462306a36Sopenharmony_ci spin_lock(&mdsc->dentry_list_lock); 153562306a36Sopenharmony_ci list_del_init(&di->lease_list); 153662306a36Sopenharmony_ci spin_unlock(&mdsc->dentry_list_lock); 153762306a36Sopenharmony_ci} 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_cienum { 154062306a36Sopenharmony_ci KEEP = 0, 154162306a36Sopenharmony_ci DELETE = 1, 154262306a36Sopenharmony_ci TOUCH = 2, 154362306a36Sopenharmony_ci STOP = 4, 154462306a36Sopenharmony_ci}; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_cistruct ceph_lease_walk_control { 154762306a36Sopenharmony_ci bool dir_lease; 154862306a36Sopenharmony_ci bool expire_dir_lease; 154962306a36Sopenharmony_ci unsigned long nr_to_scan; 155062306a36Sopenharmony_ci unsigned long dir_lease_ttl; 155162306a36Sopenharmony_ci}; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_cistatic unsigned long 155462306a36Sopenharmony_ci__dentry_leases_walk(struct ceph_mds_client *mdsc, 155562306a36Sopenharmony_ci struct ceph_lease_walk_control *lwc, 155662306a36Sopenharmony_ci int (*check)(struct dentry*, void*)) 155762306a36Sopenharmony_ci{ 155862306a36Sopenharmony_ci struct ceph_dentry_info *di, *tmp; 155962306a36Sopenharmony_ci struct dentry *dentry, *last = NULL; 156062306a36Sopenharmony_ci struct list_head* list; 156162306a36Sopenharmony_ci LIST_HEAD(dispose); 156262306a36Sopenharmony_ci unsigned long freed = 0; 156362306a36Sopenharmony_ci int ret = 0; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci list = lwc->dir_lease ? &mdsc->dentry_dir_leases : &mdsc->dentry_leases; 156662306a36Sopenharmony_ci spin_lock(&mdsc->dentry_list_lock); 156762306a36Sopenharmony_ci list_for_each_entry_safe(di, tmp, list, lease_list) { 156862306a36Sopenharmony_ci if (!lwc->nr_to_scan) 156962306a36Sopenharmony_ci break; 157062306a36Sopenharmony_ci --lwc->nr_to_scan; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci dentry = di->dentry; 157362306a36Sopenharmony_ci if (last == dentry) 157462306a36Sopenharmony_ci break; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci if (!spin_trylock(&dentry->d_lock)) 157762306a36Sopenharmony_ci continue; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci if (__lockref_is_dead(&dentry->d_lockref)) { 158062306a36Sopenharmony_ci list_del_init(&di->lease_list); 158162306a36Sopenharmony_ci goto next; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci ret = check(dentry, lwc); 158562306a36Sopenharmony_ci if (ret & TOUCH) { 158662306a36Sopenharmony_ci /* move it into tail of dir lease list */ 158762306a36Sopenharmony_ci __dentry_dir_lease_touch(mdsc, di); 158862306a36Sopenharmony_ci if (!last) 158962306a36Sopenharmony_ci last = dentry; 159062306a36Sopenharmony_ci } 159162306a36Sopenharmony_ci if (ret & DELETE) { 159262306a36Sopenharmony_ci /* stale lease */ 159362306a36Sopenharmony_ci di->flags &= ~CEPH_DENTRY_REFERENCED; 159462306a36Sopenharmony_ci if (dentry->d_lockref.count > 0) { 159562306a36Sopenharmony_ci /* update_dentry_lease() will re-add 159662306a36Sopenharmony_ci * it to lease list, or 159762306a36Sopenharmony_ci * ceph_d_delete() will return 1 when 159862306a36Sopenharmony_ci * last reference is dropped */ 159962306a36Sopenharmony_ci list_del_init(&di->lease_list); 160062306a36Sopenharmony_ci } else { 160162306a36Sopenharmony_ci di->flags |= CEPH_DENTRY_SHRINK_LIST; 160262306a36Sopenharmony_ci list_move_tail(&di->lease_list, &dispose); 160362306a36Sopenharmony_ci dget_dlock(dentry); 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci } 160662306a36Sopenharmony_cinext: 160762306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 160862306a36Sopenharmony_ci if (ret & STOP) 160962306a36Sopenharmony_ci break; 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci spin_unlock(&mdsc->dentry_list_lock); 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci while (!list_empty(&dispose)) { 161462306a36Sopenharmony_ci di = list_first_entry(&dispose, struct ceph_dentry_info, 161562306a36Sopenharmony_ci lease_list); 161662306a36Sopenharmony_ci dentry = di->dentry; 161762306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci list_del_init(&di->lease_list); 162062306a36Sopenharmony_ci di->flags &= ~CEPH_DENTRY_SHRINK_LIST; 162162306a36Sopenharmony_ci if (di->flags & CEPH_DENTRY_REFERENCED) { 162262306a36Sopenharmony_ci spin_lock(&mdsc->dentry_list_lock); 162362306a36Sopenharmony_ci if (di->flags & CEPH_DENTRY_LEASE_LIST) { 162462306a36Sopenharmony_ci list_add_tail(&di->lease_list, 162562306a36Sopenharmony_ci &mdsc->dentry_leases); 162662306a36Sopenharmony_ci } else { 162762306a36Sopenharmony_ci __dentry_dir_lease_touch(mdsc, di); 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci spin_unlock(&mdsc->dentry_list_lock); 163062306a36Sopenharmony_ci } else { 163162306a36Sopenharmony_ci freed++; 163262306a36Sopenharmony_ci } 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 163562306a36Sopenharmony_ci /* ceph_d_delete() does the trick */ 163662306a36Sopenharmony_ci dput(dentry); 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci return freed; 163962306a36Sopenharmony_ci} 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_cistatic int __dentry_lease_check(struct dentry *dentry, void *arg) 164262306a36Sopenharmony_ci{ 164362306a36Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 164462306a36Sopenharmony_ci int ret; 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci if (__dentry_lease_is_valid(di)) 164762306a36Sopenharmony_ci return STOP; 164862306a36Sopenharmony_ci ret = __dir_lease_try_check(dentry); 164962306a36Sopenharmony_ci if (ret == -EBUSY) 165062306a36Sopenharmony_ci return KEEP; 165162306a36Sopenharmony_ci if (ret > 0) 165262306a36Sopenharmony_ci return TOUCH; 165362306a36Sopenharmony_ci return DELETE; 165462306a36Sopenharmony_ci} 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_cistatic int __dir_lease_check(struct dentry *dentry, void *arg) 165762306a36Sopenharmony_ci{ 165862306a36Sopenharmony_ci struct ceph_lease_walk_control *lwc = arg; 165962306a36Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci int ret = __dir_lease_try_check(dentry); 166262306a36Sopenharmony_ci if (ret == -EBUSY) 166362306a36Sopenharmony_ci return KEEP; 166462306a36Sopenharmony_ci if (ret > 0) { 166562306a36Sopenharmony_ci if (time_before(jiffies, di->time + lwc->dir_lease_ttl)) 166662306a36Sopenharmony_ci return STOP; 166762306a36Sopenharmony_ci /* Move dentry to tail of dir lease list if we don't want 166862306a36Sopenharmony_ci * to delete it. So dentries in the list are checked in a 166962306a36Sopenharmony_ci * round robin manner */ 167062306a36Sopenharmony_ci if (!lwc->expire_dir_lease) 167162306a36Sopenharmony_ci return TOUCH; 167262306a36Sopenharmony_ci if (dentry->d_lockref.count > 0 || 167362306a36Sopenharmony_ci (di->flags & CEPH_DENTRY_REFERENCED)) 167462306a36Sopenharmony_ci return TOUCH; 167562306a36Sopenharmony_ci /* invalidate dir lease */ 167662306a36Sopenharmony_ci di->lease_shared_gen = 0; 167762306a36Sopenharmony_ci } 167862306a36Sopenharmony_ci return DELETE; 167962306a36Sopenharmony_ci} 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ciint ceph_trim_dentries(struct ceph_mds_client *mdsc) 168262306a36Sopenharmony_ci{ 168362306a36Sopenharmony_ci struct ceph_lease_walk_control lwc; 168462306a36Sopenharmony_ci unsigned long count; 168562306a36Sopenharmony_ci unsigned long freed; 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci spin_lock(&mdsc->caps_list_lock); 168862306a36Sopenharmony_ci if (mdsc->caps_use_max > 0 && 168962306a36Sopenharmony_ci mdsc->caps_use_count > mdsc->caps_use_max) 169062306a36Sopenharmony_ci count = mdsc->caps_use_count - mdsc->caps_use_max; 169162306a36Sopenharmony_ci else 169262306a36Sopenharmony_ci count = 0; 169362306a36Sopenharmony_ci spin_unlock(&mdsc->caps_list_lock); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci lwc.dir_lease = false; 169662306a36Sopenharmony_ci lwc.nr_to_scan = CEPH_CAPS_PER_RELEASE * 2; 169762306a36Sopenharmony_ci freed = __dentry_leases_walk(mdsc, &lwc, __dentry_lease_check); 169862306a36Sopenharmony_ci if (!lwc.nr_to_scan) /* more invalid leases */ 169962306a36Sopenharmony_ci return -EAGAIN; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci if (lwc.nr_to_scan < CEPH_CAPS_PER_RELEASE) 170262306a36Sopenharmony_ci lwc.nr_to_scan = CEPH_CAPS_PER_RELEASE; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci lwc.dir_lease = true; 170562306a36Sopenharmony_ci lwc.expire_dir_lease = freed < count; 170662306a36Sopenharmony_ci lwc.dir_lease_ttl = mdsc->fsc->mount_options->caps_wanted_delay_max * HZ; 170762306a36Sopenharmony_ci freed +=__dentry_leases_walk(mdsc, &lwc, __dir_lease_check); 170862306a36Sopenharmony_ci if (!lwc.nr_to_scan) /* more to check */ 170962306a36Sopenharmony_ci return -EAGAIN; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci return freed > 0 ? 1 : 0; 171262306a36Sopenharmony_ci} 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci/* 171562306a36Sopenharmony_ci * Ensure a dentry lease will no longer revalidate. 171662306a36Sopenharmony_ci */ 171762306a36Sopenharmony_civoid ceph_invalidate_dentry_lease(struct dentry *dentry) 171862306a36Sopenharmony_ci{ 171962306a36Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 172062306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 172162306a36Sopenharmony_ci di->time = jiffies; 172262306a36Sopenharmony_ci di->lease_shared_gen = 0; 172362306a36Sopenharmony_ci di->flags &= ~CEPH_DENTRY_PRIMARY_LINK; 172462306a36Sopenharmony_ci __dentry_lease_unlist(di); 172562306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 172662306a36Sopenharmony_ci} 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci/* 172962306a36Sopenharmony_ci * Check if dentry lease is valid. If not, delete the lease. Try to 173062306a36Sopenharmony_ci * renew if the least is more than half up. 173162306a36Sopenharmony_ci */ 173262306a36Sopenharmony_cistatic bool __dentry_lease_is_valid(struct ceph_dentry_info *di) 173362306a36Sopenharmony_ci{ 173462306a36Sopenharmony_ci struct ceph_mds_session *session; 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci if (!di->lease_gen) 173762306a36Sopenharmony_ci return false; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci session = di->lease_session; 174062306a36Sopenharmony_ci if (session) { 174162306a36Sopenharmony_ci u32 gen; 174262306a36Sopenharmony_ci unsigned long ttl; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci gen = atomic_read(&session->s_cap_gen); 174562306a36Sopenharmony_ci ttl = session->s_cap_ttl; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci if (di->lease_gen == gen && 174862306a36Sopenharmony_ci time_before(jiffies, ttl) && 174962306a36Sopenharmony_ci time_before(jiffies, di->time)) 175062306a36Sopenharmony_ci return true; 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci di->lease_gen = 0; 175362306a36Sopenharmony_ci return false; 175462306a36Sopenharmony_ci} 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_cistatic int dentry_lease_is_valid(struct dentry *dentry, unsigned int flags) 175762306a36Sopenharmony_ci{ 175862306a36Sopenharmony_ci struct ceph_dentry_info *di; 175962306a36Sopenharmony_ci struct ceph_mds_session *session = NULL; 176062306a36Sopenharmony_ci u32 seq = 0; 176162306a36Sopenharmony_ci int valid = 0; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 176462306a36Sopenharmony_ci di = ceph_dentry(dentry); 176562306a36Sopenharmony_ci if (di && __dentry_lease_is_valid(di)) { 176662306a36Sopenharmony_ci valid = 1; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci if (di->lease_renew_after && 176962306a36Sopenharmony_ci time_after(jiffies, di->lease_renew_after)) { 177062306a36Sopenharmony_ci /* 177162306a36Sopenharmony_ci * We should renew. If we're in RCU walk mode 177262306a36Sopenharmony_ci * though, we can't do that so just return 177362306a36Sopenharmony_ci * -ECHILD. 177462306a36Sopenharmony_ci */ 177562306a36Sopenharmony_ci if (flags & LOOKUP_RCU) { 177662306a36Sopenharmony_ci valid = -ECHILD; 177762306a36Sopenharmony_ci } else { 177862306a36Sopenharmony_ci session = ceph_get_mds_session(di->lease_session); 177962306a36Sopenharmony_ci seq = di->lease_seq; 178062306a36Sopenharmony_ci di->lease_renew_after = 0; 178162306a36Sopenharmony_ci di->lease_renew_from = jiffies; 178262306a36Sopenharmony_ci } 178362306a36Sopenharmony_ci } 178462306a36Sopenharmony_ci } 178562306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci if (session) { 178862306a36Sopenharmony_ci ceph_mdsc_lease_send_msg(session, dentry, 178962306a36Sopenharmony_ci CEPH_MDS_LEASE_RENEW, seq); 179062306a36Sopenharmony_ci ceph_put_mds_session(session); 179162306a36Sopenharmony_ci } 179262306a36Sopenharmony_ci dout("dentry_lease_is_valid - dentry %p = %d\n", dentry, valid); 179362306a36Sopenharmony_ci return valid; 179462306a36Sopenharmony_ci} 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci/* 179762306a36Sopenharmony_ci * Called under dentry->d_lock. 179862306a36Sopenharmony_ci */ 179962306a36Sopenharmony_cistatic int __dir_lease_try_check(const struct dentry *dentry) 180062306a36Sopenharmony_ci{ 180162306a36Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 180262306a36Sopenharmony_ci struct inode *dir; 180362306a36Sopenharmony_ci struct ceph_inode_info *ci; 180462306a36Sopenharmony_ci int valid = 0; 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci if (!di->lease_shared_gen) 180762306a36Sopenharmony_ci return 0; 180862306a36Sopenharmony_ci if (IS_ROOT(dentry)) 180962306a36Sopenharmony_ci return 0; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci dir = d_inode(dentry->d_parent); 181262306a36Sopenharmony_ci ci = ceph_inode(dir); 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci if (spin_trylock(&ci->i_ceph_lock)) { 181562306a36Sopenharmony_ci if (atomic_read(&ci->i_shared_gen) == di->lease_shared_gen && 181662306a36Sopenharmony_ci __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 0)) 181762306a36Sopenharmony_ci valid = 1; 181862306a36Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 181962306a36Sopenharmony_ci } else { 182062306a36Sopenharmony_ci valid = -EBUSY; 182162306a36Sopenharmony_ci } 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci if (!valid) 182462306a36Sopenharmony_ci di->lease_shared_gen = 0; 182562306a36Sopenharmony_ci return valid; 182662306a36Sopenharmony_ci} 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci/* 182962306a36Sopenharmony_ci * Check if directory-wide content lease/cap is valid. 183062306a36Sopenharmony_ci */ 183162306a36Sopenharmony_cistatic int dir_lease_is_valid(struct inode *dir, struct dentry *dentry, 183262306a36Sopenharmony_ci struct ceph_mds_client *mdsc) 183362306a36Sopenharmony_ci{ 183462306a36Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(dir); 183562306a36Sopenharmony_ci int valid; 183662306a36Sopenharmony_ci int shared_gen; 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 183962306a36Sopenharmony_ci valid = __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1); 184062306a36Sopenharmony_ci if (valid) { 184162306a36Sopenharmony_ci __ceph_touch_fmode(ci, mdsc, CEPH_FILE_MODE_RD); 184262306a36Sopenharmony_ci shared_gen = atomic_read(&ci->i_shared_gen); 184362306a36Sopenharmony_ci } 184462306a36Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 184562306a36Sopenharmony_ci if (valid) { 184662306a36Sopenharmony_ci struct ceph_dentry_info *di; 184762306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 184862306a36Sopenharmony_ci di = ceph_dentry(dentry); 184962306a36Sopenharmony_ci if (dir == d_inode(dentry->d_parent) && 185062306a36Sopenharmony_ci di && di->lease_shared_gen == shared_gen) 185162306a36Sopenharmony_ci __ceph_dentry_dir_lease_touch(di); 185262306a36Sopenharmony_ci else 185362306a36Sopenharmony_ci valid = 0; 185462306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 185562306a36Sopenharmony_ci } 185662306a36Sopenharmony_ci dout("dir_lease_is_valid dir %p v%u dentry %p = %d\n", 185762306a36Sopenharmony_ci dir, (unsigned)atomic_read(&ci->i_shared_gen), dentry, valid); 185862306a36Sopenharmony_ci return valid; 185962306a36Sopenharmony_ci} 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci/* 186262306a36Sopenharmony_ci * Check if cached dentry can be trusted. 186362306a36Sopenharmony_ci */ 186462306a36Sopenharmony_cistatic int ceph_d_revalidate(struct dentry *dentry, unsigned int flags) 186562306a36Sopenharmony_ci{ 186662306a36Sopenharmony_ci int valid = 0; 186762306a36Sopenharmony_ci struct dentry *parent; 186862306a36Sopenharmony_ci struct inode *dir, *inode; 186962306a36Sopenharmony_ci struct ceph_mds_client *mdsc; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci valid = fscrypt_d_revalidate(dentry, flags); 187262306a36Sopenharmony_ci if (valid <= 0) 187362306a36Sopenharmony_ci return valid; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci if (flags & LOOKUP_RCU) { 187662306a36Sopenharmony_ci parent = READ_ONCE(dentry->d_parent); 187762306a36Sopenharmony_ci dir = d_inode_rcu(parent); 187862306a36Sopenharmony_ci if (!dir) 187962306a36Sopenharmony_ci return -ECHILD; 188062306a36Sopenharmony_ci inode = d_inode_rcu(dentry); 188162306a36Sopenharmony_ci } else { 188262306a36Sopenharmony_ci parent = dget_parent(dentry); 188362306a36Sopenharmony_ci dir = d_inode(parent); 188462306a36Sopenharmony_ci inode = d_inode(dentry); 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci dout("d_revalidate %p '%pd' inode %p offset 0x%llx nokey %d\n", dentry, 188862306a36Sopenharmony_ci dentry, inode, ceph_dentry(dentry)->offset, 188962306a36Sopenharmony_ci !!(dentry->d_flags & DCACHE_NOKEY_NAME)); 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci mdsc = ceph_sb_to_client(dir->i_sb)->mdsc; 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci /* always trust cached snapped dentries, snapdir dentry */ 189462306a36Sopenharmony_ci if (ceph_snap(dir) != CEPH_NOSNAP) { 189562306a36Sopenharmony_ci dout("d_revalidate %p '%pd' inode %p is SNAPPED\n", dentry, 189662306a36Sopenharmony_ci dentry, inode); 189762306a36Sopenharmony_ci valid = 1; 189862306a36Sopenharmony_ci } else if (inode && ceph_snap(inode) == CEPH_SNAPDIR) { 189962306a36Sopenharmony_ci valid = 1; 190062306a36Sopenharmony_ci } else { 190162306a36Sopenharmony_ci valid = dentry_lease_is_valid(dentry, flags); 190262306a36Sopenharmony_ci if (valid == -ECHILD) 190362306a36Sopenharmony_ci return valid; 190462306a36Sopenharmony_ci if (valid || dir_lease_is_valid(dir, dentry, mdsc)) { 190562306a36Sopenharmony_ci if (inode) 190662306a36Sopenharmony_ci valid = ceph_is_any_caps(inode); 190762306a36Sopenharmony_ci else 190862306a36Sopenharmony_ci valid = 1; 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci } 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci if (!valid) { 191362306a36Sopenharmony_ci struct ceph_mds_request *req; 191462306a36Sopenharmony_ci int op, err; 191562306a36Sopenharmony_ci u32 mask; 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci if (flags & LOOKUP_RCU) 191862306a36Sopenharmony_ci return -ECHILD; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci percpu_counter_inc(&mdsc->metric.d_lease_mis); 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci op = ceph_snap(dir) == CEPH_SNAPDIR ? 192362306a36Sopenharmony_ci CEPH_MDS_OP_LOOKUPSNAP : CEPH_MDS_OP_LOOKUP; 192462306a36Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, op, USE_ANY_MDS); 192562306a36Sopenharmony_ci if (!IS_ERR(req)) { 192662306a36Sopenharmony_ci req->r_dentry = dget(dentry); 192762306a36Sopenharmony_ci req->r_num_caps = 2; 192862306a36Sopenharmony_ci req->r_parent = dir; 192962306a36Sopenharmony_ci ihold(dir); 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci mask = CEPH_STAT_CAP_INODE | CEPH_CAP_AUTH_SHARED; 193262306a36Sopenharmony_ci if (ceph_security_xattr_wanted(dir)) 193362306a36Sopenharmony_ci mask |= CEPH_CAP_XATTR_SHARED; 193462306a36Sopenharmony_ci req->r_args.getattr.mask = cpu_to_le32(mask); 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, NULL, req); 193762306a36Sopenharmony_ci switch (err) { 193862306a36Sopenharmony_ci case 0: 193962306a36Sopenharmony_ci if (d_really_is_positive(dentry) && 194062306a36Sopenharmony_ci d_inode(dentry) == req->r_target_inode) 194162306a36Sopenharmony_ci valid = 1; 194262306a36Sopenharmony_ci break; 194362306a36Sopenharmony_ci case -ENOENT: 194462306a36Sopenharmony_ci if (d_really_is_negative(dentry)) 194562306a36Sopenharmony_ci valid = 1; 194662306a36Sopenharmony_ci fallthrough; 194762306a36Sopenharmony_ci default: 194862306a36Sopenharmony_ci break; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci ceph_mdsc_put_request(req); 195162306a36Sopenharmony_ci dout("d_revalidate %p lookup result=%d\n", 195262306a36Sopenharmony_ci dentry, err); 195362306a36Sopenharmony_ci } 195462306a36Sopenharmony_ci } else { 195562306a36Sopenharmony_ci percpu_counter_inc(&mdsc->metric.d_lease_hit); 195662306a36Sopenharmony_ci } 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci dout("d_revalidate %p %s\n", dentry, valid ? "valid" : "invalid"); 195962306a36Sopenharmony_ci if (!valid) 196062306a36Sopenharmony_ci ceph_dir_clear_complete(dir); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci if (!(flags & LOOKUP_RCU)) 196362306a36Sopenharmony_ci dput(parent); 196462306a36Sopenharmony_ci return valid; 196562306a36Sopenharmony_ci} 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci/* 196862306a36Sopenharmony_ci * Delete unused dentry that doesn't have valid lease 196962306a36Sopenharmony_ci * 197062306a36Sopenharmony_ci * Called under dentry->d_lock. 197162306a36Sopenharmony_ci */ 197262306a36Sopenharmony_cistatic int ceph_d_delete(const struct dentry *dentry) 197362306a36Sopenharmony_ci{ 197462306a36Sopenharmony_ci struct ceph_dentry_info *di; 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci /* won't release caps */ 197762306a36Sopenharmony_ci if (d_really_is_negative(dentry)) 197862306a36Sopenharmony_ci return 0; 197962306a36Sopenharmony_ci if (ceph_snap(d_inode(dentry)) != CEPH_NOSNAP) 198062306a36Sopenharmony_ci return 0; 198162306a36Sopenharmony_ci /* vaild lease? */ 198262306a36Sopenharmony_ci di = ceph_dentry(dentry); 198362306a36Sopenharmony_ci if (di) { 198462306a36Sopenharmony_ci if (__dentry_lease_is_valid(di)) 198562306a36Sopenharmony_ci return 0; 198662306a36Sopenharmony_ci if (__dir_lease_try_check(dentry)) 198762306a36Sopenharmony_ci return 0; 198862306a36Sopenharmony_ci } 198962306a36Sopenharmony_ci return 1; 199062306a36Sopenharmony_ci} 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci/* 199362306a36Sopenharmony_ci * Release our ceph_dentry_info. 199462306a36Sopenharmony_ci */ 199562306a36Sopenharmony_cistatic void ceph_d_release(struct dentry *dentry) 199662306a36Sopenharmony_ci{ 199762306a36Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 199862306a36Sopenharmony_ci struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci dout("d_release %p\n", dentry); 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci atomic64_dec(&fsc->mdsc->metric.total_dentries); 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 200562306a36Sopenharmony_ci __dentry_lease_unlist(di); 200662306a36Sopenharmony_ci dentry->d_fsdata = NULL; 200762306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci ceph_put_mds_session(di->lease_session); 201062306a36Sopenharmony_ci kmem_cache_free(ceph_dentry_cachep, di); 201162306a36Sopenharmony_ci} 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci/* 201462306a36Sopenharmony_ci * When the VFS prunes a dentry from the cache, we need to clear the 201562306a36Sopenharmony_ci * complete flag on the parent directory. 201662306a36Sopenharmony_ci * 201762306a36Sopenharmony_ci * Called under dentry->d_lock. 201862306a36Sopenharmony_ci */ 201962306a36Sopenharmony_cistatic void ceph_d_prune(struct dentry *dentry) 202062306a36Sopenharmony_ci{ 202162306a36Sopenharmony_ci struct ceph_inode_info *dir_ci; 202262306a36Sopenharmony_ci struct ceph_dentry_info *di; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci dout("ceph_d_prune %pd %p\n", dentry, dentry); 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci /* do we have a valid parent? */ 202762306a36Sopenharmony_ci if (IS_ROOT(dentry)) 202862306a36Sopenharmony_ci return; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci /* we hold d_lock, so d_parent is stable */ 203162306a36Sopenharmony_ci dir_ci = ceph_inode(d_inode(dentry->d_parent)); 203262306a36Sopenharmony_ci if (dir_ci->i_vino.snap == CEPH_SNAPDIR) 203362306a36Sopenharmony_ci return; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci /* who calls d_delete() should also disable dcache readdir */ 203662306a36Sopenharmony_ci if (d_really_is_negative(dentry)) 203762306a36Sopenharmony_ci return; 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci /* d_fsdata does not get cleared until d_release */ 204062306a36Sopenharmony_ci if (!d_unhashed(dentry)) { 204162306a36Sopenharmony_ci __ceph_dir_clear_complete(dir_ci); 204262306a36Sopenharmony_ci return; 204362306a36Sopenharmony_ci } 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci /* Disable dcache readdir just in case that someone called d_drop() 204662306a36Sopenharmony_ci * or d_invalidate(), but MDS didn't revoke CEPH_CAP_FILE_SHARED 204762306a36Sopenharmony_ci * properly (dcache readdir is still enabled) */ 204862306a36Sopenharmony_ci di = ceph_dentry(dentry); 204962306a36Sopenharmony_ci if (di->offset > 0 && 205062306a36Sopenharmony_ci di->lease_shared_gen == atomic_read(&dir_ci->i_shared_gen)) 205162306a36Sopenharmony_ci __ceph_dir_clear_ordered(dir_ci); 205262306a36Sopenharmony_ci} 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci/* 205562306a36Sopenharmony_ci * read() on a dir. This weird interface hack only works if mounted 205662306a36Sopenharmony_ci * with '-o dirstat'. 205762306a36Sopenharmony_ci */ 205862306a36Sopenharmony_cistatic ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size, 205962306a36Sopenharmony_ci loff_t *ppos) 206062306a36Sopenharmony_ci{ 206162306a36Sopenharmony_ci struct ceph_dir_file_info *dfi = file->private_data; 206262306a36Sopenharmony_ci struct inode *inode = file_inode(file); 206362306a36Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(inode); 206462306a36Sopenharmony_ci int left; 206562306a36Sopenharmony_ci const int bufsize = 1024; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci if (!ceph_test_mount_opt(ceph_sb_to_client(inode->i_sb), DIRSTAT)) 206862306a36Sopenharmony_ci return -EISDIR; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci if (!dfi->dir_info) { 207162306a36Sopenharmony_ci dfi->dir_info = kmalloc(bufsize, GFP_KERNEL); 207262306a36Sopenharmony_ci if (!dfi->dir_info) 207362306a36Sopenharmony_ci return -ENOMEM; 207462306a36Sopenharmony_ci dfi->dir_info_len = 207562306a36Sopenharmony_ci snprintf(dfi->dir_info, bufsize, 207662306a36Sopenharmony_ci "entries: %20lld\n" 207762306a36Sopenharmony_ci " files: %20lld\n" 207862306a36Sopenharmony_ci " subdirs: %20lld\n" 207962306a36Sopenharmony_ci "rentries: %20lld\n" 208062306a36Sopenharmony_ci " rfiles: %20lld\n" 208162306a36Sopenharmony_ci " rsubdirs: %20lld\n" 208262306a36Sopenharmony_ci "rbytes: %20lld\n" 208362306a36Sopenharmony_ci "rctime: %10lld.%09ld\n", 208462306a36Sopenharmony_ci ci->i_files + ci->i_subdirs, 208562306a36Sopenharmony_ci ci->i_files, 208662306a36Sopenharmony_ci ci->i_subdirs, 208762306a36Sopenharmony_ci ci->i_rfiles + ci->i_rsubdirs, 208862306a36Sopenharmony_ci ci->i_rfiles, 208962306a36Sopenharmony_ci ci->i_rsubdirs, 209062306a36Sopenharmony_ci ci->i_rbytes, 209162306a36Sopenharmony_ci ci->i_rctime.tv_sec, 209262306a36Sopenharmony_ci ci->i_rctime.tv_nsec); 209362306a36Sopenharmony_ci } 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci if (*ppos >= dfi->dir_info_len) 209662306a36Sopenharmony_ci return 0; 209762306a36Sopenharmony_ci size = min_t(unsigned, size, dfi->dir_info_len-*ppos); 209862306a36Sopenharmony_ci left = copy_to_user(buf, dfi->dir_info + *ppos, size); 209962306a36Sopenharmony_ci if (left == size) 210062306a36Sopenharmony_ci return -EFAULT; 210162306a36Sopenharmony_ci *ppos += (size - left); 210262306a36Sopenharmony_ci return size - left; 210362306a36Sopenharmony_ci} 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci/* 210862306a36Sopenharmony_ci * Return name hash for a given dentry. This is dependent on 210962306a36Sopenharmony_ci * the parent directory's hash function. 211062306a36Sopenharmony_ci */ 211162306a36Sopenharmony_ciunsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn) 211262306a36Sopenharmony_ci{ 211362306a36Sopenharmony_ci struct ceph_inode_info *dci = ceph_inode(dir); 211462306a36Sopenharmony_ci unsigned hash; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci switch (dci->i_dir_layout.dl_dir_hash) { 211762306a36Sopenharmony_ci case 0: /* for backward compat */ 211862306a36Sopenharmony_ci case CEPH_STR_HASH_LINUX: 211962306a36Sopenharmony_ci return dn->d_name.hash; 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci default: 212262306a36Sopenharmony_ci spin_lock(&dn->d_lock); 212362306a36Sopenharmony_ci hash = ceph_str_hash(dci->i_dir_layout.dl_dir_hash, 212462306a36Sopenharmony_ci dn->d_name.name, dn->d_name.len); 212562306a36Sopenharmony_ci spin_unlock(&dn->d_lock); 212662306a36Sopenharmony_ci return hash; 212762306a36Sopenharmony_ci } 212862306a36Sopenharmony_ci} 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ciWRAP_DIR_ITER(ceph_readdir) // FIXME! 213162306a36Sopenharmony_ciconst struct file_operations ceph_dir_fops = { 213262306a36Sopenharmony_ci .read = ceph_read_dir, 213362306a36Sopenharmony_ci .iterate_shared = shared_ceph_readdir, 213462306a36Sopenharmony_ci .llseek = ceph_dir_llseek, 213562306a36Sopenharmony_ci .open = ceph_open, 213662306a36Sopenharmony_ci .release = ceph_release, 213762306a36Sopenharmony_ci .unlocked_ioctl = ceph_ioctl, 213862306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 213962306a36Sopenharmony_ci .fsync = ceph_fsync, 214062306a36Sopenharmony_ci .lock = ceph_lock, 214162306a36Sopenharmony_ci .flock = ceph_flock, 214262306a36Sopenharmony_ci}; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ciconst struct file_operations ceph_snapdir_fops = { 214562306a36Sopenharmony_ci .iterate_shared = shared_ceph_readdir, 214662306a36Sopenharmony_ci .llseek = ceph_dir_llseek, 214762306a36Sopenharmony_ci .open = ceph_open, 214862306a36Sopenharmony_ci .release = ceph_release, 214962306a36Sopenharmony_ci}; 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ciconst struct inode_operations ceph_dir_iops = { 215262306a36Sopenharmony_ci .lookup = ceph_lookup, 215362306a36Sopenharmony_ci .permission = ceph_permission, 215462306a36Sopenharmony_ci .getattr = ceph_getattr, 215562306a36Sopenharmony_ci .setattr = ceph_setattr, 215662306a36Sopenharmony_ci .listxattr = ceph_listxattr, 215762306a36Sopenharmony_ci .get_inode_acl = ceph_get_acl, 215862306a36Sopenharmony_ci .set_acl = ceph_set_acl, 215962306a36Sopenharmony_ci .mknod = ceph_mknod, 216062306a36Sopenharmony_ci .symlink = ceph_symlink, 216162306a36Sopenharmony_ci .mkdir = ceph_mkdir, 216262306a36Sopenharmony_ci .link = ceph_link, 216362306a36Sopenharmony_ci .unlink = ceph_unlink, 216462306a36Sopenharmony_ci .rmdir = ceph_unlink, 216562306a36Sopenharmony_ci .rename = ceph_rename, 216662306a36Sopenharmony_ci .create = ceph_create, 216762306a36Sopenharmony_ci .atomic_open = ceph_atomic_open, 216862306a36Sopenharmony_ci}; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ciconst struct inode_operations ceph_snapdir_iops = { 217162306a36Sopenharmony_ci .lookup = ceph_lookup, 217262306a36Sopenharmony_ci .permission = ceph_permission, 217362306a36Sopenharmony_ci .getattr = ceph_getattr, 217462306a36Sopenharmony_ci .mkdir = ceph_mkdir, 217562306a36Sopenharmony_ci .rmdir = ceph_unlink, 217662306a36Sopenharmony_ci .rename = ceph_rename, 217762306a36Sopenharmony_ci}; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ciconst struct dentry_operations ceph_dentry_ops = { 218062306a36Sopenharmony_ci .d_revalidate = ceph_d_revalidate, 218162306a36Sopenharmony_ci .d_delete = ceph_d_delete, 218262306a36Sopenharmony_ci .d_release = ceph_d_release, 218362306a36Sopenharmony_ci .d_prune = ceph_d_prune, 218462306a36Sopenharmony_ci .d_init = ceph_d_init, 218562306a36Sopenharmony_ci}; 2186