#include "ceph_debug.h"
#include <linux/spinlock.h>
#include <linux/fs_struct.h>
#include <linux/namei.h>
#include <linux/sched.h>
#include "super.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;
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;
if (ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
dentry->d_op = &ceph_dentry_ops;
else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)
dentry->d_op = &ceph_snapdir_dentry_ops;
else
dentry->d_op = &ceph_snap_dentry_ops;
di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS);
if (!di)
return -ENOMEM; /* oh well */
spin_lock(&dentry->d_lock);
if (dentry->d_fsdata) /* lost a race */
goto out_unlock;
di->dentry = dentry;
di->lease_session = NULL;
dentry->d_fsdata = di;
dentry->d_time = jiffies;
ceph_dentry_lru_add(dentry);
out_unlock:
spin_unlock(&dentry->d_lock);
return 0;
}
/*
* 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.
*
* I_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 inode *inode = filp->f_dentry->d_inode;
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(&dcache_lock);
/* start at beginning? */
if (filp->f_pos == 2 || (last &&
filp->f_pos <