18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/ceph/ceph_debug.h> 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 58c2ecf20Sopenharmony_ci#include <linux/namei.h> 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci#include <linux/sched.h> 88c2ecf20Sopenharmony_ci#include <linux/xattr.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "super.h" 118c2ecf20Sopenharmony_ci#include "mds_client.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * Directory operations: readdir, lookup, create, link, unlink, 158c2ecf20Sopenharmony_ci * rename, etc. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* 198c2ecf20Sopenharmony_ci * Ceph MDS operations are specified in terms of a base ino and 208c2ecf20Sopenharmony_ci * relative path. Thus, the client can specify an operation on a 218c2ecf20Sopenharmony_ci * specific inode (e.g., a getattr due to fstat(2)), or as a path 228c2ecf20Sopenharmony_ci * relative to, say, the root directory. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Normally, we limit ourselves to strict inode ops (no path component) 258c2ecf20Sopenharmony_ci * or dentry operations (a single path component relative to an ino). The 268c2ecf20Sopenharmony_ci * exception to this is open_root_dentry(), which will open the mount 278c2ecf20Sopenharmony_ci * point by name. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciconst struct dentry_operations ceph_dentry_ops; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic bool __dentry_lease_is_valid(struct ceph_dentry_info *di); 338c2ecf20Sopenharmony_cistatic int __dir_lease_try_check(const struct dentry *dentry); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * Initialize ceph dentry state. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistatic int ceph_d_init(struct dentry *dentry) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct ceph_dentry_info *di; 418c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dentry->d_sb); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci di = kmem_cache_zalloc(ceph_dentry_cachep, GFP_KERNEL); 448c2ecf20Sopenharmony_ci if (!di) 458c2ecf20Sopenharmony_ci return -ENOMEM; /* oh well */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci di->dentry = dentry; 488c2ecf20Sopenharmony_ci di->lease_session = NULL; 498c2ecf20Sopenharmony_ci di->time = jiffies; 508c2ecf20Sopenharmony_ci dentry->d_fsdata = di; 518c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&di->lease_list); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci atomic64_inc(&mdsc->metric.total_dentries); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * for f_pos for readdir: 608c2ecf20Sopenharmony_ci * - hash order: 618c2ecf20Sopenharmony_ci * (0xff << 52) | ((24 bits hash) << 28) | 628c2ecf20Sopenharmony_ci * (the nth entry has hash collision); 638c2ecf20Sopenharmony_ci * - frag+name order; 648c2ecf20Sopenharmony_ci * ((frag value) << 28) | (the nth entry in frag); 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci#define OFFSET_BITS 28 678c2ecf20Sopenharmony_ci#define OFFSET_MASK ((1 << OFFSET_BITS) - 1) 688c2ecf20Sopenharmony_ci#define HASH_ORDER (0xffull << (OFFSET_BITS + 24)) 698c2ecf20Sopenharmony_ciloff_t ceph_make_fpos(unsigned high, unsigned off, bool hash_order) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci loff_t fpos = ((loff_t)high << 28) | (loff_t)off; 728c2ecf20Sopenharmony_ci if (hash_order) 738c2ecf20Sopenharmony_ci fpos |= HASH_ORDER; 748c2ecf20Sopenharmony_ci return fpos; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic bool is_hash_order(loff_t p) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return (p & HASH_ORDER) == HASH_ORDER; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic unsigned fpos_frag(loff_t p) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci return p >> OFFSET_BITS; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic unsigned fpos_hash(loff_t p) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci return ceph_frag_value(fpos_frag(p)); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic unsigned fpos_off(loff_t p) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci return p & OFFSET_MASK; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int fpos_cmp(loff_t l, loff_t r) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci int v = ceph_frag_compare(fpos_frag(l), fpos_frag(r)); 1008c2ecf20Sopenharmony_ci if (v) 1018c2ecf20Sopenharmony_ci return v; 1028c2ecf20Sopenharmony_ci return (int)(fpos_off(l) - fpos_off(r)); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* 1068c2ecf20Sopenharmony_ci * make note of the last dentry we read, so we can 1078c2ecf20Sopenharmony_ci * continue at the same lexicographical point, 1088c2ecf20Sopenharmony_ci * regardless of what dir changes take place on the 1098c2ecf20Sopenharmony_ci * server. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_cistatic int note_last_dentry(struct ceph_dir_file_info *dfi, const char *name, 1128c2ecf20Sopenharmony_ci int len, unsigned next_offset) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci char *buf = kmalloc(len+1, GFP_KERNEL); 1158c2ecf20Sopenharmony_ci if (!buf) 1168c2ecf20Sopenharmony_ci return -ENOMEM; 1178c2ecf20Sopenharmony_ci kfree(dfi->last_name); 1188c2ecf20Sopenharmony_ci dfi->last_name = buf; 1198c2ecf20Sopenharmony_ci memcpy(dfi->last_name, name, len); 1208c2ecf20Sopenharmony_ci dfi->last_name[len] = 0; 1218c2ecf20Sopenharmony_ci dfi->next_offset = next_offset; 1228c2ecf20Sopenharmony_ci dout("note_last_dentry '%s'\n", dfi->last_name); 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic struct dentry * 1288c2ecf20Sopenharmony_ci__dcache_find_get_entry(struct dentry *parent, u64 idx, 1298c2ecf20Sopenharmony_ci struct ceph_readdir_cache_control *cache_ctl) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct inode *dir = d_inode(parent); 1328c2ecf20Sopenharmony_ci struct dentry *dentry; 1338c2ecf20Sopenharmony_ci unsigned idx_mask = (PAGE_SIZE / sizeof(struct dentry *)) - 1; 1348c2ecf20Sopenharmony_ci loff_t ptr_pos = idx * sizeof(struct dentry *); 1358c2ecf20Sopenharmony_ci pgoff_t ptr_pgoff = ptr_pos >> PAGE_SHIFT; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (ptr_pos >= i_size_read(dir)) 1388c2ecf20Sopenharmony_ci return NULL; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (!cache_ctl->page || ptr_pgoff != page_index(cache_ctl->page)) { 1418c2ecf20Sopenharmony_ci ceph_readdir_cache_release(cache_ctl); 1428c2ecf20Sopenharmony_ci cache_ctl->page = find_lock_page(&dir->i_data, ptr_pgoff); 1438c2ecf20Sopenharmony_ci if (!cache_ctl->page) { 1448c2ecf20Sopenharmony_ci dout(" page %lu not found\n", ptr_pgoff); 1458c2ecf20Sopenharmony_ci return ERR_PTR(-EAGAIN); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci /* reading/filling the cache are serialized by 1488c2ecf20Sopenharmony_ci i_mutex, no need to use page lock */ 1498c2ecf20Sopenharmony_ci unlock_page(cache_ctl->page); 1508c2ecf20Sopenharmony_ci cache_ctl->dentries = kmap(cache_ctl->page); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci cache_ctl->index = idx & idx_mask; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci rcu_read_lock(); 1568c2ecf20Sopenharmony_ci spin_lock(&parent->d_lock); 1578c2ecf20Sopenharmony_ci /* check i_size again here, because empty directory can be 1588c2ecf20Sopenharmony_ci * marked as complete while not holding the i_mutex. */ 1598c2ecf20Sopenharmony_ci if (ceph_dir_is_complete_ordered(dir) && ptr_pos < i_size_read(dir)) 1608c2ecf20Sopenharmony_ci dentry = cache_ctl->dentries[cache_ctl->index]; 1618c2ecf20Sopenharmony_ci else 1628c2ecf20Sopenharmony_ci dentry = NULL; 1638c2ecf20Sopenharmony_ci spin_unlock(&parent->d_lock); 1648c2ecf20Sopenharmony_ci if (dentry && !lockref_get_not_dead(&dentry->d_lockref)) 1658c2ecf20Sopenharmony_ci dentry = NULL; 1668c2ecf20Sopenharmony_ci rcu_read_unlock(); 1678c2ecf20Sopenharmony_ci return dentry ? : ERR_PTR(-EAGAIN); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/* 1718c2ecf20Sopenharmony_ci * When possible, we try to satisfy a readdir by peeking at the 1728c2ecf20Sopenharmony_ci * dcache. We make this work by carefully ordering dentries on 1738c2ecf20Sopenharmony_ci * d_child when we initially get results back from the MDS, and 1748c2ecf20Sopenharmony_ci * falling back to a "normal" sync readdir if any dentries in the dir 1758c2ecf20Sopenharmony_ci * are dropped. 1768c2ecf20Sopenharmony_ci * 1778c2ecf20Sopenharmony_ci * Complete dir indicates that we have all dentries in the dir. It is 1788c2ecf20Sopenharmony_ci * defined IFF we hold CEPH_CAP_FILE_SHARED (which will be revoked by 1798c2ecf20Sopenharmony_ci * the MDS if/when the directory is modified). 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_cistatic int __dcache_readdir(struct file *file, struct dir_context *ctx, 1828c2ecf20Sopenharmony_ci int shared_gen) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct ceph_dir_file_info *dfi = file->private_data; 1858c2ecf20Sopenharmony_ci struct dentry *parent = file->f_path.dentry; 1868c2ecf20Sopenharmony_ci struct inode *dir = d_inode(parent); 1878c2ecf20Sopenharmony_ci struct dentry *dentry, *last = NULL; 1888c2ecf20Sopenharmony_ci struct ceph_dentry_info *di; 1898c2ecf20Sopenharmony_ci struct ceph_readdir_cache_control cache_ctl = {}; 1908c2ecf20Sopenharmony_ci u64 idx = 0; 1918c2ecf20Sopenharmony_ci int err = 0; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci dout("__dcache_readdir %p v%u at %llx\n", dir, (unsigned)shared_gen, ctx->pos); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* search start position */ 1968c2ecf20Sopenharmony_ci if (ctx->pos > 2) { 1978c2ecf20Sopenharmony_ci u64 count = div_u64(i_size_read(dir), sizeof(struct dentry *)); 1988c2ecf20Sopenharmony_ci while (count > 0) { 1998c2ecf20Sopenharmony_ci u64 step = count >> 1; 2008c2ecf20Sopenharmony_ci dentry = __dcache_find_get_entry(parent, idx + step, 2018c2ecf20Sopenharmony_ci &cache_ctl); 2028c2ecf20Sopenharmony_ci if (!dentry) { 2038c2ecf20Sopenharmony_ci /* use linar search */ 2048c2ecf20Sopenharmony_ci idx = 0; 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci if (IS_ERR(dentry)) { 2088c2ecf20Sopenharmony_ci err = PTR_ERR(dentry); 2098c2ecf20Sopenharmony_ci goto out; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci di = ceph_dentry(dentry); 2128c2ecf20Sopenharmony_ci spin_lock(&dentry->d_lock); 2138c2ecf20Sopenharmony_ci if (fpos_cmp(di->offset, ctx->pos) < 0) { 2148c2ecf20Sopenharmony_ci idx += step + 1; 2158c2ecf20Sopenharmony_ci count -= step + 1; 2168c2ecf20Sopenharmony_ci } else { 2178c2ecf20Sopenharmony_ci count = step; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci spin_unlock(&dentry->d_lock); 2208c2ecf20Sopenharmony_ci dput(dentry); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci dout("__dcache_readdir %p cache idx %llu\n", dir, idx); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci for (;;) { 2288c2ecf20Sopenharmony_ci bool emit_dentry = false; 2298c2ecf20Sopenharmony_ci dentry = __dcache_find_get_entry(parent, idx++, &cache_ctl); 2308c2ecf20Sopenharmony_ci if (!dentry) { 2318c2ecf20Sopenharmony_ci dfi->file_info.flags |= CEPH_F_ATEND; 2328c2ecf20Sopenharmony_ci err = 0; 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci if (IS_ERR(dentry)) { 2368c2ecf20Sopenharmony_ci err = PTR_ERR(dentry); 2378c2ecf20Sopenharmony_ci goto out; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci spin_lock(&dentry->d_lock); 2418c2ecf20Sopenharmony_ci di = ceph_dentry(dentry); 2428c2ecf20Sopenharmony_ci if (d_unhashed(dentry) || 2438c2ecf20Sopenharmony_ci d_really_is_negative(dentry) || 2448c2ecf20Sopenharmony_ci di->lease_shared_gen != shared_gen) { 2458c2ecf20Sopenharmony_ci spin_unlock(&dentry->d_lock); 2468c2ecf20Sopenharmony_ci dput(dentry); 2478c2ecf20Sopenharmony_ci err = -EAGAIN; 2488c2ecf20Sopenharmony_ci goto out; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci if (fpos_cmp(ctx->pos, di->offset) <= 0) { 2518c2ecf20Sopenharmony_ci __ceph_dentry_dir_lease_touch(di); 2528c2ecf20Sopenharmony_ci emit_dentry = true; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci spin_unlock(&dentry->d_lock); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (emit_dentry) { 2578c2ecf20Sopenharmony_ci dout(" %llx dentry %p %pd %p\n", di->offset, 2588c2ecf20Sopenharmony_ci dentry, dentry, d_inode(dentry)); 2598c2ecf20Sopenharmony_ci ctx->pos = di->offset; 2608c2ecf20Sopenharmony_ci if (!dir_emit(ctx, dentry->d_name.name, 2618c2ecf20Sopenharmony_ci dentry->d_name.len, ceph_present_inode(d_inode(dentry)), 2628c2ecf20Sopenharmony_ci d_inode(dentry)->i_mode >> 12)) { 2638c2ecf20Sopenharmony_ci dput(dentry); 2648c2ecf20Sopenharmony_ci err = 0; 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci ctx->pos++; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (last) 2708c2ecf20Sopenharmony_ci dput(last); 2718c2ecf20Sopenharmony_ci last = dentry; 2728c2ecf20Sopenharmony_ci } else { 2738c2ecf20Sopenharmony_ci dput(dentry); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ciout: 2778c2ecf20Sopenharmony_ci ceph_readdir_cache_release(&cache_ctl); 2788c2ecf20Sopenharmony_ci if (last) { 2798c2ecf20Sopenharmony_ci int ret; 2808c2ecf20Sopenharmony_ci di = ceph_dentry(last); 2818c2ecf20Sopenharmony_ci ret = note_last_dentry(dfi, last->d_name.name, last->d_name.len, 2828c2ecf20Sopenharmony_ci fpos_off(di->offset) + 1); 2838c2ecf20Sopenharmony_ci if (ret < 0) 2848c2ecf20Sopenharmony_ci err = ret; 2858c2ecf20Sopenharmony_ci dput(last); 2868c2ecf20Sopenharmony_ci /* last_name no longer match cache index */ 2878c2ecf20Sopenharmony_ci if (dfi->readdir_cache_idx >= 0) { 2888c2ecf20Sopenharmony_ci dfi->readdir_cache_idx = -1; 2898c2ecf20Sopenharmony_ci dfi->dir_release_count = 0; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci return err; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic bool need_send_readdir(struct ceph_dir_file_info *dfi, loff_t pos) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci if (!dfi->last_readdir) 2988c2ecf20Sopenharmony_ci return true; 2998c2ecf20Sopenharmony_ci if (is_hash_order(pos)) 3008c2ecf20Sopenharmony_ci return !ceph_frag_contains_value(dfi->frag, fpos_hash(pos)); 3018c2ecf20Sopenharmony_ci else 3028c2ecf20Sopenharmony_ci return dfi->frag != fpos_frag(pos); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int ceph_readdir(struct file *file, struct dir_context *ctx) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct ceph_dir_file_info *dfi = file->private_data; 3088c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 3098c2ecf20Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(inode); 3108c2ecf20Sopenharmony_ci struct ceph_fs_client *fsc = ceph_inode_to_client(inode); 3118c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc = fsc->mdsc; 3128c2ecf20Sopenharmony_ci int i; 3138c2ecf20Sopenharmony_ci int err; 3148c2ecf20Sopenharmony_ci unsigned frag = -1; 3158c2ecf20Sopenharmony_ci struct ceph_mds_reply_info_parsed *rinfo; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci dout("readdir %p file %p pos %llx\n", inode, file, ctx->pos); 3188c2ecf20Sopenharmony_ci if (dfi->file_info.flags & CEPH_F_ATEND) 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* always start with . and .. */ 3228c2ecf20Sopenharmony_ci if (ctx->pos == 0) { 3238c2ecf20Sopenharmony_ci dout("readdir off 0 -> '.'\n"); 3248c2ecf20Sopenharmony_ci if (!dir_emit(ctx, ".", 1, ceph_present_inode(inode), 3258c2ecf20Sopenharmony_ci inode->i_mode >> 12)) 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci ctx->pos = 1; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci if (ctx->pos == 1) { 3308c2ecf20Sopenharmony_ci u64 ino; 3318c2ecf20Sopenharmony_ci struct dentry *dentry = file->f_path.dentry; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci spin_lock(&dentry->d_lock); 3348c2ecf20Sopenharmony_ci ino = ceph_present_inode(dentry->d_parent->d_inode); 3358c2ecf20Sopenharmony_ci spin_unlock(&dentry->d_lock); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci dout("readdir off 1 -> '..'\n"); 3388c2ecf20Sopenharmony_ci if (!dir_emit(ctx, "..", 2, ino, inode->i_mode >> 12)) 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci ctx->pos = 2; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 3448c2ecf20Sopenharmony_ci /* request Fx cap. if have Fx, we don't need to release Fs cap 3458c2ecf20Sopenharmony_ci * for later create/unlink. */ 3468c2ecf20Sopenharmony_ci __ceph_touch_fmode(ci, mdsc, CEPH_FILE_MODE_WR); 3478c2ecf20Sopenharmony_ci /* can we use the dcache? */ 3488c2ecf20Sopenharmony_ci if (ceph_test_mount_opt(fsc, DCACHE) && 3498c2ecf20Sopenharmony_ci !ceph_test_mount_opt(fsc, NOASYNCREADDIR) && 3508c2ecf20Sopenharmony_ci ceph_snap(inode) != CEPH_SNAPDIR && 3518c2ecf20Sopenharmony_ci __ceph_dir_is_complete_ordered(ci) && 3528c2ecf20Sopenharmony_ci __ceph_caps_issued_mask_metric(ci, CEPH_CAP_FILE_SHARED, 1)) { 3538c2ecf20Sopenharmony_ci int shared_gen = atomic_read(&ci->i_shared_gen); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 3568c2ecf20Sopenharmony_ci err = __dcache_readdir(file, ctx, shared_gen); 3578c2ecf20Sopenharmony_ci if (err != -EAGAIN) 3588c2ecf20Sopenharmony_ci return err; 3598c2ecf20Sopenharmony_ci } else { 3608c2ecf20Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* proceed with a normal readdir */ 3648c2ecf20Sopenharmony_cimore: 3658c2ecf20Sopenharmony_ci /* do we have the correct frag content buffered? */ 3668c2ecf20Sopenharmony_ci if (need_send_readdir(dfi, ctx->pos)) { 3678c2ecf20Sopenharmony_ci struct ceph_mds_request *req; 3688c2ecf20Sopenharmony_ci int op = ceph_snap(inode) == CEPH_SNAPDIR ? 3698c2ecf20Sopenharmony_ci CEPH_MDS_OP_LSSNAP : CEPH_MDS_OP_READDIR; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* discard old result, if any */ 3728c2ecf20Sopenharmony_ci if (dfi->last_readdir) { 3738c2ecf20Sopenharmony_ci ceph_mdsc_put_request(dfi->last_readdir); 3748c2ecf20Sopenharmony_ci dfi->last_readdir = NULL; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (is_hash_order(ctx->pos)) { 3788c2ecf20Sopenharmony_ci /* fragtree isn't always accurate. choose frag 3798c2ecf20Sopenharmony_ci * based on previous reply when possible. */ 3808c2ecf20Sopenharmony_ci if (frag == (unsigned)-1) 3818c2ecf20Sopenharmony_ci frag = ceph_choose_frag(ci, fpos_hash(ctx->pos), 3828c2ecf20Sopenharmony_ci NULL, NULL); 3838c2ecf20Sopenharmony_ci } else { 3848c2ecf20Sopenharmony_ci frag = fpos_frag(ctx->pos); 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci dout("readdir fetching %llx.%llx frag %x offset '%s'\n", 3888c2ecf20Sopenharmony_ci ceph_vinop(inode), frag, dfi->last_name); 3898c2ecf20Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); 3908c2ecf20Sopenharmony_ci if (IS_ERR(req)) 3918c2ecf20Sopenharmony_ci return PTR_ERR(req); 3928c2ecf20Sopenharmony_ci err = ceph_alloc_readdir_reply_buffer(req, inode); 3938c2ecf20Sopenharmony_ci if (err) { 3948c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); 3958c2ecf20Sopenharmony_ci return err; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci /* hints to request -> mds selection code */ 3988c2ecf20Sopenharmony_ci req->r_direct_mode = USE_AUTH_MDS; 3998c2ecf20Sopenharmony_ci if (op == CEPH_MDS_OP_READDIR) { 4008c2ecf20Sopenharmony_ci req->r_direct_hash = ceph_frag_value(frag); 4018c2ecf20Sopenharmony_ci __set_bit(CEPH_MDS_R_DIRECT_IS_HASH, &req->r_req_flags); 4028c2ecf20Sopenharmony_ci req->r_inode_drop = CEPH_CAP_FILE_EXCL; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci if (dfi->last_name) { 4058c2ecf20Sopenharmony_ci req->r_path2 = kstrdup(dfi->last_name, GFP_KERNEL); 4068c2ecf20Sopenharmony_ci if (!req->r_path2) { 4078c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); 4088c2ecf20Sopenharmony_ci return -ENOMEM; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci } else if (is_hash_order(ctx->pos)) { 4118c2ecf20Sopenharmony_ci req->r_args.readdir.offset_hash = 4128c2ecf20Sopenharmony_ci cpu_to_le32(fpos_hash(ctx->pos)); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci req->r_dir_release_cnt = dfi->dir_release_count; 4168c2ecf20Sopenharmony_ci req->r_dir_ordered_cnt = dfi->dir_ordered_count; 4178c2ecf20Sopenharmony_ci req->r_readdir_cache_idx = dfi->readdir_cache_idx; 4188c2ecf20Sopenharmony_ci req->r_readdir_offset = dfi->next_offset; 4198c2ecf20Sopenharmony_ci req->r_args.readdir.frag = cpu_to_le32(frag); 4208c2ecf20Sopenharmony_ci req->r_args.readdir.flags = 4218c2ecf20Sopenharmony_ci cpu_to_le16(CEPH_READDIR_REPLY_BITFLAGS); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci req->r_inode = inode; 4248c2ecf20Sopenharmony_ci ihold(inode); 4258c2ecf20Sopenharmony_ci req->r_dentry = dget(file->f_path.dentry); 4268c2ecf20Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, NULL, req); 4278c2ecf20Sopenharmony_ci if (err < 0) { 4288c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); 4298c2ecf20Sopenharmony_ci return err; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci dout("readdir got and parsed readdir result=%d on " 4328c2ecf20Sopenharmony_ci "frag %x, end=%d, complete=%d, hash_order=%d\n", 4338c2ecf20Sopenharmony_ci err, frag, 4348c2ecf20Sopenharmony_ci (int)req->r_reply_info.dir_end, 4358c2ecf20Sopenharmony_ci (int)req->r_reply_info.dir_complete, 4368c2ecf20Sopenharmony_ci (int)req->r_reply_info.hash_order); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci rinfo = &req->r_reply_info; 4398c2ecf20Sopenharmony_ci if (le32_to_cpu(rinfo->dir_dir->frag) != frag) { 4408c2ecf20Sopenharmony_ci frag = le32_to_cpu(rinfo->dir_dir->frag); 4418c2ecf20Sopenharmony_ci if (!rinfo->hash_order) { 4428c2ecf20Sopenharmony_ci dfi->next_offset = req->r_readdir_offset; 4438c2ecf20Sopenharmony_ci /* adjust ctx->pos to beginning of frag */ 4448c2ecf20Sopenharmony_ci ctx->pos = ceph_make_fpos(frag, 4458c2ecf20Sopenharmony_ci dfi->next_offset, 4468c2ecf20Sopenharmony_ci false); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci dfi->frag = frag; 4518c2ecf20Sopenharmony_ci dfi->last_readdir = req; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (test_bit(CEPH_MDS_R_DID_PREPOPULATE, &req->r_req_flags)) { 4548c2ecf20Sopenharmony_ci dfi->readdir_cache_idx = req->r_readdir_cache_idx; 4558c2ecf20Sopenharmony_ci if (dfi->readdir_cache_idx < 0) { 4568c2ecf20Sopenharmony_ci /* preclude from marking dir ordered */ 4578c2ecf20Sopenharmony_ci dfi->dir_ordered_count = 0; 4588c2ecf20Sopenharmony_ci } else if (ceph_frag_is_leftmost(frag) && 4598c2ecf20Sopenharmony_ci dfi->next_offset == 2) { 4608c2ecf20Sopenharmony_ci /* note dir version at start of readdir so 4618c2ecf20Sopenharmony_ci * we can tell if any dentries get dropped */ 4628c2ecf20Sopenharmony_ci dfi->dir_release_count = req->r_dir_release_cnt; 4638c2ecf20Sopenharmony_ci dfi->dir_ordered_count = req->r_dir_ordered_cnt; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci } else { 4668c2ecf20Sopenharmony_ci dout("readdir !did_prepopulate\n"); 4678c2ecf20Sopenharmony_ci /* disable readdir cache */ 4688c2ecf20Sopenharmony_ci dfi->readdir_cache_idx = -1; 4698c2ecf20Sopenharmony_ci /* preclude from marking dir complete */ 4708c2ecf20Sopenharmony_ci dfi->dir_release_count = 0; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* note next offset and last dentry name */ 4748c2ecf20Sopenharmony_ci if (rinfo->dir_nr > 0) { 4758c2ecf20Sopenharmony_ci struct ceph_mds_reply_dir_entry *rde = 4768c2ecf20Sopenharmony_ci rinfo->dir_entries + (rinfo->dir_nr-1); 4778c2ecf20Sopenharmony_ci unsigned next_offset = req->r_reply_info.dir_end ? 4788c2ecf20Sopenharmony_ci 2 : (fpos_off(rde->offset) + 1); 4798c2ecf20Sopenharmony_ci err = note_last_dentry(dfi, rde->name, rde->name_len, 4808c2ecf20Sopenharmony_ci next_offset); 4818c2ecf20Sopenharmony_ci if (err) { 4828c2ecf20Sopenharmony_ci ceph_mdsc_put_request(dfi->last_readdir); 4838c2ecf20Sopenharmony_ci dfi->last_readdir = NULL; 4848c2ecf20Sopenharmony_ci return err; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci } else if (req->r_reply_info.dir_end) { 4878c2ecf20Sopenharmony_ci dfi->next_offset = 2; 4888c2ecf20Sopenharmony_ci /* keep last name */ 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci rinfo = &dfi->last_readdir->r_reply_info; 4938c2ecf20Sopenharmony_ci dout("readdir frag %x num %d pos %llx chunk first %llx\n", 4948c2ecf20Sopenharmony_ci dfi->frag, rinfo->dir_nr, ctx->pos, 4958c2ecf20Sopenharmony_ci rinfo->dir_nr ? rinfo->dir_entries[0].offset : 0LL); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci i = 0; 4988c2ecf20Sopenharmony_ci /* search start position */ 4998c2ecf20Sopenharmony_ci if (rinfo->dir_nr > 0) { 5008c2ecf20Sopenharmony_ci int step, nr = rinfo->dir_nr; 5018c2ecf20Sopenharmony_ci while (nr > 0) { 5028c2ecf20Sopenharmony_ci step = nr >> 1; 5038c2ecf20Sopenharmony_ci if (rinfo->dir_entries[i + step].offset < ctx->pos) { 5048c2ecf20Sopenharmony_ci i += step + 1; 5058c2ecf20Sopenharmony_ci nr -= step + 1; 5068c2ecf20Sopenharmony_ci } else { 5078c2ecf20Sopenharmony_ci nr = step; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci for (; i < rinfo->dir_nr; i++) { 5128c2ecf20Sopenharmony_ci struct ceph_mds_reply_dir_entry *rde = rinfo->dir_entries + i; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci BUG_ON(rde->offset < ctx->pos); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci ctx->pos = rde->offset; 5178c2ecf20Sopenharmony_ci dout("readdir (%d/%d) -> %llx '%.*s' %p\n", 5188c2ecf20Sopenharmony_ci i, rinfo->dir_nr, ctx->pos, 5198c2ecf20Sopenharmony_ci rde->name_len, rde->name, &rde->inode.in); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci BUG_ON(!rde->inode.in); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (!dir_emit(ctx, rde->name, rde->name_len, 5248c2ecf20Sopenharmony_ci ceph_present_ino(inode->i_sb, le64_to_cpu(rde->inode.in->ino)), 5258c2ecf20Sopenharmony_ci le32_to_cpu(rde->inode.in->mode) >> 12)) { 5268c2ecf20Sopenharmony_ci /* 5278c2ecf20Sopenharmony_ci * NOTE: Here no need to put the 'dfi->last_readdir', 5288c2ecf20Sopenharmony_ci * because when dir_emit stops us it's most likely 5298c2ecf20Sopenharmony_ci * doesn't have enough memory, etc. So for next readdir 5308c2ecf20Sopenharmony_ci * it will continue. 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ci dout("filldir stopping us...\n"); 5338c2ecf20Sopenharmony_ci return 0; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci ctx->pos++; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci ceph_mdsc_put_request(dfi->last_readdir); 5398c2ecf20Sopenharmony_ci dfi->last_readdir = NULL; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (dfi->next_offset > 2) { 5428c2ecf20Sopenharmony_ci frag = dfi->frag; 5438c2ecf20Sopenharmony_ci goto more; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* more frags? */ 5478c2ecf20Sopenharmony_ci if (!ceph_frag_is_rightmost(dfi->frag)) { 5488c2ecf20Sopenharmony_ci frag = ceph_frag_next(dfi->frag); 5498c2ecf20Sopenharmony_ci if (is_hash_order(ctx->pos)) { 5508c2ecf20Sopenharmony_ci loff_t new_pos = ceph_make_fpos(ceph_frag_value(frag), 5518c2ecf20Sopenharmony_ci dfi->next_offset, true); 5528c2ecf20Sopenharmony_ci if (new_pos > ctx->pos) 5538c2ecf20Sopenharmony_ci ctx->pos = new_pos; 5548c2ecf20Sopenharmony_ci /* keep last_name */ 5558c2ecf20Sopenharmony_ci } else { 5568c2ecf20Sopenharmony_ci ctx->pos = ceph_make_fpos(frag, dfi->next_offset, 5578c2ecf20Sopenharmony_ci false); 5588c2ecf20Sopenharmony_ci kfree(dfi->last_name); 5598c2ecf20Sopenharmony_ci dfi->last_name = NULL; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci dout("readdir next frag is %x\n", frag); 5628c2ecf20Sopenharmony_ci goto more; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci dfi->file_info.flags |= CEPH_F_ATEND; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* 5678c2ecf20Sopenharmony_ci * if dir_release_count still matches the dir, no dentries 5688c2ecf20Sopenharmony_ci * were released during the whole readdir, and we should have 5698c2ecf20Sopenharmony_ci * the complete dir contents in our cache. 5708c2ecf20Sopenharmony_ci */ 5718c2ecf20Sopenharmony_ci if (atomic64_read(&ci->i_release_count) == 5728c2ecf20Sopenharmony_ci dfi->dir_release_count) { 5738c2ecf20Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 5748c2ecf20Sopenharmony_ci if (dfi->dir_ordered_count == 5758c2ecf20Sopenharmony_ci atomic64_read(&ci->i_ordered_count)) { 5768c2ecf20Sopenharmony_ci dout(" marking %p complete and ordered\n", inode); 5778c2ecf20Sopenharmony_ci /* use i_size to track number of entries in 5788c2ecf20Sopenharmony_ci * readdir cache */ 5798c2ecf20Sopenharmony_ci BUG_ON(dfi->readdir_cache_idx < 0); 5808c2ecf20Sopenharmony_ci i_size_write(inode, dfi->readdir_cache_idx * 5818c2ecf20Sopenharmony_ci sizeof(struct dentry*)); 5828c2ecf20Sopenharmony_ci } else { 5838c2ecf20Sopenharmony_ci dout(" marking %p complete\n", inode); 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci __ceph_dir_set_complete(ci, dfi->dir_release_count, 5868c2ecf20Sopenharmony_ci dfi->dir_ordered_count); 5878c2ecf20Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci dout("readdir %p file %p done.\n", inode, file); 5918c2ecf20Sopenharmony_ci return 0; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic void reset_readdir(struct ceph_dir_file_info *dfi) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci if (dfi->last_readdir) { 5978c2ecf20Sopenharmony_ci ceph_mdsc_put_request(dfi->last_readdir); 5988c2ecf20Sopenharmony_ci dfi->last_readdir = NULL; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci kfree(dfi->last_name); 6018c2ecf20Sopenharmony_ci dfi->last_name = NULL; 6028c2ecf20Sopenharmony_ci dfi->dir_release_count = 0; 6038c2ecf20Sopenharmony_ci dfi->readdir_cache_idx = -1; 6048c2ecf20Sopenharmony_ci dfi->next_offset = 2; /* compensate for . and .. */ 6058c2ecf20Sopenharmony_ci dfi->file_info.flags &= ~CEPH_F_ATEND; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci/* 6098c2ecf20Sopenharmony_ci * discard buffered readdir content on seekdir(0), or seek to new frag, 6108c2ecf20Sopenharmony_ci * or seek prior to current chunk 6118c2ecf20Sopenharmony_ci */ 6128c2ecf20Sopenharmony_cistatic bool need_reset_readdir(struct ceph_dir_file_info *dfi, loff_t new_pos) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci struct ceph_mds_reply_info_parsed *rinfo; 6158c2ecf20Sopenharmony_ci loff_t chunk_offset; 6168c2ecf20Sopenharmony_ci if (new_pos == 0) 6178c2ecf20Sopenharmony_ci return true; 6188c2ecf20Sopenharmony_ci if (is_hash_order(new_pos)) { 6198c2ecf20Sopenharmony_ci /* no need to reset last_name for a forward seek when 6208c2ecf20Sopenharmony_ci * dentries are sotred in hash order */ 6218c2ecf20Sopenharmony_ci } else if (dfi->frag != fpos_frag(new_pos)) { 6228c2ecf20Sopenharmony_ci return true; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci rinfo = dfi->last_readdir ? &dfi->last_readdir->r_reply_info : NULL; 6258c2ecf20Sopenharmony_ci if (!rinfo || !rinfo->dir_nr) 6268c2ecf20Sopenharmony_ci return true; 6278c2ecf20Sopenharmony_ci chunk_offset = rinfo->dir_entries[0].offset; 6288c2ecf20Sopenharmony_ci return new_pos < chunk_offset || 6298c2ecf20Sopenharmony_ci is_hash_order(new_pos) != is_hash_order(chunk_offset); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct ceph_dir_file_info *dfi = file->private_data; 6358c2ecf20Sopenharmony_ci struct inode *inode = file->f_mapping->host; 6368c2ecf20Sopenharmony_ci loff_t retval; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci inode_lock(inode); 6398c2ecf20Sopenharmony_ci retval = -EINVAL; 6408c2ecf20Sopenharmony_ci switch (whence) { 6418c2ecf20Sopenharmony_ci case SEEK_CUR: 6428c2ecf20Sopenharmony_ci offset += file->f_pos; 6438c2ecf20Sopenharmony_ci case SEEK_SET: 6448c2ecf20Sopenharmony_ci break; 6458c2ecf20Sopenharmony_ci case SEEK_END: 6468c2ecf20Sopenharmony_ci retval = -EOPNOTSUPP; 6478c2ecf20Sopenharmony_ci default: 6488c2ecf20Sopenharmony_ci goto out; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (offset >= 0) { 6528c2ecf20Sopenharmony_ci if (need_reset_readdir(dfi, offset)) { 6538c2ecf20Sopenharmony_ci dout("dir_llseek dropping %p content\n", file); 6548c2ecf20Sopenharmony_ci reset_readdir(dfi); 6558c2ecf20Sopenharmony_ci } else if (is_hash_order(offset) && offset > file->f_pos) { 6568c2ecf20Sopenharmony_ci /* for hash offset, we don't know if a forward seek 6578c2ecf20Sopenharmony_ci * is within same frag */ 6588c2ecf20Sopenharmony_ci dfi->dir_release_count = 0; 6598c2ecf20Sopenharmony_ci dfi->readdir_cache_idx = -1; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (offset != file->f_pos) { 6638c2ecf20Sopenharmony_ci file->f_pos = offset; 6648c2ecf20Sopenharmony_ci file->f_version = 0; 6658c2ecf20Sopenharmony_ci dfi->file_info.flags &= ~CEPH_F_ATEND; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci retval = offset; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ciout: 6708c2ecf20Sopenharmony_ci inode_unlock(inode); 6718c2ecf20Sopenharmony_ci return retval; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci/* 6758c2ecf20Sopenharmony_ci * Handle lookups for the hidden .snap directory. 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_ciint ceph_handle_snapdir(struct ceph_mds_request *req, 6788c2ecf20Sopenharmony_ci struct dentry *dentry, int err) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); 6818c2ecf20Sopenharmony_ci struct inode *parent = d_inode(dentry->d_parent); /* we hold i_mutex */ 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* .snap dir? */ 6848c2ecf20Sopenharmony_ci if (err == -ENOENT && 6858c2ecf20Sopenharmony_ci ceph_snap(parent) == CEPH_NOSNAP && 6868c2ecf20Sopenharmony_ci strcmp(dentry->d_name.name, 6878c2ecf20Sopenharmony_ci fsc->mount_options->snapdir_name) == 0) { 6888c2ecf20Sopenharmony_ci struct inode *inode = ceph_get_snapdir(parent); 6898c2ecf20Sopenharmony_ci dout("ENOENT on snapdir %p '%pd', linking to snapdir %p\n", 6908c2ecf20Sopenharmony_ci dentry, dentry, inode); 6918c2ecf20Sopenharmony_ci BUG_ON(!d_unhashed(dentry)); 6928c2ecf20Sopenharmony_ci d_add(dentry, inode); 6938c2ecf20Sopenharmony_ci err = 0; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci return err; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci/* 6998c2ecf20Sopenharmony_ci * Figure out final result of a lookup/open request. 7008c2ecf20Sopenharmony_ci * 7018c2ecf20Sopenharmony_ci * Mainly, make sure we return the final req->r_dentry (if it already 7028c2ecf20Sopenharmony_ci * existed) in place of the original VFS-provided dentry when they 7038c2ecf20Sopenharmony_ci * differ. 7048c2ecf20Sopenharmony_ci * 7058c2ecf20Sopenharmony_ci * Gracefully handle the case where the MDS replies with -ENOENT and 7068c2ecf20Sopenharmony_ci * no trace (which it may do, at its discretion, e.g., if it doesn't 7078c2ecf20Sopenharmony_ci * care to issue a lease on the negative dentry). 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_cistruct dentry *ceph_finish_lookup(struct ceph_mds_request *req, 7108c2ecf20Sopenharmony_ci struct dentry *dentry, int err) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci if (err == -ENOENT) { 7138c2ecf20Sopenharmony_ci /* no trace? */ 7148c2ecf20Sopenharmony_ci err = 0; 7158c2ecf20Sopenharmony_ci if (!req->r_reply_info.head->is_dentry) { 7168c2ecf20Sopenharmony_ci dout("ENOENT and no trace, dentry %p inode %p\n", 7178c2ecf20Sopenharmony_ci dentry, d_inode(dentry)); 7188c2ecf20Sopenharmony_ci if (d_really_is_positive(dentry)) { 7198c2ecf20Sopenharmony_ci d_drop(dentry); 7208c2ecf20Sopenharmony_ci err = -ENOENT; 7218c2ecf20Sopenharmony_ci } else { 7228c2ecf20Sopenharmony_ci d_add(dentry, NULL); 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci if (err) 7278c2ecf20Sopenharmony_ci dentry = ERR_PTR(err); 7288c2ecf20Sopenharmony_ci else if (dentry != req->r_dentry) 7298c2ecf20Sopenharmony_ci dentry = dget(req->r_dentry); /* we got spliced */ 7308c2ecf20Sopenharmony_ci else 7318c2ecf20Sopenharmony_ci dentry = NULL; 7328c2ecf20Sopenharmony_ci return dentry; 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic bool is_root_ceph_dentry(struct inode *inode, struct dentry *dentry) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci return ceph_ino(inode) == CEPH_INO_ROOT && 7388c2ecf20Sopenharmony_ci strncmp(dentry->d_name.name, ".ceph", 5) == 0; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci/* 7428c2ecf20Sopenharmony_ci * Look up a single dir entry. If there is a lookup intent, inform 7438c2ecf20Sopenharmony_ci * the MDS so that it gets our 'caps wanted' value in a single op. 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_cistatic struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry, 7468c2ecf20Sopenharmony_ci unsigned int flags) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); 7498c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); 7508c2ecf20Sopenharmony_ci struct ceph_mds_request *req; 7518c2ecf20Sopenharmony_ci int op; 7528c2ecf20Sopenharmony_ci int mask; 7538c2ecf20Sopenharmony_ci int err; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci dout("lookup %p dentry %p '%pd'\n", 7568c2ecf20Sopenharmony_ci dir, dentry, dentry); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (dentry->d_name.len > NAME_MAX) 7598c2ecf20Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* can we conclude ENOENT locally? */ 7628c2ecf20Sopenharmony_ci if (d_really_is_negative(dentry)) { 7638c2ecf20Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(dir); 7648c2ecf20Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 7678c2ecf20Sopenharmony_ci dout(" dir %p flags are 0x%lx\n", dir, ci->i_ceph_flags); 7688c2ecf20Sopenharmony_ci if (strncmp(dentry->d_name.name, 7698c2ecf20Sopenharmony_ci fsc->mount_options->snapdir_name, 7708c2ecf20Sopenharmony_ci dentry->d_name.len) && 7718c2ecf20Sopenharmony_ci !is_root_ceph_dentry(dir, dentry) && 7728c2ecf20Sopenharmony_ci ceph_test_mount_opt(fsc, DCACHE) && 7738c2ecf20Sopenharmony_ci __ceph_dir_is_complete(ci) && 7748c2ecf20Sopenharmony_ci __ceph_caps_issued_mask_metric(ci, CEPH_CAP_FILE_SHARED, 1)) { 7758c2ecf20Sopenharmony_ci __ceph_touch_fmode(ci, mdsc, CEPH_FILE_MODE_RD); 7768c2ecf20Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 7778c2ecf20Sopenharmony_ci dout(" dir %p complete, -ENOENT\n", dir); 7788c2ecf20Sopenharmony_ci d_add(dentry, NULL); 7798c2ecf20Sopenharmony_ci di->lease_shared_gen = atomic_read(&ci->i_shared_gen); 7808c2ecf20Sopenharmony_ci return NULL; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci op = ceph_snap(dir) == CEPH_SNAPDIR ? 7868c2ecf20Sopenharmony_ci CEPH_MDS_OP_LOOKUPSNAP : CEPH_MDS_OP_LOOKUP; 7878c2ecf20Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, op, USE_ANY_MDS); 7888c2ecf20Sopenharmony_ci if (IS_ERR(req)) 7898c2ecf20Sopenharmony_ci return ERR_CAST(req); 7908c2ecf20Sopenharmony_ci req->r_dentry = dget(dentry); 7918c2ecf20Sopenharmony_ci req->r_num_caps = 2; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci mask = CEPH_STAT_CAP_INODE | CEPH_CAP_AUTH_SHARED; 7948c2ecf20Sopenharmony_ci if (ceph_security_xattr_wanted(dir)) 7958c2ecf20Sopenharmony_ci mask |= CEPH_CAP_XATTR_SHARED; 7968c2ecf20Sopenharmony_ci req->r_args.getattr.mask = cpu_to_le32(mask); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci req->r_parent = dir; 7998c2ecf20Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 8008c2ecf20Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, NULL, req); 8018c2ecf20Sopenharmony_ci err = ceph_handle_snapdir(req, dentry, err); 8028c2ecf20Sopenharmony_ci dentry = ceph_finish_lookup(req, dentry, err); 8038c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); /* will dput(dentry) */ 8048c2ecf20Sopenharmony_ci dout("lookup result=%p\n", dentry); 8058c2ecf20Sopenharmony_ci return dentry; 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci/* 8098c2ecf20Sopenharmony_ci * If we do a create but get no trace back from the MDS, follow up with 8108c2ecf20Sopenharmony_ci * a lookup (the VFS expects us to link up the provided dentry). 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_ciint ceph_handle_notrace_create(struct inode *dir, struct dentry *dentry) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci struct dentry *result = ceph_lookup(dir, dentry, 0); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (result && !IS_ERR(result)) { 8178c2ecf20Sopenharmony_ci /* 8188c2ecf20Sopenharmony_ci * We created the item, then did a lookup, and found 8198c2ecf20Sopenharmony_ci * it was already linked to another inode we already 8208c2ecf20Sopenharmony_ci * had in our cache (and thus got spliced). To not 8218c2ecf20Sopenharmony_ci * confuse VFS (especially when inode is a directory), 8228c2ecf20Sopenharmony_ci * we don't link our dentry to that inode, return an 8238c2ecf20Sopenharmony_ci * error instead. 8248c2ecf20Sopenharmony_ci * 8258c2ecf20Sopenharmony_ci * This event should be rare and it happens only when 8268c2ecf20Sopenharmony_ci * we talk to old MDS. Recent MDS does not send traceless 8278c2ecf20Sopenharmony_ci * reply for request that creates new inode. 8288c2ecf20Sopenharmony_ci */ 8298c2ecf20Sopenharmony_ci d_drop(result); 8308c2ecf20Sopenharmony_ci return -ESTALE; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci return PTR_ERR(result); 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic int ceph_mknod(struct inode *dir, struct dentry *dentry, 8368c2ecf20Sopenharmony_ci umode_t mode, dev_t rdev) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); 8398c2ecf20Sopenharmony_ci struct ceph_mds_request *req; 8408c2ecf20Sopenharmony_ci struct ceph_acl_sec_ctx as_ctx = {}; 8418c2ecf20Sopenharmony_ci int err; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (ceph_snap(dir) != CEPH_NOSNAP) 8448c2ecf20Sopenharmony_ci return -EROFS; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (ceph_quota_is_max_files_exceeded(dir)) { 8478c2ecf20Sopenharmony_ci err = -EDQUOT; 8488c2ecf20Sopenharmony_ci goto out; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci err = ceph_pre_init_acls(dir, &mode, &as_ctx); 8528c2ecf20Sopenharmony_ci if (err < 0) 8538c2ecf20Sopenharmony_ci goto out; 8548c2ecf20Sopenharmony_ci err = ceph_security_init_secctx(dentry, mode, &as_ctx); 8558c2ecf20Sopenharmony_ci if (err < 0) 8568c2ecf20Sopenharmony_ci goto out; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci dout("mknod in dir %p dentry %p mode 0%ho rdev %d\n", 8598c2ecf20Sopenharmony_ci dir, dentry, mode, rdev); 8608c2ecf20Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_MKNOD, USE_AUTH_MDS); 8618c2ecf20Sopenharmony_ci if (IS_ERR(req)) { 8628c2ecf20Sopenharmony_ci err = PTR_ERR(req); 8638c2ecf20Sopenharmony_ci goto out; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci req->r_dentry = dget(dentry); 8668c2ecf20Sopenharmony_ci req->r_num_caps = 2; 8678c2ecf20Sopenharmony_ci req->r_parent = dir; 8688c2ecf20Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 8698c2ecf20Sopenharmony_ci req->r_args.mknod.mode = cpu_to_le32(mode); 8708c2ecf20Sopenharmony_ci req->r_args.mknod.rdev = cpu_to_le32(rdev); 8718c2ecf20Sopenharmony_ci req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL; 8728c2ecf20Sopenharmony_ci req->r_dentry_unless = CEPH_CAP_FILE_EXCL; 8738c2ecf20Sopenharmony_ci if (as_ctx.pagelist) { 8748c2ecf20Sopenharmony_ci req->r_pagelist = as_ctx.pagelist; 8758c2ecf20Sopenharmony_ci as_ctx.pagelist = NULL; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, dir, req); 8788c2ecf20Sopenharmony_ci if (!err && !req->r_reply_info.head->is_dentry) 8798c2ecf20Sopenharmony_ci err = ceph_handle_notrace_create(dir, dentry); 8808c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); 8818c2ecf20Sopenharmony_ciout: 8828c2ecf20Sopenharmony_ci if (!err) 8838c2ecf20Sopenharmony_ci ceph_init_inode_acls(d_inode(dentry), &as_ctx); 8848c2ecf20Sopenharmony_ci else 8858c2ecf20Sopenharmony_ci d_drop(dentry); 8868c2ecf20Sopenharmony_ci ceph_release_acl_sec_ctx(&as_ctx); 8878c2ecf20Sopenharmony_ci return err; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic int ceph_create(struct inode *dir, struct dentry *dentry, umode_t mode, 8918c2ecf20Sopenharmony_ci bool excl) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci return ceph_mknod(dir, dentry, mode, 0); 8948c2ecf20Sopenharmony_ci} 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_cistatic int ceph_symlink(struct inode *dir, struct dentry *dentry, 8978c2ecf20Sopenharmony_ci const char *dest) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); 9008c2ecf20Sopenharmony_ci struct ceph_mds_request *req; 9018c2ecf20Sopenharmony_ci struct ceph_acl_sec_ctx as_ctx = {}; 9028c2ecf20Sopenharmony_ci int err; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (ceph_snap(dir) != CEPH_NOSNAP) 9058c2ecf20Sopenharmony_ci return -EROFS; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (ceph_quota_is_max_files_exceeded(dir)) { 9088c2ecf20Sopenharmony_ci err = -EDQUOT; 9098c2ecf20Sopenharmony_ci goto out; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci err = ceph_security_init_secctx(dentry, S_IFLNK | 0777, &as_ctx); 9138c2ecf20Sopenharmony_ci if (err < 0) 9148c2ecf20Sopenharmony_ci goto out; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest); 9178c2ecf20Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS); 9188c2ecf20Sopenharmony_ci if (IS_ERR(req)) { 9198c2ecf20Sopenharmony_ci err = PTR_ERR(req); 9208c2ecf20Sopenharmony_ci goto out; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci req->r_path2 = kstrdup(dest, GFP_KERNEL); 9238c2ecf20Sopenharmony_ci if (!req->r_path2) { 9248c2ecf20Sopenharmony_ci err = -ENOMEM; 9258c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); 9268c2ecf20Sopenharmony_ci goto out; 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci req->r_parent = dir; 9298c2ecf20Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 9308c2ecf20Sopenharmony_ci req->r_dentry = dget(dentry); 9318c2ecf20Sopenharmony_ci req->r_num_caps = 2; 9328c2ecf20Sopenharmony_ci req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL; 9338c2ecf20Sopenharmony_ci req->r_dentry_unless = CEPH_CAP_FILE_EXCL; 9348c2ecf20Sopenharmony_ci if (as_ctx.pagelist) { 9358c2ecf20Sopenharmony_ci req->r_pagelist = as_ctx.pagelist; 9368c2ecf20Sopenharmony_ci as_ctx.pagelist = NULL; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, dir, req); 9398c2ecf20Sopenharmony_ci if (!err && !req->r_reply_info.head->is_dentry) 9408c2ecf20Sopenharmony_ci err = ceph_handle_notrace_create(dir, dentry); 9418c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); 9428c2ecf20Sopenharmony_ciout: 9438c2ecf20Sopenharmony_ci if (err) 9448c2ecf20Sopenharmony_ci d_drop(dentry); 9458c2ecf20Sopenharmony_ci ceph_release_acl_sec_ctx(&as_ctx); 9468c2ecf20Sopenharmony_ci return err; 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); 9528c2ecf20Sopenharmony_ci struct ceph_mds_request *req; 9538c2ecf20Sopenharmony_ci struct ceph_acl_sec_ctx as_ctx = {}; 9548c2ecf20Sopenharmony_ci int err = -EROFS; 9558c2ecf20Sopenharmony_ci int op; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (ceph_snap(dir) == CEPH_SNAPDIR) { 9588c2ecf20Sopenharmony_ci /* mkdir .snap/foo is a MKSNAP */ 9598c2ecf20Sopenharmony_ci op = CEPH_MDS_OP_MKSNAP; 9608c2ecf20Sopenharmony_ci dout("mksnap dir %p snap '%pd' dn %p\n", dir, 9618c2ecf20Sopenharmony_ci dentry, dentry); 9628c2ecf20Sopenharmony_ci } else if (ceph_snap(dir) == CEPH_NOSNAP) { 9638c2ecf20Sopenharmony_ci dout("mkdir dir %p dn %p mode 0%ho\n", dir, dentry, mode); 9648c2ecf20Sopenharmony_ci op = CEPH_MDS_OP_MKDIR; 9658c2ecf20Sopenharmony_ci } else { 9668c2ecf20Sopenharmony_ci goto out; 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci if (op == CEPH_MDS_OP_MKDIR && 9708c2ecf20Sopenharmony_ci ceph_quota_is_max_files_exceeded(dir)) { 9718c2ecf20Sopenharmony_ci err = -EDQUOT; 9728c2ecf20Sopenharmony_ci goto out; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci mode |= S_IFDIR; 9768c2ecf20Sopenharmony_ci err = ceph_pre_init_acls(dir, &mode, &as_ctx); 9778c2ecf20Sopenharmony_ci if (err < 0) 9788c2ecf20Sopenharmony_ci goto out; 9798c2ecf20Sopenharmony_ci err = ceph_security_init_secctx(dentry, mode, &as_ctx); 9808c2ecf20Sopenharmony_ci if (err < 0) 9818c2ecf20Sopenharmony_ci goto out; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); 9848c2ecf20Sopenharmony_ci if (IS_ERR(req)) { 9858c2ecf20Sopenharmony_ci err = PTR_ERR(req); 9868c2ecf20Sopenharmony_ci goto out; 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci req->r_dentry = dget(dentry); 9908c2ecf20Sopenharmony_ci req->r_num_caps = 2; 9918c2ecf20Sopenharmony_ci req->r_parent = dir; 9928c2ecf20Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 9938c2ecf20Sopenharmony_ci req->r_args.mkdir.mode = cpu_to_le32(mode); 9948c2ecf20Sopenharmony_ci req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL; 9958c2ecf20Sopenharmony_ci req->r_dentry_unless = CEPH_CAP_FILE_EXCL; 9968c2ecf20Sopenharmony_ci if (as_ctx.pagelist) { 9978c2ecf20Sopenharmony_ci req->r_pagelist = as_ctx.pagelist; 9988c2ecf20Sopenharmony_ci as_ctx.pagelist = NULL; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, dir, req); 10018c2ecf20Sopenharmony_ci if (!err && 10028c2ecf20Sopenharmony_ci !req->r_reply_info.head->is_target && 10038c2ecf20Sopenharmony_ci !req->r_reply_info.head->is_dentry) 10048c2ecf20Sopenharmony_ci err = ceph_handle_notrace_create(dir, dentry); 10058c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); 10068c2ecf20Sopenharmony_ciout: 10078c2ecf20Sopenharmony_ci if (!err) 10088c2ecf20Sopenharmony_ci ceph_init_inode_acls(d_inode(dentry), &as_ctx); 10098c2ecf20Sopenharmony_ci else 10108c2ecf20Sopenharmony_ci d_drop(dentry); 10118c2ecf20Sopenharmony_ci ceph_release_acl_sec_ctx(&as_ctx); 10128c2ecf20Sopenharmony_ci return err; 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_cistatic int ceph_link(struct dentry *old_dentry, struct inode *dir, 10168c2ecf20Sopenharmony_ci struct dentry *dentry) 10178c2ecf20Sopenharmony_ci{ 10188c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); 10198c2ecf20Sopenharmony_ci struct ceph_mds_request *req; 10208c2ecf20Sopenharmony_ci int err; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (ceph_snap(dir) != CEPH_NOSNAP) 10238c2ecf20Sopenharmony_ci return -EROFS; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci dout("link in dir %p old_dentry %p dentry %p\n", dir, 10268c2ecf20Sopenharmony_ci old_dentry, dentry); 10278c2ecf20Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LINK, USE_AUTH_MDS); 10288c2ecf20Sopenharmony_ci if (IS_ERR(req)) { 10298c2ecf20Sopenharmony_ci d_drop(dentry); 10308c2ecf20Sopenharmony_ci return PTR_ERR(req); 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci req->r_dentry = dget(dentry); 10338c2ecf20Sopenharmony_ci req->r_num_caps = 2; 10348c2ecf20Sopenharmony_ci req->r_old_dentry = dget(old_dentry); 10358c2ecf20Sopenharmony_ci req->r_parent = dir; 10368c2ecf20Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 10378c2ecf20Sopenharmony_ci req->r_dentry_drop = CEPH_CAP_FILE_SHARED; 10388c2ecf20Sopenharmony_ci req->r_dentry_unless = CEPH_CAP_FILE_EXCL; 10398c2ecf20Sopenharmony_ci /* release LINK_SHARED on source inode (mds will lock it) */ 10408c2ecf20Sopenharmony_ci req->r_old_inode_drop = CEPH_CAP_LINK_SHARED | CEPH_CAP_LINK_EXCL; 10418c2ecf20Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, dir, req); 10428c2ecf20Sopenharmony_ci if (err) { 10438c2ecf20Sopenharmony_ci d_drop(dentry); 10448c2ecf20Sopenharmony_ci } else if (!req->r_reply_info.head->is_dentry) { 10458c2ecf20Sopenharmony_ci ihold(d_inode(old_dentry)); 10468c2ecf20Sopenharmony_ci d_instantiate(dentry, d_inode(old_dentry)); 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); 10498c2ecf20Sopenharmony_ci return err; 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic void ceph_async_unlink_cb(struct ceph_mds_client *mdsc, 10538c2ecf20Sopenharmony_ci struct ceph_mds_request *req) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci int result = req->r_err ? req->r_err : 10568c2ecf20Sopenharmony_ci le32_to_cpu(req->r_reply_info.head->result); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (result == -EJUKEBOX) 10598c2ecf20Sopenharmony_ci goto out; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* If op failed, mark everyone involved for errors */ 10628c2ecf20Sopenharmony_ci if (result) { 10638c2ecf20Sopenharmony_ci int pathlen = 0; 10648c2ecf20Sopenharmony_ci u64 base = 0; 10658c2ecf20Sopenharmony_ci char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen, 10668c2ecf20Sopenharmony_ci &base, 0); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci /* mark error on parent + clear complete */ 10698c2ecf20Sopenharmony_ci mapping_set_error(req->r_parent->i_mapping, result); 10708c2ecf20Sopenharmony_ci ceph_dir_clear_complete(req->r_parent); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci /* drop the dentry -- we don't know its status */ 10738c2ecf20Sopenharmony_ci if (!d_unhashed(req->r_dentry)) 10748c2ecf20Sopenharmony_ci d_drop(req->r_dentry); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci /* mark inode itself for an error (since metadata is bogus) */ 10778c2ecf20Sopenharmony_ci mapping_set_error(req->r_old_inode->i_mapping, result); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci pr_warn("ceph: async unlink failure path=(%llx)%s result=%d!\n", 10808c2ecf20Sopenharmony_ci base, IS_ERR(path) ? "<<bad>>" : path, result); 10818c2ecf20Sopenharmony_ci ceph_mdsc_free_path(path, pathlen); 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ciout: 10848c2ecf20Sopenharmony_ci iput(req->r_old_inode); 10858c2ecf20Sopenharmony_ci ceph_mdsc_release_dir_caps(req); 10868c2ecf20Sopenharmony_ci} 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_cistatic int get_caps_for_async_unlink(struct inode *dir, struct dentry *dentry) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(dir); 10918c2ecf20Sopenharmony_ci struct ceph_dentry_info *di; 10928c2ecf20Sopenharmony_ci int got = 0, want = CEPH_CAP_FILE_EXCL | CEPH_CAP_DIR_UNLINK; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 10958c2ecf20Sopenharmony_ci if ((__ceph_caps_issued(ci, NULL) & want) == want) { 10968c2ecf20Sopenharmony_ci ceph_take_cap_refs(ci, want, false); 10978c2ecf20Sopenharmony_ci got = want; 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* If we didn't get anything, return 0 */ 11028c2ecf20Sopenharmony_ci if (!got) 11038c2ecf20Sopenharmony_ci return 0; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci spin_lock(&dentry->d_lock); 11068c2ecf20Sopenharmony_ci di = ceph_dentry(dentry); 11078c2ecf20Sopenharmony_ci /* 11088c2ecf20Sopenharmony_ci * - We are holding Fx, which implies Fs caps. 11098c2ecf20Sopenharmony_ci * - Only support async unlink for primary linkage 11108c2ecf20Sopenharmony_ci */ 11118c2ecf20Sopenharmony_ci if (atomic_read(&ci->i_shared_gen) != di->lease_shared_gen || 11128c2ecf20Sopenharmony_ci !(di->flags & CEPH_DENTRY_PRIMARY_LINK)) 11138c2ecf20Sopenharmony_ci want = 0; 11148c2ecf20Sopenharmony_ci spin_unlock(&dentry->d_lock); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci /* Do we still want what we've got? */ 11178c2ecf20Sopenharmony_ci if (want == got) 11188c2ecf20Sopenharmony_ci return got; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci ceph_put_cap_refs(ci, got); 11218c2ecf20Sopenharmony_ci return 0; 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci/* 11258c2ecf20Sopenharmony_ci * rmdir and unlink are differ only by the metadata op code 11268c2ecf20Sopenharmony_ci */ 11278c2ecf20Sopenharmony_cistatic int ceph_unlink(struct inode *dir, struct dentry *dentry) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); 11308c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc = fsc->mdsc; 11318c2ecf20Sopenharmony_ci struct inode *inode = d_inode(dentry); 11328c2ecf20Sopenharmony_ci struct ceph_mds_request *req; 11338c2ecf20Sopenharmony_ci bool try_async = ceph_test_mount_opt(fsc, ASYNC_DIROPS); 11348c2ecf20Sopenharmony_ci int err = -EROFS; 11358c2ecf20Sopenharmony_ci int op; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci if (ceph_snap(dir) == CEPH_SNAPDIR) { 11388c2ecf20Sopenharmony_ci /* rmdir .snap/foo is RMSNAP */ 11398c2ecf20Sopenharmony_ci dout("rmsnap dir %p '%pd' dn %p\n", dir, dentry, dentry); 11408c2ecf20Sopenharmony_ci op = CEPH_MDS_OP_RMSNAP; 11418c2ecf20Sopenharmony_ci } else if (ceph_snap(dir) == CEPH_NOSNAP) { 11428c2ecf20Sopenharmony_ci dout("unlink/rmdir dir %p dn %p inode %p\n", 11438c2ecf20Sopenharmony_ci dir, dentry, inode); 11448c2ecf20Sopenharmony_ci op = d_is_dir(dentry) ? 11458c2ecf20Sopenharmony_ci CEPH_MDS_OP_RMDIR : CEPH_MDS_OP_UNLINK; 11468c2ecf20Sopenharmony_ci } else 11478c2ecf20Sopenharmony_ci goto out; 11488c2ecf20Sopenharmony_ciretry: 11498c2ecf20Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); 11508c2ecf20Sopenharmony_ci if (IS_ERR(req)) { 11518c2ecf20Sopenharmony_ci err = PTR_ERR(req); 11528c2ecf20Sopenharmony_ci goto out; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci req->r_dentry = dget(dentry); 11558c2ecf20Sopenharmony_ci req->r_num_caps = 2; 11568c2ecf20Sopenharmony_ci req->r_parent = dir; 11578c2ecf20Sopenharmony_ci req->r_dentry_drop = CEPH_CAP_FILE_SHARED; 11588c2ecf20Sopenharmony_ci req->r_dentry_unless = CEPH_CAP_FILE_EXCL; 11598c2ecf20Sopenharmony_ci req->r_inode_drop = ceph_drop_caps_for_unlink(inode); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if (try_async && op == CEPH_MDS_OP_UNLINK && 11628c2ecf20Sopenharmony_ci (req->r_dir_caps = get_caps_for_async_unlink(dir, dentry))) { 11638c2ecf20Sopenharmony_ci dout("async unlink on %llu/%.*s caps=%s", ceph_ino(dir), 11648c2ecf20Sopenharmony_ci dentry->d_name.len, dentry->d_name.name, 11658c2ecf20Sopenharmony_ci ceph_cap_string(req->r_dir_caps)); 11668c2ecf20Sopenharmony_ci set_bit(CEPH_MDS_R_ASYNC, &req->r_req_flags); 11678c2ecf20Sopenharmony_ci req->r_callback = ceph_async_unlink_cb; 11688c2ecf20Sopenharmony_ci req->r_old_inode = d_inode(dentry); 11698c2ecf20Sopenharmony_ci ihold(req->r_old_inode); 11708c2ecf20Sopenharmony_ci err = ceph_mdsc_submit_request(mdsc, dir, req); 11718c2ecf20Sopenharmony_ci if (!err) { 11728c2ecf20Sopenharmony_ci /* 11738c2ecf20Sopenharmony_ci * We have enough caps, so we assume that the unlink 11748c2ecf20Sopenharmony_ci * will succeed. Fix up the target inode and dcache. 11758c2ecf20Sopenharmony_ci */ 11768c2ecf20Sopenharmony_ci drop_nlink(inode); 11778c2ecf20Sopenharmony_ci d_delete(dentry); 11788c2ecf20Sopenharmony_ci } else if (err == -EJUKEBOX) { 11798c2ecf20Sopenharmony_ci try_async = false; 11808c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); 11818c2ecf20Sopenharmony_ci goto retry; 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci } else { 11848c2ecf20Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 11858c2ecf20Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, dir, req); 11868c2ecf20Sopenharmony_ci if (!err && !req->r_reply_info.head->is_dentry) 11878c2ecf20Sopenharmony_ci d_delete(dentry); 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); 11918c2ecf20Sopenharmony_ciout: 11928c2ecf20Sopenharmony_ci return err; 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic int ceph_rename(struct inode *old_dir, struct dentry *old_dentry, 11968c2ecf20Sopenharmony_ci struct inode *new_dir, struct dentry *new_dentry, 11978c2ecf20Sopenharmony_ci unsigned int flags) 11988c2ecf20Sopenharmony_ci{ 11998c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(old_dir->i_sb); 12008c2ecf20Sopenharmony_ci struct ceph_mds_request *req; 12018c2ecf20Sopenharmony_ci int op = CEPH_MDS_OP_RENAME; 12028c2ecf20Sopenharmony_ci int err; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci if (flags) 12058c2ecf20Sopenharmony_ci return -EINVAL; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci if (ceph_snap(old_dir) != ceph_snap(new_dir)) 12088c2ecf20Sopenharmony_ci return -EXDEV; 12098c2ecf20Sopenharmony_ci if (ceph_snap(old_dir) != CEPH_NOSNAP) { 12108c2ecf20Sopenharmony_ci if (old_dir == new_dir && ceph_snap(old_dir) == CEPH_SNAPDIR) 12118c2ecf20Sopenharmony_ci op = CEPH_MDS_OP_RENAMESNAP; 12128c2ecf20Sopenharmony_ci else 12138c2ecf20Sopenharmony_ci return -EROFS; 12148c2ecf20Sopenharmony_ci } else if (old_dir != new_dir) { 12158c2ecf20Sopenharmony_ci err = ceph_quota_check_rename(mdsc, d_inode(old_dentry), 12168c2ecf20Sopenharmony_ci new_dir); 12178c2ecf20Sopenharmony_ci if (err) 12188c2ecf20Sopenharmony_ci return err; 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci dout("rename dir %p dentry %p to dir %p dentry %p\n", 12228c2ecf20Sopenharmony_ci old_dir, old_dentry, new_dir, new_dentry); 12238c2ecf20Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); 12248c2ecf20Sopenharmony_ci if (IS_ERR(req)) 12258c2ecf20Sopenharmony_ci return PTR_ERR(req); 12268c2ecf20Sopenharmony_ci ihold(old_dir); 12278c2ecf20Sopenharmony_ci req->r_dentry = dget(new_dentry); 12288c2ecf20Sopenharmony_ci req->r_num_caps = 2; 12298c2ecf20Sopenharmony_ci req->r_old_dentry = dget(old_dentry); 12308c2ecf20Sopenharmony_ci req->r_old_dentry_dir = old_dir; 12318c2ecf20Sopenharmony_ci req->r_parent = new_dir; 12328c2ecf20Sopenharmony_ci set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); 12338c2ecf20Sopenharmony_ci req->r_old_dentry_drop = CEPH_CAP_FILE_SHARED; 12348c2ecf20Sopenharmony_ci req->r_old_dentry_unless = CEPH_CAP_FILE_EXCL; 12358c2ecf20Sopenharmony_ci req->r_dentry_drop = CEPH_CAP_FILE_SHARED; 12368c2ecf20Sopenharmony_ci req->r_dentry_unless = CEPH_CAP_FILE_EXCL; 12378c2ecf20Sopenharmony_ci /* release LINK_RDCACHE on source inode (mds will lock it) */ 12388c2ecf20Sopenharmony_ci req->r_old_inode_drop = CEPH_CAP_LINK_SHARED | CEPH_CAP_LINK_EXCL; 12398c2ecf20Sopenharmony_ci if (d_really_is_positive(new_dentry)) { 12408c2ecf20Sopenharmony_ci req->r_inode_drop = 12418c2ecf20Sopenharmony_ci ceph_drop_caps_for_unlink(d_inode(new_dentry)); 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, old_dir, req); 12448c2ecf20Sopenharmony_ci if (!err && !req->r_reply_info.head->is_dentry) { 12458c2ecf20Sopenharmony_ci /* 12468c2ecf20Sopenharmony_ci * Normally d_move() is done by fill_trace (called by 12478c2ecf20Sopenharmony_ci * do_request, above). If there is no trace, we need 12488c2ecf20Sopenharmony_ci * to do it here. 12498c2ecf20Sopenharmony_ci */ 12508c2ecf20Sopenharmony_ci d_move(old_dentry, new_dentry); 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); 12538c2ecf20Sopenharmony_ci return err; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci/* 12578c2ecf20Sopenharmony_ci * Move dentry to tail of mdsc->dentry_leases list when lease is updated. 12588c2ecf20Sopenharmony_ci * Leases at front of the list will expire first. (Assume all leases have 12598c2ecf20Sopenharmony_ci * similar duration) 12608c2ecf20Sopenharmony_ci * 12618c2ecf20Sopenharmony_ci * Called under dentry->d_lock. 12628c2ecf20Sopenharmony_ci */ 12638c2ecf20Sopenharmony_civoid __ceph_dentry_lease_touch(struct ceph_dentry_info *di) 12648c2ecf20Sopenharmony_ci{ 12658c2ecf20Sopenharmony_ci struct dentry *dn = di->dentry; 12668c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci dout("dentry_lease_touch %p %p '%pd'\n", di, dn, dn); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci di->flags |= CEPH_DENTRY_LEASE_LIST; 12718c2ecf20Sopenharmony_ci if (di->flags & CEPH_DENTRY_SHRINK_LIST) { 12728c2ecf20Sopenharmony_ci di->flags |= CEPH_DENTRY_REFERENCED; 12738c2ecf20Sopenharmony_ci return; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci mdsc = ceph_sb_to_client(dn->d_sb)->mdsc; 12778c2ecf20Sopenharmony_ci spin_lock(&mdsc->dentry_list_lock); 12788c2ecf20Sopenharmony_ci list_move_tail(&di->lease_list, &mdsc->dentry_leases); 12798c2ecf20Sopenharmony_ci spin_unlock(&mdsc->dentry_list_lock); 12808c2ecf20Sopenharmony_ci} 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_cistatic void __dentry_dir_lease_touch(struct ceph_mds_client* mdsc, 12838c2ecf20Sopenharmony_ci struct ceph_dentry_info *di) 12848c2ecf20Sopenharmony_ci{ 12858c2ecf20Sopenharmony_ci di->flags &= ~(CEPH_DENTRY_LEASE_LIST | CEPH_DENTRY_REFERENCED); 12868c2ecf20Sopenharmony_ci di->lease_gen = 0; 12878c2ecf20Sopenharmony_ci di->time = jiffies; 12888c2ecf20Sopenharmony_ci list_move_tail(&di->lease_list, &mdsc->dentry_dir_leases); 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci/* 12928c2ecf20Sopenharmony_ci * When dir lease is used, add dentry to tail of mdsc->dentry_dir_leases 12938c2ecf20Sopenharmony_ci * list if it's not in the list, otherwise set 'referenced' flag. 12948c2ecf20Sopenharmony_ci * 12958c2ecf20Sopenharmony_ci * Called under dentry->d_lock. 12968c2ecf20Sopenharmony_ci */ 12978c2ecf20Sopenharmony_civoid __ceph_dentry_dir_lease_touch(struct ceph_dentry_info *di) 12988c2ecf20Sopenharmony_ci{ 12998c2ecf20Sopenharmony_ci struct dentry *dn = di->dentry; 13008c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci dout("dentry_dir_lease_touch %p %p '%pd' (offset 0x%llx)\n", 13038c2ecf20Sopenharmony_ci di, dn, dn, di->offset); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci if (!list_empty(&di->lease_list)) { 13068c2ecf20Sopenharmony_ci if (di->flags & CEPH_DENTRY_LEASE_LIST) { 13078c2ecf20Sopenharmony_ci /* don't remove dentry from dentry lease list 13088c2ecf20Sopenharmony_ci * if its lease is valid */ 13098c2ecf20Sopenharmony_ci if (__dentry_lease_is_valid(di)) 13108c2ecf20Sopenharmony_ci return; 13118c2ecf20Sopenharmony_ci } else { 13128c2ecf20Sopenharmony_ci di->flags |= CEPH_DENTRY_REFERENCED; 13138c2ecf20Sopenharmony_ci return; 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci if (di->flags & CEPH_DENTRY_SHRINK_LIST) { 13188c2ecf20Sopenharmony_ci di->flags |= CEPH_DENTRY_REFERENCED; 13198c2ecf20Sopenharmony_ci di->flags &= ~CEPH_DENTRY_LEASE_LIST; 13208c2ecf20Sopenharmony_ci return; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci mdsc = ceph_sb_to_client(dn->d_sb)->mdsc; 13248c2ecf20Sopenharmony_ci spin_lock(&mdsc->dentry_list_lock); 13258c2ecf20Sopenharmony_ci __dentry_dir_lease_touch(mdsc, di), 13268c2ecf20Sopenharmony_ci spin_unlock(&mdsc->dentry_list_lock); 13278c2ecf20Sopenharmony_ci} 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_cistatic void __dentry_lease_unlist(struct ceph_dentry_info *di) 13308c2ecf20Sopenharmony_ci{ 13318c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc; 13328c2ecf20Sopenharmony_ci if (di->flags & CEPH_DENTRY_SHRINK_LIST) 13338c2ecf20Sopenharmony_ci return; 13348c2ecf20Sopenharmony_ci if (list_empty(&di->lease_list)) 13358c2ecf20Sopenharmony_ci return; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci mdsc = ceph_sb_to_client(di->dentry->d_sb)->mdsc; 13388c2ecf20Sopenharmony_ci spin_lock(&mdsc->dentry_list_lock); 13398c2ecf20Sopenharmony_ci list_del_init(&di->lease_list); 13408c2ecf20Sopenharmony_ci spin_unlock(&mdsc->dentry_list_lock); 13418c2ecf20Sopenharmony_ci} 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_cienum { 13448c2ecf20Sopenharmony_ci KEEP = 0, 13458c2ecf20Sopenharmony_ci DELETE = 1, 13468c2ecf20Sopenharmony_ci TOUCH = 2, 13478c2ecf20Sopenharmony_ci STOP = 4, 13488c2ecf20Sopenharmony_ci}; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_cistruct ceph_lease_walk_control { 13518c2ecf20Sopenharmony_ci bool dir_lease; 13528c2ecf20Sopenharmony_ci bool expire_dir_lease; 13538c2ecf20Sopenharmony_ci unsigned long nr_to_scan; 13548c2ecf20Sopenharmony_ci unsigned long dir_lease_ttl; 13558c2ecf20Sopenharmony_ci}; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_cistatic unsigned long 13588c2ecf20Sopenharmony_ci__dentry_leases_walk(struct ceph_mds_client *mdsc, 13598c2ecf20Sopenharmony_ci struct ceph_lease_walk_control *lwc, 13608c2ecf20Sopenharmony_ci int (*check)(struct dentry*, void*)) 13618c2ecf20Sopenharmony_ci{ 13628c2ecf20Sopenharmony_ci struct ceph_dentry_info *di, *tmp; 13638c2ecf20Sopenharmony_ci struct dentry *dentry, *last = NULL; 13648c2ecf20Sopenharmony_ci struct list_head* list; 13658c2ecf20Sopenharmony_ci LIST_HEAD(dispose); 13668c2ecf20Sopenharmony_ci unsigned long freed = 0; 13678c2ecf20Sopenharmony_ci int ret = 0; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci list = lwc->dir_lease ? &mdsc->dentry_dir_leases : &mdsc->dentry_leases; 13708c2ecf20Sopenharmony_ci spin_lock(&mdsc->dentry_list_lock); 13718c2ecf20Sopenharmony_ci list_for_each_entry_safe(di, tmp, list, lease_list) { 13728c2ecf20Sopenharmony_ci if (!lwc->nr_to_scan) 13738c2ecf20Sopenharmony_ci break; 13748c2ecf20Sopenharmony_ci --lwc->nr_to_scan; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci dentry = di->dentry; 13778c2ecf20Sopenharmony_ci if (last == dentry) 13788c2ecf20Sopenharmony_ci break; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci if (!spin_trylock(&dentry->d_lock)) 13818c2ecf20Sopenharmony_ci continue; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci if (__lockref_is_dead(&dentry->d_lockref)) { 13848c2ecf20Sopenharmony_ci list_del_init(&di->lease_list); 13858c2ecf20Sopenharmony_ci goto next; 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci ret = check(dentry, lwc); 13898c2ecf20Sopenharmony_ci if (ret & TOUCH) { 13908c2ecf20Sopenharmony_ci /* move it into tail of dir lease list */ 13918c2ecf20Sopenharmony_ci __dentry_dir_lease_touch(mdsc, di); 13928c2ecf20Sopenharmony_ci if (!last) 13938c2ecf20Sopenharmony_ci last = dentry; 13948c2ecf20Sopenharmony_ci } 13958c2ecf20Sopenharmony_ci if (ret & DELETE) { 13968c2ecf20Sopenharmony_ci /* stale lease */ 13978c2ecf20Sopenharmony_ci di->flags &= ~CEPH_DENTRY_REFERENCED; 13988c2ecf20Sopenharmony_ci if (dentry->d_lockref.count > 0) { 13998c2ecf20Sopenharmony_ci /* update_dentry_lease() will re-add 14008c2ecf20Sopenharmony_ci * it to lease list, or 14018c2ecf20Sopenharmony_ci * ceph_d_delete() will return 1 when 14028c2ecf20Sopenharmony_ci * last reference is dropped */ 14038c2ecf20Sopenharmony_ci list_del_init(&di->lease_list); 14048c2ecf20Sopenharmony_ci } else { 14058c2ecf20Sopenharmony_ci di->flags |= CEPH_DENTRY_SHRINK_LIST; 14068c2ecf20Sopenharmony_ci list_move_tail(&di->lease_list, &dispose); 14078c2ecf20Sopenharmony_ci dget_dlock(dentry); 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_cinext: 14118c2ecf20Sopenharmony_ci spin_unlock(&dentry->d_lock); 14128c2ecf20Sopenharmony_ci if (ret & STOP) 14138c2ecf20Sopenharmony_ci break; 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci spin_unlock(&mdsc->dentry_list_lock); 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci while (!list_empty(&dispose)) { 14188c2ecf20Sopenharmony_ci di = list_first_entry(&dispose, struct ceph_dentry_info, 14198c2ecf20Sopenharmony_ci lease_list); 14208c2ecf20Sopenharmony_ci dentry = di->dentry; 14218c2ecf20Sopenharmony_ci spin_lock(&dentry->d_lock); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci list_del_init(&di->lease_list); 14248c2ecf20Sopenharmony_ci di->flags &= ~CEPH_DENTRY_SHRINK_LIST; 14258c2ecf20Sopenharmony_ci if (di->flags & CEPH_DENTRY_REFERENCED) { 14268c2ecf20Sopenharmony_ci spin_lock(&mdsc->dentry_list_lock); 14278c2ecf20Sopenharmony_ci if (di->flags & CEPH_DENTRY_LEASE_LIST) { 14288c2ecf20Sopenharmony_ci list_add_tail(&di->lease_list, 14298c2ecf20Sopenharmony_ci &mdsc->dentry_leases); 14308c2ecf20Sopenharmony_ci } else { 14318c2ecf20Sopenharmony_ci __dentry_dir_lease_touch(mdsc, di); 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci spin_unlock(&mdsc->dentry_list_lock); 14348c2ecf20Sopenharmony_ci } else { 14358c2ecf20Sopenharmony_ci freed++; 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci spin_unlock(&dentry->d_lock); 14398c2ecf20Sopenharmony_ci /* ceph_d_delete() does the trick */ 14408c2ecf20Sopenharmony_ci dput(dentry); 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci return freed; 14438c2ecf20Sopenharmony_ci} 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_cistatic int __dentry_lease_check(struct dentry *dentry, void *arg) 14468c2ecf20Sopenharmony_ci{ 14478c2ecf20Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 14488c2ecf20Sopenharmony_ci int ret; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (__dentry_lease_is_valid(di)) 14518c2ecf20Sopenharmony_ci return STOP; 14528c2ecf20Sopenharmony_ci ret = __dir_lease_try_check(dentry); 14538c2ecf20Sopenharmony_ci if (ret == -EBUSY) 14548c2ecf20Sopenharmony_ci return KEEP; 14558c2ecf20Sopenharmony_ci if (ret > 0) 14568c2ecf20Sopenharmony_ci return TOUCH; 14578c2ecf20Sopenharmony_ci return DELETE; 14588c2ecf20Sopenharmony_ci} 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_cistatic int __dir_lease_check(struct dentry *dentry, void *arg) 14618c2ecf20Sopenharmony_ci{ 14628c2ecf20Sopenharmony_ci struct ceph_lease_walk_control *lwc = arg; 14638c2ecf20Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci int ret = __dir_lease_try_check(dentry); 14668c2ecf20Sopenharmony_ci if (ret == -EBUSY) 14678c2ecf20Sopenharmony_ci return KEEP; 14688c2ecf20Sopenharmony_ci if (ret > 0) { 14698c2ecf20Sopenharmony_ci if (time_before(jiffies, di->time + lwc->dir_lease_ttl)) 14708c2ecf20Sopenharmony_ci return STOP; 14718c2ecf20Sopenharmony_ci /* Move dentry to tail of dir lease list if we don't want 14728c2ecf20Sopenharmony_ci * to delete it. So dentries in the list are checked in a 14738c2ecf20Sopenharmony_ci * round robin manner */ 14748c2ecf20Sopenharmony_ci if (!lwc->expire_dir_lease) 14758c2ecf20Sopenharmony_ci return TOUCH; 14768c2ecf20Sopenharmony_ci if (dentry->d_lockref.count > 0 || 14778c2ecf20Sopenharmony_ci (di->flags & CEPH_DENTRY_REFERENCED)) 14788c2ecf20Sopenharmony_ci return TOUCH; 14798c2ecf20Sopenharmony_ci /* invalidate dir lease */ 14808c2ecf20Sopenharmony_ci di->lease_shared_gen = 0; 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci return DELETE; 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ciint ceph_trim_dentries(struct ceph_mds_client *mdsc) 14868c2ecf20Sopenharmony_ci{ 14878c2ecf20Sopenharmony_ci struct ceph_lease_walk_control lwc; 14888c2ecf20Sopenharmony_ci unsigned long count; 14898c2ecf20Sopenharmony_ci unsigned long freed; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci spin_lock(&mdsc->caps_list_lock); 14928c2ecf20Sopenharmony_ci if (mdsc->caps_use_max > 0 && 14938c2ecf20Sopenharmony_ci mdsc->caps_use_count > mdsc->caps_use_max) 14948c2ecf20Sopenharmony_ci count = mdsc->caps_use_count - mdsc->caps_use_max; 14958c2ecf20Sopenharmony_ci else 14968c2ecf20Sopenharmony_ci count = 0; 14978c2ecf20Sopenharmony_ci spin_unlock(&mdsc->caps_list_lock); 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci lwc.dir_lease = false; 15008c2ecf20Sopenharmony_ci lwc.nr_to_scan = CEPH_CAPS_PER_RELEASE * 2; 15018c2ecf20Sopenharmony_ci freed = __dentry_leases_walk(mdsc, &lwc, __dentry_lease_check); 15028c2ecf20Sopenharmony_ci if (!lwc.nr_to_scan) /* more invalid leases */ 15038c2ecf20Sopenharmony_ci return -EAGAIN; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci if (lwc.nr_to_scan < CEPH_CAPS_PER_RELEASE) 15068c2ecf20Sopenharmony_ci lwc.nr_to_scan = CEPH_CAPS_PER_RELEASE; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci lwc.dir_lease = true; 15098c2ecf20Sopenharmony_ci lwc.expire_dir_lease = freed < count; 15108c2ecf20Sopenharmony_ci lwc.dir_lease_ttl = mdsc->fsc->mount_options->caps_wanted_delay_max * HZ; 15118c2ecf20Sopenharmony_ci freed +=__dentry_leases_walk(mdsc, &lwc, __dir_lease_check); 15128c2ecf20Sopenharmony_ci if (!lwc.nr_to_scan) /* more to check */ 15138c2ecf20Sopenharmony_ci return -EAGAIN; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci return freed > 0 ? 1 : 0; 15168c2ecf20Sopenharmony_ci} 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci/* 15198c2ecf20Sopenharmony_ci * Ensure a dentry lease will no longer revalidate. 15208c2ecf20Sopenharmony_ci */ 15218c2ecf20Sopenharmony_civoid ceph_invalidate_dentry_lease(struct dentry *dentry) 15228c2ecf20Sopenharmony_ci{ 15238c2ecf20Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 15248c2ecf20Sopenharmony_ci spin_lock(&dentry->d_lock); 15258c2ecf20Sopenharmony_ci di->time = jiffies; 15268c2ecf20Sopenharmony_ci di->lease_shared_gen = 0; 15278c2ecf20Sopenharmony_ci di->flags &= ~CEPH_DENTRY_PRIMARY_LINK; 15288c2ecf20Sopenharmony_ci __dentry_lease_unlist(di); 15298c2ecf20Sopenharmony_ci spin_unlock(&dentry->d_lock); 15308c2ecf20Sopenharmony_ci} 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci/* 15338c2ecf20Sopenharmony_ci * Check if dentry lease is valid. If not, delete the lease. Try to 15348c2ecf20Sopenharmony_ci * renew if the least is more than half up. 15358c2ecf20Sopenharmony_ci */ 15368c2ecf20Sopenharmony_cistatic bool __dentry_lease_is_valid(struct ceph_dentry_info *di) 15378c2ecf20Sopenharmony_ci{ 15388c2ecf20Sopenharmony_ci struct ceph_mds_session *session; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci if (!di->lease_gen) 15418c2ecf20Sopenharmony_ci return false; 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci session = di->lease_session; 15448c2ecf20Sopenharmony_ci if (session) { 15458c2ecf20Sopenharmony_ci u32 gen; 15468c2ecf20Sopenharmony_ci unsigned long ttl; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci spin_lock(&session->s_gen_ttl_lock); 15498c2ecf20Sopenharmony_ci gen = session->s_cap_gen; 15508c2ecf20Sopenharmony_ci ttl = session->s_cap_ttl; 15518c2ecf20Sopenharmony_ci spin_unlock(&session->s_gen_ttl_lock); 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci if (di->lease_gen == gen && 15548c2ecf20Sopenharmony_ci time_before(jiffies, ttl) && 15558c2ecf20Sopenharmony_ci time_before(jiffies, di->time)) 15568c2ecf20Sopenharmony_ci return true; 15578c2ecf20Sopenharmony_ci } 15588c2ecf20Sopenharmony_ci di->lease_gen = 0; 15598c2ecf20Sopenharmony_ci return false; 15608c2ecf20Sopenharmony_ci} 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_cistatic int dentry_lease_is_valid(struct dentry *dentry, unsigned int flags) 15638c2ecf20Sopenharmony_ci{ 15648c2ecf20Sopenharmony_ci struct ceph_dentry_info *di; 15658c2ecf20Sopenharmony_ci struct ceph_mds_session *session = NULL; 15668c2ecf20Sopenharmony_ci u32 seq = 0; 15678c2ecf20Sopenharmony_ci int valid = 0; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci spin_lock(&dentry->d_lock); 15708c2ecf20Sopenharmony_ci di = ceph_dentry(dentry); 15718c2ecf20Sopenharmony_ci if (di && __dentry_lease_is_valid(di)) { 15728c2ecf20Sopenharmony_ci valid = 1; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci if (di->lease_renew_after && 15758c2ecf20Sopenharmony_ci time_after(jiffies, di->lease_renew_after)) { 15768c2ecf20Sopenharmony_ci /* 15778c2ecf20Sopenharmony_ci * We should renew. If we're in RCU walk mode 15788c2ecf20Sopenharmony_ci * though, we can't do that so just return 15798c2ecf20Sopenharmony_ci * -ECHILD. 15808c2ecf20Sopenharmony_ci */ 15818c2ecf20Sopenharmony_ci if (flags & LOOKUP_RCU) { 15828c2ecf20Sopenharmony_ci valid = -ECHILD; 15838c2ecf20Sopenharmony_ci } else { 15848c2ecf20Sopenharmony_ci session = ceph_get_mds_session(di->lease_session); 15858c2ecf20Sopenharmony_ci seq = di->lease_seq; 15868c2ecf20Sopenharmony_ci di->lease_renew_after = 0; 15878c2ecf20Sopenharmony_ci di->lease_renew_from = jiffies; 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci } 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci spin_unlock(&dentry->d_lock); 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci if (session) { 15948c2ecf20Sopenharmony_ci ceph_mdsc_lease_send_msg(session, dentry, 15958c2ecf20Sopenharmony_ci CEPH_MDS_LEASE_RENEW, seq); 15968c2ecf20Sopenharmony_ci ceph_put_mds_session(session); 15978c2ecf20Sopenharmony_ci } 15988c2ecf20Sopenharmony_ci dout("dentry_lease_is_valid - dentry %p = %d\n", dentry, valid); 15998c2ecf20Sopenharmony_ci return valid; 16008c2ecf20Sopenharmony_ci} 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci/* 16038c2ecf20Sopenharmony_ci * Called under dentry->d_lock. 16048c2ecf20Sopenharmony_ci */ 16058c2ecf20Sopenharmony_cistatic int __dir_lease_try_check(const struct dentry *dentry) 16068c2ecf20Sopenharmony_ci{ 16078c2ecf20Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 16088c2ecf20Sopenharmony_ci struct inode *dir; 16098c2ecf20Sopenharmony_ci struct ceph_inode_info *ci; 16108c2ecf20Sopenharmony_ci int valid = 0; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if (!di->lease_shared_gen) 16138c2ecf20Sopenharmony_ci return 0; 16148c2ecf20Sopenharmony_ci if (IS_ROOT(dentry)) 16158c2ecf20Sopenharmony_ci return 0; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci dir = d_inode(dentry->d_parent); 16188c2ecf20Sopenharmony_ci ci = ceph_inode(dir); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci if (spin_trylock(&ci->i_ceph_lock)) { 16218c2ecf20Sopenharmony_ci if (atomic_read(&ci->i_shared_gen) == di->lease_shared_gen && 16228c2ecf20Sopenharmony_ci __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 0)) 16238c2ecf20Sopenharmony_ci valid = 1; 16248c2ecf20Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 16258c2ecf20Sopenharmony_ci } else { 16268c2ecf20Sopenharmony_ci valid = -EBUSY; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci if (!valid) 16308c2ecf20Sopenharmony_ci di->lease_shared_gen = 0; 16318c2ecf20Sopenharmony_ci return valid; 16328c2ecf20Sopenharmony_ci} 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci/* 16358c2ecf20Sopenharmony_ci * Check if directory-wide content lease/cap is valid. 16368c2ecf20Sopenharmony_ci */ 16378c2ecf20Sopenharmony_cistatic int dir_lease_is_valid(struct inode *dir, struct dentry *dentry, 16388c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc) 16398c2ecf20Sopenharmony_ci{ 16408c2ecf20Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(dir); 16418c2ecf20Sopenharmony_ci int valid; 16428c2ecf20Sopenharmony_ci int shared_gen; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 16458c2ecf20Sopenharmony_ci valid = __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1); 16468c2ecf20Sopenharmony_ci if (valid) { 16478c2ecf20Sopenharmony_ci __ceph_touch_fmode(ci, mdsc, CEPH_FILE_MODE_RD); 16488c2ecf20Sopenharmony_ci shared_gen = atomic_read(&ci->i_shared_gen); 16498c2ecf20Sopenharmony_ci } 16508c2ecf20Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 16518c2ecf20Sopenharmony_ci if (valid) { 16528c2ecf20Sopenharmony_ci struct ceph_dentry_info *di; 16538c2ecf20Sopenharmony_ci spin_lock(&dentry->d_lock); 16548c2ecf20Sopenharmony_ci di = ceph_dentry(dentry); 16558c2ecf20Sopenharmony_ci if (dir == d_inode(dentry->d_parent) && 16568c2ecf20Sopenharmony_ci di && di->lease_shared_gen == shared_gen) 16578c2ecf20Sopenharmony_ci __ceph_dentry_dir_lease_touch(di); 16588c2ecf20Sopenharmony_ci else 16598c2ecf20Sopenharmony_ci valid = 0; 16608c2ecf20Sopenharmony_ci spin_unlock(&dentry->d_lock); 16618c2ecf20Sopenharmony_ci } 16628c2ecf20Sopenharmony_ci dout("dir_lease_is_valid dir %p v%u dentry %p = %d\n", 16638c2ecf20Sopenharmony_ci dir, (unsigned)atomic_read(&ci->i_shared_gen), dentry, valid); 16648c2ecf20Sopenharmony_ci return valid; 16658c2ecf20Sopenharmony_ci} 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci/* 16688c2ecf20Sopenharmony_ci * Check if cached dentry can be trusted. 16698c2ecf20Sopenharmony_ci */ 16708c2ecf20Sopenharmony_cistatic int ceph_d_revalidate(struct dentry *dentry, unsigned int flags) 16718c2ecf20Sopenharmony_ci{ 16728c2ecf20Sopenharmony_ci int valid = 0; 16738c2ecf20Sopenharmony_ci struct dentry *parent; 16748c2ecf20Sopenharmony_ci struct inode *dir, *inode; 16758c2ecf20Sopenharmony_ci struct ceph_mds_client *mdsc; 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci if (flags & LOOKUP_RCU) { 16788c2ecf20Sopenharmony_ci parent = READ_ONCE(dentry->d_parent); 16798c2ecf20Sopenharmony_ci dir = d_inode_rcu(parent); 16808c2ecf20Sopenharmony_ci if (!dir) 16818c2ecf20Sopenharmony_ci return -ECHILD; 16828c2ecf20Sopenharmony_ci inode = d_inode_rcu(dentry); 16838c2ecf20Sopenharmony_ci } else { 16848c2ecf20Sopenharmony_ci parent = dget_parent(dentry); 16858c2ecf20Sopenharmony_ci dir = d_inode(parent); 16868c2ecf20Sopenharmony_ci inode = d_inode(dentry); 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci dout("d_revalidate %p '%pd' inode %p offset 0x%llx\n", dentry, 16908c2ecf20Sopenharmony_ci dentry, inode, ceph_dentry(dentry)->offset); 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci mdsc = ceph_sb_to_client(dir->i_sb)->mdsc; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci /* always trust cached snapped dentries, snapdir dentry */ 16958c2ecf20Sopenharmony_ci if (ceph_snap(dir) != CEPH_NOSNAP) { 16968c2ecf20Sopenharmony_ci dout("d_revalidate %p '%pd' inode %p is SNAPPED\n", dentry, 16978c2ecf20Sopenharmony_ci dentry, inode); 16988c2ecf20Sopenharmony_ci valid = 1; 16998c2ecf20Sopenharmony_ci } else if (inode && ceph_snap(inode) == CEPH_SNAPDIR) { 17008c2ecf20Sopenharmony_ci valid = 1; 17018c2ecf20Sopenharmony_ci } else { 17028c2ecf20Sopenharmony_ci valid = dentry_lease_is_valid(dentry, flags); 17038c2ecf20Sopenharmony_ci if (valid == -ECHILD) 17048c2ecf20Sopenharmony_ci return valid; 17058c2ecf20Sopenharmony_ci if (valid || dir_lease_is_valid(dir, dentry, mdsc)) { 17068c2ecf20Sopenharmony_ci if (inode) 17078c2ecf20Sopenharmony_ci valid = ceph_is_any_caps(inode); 17088c2ecf20Sopenharmony_ci else 17098c2ecf20Sopenharmony_ci valid = 1; 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci if (!valid) { 17148c2ecf20Sopenharmony_ci struct ceph_mds_request *req; 17158c2ecf20Sopenharmony_ci int op, err; 17168c2ecf20Sopenharmony_ci u32 mask; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci if (flags & LOOKUP_RCU) 17198c2ecf20Sopenharmony_ci return -ECHILD; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci percpu_counter_inc(&mdsc->metric.d_lease_mis); 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci op = ceph_snap(dir) == CEPH_SNAPDIR ? 17248c2ecf20Sopenharmony_ci CEPH_MDS_OP_LOOKUPSNAP : CEPH_MDS_OP_LOOKUP; 17258c2ecf20Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, op, USE_ANY_MDS); 17268c2ecf20Sopenharmony_ci if (!IS_ERR(req)) { 17278c2ecf20Sopenharmony_ci req->r_dentry = dget(dentry); 17288c2ecf20Sopenharmony_ci req->r_num_caps = 2; 17298c2ecf20Sopenharmony_ci req->r_parent = dir; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci mask = CEPH_STAT_CAP_INODE | CEPH_CAP_AUTH_SHARED; 17328c2ecf20Sopenharmony_ci if (ceph_security_xattr_wanted(dir)) 17338c2ecf20Sopenharmony_ci mask |= CEPH_CAP_XATTR_SHARED; 17348c2ecf20Sopenharmony_ci req->r_args.getattr.mask = cpu_to_le32(mask); 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, NULL, req); 17378c2ecf20Sopenharmony_ci switch (err) { 17388c2ecf20Sopenharmony_ci case 0: 17398c2ecf20Sopenharmony_ci if (d_really_is_positive(dentry) && 17408c2ecf20Sopenharmony_ci d_inode(dentry) == req->r_target_inode) 17418c2ecf20Sopenharmony_ci valid = 1; 17428c2ecf20Sopenharmony_ci break; 17438c2ecf20Sopenharmony_ci case -ENOENT: 17448c2ecf20Sopenharmony_ci if (d_really_is_negative(dentry)) 17458c2ecf20Sopenharmony_ci valid = 1; 17468c2ecf20Sopenharmony_ci fallthrough; 17478c2ecf20Sopenharmony_ci default: 17488c2ecf20Sopenharmony_ci break; 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci ceph_mdsc_put_request(req); 17518c2ecf20Sopenharmony_ci dout("d_revalidate %p lookup result=%d\n", 17528c2ecf20Sopenharmony_ci dentry, err); 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci } else { 17558c2ecf20Sopenharmony_ci percpu_counter_inc(&mdsc->metric.d_lease_hit); 17568c2ecf20Sopenharmony_ci } 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci dout("d_revalidate %p %s\n", dentry, valid ? "valid" : "invalid"); 17598c2ecf20Sopenharmony_ci if (!valid) 17608c2ecf20Sopenharmony_ci ceph_dir_clear_complete(dir); 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if (!(flags & LOOKUP_RCU)) 17638c2ecf20Sopenharmony_ci dput(parent); 17648c2ecf20Sopenharmony_ci return valid; 17658c2ecf20Sopenharmony_ci} 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci/* 17688c2ecf20Sopenharmony_ci * Delete unused dentry that doesn't have valid lease 17698c2ecf20Sopenharmony_ci * 17708c2ecf20Sopenharmony_ci * Called under dentry->d_lock. 17718c2ecf20Sopenharmony_ci */ 17728c2ecf20Sopenharmony_cistatic int ceph_d_delete(const struct dentry *dentry) 17738c2ecf20Sopenharmony_ci{ 17748c2ecf20Sopenharmony_ci struct ceph_dentry_info *di; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci /* won't release caps */ 17778c2ecf20Sopenharmony_ci if (d_really_is_negative(dentry)) 17788c2ecf20Sopenharmony_ci return 0; 17798c2ecf20Sopenharmony_ci if (ceph_snap(d_inode(dentry)) != CEPH_NOSNAP) 17808c2ecf20Sopenharmony_ci return 0; 17818c2ecf20Sopenharmony_ci /* vaild lease? */ 17828c2ecf20Sopenharmony_ci di = ceph_dentry(dentry); 17838c2ecf20Sopenharmony_ci if (di) { 17848c2ecf20Sopenharmony_ci if (__dentry_lease_is_valid(di)) 17858c2ecf20Sopenharmony_ci return 0; 17868c2ecf20Sopenharmony_ci if (__dir_lease_try_check(dentry)) 17878c2ecf20Sopenharmony_ci return 0; 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ci return 1; 17908c2ecf20Sopenharmony_ci} 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci/* 17938c2ecf20Sopenharmony_ci * Release our ceph_dentry_info. 17948c2ecf20Sopenharmony_ci */ 17958c2ecf20Sopenharmony_cistatic void ceph_d_release(struct dentry *dentry) 17968c2ecf20Sopenharmony_ci{ 17978c2ecf20Sopenharmony_ci struct ceph_dentry_info *di = ceph_dentry(dentry); 17988c2ecf20Sopenharmony_ci struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci dout("d_release %p\n", dentry); 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci atomic64_dec(&fsc->mdsc->metric.total_dentries); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci spin_lock(&dentry->d_lock); 18058c2ecf20Sopenharmony_ci __dentry_lease_unlist(di); 18068c2ecf20Sopenharmony_ci dentry->d_fsdata = NULL; 18078c2ecf20Sopenharmony_ci spin_unlock(&dentry->d_lock); 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci ceph_put_mds_session(di->lease_session); 18108c2ecf20Sopenharmony_ci kmem_cache_free(ceph_dentry_cachep, di); 18118c2ecf20Sopenharmony_ci} 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci/* 18148c2ecf20Sopenharmony_ci * When the VFS prunes a dentry from the cache, we need to clear the 18158c2ecf20Sopenharmony_ci * complete flag on the parent directory. 18168c2ecf20Sopenharmony_ci * 18178c2ecf20Sopenharmony_ci * Called under dentry->d_lock. 18188c2ecf20Sopenharmony_ci */ 18198c2ecf20Sopenharmony_cistatic void ceph_d_prune(struct dentry *dentry) 18208c2ecf20Sopenharmony_ci{ 18218c2ecf20Sopenharmony_ci struct ceph_inode_info *dir_ci; 18228c2ecf20Sopenharmony_ci struct ceph_dentry_info *di; 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci dout("ceph_d_prune %pd %p\n", dentry, dentry); 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci /* do we have a valid parent? */ 18278c2ecf20Sopenharmony_ci if (IS_ROOT(dentry)) 18288c2ecf20Sopenharmony_ci return; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci /* we hold d_lock, so d_parent is stable */ 18318c2ecf20Sopenharmony_ci dir_ci = ceph_inode(d_inode(dentry->d_parent)); 18328c2ecf20Sopenharmony_ci if (dir_ci->i_vino.snap == CEPH_SNAPDIR) 18338c2ecf20Sopenharmony_ci return; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci /* who calls d_delete() should also disable dcache readdir */ 18368c2ecf20Sopenharmony_ci if (d_really_is_negative(dentry)) 18378c2ecf20Sopenharmony_ci return; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci /* d_fsdata does not get cleared until d_release */ 18408c2ecf20Sopenharmony_ci if (!d_unhashed(dentry)) { 18418c2ecf20Sopenharmony_ci __ceph_dir_clear_complete(dir_ci); 18428c2ecf20Sopenharmony_ci return; 18438c2ecf20Sopenharmony_ci } 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci /* Disable dcache readdir just in case that someone called d_drop() 18468c2ecf20Sopenharmony_ci * or d_invalidate(), but MDS didn't revoke CEPH_CAP_FILE_SHARED 18478c2ecf20Sopenharmony_ci * properly (dcache readdir is still enabled) */ 18488c2ecf20Sopenharmony_ci di = ceph_dentry(dentry); 18498c2ecf20Sopenharmony_ci if (di->offset > 0 && 18508c2ecf20Sopenharmony_ci di->lease_shared_gen == atomic_read(&dir_ci->i_shared_gen)) 18518c2ecf20Sopenharmony_ci __ceph_dir_clear_ordered(dir_ci); 18528c2ecf20Sopenharmony_ci} 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci/* 18558c2ecf20Sopenharmony_ci * read() on a dir. This weird interface hack only works if mounted 18568c2ecf20Sopenharmony_ci * with '-o dirstat'. 18578c2ecf20Sopenharmony_ci */ 18588c2ecf20Sopenharmony_cistatic ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size, 18598c2ecf20Sopenharmony_ci loff_t *ppos) 18608c2ecf20Sopenharmony_ci{ 18618c2ecf20Sopenharmony_ci struct ceph_dir_file_info *dfi = file->private_data; 18628c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 18638c2ecf20Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(inode); 18648c2ecf20Sopenharmony_ci int left; 18658c2ecf20Sopenharmony_ci const int bufsize = 1024; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci if (!ceph_test_mount_opt(ceph_sb_to_client(inode->i_sb), DIRSTAT)) 18688c2ecf20Sopenharmony_ci return -EISDIR; 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci if (!dfi->dir_info) { 18718c2ecf20Sopenharmony_ci dfi->dir_info = kmalloc(bufsize, GFP_KERNEL); 18728c2ecf20Sopenharmony_ci if (!dfi->dir_info) 18738c2ecf20Sopenharmony_ci return -ENOMEM; 18748c2ecf20Sopenharmony_ci dfi->dir_info_len = 18758c2ecf20Sopenharmony_ci snprintf(dfi->dir_info, bufsize, 18768c2ecf20Sopenharmony_ci "entries: %20lld\n" 18778c2ecf20Sopenharmony_ci " files: %20lld\n" 18788c2ecf20Sopenharmony_ci " subdirs: %20lld\n" 18798c2ecf20Sopenharmony_ci "rentries: %20lld\n" 18808c2ecf20Sopenharmony_ci " rfiles: %20lld\n" 18818c2ecf20Sopenharmony_ci " rsubdirs: %20lld\n" 18828c2ecf20Sopenharmony_ci "rbytes: %20lld\n" 18838c2ecf20Sopenharmony_ci "rctime: %10lld.%09ld\n", 18848c2ecf20Sopenharmony_ci ci->i_files + ci->i_subdirs, 18858c2ecf20Sopenharmony_ci ci->i_files, 18868c2ecf20Sopenharmony_ci ci->i_subdirs, 18878c2ecf20Sopenharmony_ci ci->i_rfiles + ci->i_rsubdirs, 18888c2ecf20Sopenharmony_ci ci->i_rfiles, 18898c2ecf20Sopenharmony_ci ci->i_rsubdirs, 18908c2ecf20Sopenharmony_ci ci->i_rbytes, 18918c2ecf20Sopenharmony_ci ci->i_rctime.tv_sec, 18928c2ecf20Sopenharmony_ci ci->i_rctime.tv_nsec); 18938c2ecf20Sopenharmony_ci } 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci if (*ppos >= dfi->dir_info_len) 18968c2ecf20Sopenharmony_ci return 0; 18978c2ecf20Sopenharmony_ci size = min_t(unsigned, size, dfi->dir_info_len-*ppos); 18988c2ecf20Sopenharmony_ci left = copy_to_user(buf, dfi->dir_info + *ppos, size); 18998c2ecf20Sopenharmony_ci if (left == size) 19008c2ecf20Sopenharmony_ci return -EFAULT; 19018c2ecf20Sopenharmony_ci *ppos += (size - left); 19028c2ecf20Sopenharmony_ci return size - left; 19038c2ecf20Sopenharmony_ci} 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci/* 19088c2ecf20Sopenharmony_ci * Return name hash for a given dentry. This is dependent on 19098c2ecf20Sopenharmony_ci * the parent directory's hash function. 19108c2ecf20Sopenharmony_ci */ 19118c2ecf20Sopenharmony_ciunsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn) 19128c2ecf20Sopenharmony_ci{ 19138c2ecf20Sopenharmony_ci struct ceph_inode_info *dci = ceph_inode(dir); 19148c2ecf20Sopenharmony_ci unsigned hash; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci switch (dci->i_dir_layout.dl_dir_hash) { 19178c2ecf20Sopenharmony_ci case 0: /* for backward compat */ 19188c2ecf20Sopenharmony_ci case CEPH_STR_HASH_LINUX: 19198c2ecf20Sopenharmony_ci return dn->d_name.hash; 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci default: 19228c2ecf20Sopenharmony_ci spin_lock(&dn->d_lock); 19238c2ecf20Sopenharmony_ci hash = ceph_str_hash(dci->i_dir_layout.dl_dir_hash, 19248c2ecf20Sopenharmony_ci dn->d_name.name, dn->d_name.len); 19258c2ecf20Sopenharmony_ci spin_unlock(&dn->d_lock); 19268c2ecf20Sopenharmony_ci return hash; 19278c2ecf20Sopenharmony_ci } 19288c2ecf20Sopenharmony_ci} 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ciconst struct file_operations ceph_dir_fops = { 19318c2ecf20Sopenharmony_ci .read = ceph_read_dir, 19328c2ecf20Sopenharmony_ci .iterate = ceph_readdir, 19338c2ecf20Sopenharmony_ci .llseek = ceph_dir_llseek, 19348c2ecf20Sopenharmony_ci .open = ceph_open, 19358c2ecf20Sopenharmony_ci .release = ceph_release, 19368c2ecf20Sopenharmony_ci .unlocked_ioctl = ceph_ioctl, 19378c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 19388c2ecf20Sopenharmony_ci .fsync = ceph_fsync, 19398c2ecf20Sopenharmony_ci .lock = ceph_lock, 19408c2ecf20Sopenharmony_ci .flock = ceph_flock, 19418c2ecf20Sopenharmony_ci}; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ciconst struct file_operations ceph_snapdir_fops = { 19448c2ecf20Sopenharmony_ci .iterate = ceph_readdir, 19458c2ecf20Sopenharmony_ci .llseek = ceph_dir_llseek, 19468c2ecf20Sopenharmony_ci .open = ceph_open, 19478c2ecf20Sopenharmony_ci .release = ceph_release, 19488c2ecf20Sopenharmony_ci}; 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ciconst struct inode_operations ceph_dir_iops = { 19518c2ecf20Sopenharmony_ci .lookup = ceph_lookup, 19528c2ecf20Sopenharmony_ci .permission = ceph_permission, 19538c2ecf20Sopenharmony_ci .getattr = ceph_getattr, 19548c2ecf20Sopenharmony_ci .setattr = ceph_setattr, 19558c2ecf20Sopenharmony_ci .listxattr = ceph_listxattr, 19568c2ecf20Sopenharmony_ci .get_acl = ceph_get_acl, 19578c2ecf20Sopenharmony_ci .set_acl = ceph_set_acl, 19588c2ecf20Sopenharmony_ci .mknod = ceph_mknod, 19598c2ecf20Sopenharmony_ci .symlink = ceph_symlink, 19608c2ecf20Sopenharmony_ci .mkdir = ceph_mkdir, 19618c2ecf20Sopenharmony_ci .link = ceph_link, 19628c2ecf20Sopenharmony_ci .unlink = ceph_unlink, 19638c2ecf20Sopenharmony_ci .rmdir = ceph_unlink, 19648c2ecf20Sopenharmony_ci .rename = ceph_rename, 19658c2ecf20Sopenharmony_ci .create = ceph_create, 19668c2ecf20Sopenharmony_ci .atomic_open = ceph_atomic_open, 19678c2ecf20Sopenharmony_ci}; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ciconst struct inode_operations ceph_snapdir_iops = { 19708c2ecf20Sopenharmony_ci .lookup = ceph_lookup, 19718c2ecf20Sopenharmony_ci .permission = ceph_permission, 19728c2ecf20Sopenharmony_ci .getattr = ceph_getattr, 19738c2ecf20Sopenharmony_ci .mkdir = ceph_mkdir, 19748c2ecf20Sopenharmony_ci .rmdir = ceph_unlink, 19758c2ecf20Sopenharmony_ci .rename = ceph_rename, 19768c2ecf20Sopenharmony_ci}; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ciconst struct dentry_operations ceph_dentry_ops = { 19798c2ecf20Sopenharmony_ci .d_revalidate = ceph_d_revalidate, 19808c2ecf20Sopenharmony_ci .d_delete = ceph_d_delete, 19818c2ecf20Sopenharmony_ci .d_release = ceph_d_release, 19828c2ecf20Sopenharmony_ci .d_prune = ceph_d_prune, 19838c2ecf20Sopenharmony_ci .d_init = ceph_d_init, 19848c2ecf20Sopenharmony_ci}; 1985