#include <linux/ceph/ceph_debug.h> #include <linux/spinlock.h>#include <linux/fs_struct.h>#include <linux/namei.h>#include <linux/slab.h>#include <linux/sched.h> #include "super.h"#include "mds_client.h" /* * Directory operations: readdir, lookup, create, link, unlink, * rename, etc. */ /* * Ceph MDS operations are specified in terms of a base ino and * relative path. Thus, the client can specify an operation on a * specific inode (e.g., a getattr due to fstat(2)), or as a path * relative to, say, the root directory. * * Normally, we limit ourselves to strict inode ops (no path component) * or dentry operations (a single path component relative to an ino). The * exception to this is open_root_dentry(), which will open the mount * point by name. */ const struct inode_operations ceph_dir_iops;const struct file_operations ceph_dir_fops;const struct dentry_operations ceph_dentry_ops; /* * Initialize ceph dentry state. */int ceph_init_dentry(struct dentry *dentry){ struct ceph_dentry_info *di; if (dentry->d_fsdata) return 0; di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO); if (!di) return -ENOMEM; /* oh well */ spin_lock(&dentry->d_lock); if (dentry->d_fsdata) { /* lost a race */ kmem_cache_free(ceph_dentry_cachep, di); goto out_unlock; } if (dentry->d_parent == NULL || /* nfs fh_to_dentry */ ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP) d_set_d_op(dentry, &ceph_dentry_ops); else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR) d_set_d_op(dentry, &ceph_snapdir_dentry_ops); else d_set_d_op(dentry, &ceph_snap_dentry_ops); di->dentry = dentry; di->lease_session = NULL; dentry->d_time = jiffies; /* avoid reordering d_fsdata setup so that the check above is safe */ smp_mb(); dentry->d_fsdata = di; ceph_dentry_lru_add(dentry);out_unlock: spin_unlock(&dentry->d_lock); return 0;} struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry){ struct inode *inode = NULL; if (!dentry) return NULL; spin_lock(&dentry->d_lock); if (dentry->d_parent) { inode = dentry->d_parent->d_inode; ihold(inode); } spin_unlock(&dentry->d_lock); return inode;} /* * for readdir, we encode the directory frag and offset within that * frag into f_pos. */static unsigned fpos_frag(loff_t p){ return p >> 32;}static unsigned fpos_off(loff_t p){ return p & 0xffffffff;} /* * When possible, we try to satisfy a readdir by peeking at the * dcache. We make this work by carefully ordering dentries on * d_u.d_child when we initially get results back from the MDS, and * falling back to a "normal" sync readdir if any dentries in the dir * are dropped. * * D_COMPLETE tells indicates we have all dentries in the dir. It is * defined IFF we hold CEPH_CAP_FILE_SHARED (which will be revoked by * the MDS if/when the directory is modified). */static int __dcache_readdir(struct file *filp, void *dirent, filldir_t filldir){ struct ceph_file_info *fi = filp->private_data; struct dentry *parent = filp->f_dentry; struct inode *dir = parent->d_inode; struct list_head *p; struct dentry *dentry, *last; struct ceph_dentry_info *di; int err = 0; /* claim ref on last dentry we returned */ last = fi->dentry; fi->dentry = NULL; dout("__dcache_readdir %p at %llu (last %p)\n", dir, filp->f_pos, last); spin_lock(&parent->d_lock); /* start at beginning? */ if (filp->f_pos == 2 || last == NULL || filp->f_pos < ceph_dentry(last)->offset) { if (list_empty(&parent->d_subdirs)) goto out_unlock; p = parent->d_subdirs.prev; dout(" initial p %p/%p\n", p->prev, p->next); } else { p = last->d_u.d_child.prev; } more: dentry = list_entry(p, struct dentry, d_u.d_child); di = ceph_dentry(dentry); while (1) { dout(" p %p/%p %s d_subdirs %p/%p\n", p->prev, p->next, d_unhashed(dentry) ? "!hashed" : "hashed", parent->d_subdirs.prev, parent->d_subdirs.next); if (p == &parent->d_subdirs) { fi->flags |= CEPH_F_ATEND; goto out_unlock; } spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); if (!d_unhashed(dentry) && dentry->d_inode && ceph_snap(dentry->d_inode) != CEPH_SNAPDIR && ceph_ino(dentry->d_inode) != CEPH_INO_CEPH && filp->f_pos <= di->offset) break; dout(" skipping %p %.*s at %llu (%llu)%s%s\n", dentry, dentry->d_name.len, dentry->d_name.name, di->offset, filp->f_pos, d_unhashed(dentry) ? " unhashed" : "", !dentry->d_inode ? " null" : ""); spin_unlock(&dentry->d_lock); p = p->prev; dentry = list_entry(p, struct dentry, d_u.d_child); di = ceph_dentry(dentry); } dget_dlock(dentry); spin_unlock(&dentry->d_lock); spin_unlock(&parent->d_lock); dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos, dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); filp->f_pos = di->offset; err = filldir(dirent, dentry->d_name.name, dentry->d_name.len, di->offset, ceph_translate_ino(dentry->d_sb, dentry->d_inode->i_ino), dentry->d_inode->i_mode >> 12); if (last) { if (err < 0) { /* remember our position */ fi->dentry = last; fi->next_offset = di->offset; } else { dput(last); } } last = dentry; if (err < 0) goto out; filp->f_pos++; /* make sure a dentry wasn't dropped while we didn't have parent lock */ if (!ceph_dir_test_complete(dir)) { dout(" lost D_COMPLETE on %p; falling back to mds\n", dir); err = -EAGAIN; goto out; } spin_lock(&parent->d_lock); p = p->prev; /* advance to next dentry */ goto more; out_unlock: spin_unlock(&parent->d_lock);out: if (last) dput(last); return err;} /* * make note of the last dentry we read, so we can * continue at the same lexicographical point, * regardless of what dir changes take place on the * server. */static int note_last_dentry(struct ceph_file_info *fi, const char *name, int len){ kfree(fi->last_name); fi->last_name = kmalloc(len+1, GFP_NOFS); if (!fi->last_name) return -ENOMEM; memcpy(fi->last_name, name, len); fi->last_name[len] = 0; dout("note_last_dentry '%s'\n", fi->last_name); return 0;} static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir){ struct ceph_file_info *fi = filp->private_data; struct inode *inode = filp->f_dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_fs_client *fsc = ceph_inode_to_client(inode); struct ceph_mds_client *mdsc = fsc->mdsc; unsigned frag = fpos_frag(filp->f_pos); int off = fpos_off(filp->f_pos); int err; u32 ftype; struct ceph_mds_reply_info_parsed *rinfo; const int max_entries = fsc->mount_options->max_readdir; const int max_bytes = fsc->mount_options->max_readdir_bytes; dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off); if (fi->flags & CEPH_F_ATEND) return 0; /* always start with . and .. */ if (filp->f_pos == 0) { /* note dir version at start of readdir so we can tell * if any dentries get dropped */ fi->dir_release_count = ci->i_release_count; dout("readdir off 0 -> '.'\n"); if (filldir(dirent, ".", 1, ceph_make_fpos(0, 0), ceph_translate_ino(inode->i_sb, inode->i_ino), inode->i_mode >> 12) < 0) return 0; filp->f_pos = 1; off = 1; } if (filp->f_pos == 1) { ino_t ino = parent_ino(filp->f_dentry); dout("readdir off 1 -> '..'\n"); if (filldir(dirent, "..", 2, ceph_make_fpos(0, 1), ceph_translate_ino(inode->i_sb, ino), inode->i_mode >> 12) < 0) return 0; filp->f_pos = 2; off = 2; } /* can we use the dcache? */ spin_lock(&ci->i_ceph_lock); if ((filp->f_pos == 2 || fi->dentry) && !ceph_test_mount_opt(fsc, NOASYNCREADDIR) && ceph_snap(inode) != CEPH_SNAPDIR && ceph_dir_test_complete(inode) && __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1)) { spin_unlock(&ci->i_ceph_lock); err = __dcache_readdir(filp, dirent, filldir); if (err != -EAGAIN) return err; } else { spin_unlock(&ci->i_ceph_lock); } if (fi->dentry) { err = note_last_dentry(fi, fi->dentry->d_name.name, fi->dentry->d_name.len); if (err) return err; dput(fi->dentry); fi->dentry = NULL; } /* proceed with a normal readdir */ more: /* do we have the correct frag content buffered? */ if (fi->frag != frag || fi->last_readdir == NULL) { struct ceph_mds_request *req; int op = ceph_snap(inode) == CEPH_SNAPDIR ? CEPH_MDS_OP_LSSNAP : CEPH_MDS_OP_READDIR; /* discard old result, if any */ if (fi->last_readdir) { ceph_mdsc_put_request(fi->last_readdir); fi->last_readdir = NULL; } /* requery frag tree, as the frag topology may have changed */ frag = ceph_choose_frag(ceph_inode(inode), frag, NULL, NULL); dout("readdir fetching %llx.%llx frag %x offset '%s'\n", ceph_vinop(inode), frag, fi->last_name); req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); if (IS_ERR(req)) return PTR_ERR(req); req->r_inode = inode; ihold(inode); req->r_dentry = dget(filp->f_dentry); /* hints to request -> mds selection code */ req->r_direct_mode = USE_AUTH_MDS; req->r_direct_hash = ceph_frag_value(frag); req->r_direct_is_hash = true; req->r_path2 = kstrdup(fi->last_name, GFP_NOFS); req->r_readdir_offset = fi->next_offset; req->r_args.readdir.frag = cpu_to_le32(frag); req->r_args.readdir.max_entries = cpu_to_le32(max_entries); req->r_args.readdir.max_bytes = cpu_to_le32(max_bytes); req->r_num_caps = max_entries + 1; err = ceph_mdsc_do_request(mdsc, NULL, req); if (err < 0) { ceph_mdsc_put_request(req); return err; } dout("readdir got and parsed readdir result=%d" " on frag %x, end=%d, complete=%d\n", err, frag, (int)req->r_reply_info.dir_end, (int)req->r_reply_info.dir_complete); if (!req->r_did_prepopulate) { dout("readdir !did_prepopulate"); fi->dir_release_count--; /* preclude D_COMPLETE */ } /* note next offset and last dentry name */ fi->offset = fi->next_offset; fi->last_readdir = req; if (req->r_reply_info.dir_end) { kfree(fi->last_name); fi->last_name = NULL; if (ceph_frag_is_rightmost(frag)) fi->next_offset = 2; else fi->next_offset = 0; } else { rinfo = &req->r_reply_info; err = note_last_dentry(fi, rinfo->dir_dname[rinfo->dir_nr-1], rinfo->dir_dname_len[rinfo->dir_nr-1]); if (err) return err; fi->next_offset += rinfo->dir_nr; } } rinfo = &fi->last_readdir->r_reply_info; dout("readdir frag %x num %d off %d chunkoff %d\n", frag, rinfo->dir_nr, off, fi->offset); while (off >= fi->offset && off - fi->offset < rinfo->dir_nr) { u64 pos = ceph_make_fpos(frag, off); struct ceph_mds_reply_inode *in = rinfo->dir_in[off - fi->offset].in; struct ceph_vino vino; ino_t ino; dout("readdir off %d (%d/%d) -> %lld '%.*s' %p\n", off, off - fi->offset, rinfo->dir_nr, pos, rinfo->dir_dname_len[off - fi->offset], rinfo->dir_dname[off - fi->offset], in); BUG_ON(!in); ftype = le32_to_cpu(in->mode) >> 12; vino.ino = le64_to_cpu(in->ino); vino.snap = le64_to_cpu(in->snapid); ino = ceph_vino_to_ino(vino); if (filldir(dirent, rinfo->dir_dname[off - fi->offset], rinfo->dir_dname_len[off - fi->offset], pos, ceph_translate_ino(inode->i_sb, ino), ftype) < 0) { dout("filldir stopping us...\n"); return 0; } off++; filp->f_pos = pos + 1; } if (fi->last_name) { ceph_mdsc_put_request(fi->last_readdir); fi->last_readdir = NULL; goto more; } /* more frags? */ if (!ceph_frag_is_rightmost(frag)) { frag = ceph_frag_next(frag); off = 0; filp->f_pos = ceph_make_fpos(frag, off); dout("readdir next frag is %x\n", frag); goto more; } fi->flags |= CEPH_F_ATEND; /* * if dir_release_count still matches the dir, no dentries * were released during the whole readdir, and we should have * the complete dir contents in our cache. */ spin_lock(&ci->i_ceph_lock); if (ci->i_release_count == fi->dir_release_count) { ceph_dir_set_complete(inode); ci->i_max_offset = filp->f_pos; } spin_unlock(&ci->i_ceph_lock); dout("readdir %p filp %p done.\n", inode, filp); return 0;} static void reset_readdir(struct ceph_file_info *fi){ if (fi->last_readdir) { ceph_mdsc_put_request(fi->last_readdir); fi->last_readdir = NULL; } kfree(fi->last_name); fi->last_name = NULL; fi->next_offset = 2; /* compensate for . and .. */ if (fi->dentry) { dput(fi->dentry); fi->dentry = NULL; } fi->flags &= ~CEPH_F_ATEND;} static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin){ struct ceph_file_info *fi = file->private_data; struct inode *inode = file->f_mapping->host; loff_t old_offset = offset; loff_t retval; mutex_lock(&inode->i_mutex); retval = -EINVAL; switch (origin) { case SEEK_END: offset += inode->i_size + 2; /* FIXME */ break; case SEEK_CUR: offset += file->f_pos; case SEEK_SET: break; default: goto out; } if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) { if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; fi->flags &= ~CEPH_F_ATEND; } retval = offset; /* * discard buffered readdir content on seekdir(0), or * seek to new frag, or seek prior to current chunk. */ if (offset == 0 || fpos_frag(offset) != fpos_frag(old_offset) || fpos_off(offset) < fi->offset) { dout("dir_llseek dropping %p content\n", file); reset_readdir(fi); } /* bump dir_release_count if we did a forward seek */ if (offset > old_offset) fi->dir_release_count--;Lines 1–500 of 1376. Load more
#include <linux/ceph/ceph_debug.h> #include <linux/spinlock.h> #include <linux/fs_struct.h> #include <linux/namei.h> #include <linux/slab.h> #include <linux/sched.h> #include "super.h" #include "mds_client.h" /* * Directory operations: readdir, lookup, create, link, unlink, * rename, etc. */ /* * Ceph MDS operations are specified in terms of a base ino and * relative path. Thus, the client can specify an operation on a * specific inode (e.g., a getattr due to fstat(2)), or as a path * relative to, say, the root directory. * * Normally, we limit ourselves to strict inode ops (no path component) * or dentry operations (a single path component relative to an ino). The * exception to this is open_root_dentry(), which will open the mount * point by name. */ const struct inode_operations ceph_dir_iops; const struct file_operations ceph_dir_fops; const struct dentry_operations ceph_dentry_ops; /* * Initialize ceph dentry state. */ int ceph_init_dentry(struct dentry *dentry) { struct ceph_dentry_info *di; if (dentry->d_fsdata) return 0; di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO); if (!di) return -ENOMEM; /* oh well */ spin_lock(&dentry->d_lock); if (dentry->d_fsdata) { /* lost a race */ kmem_cache_free(ceph_dentry_cachep, di); goto out_unlock; } if (dentry->d_parent == NULL || /* nfs fh_to_dentry */ ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP) d_set_d_op(dentry, &ceph_dentry_ops); else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR) d_set_d_op(dentry, &ceph_snapdir_dentry_ops); else d_set_d_op(dentry, &ceph_snap_dentry_ops); di->dentry = dentry; di->lease_session = NULL; dentry->d_time = jiffies; /* avoid reordering d_fsdata setup so that the check above is safe */ smp_mb(); dentry->d_fsdata = di; ceph_dentry_lru_add(dentry); out_unlock: spin_unlock(&dentry->d_lock); return 0; } struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry) { struct inode *inode = NULL; if (!dentry) return NULL; spin_lock(&dentry->d_lock); if (dentry->d_parent) { inode = dentry->d_parent->d_inode; ihold(inode); } spin_unlock(&dentry->d_lock); return inode; } /* * for readdir, we encode the directory frag and offset within that * frag into f_pos. */ static unsigned fpos_frag(loff_t p) { return p >> 32; } static unsigned fpos_off(loff_t p) { return p & 0xffffffff; } /* * When possible, we try to satisfy a readdir by peeking at the * dcache. We make this work by carefully ordering dentries on * d_u.d_child when we initially get results back from the MDS, and * falling back to a "normal" sync readdir if any dentries in the dir * are dropped. * * D_COMPLETE tells indicates we have all dentries in the dir. It is * defined IFF we hold CEPH_CAP_FILE_SHARED (which will be revoked by * the MDS if/when the directory is modified). */ static int __dcache_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct ceph_file_info *fi = filp->private_data; struct dentry *parent = filp->f_dentry; struct inode *dir = parent->d_inode; struct list_head *p; struct dentry *dentry, *last; struct ceph_dentry_info *di; int err = 0; /* claim ref on last dentry we returned */ last = fi->dentry; fi->dentry = NULL; dout("__dcache_readdir %p at %llu (last %p)\n", dir, filp->f_pos, last); spin_lock(&parent->d_lock); /* start at beginning? */ if (filp->f_pos == 2 || last == NULL || filp->f_pos < ceph_dentry(last)->offset) { if (list_empty(&parent->d_subdirs)) goto out_unlock; p = parent->d_subdirs.prev; dout(" initial p %p/%p\n", p->prev, p->next); } else { p = last->d_u.d_child.prev; } more: dentry = list_entry(p, struct dentry, d_u.d_child); di = ceph_dentry(dentry); while (1) { dout(" p %p/%p %s d_subdirs %p/%p\n", p->prev, p->next, d_unhashed(dentry) ? "!hashed" : "hashed", parent->d_subdirs.prev, parent->d_subdirs.next); if (p == &parent->d_subdirs) { fi->flags |= CEPH_F_ATEND; goto out_unlock; } spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); if (!d_unhashed(dentry) && dentry->d_inode && ceph_snap(dentry->d_inode) != CEPH_SNAPDIR && ceph_ino(dentry->d_inode) != CEPH_INO_CEPH && filp->f_pos <= di->offset) break; dout(" skipping %p %.*s at %llu (%llu)%s%s\n", dentry, dentry->d_name.len, dentry->d_name.name, di->offset, filp->f_pos, d_unhashed(dentry) ? " unhashed" : "", !dentry->d_inode ? " null" : ""); spin_unlock(&dentry->d_lock); p = p->prev; dentry = list_entry(p, struct dentry, d_u.d_child); di = ceph_dentry(dentry); } dget_dlock(dentry); spin_unlock(&dentry->d_lock); spin_unlock(&parent->d_lock); dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos, dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); filp->f_pos = di->offset; err = filldir(dirent, dentry->d_name.name, dentry->d_name.len, di->offset, ceph_translate_ino(dentry->d_sb, dentry->d_inode->i_ino), dentry->d_inode->i_mode >> 12); if (last) { if (err < 0) { /* remember our position */ fi->dentry = last; fi->next_offset = di->offset; } else { dput(last); } } last = dentry; if (err < 0) goto out; filp->f_pos++; /* make sure a dentry wasn't dropped while we didn't have parent lock */ if (!ceph_dir_test_complete(dir)) { dout(" lost D_COMPLETE on %p; falling back to mds\n", dir); err = -EAGAIN; goto out; } spin_lock(&parent->d_lock); p = p->prev; /* advance to next dentry */ goto more; out_unlock: spin_unlock(&parent->d_lock); out: if (last) dput(last); return err; } /* * make note of the last dentry we read, so we can * continue at the same lexicographical point, * regardless of what dir changes take place on the * server. */ static int note_last_dentry(struct ceph_file_info *fi, const char *name, int len) { kfree(fi->last_name); fi->last_name = kmalloc(len+1, GFP_NOFS); if (!fi->last_name) return -ENOMEM; memcpy(fi->last_name, name, len); fi->last_name[len] = 0; dout("note_last_dentry '%s'\n", fi->last_name); return 0; } static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct ceph_file_info *fi = filp->private_data; struct inode *inode = filp->f_dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_fs_client *fsc = ceph_inode_to_client(inode); struct ceph_mds_client *mdsc = fsc->mdsc; unsigned frag = fpos_frag(filp->f_pos); int off = fpos_off(filp->f_pos); int err; u32 ftype; struct ceph_mds_reply_info_parsed *rinfo; const int max_entries = fsc->mount_options->max_readdir; const int max_bytes = fsc->mount_options->max_readdir_bytes; dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off); if (fi->flags & CEPH_F_ATEND) return 0; /* always start with . and .. */ if (filp->f_pos == 0) { /* note dir version at start of readdir so we can tell * if any dentries get dropped */ fi->dir_release_count = ci->i_release_count; dout("readdir off 0 -> '.'\n"); if (filldir(dirent, ".", 1, ceph_make_fpos(0, 0), ceph_translate_ino(inode->i_sb, inode->i_ino), inode->i_mode >> 12) < 0) return 0; filp->f_pos = 1; off = 1; } if (filp->f_pos == 1) { ino_t ino = parent_ino(filp->f_dentry); dout("readdir off 1 -> '..'\n"); if (filldir(dirent, "..", 2, ceph_make_fpos(0, 1), ceph_translate_ino(inode->i_sb, ino), inode->i_mode >> 12) < 0) return 0; filp->f_pos = 2; off = 2; } /* can we use the dcache? */ spin_lock(&ci->i_ceph_lock); if ((filp->f_pos == 2 || fi->dentry) && !ceph_test_mount_opt(fsc, NOASYNCREADDIR) && ceph_snap(inode) != CEPH_SNAPDIR && ceph_dir_test_complete(inode) && __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1)) { spin_unlock(&ci->i_ceph_lock); err = __dcache_readdir(filp, dirent, filldir); if (err != -EAGAIN) return err; } else { spin_unlock(&ci->i_ceph_lock); } if (fi->dentry) { err = note_last_dentry(fi, fi->dentry->d_name.name, fi->dentry->d_name.len); if (err) return err; dput(fi->dentry); fi->dentry = NULL; } /* proceed with a normal readdir */ more: /* do we have the correct frag content buffered? */ if (fi->frag != frag || fi->last_readdir == NULL) { struct ceph_mds_request *req; int op = ceph_snap(inode) == CEPH_SNAPDIR ? CEPH_MDS_OP_LSSNAP : CEPH_MDS_OP_READDIR; /* discard old result, if any */ if (fi->last_readdir) { ceph_mdsc_put_request(fi->last_readdir); fi->last_readdir = NULL; } /* requery frag tree, as the frag topology may have changed */ frag = ceph_choose_frag(ceph_inode(inode), frag, NULL, NULL); dout("readdir fetching %llx.%llx frag %x offset '%s'\n", ceph_vinop(inode), frag, fi->last_name); req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); if (IS_ERR(req)) return PTR_ERR(req); req->r_inode = inode; ihold(inode); req->r_dentry = dget(filp->f_dentry); /* hints to request -> mds selection code */ req->r_direct_mode = USE_AUTH_MDS; req->r_direct_hash = ceph_frag_value(frag); req->r_direct_is_hash = true; req->r_path2 = kstrdup(fi->last_name, GFP_NOFS); req->r_readdir_offset = fi->next_offset; req->r_args.readdir.frag = cpu_to_le32(frag); req->r_args.readdir.max_entries = cpu_to_le32(max_entries); req->r_args.readdir.max_bytes = cpu_to_le32(max_bytes); req->r_num_caps = max_entries + 1; err = ceph_mdsc_do_request(mdsc, NULL, req); if (err < 0) { ceph_mdsc_put_request(req); return err; } dout("readdir got and parsed readdir result=%d" " on frag %x, end=%d, complete=%d\n", err, frag, (int)req->r_reply_info.dir_end, (int)req->r_reply_info.dir_complete); if (!req->r_did_prepopulate) { dout("readdir !did_prepopulate"); fi->dir_release_count--; /* preclude D_COMPLETE */ } /* note next offset and last dentry name */ fi->offset = fi->next_offset; fi->last_readdir = req; if (req->r_reply_info.dir_end) { kfree(fi->last_name); fi->last_name = NULL; if (ceph_frag_is_rightmost(frag)) fi->next_offset = 2; else fi->next_offset = 0; } else { rinfo = &req->r_reply_info; err = note_last_dentry(fi, rinfo->dir_dname[rinfo->dir_nr-1], rinfo->dir_dname_len[rinfo->dir_nr-1]); if (err) return err; fi->next_offset += rinfo->dir_nr; } } rinfo = &fi->last_readdir->r_reply_info; dout("readdir frag %x num %d off %d chunkoff %d\n", frag, rinfo->dir_nr, off, fi->offset); while (off >= fi->offset && off - fi->offset < rinfo->dir_nr) { u64 pos = ceph_make_fpos(frag, off); struct ceph_mds_reply_inode *in = rinfo->dir_in[off - fi->offset].in; struct ceph_vino vino; ino_t ino; dout("readdir off %d (%d/%d) -> %lld '%.*s' %p\n", off, off - fi->offset, rinfo->dir_nr, pos, rinfo->dir_dname_len[off - fi->offset], rinfo->dir_dname[off - fi->offset], in); BUG_ON(!in); ftype = le32_to_cpu(in->mode) >> 12; vino.ino = le64_to_cpu(in->ino); vino.snap = le64_to_cpu(in->snapid); ino = ceph_vino_to_ino(vino); if (filldir(dirent, rinfo->dir_dname[off - fi->offset], rinfo->dir_dname_len[off - fi->offset], pos, ceph_translate_ino(inode->i_sb, ino), ftype) < 0) { dout("filldir stopping us...\n"); return 0; } off++; filp->f_pos = pos + 1; } if (fi->last_name) { ceph_mdsc_put_request(fi->last_readdir); fi->last_readdir = NULL; goto more; } /* more frags? */ if (!ceph_frag_is_rightmost(frag)) { frag = ceph_frag_next(frag); off = 0; filp->f_pos = ceph_make_fpos(frag, off); dout("readdir next frag is %x\n", frag); goto more; } fi->flags |= CEPH_F_ATEND; /* * if dir_release_count still matches the dir, no dentries * were released during the whole readdir, and we should have * the complete dir contents in our cache. */ spin_lock(&ci->i_ceph_lock); if (ci->i_release_count == fi->dir_release_count) { ceph_dir_set_complete(inode); ci->i_max_offset = filp->f_pos; } spin_unlock(&ci->i_ceph_lock); dout("readdir %p filp %p done.\n", inode, filp); return 0; } static void reset_readdir(struct ceph_file_info *fi) { if (fi->last_readdir) { ceph_mdsc_put_request(fi->last_readdir); fi->last_readdir = NULL; } kfree(fi->last_name); fi->last_name = NULL; fi->next_offset = 2; /* compensate for . and .. */ if (fi->dentry) { dput(fi->dentry); fi->dentry = NULL; } fi->flags &= ~CEPH_F_ATEND; } static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin) { struct ceph_file_info *fi = file->private_data; struct inode *inode = file->f_mapping->host; loff_t old_offset = offset; loff_t retval; mutex_lock(&inode->i_mutex); retval = -EINVAL; switch (origin) { case SEEK_END: offset += inode->i_size + 2; /* FIXME */ break; case SEEK_CUR: offset += file->f_pos; case SEEK_SET: break; default: goto out; } if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) { if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; fi->flags &= ~CEPH_F_ATEND; } retval = offset; /* * discard buffered readdir content on seekdir(0), or * seek to new frag, or seek prior to current chunk. */ if (offset == 0 || fpos_frag(offset) != fpos_frag(old_offset) || fpos_off(offset) < fi->offset) { dout("dir_llseek dropping %p content\n", file); reset_readdir(fi); } /* bump dir_release_count if we did a forward seek */ if (offset > old_offset) fi->dir_release_count--;