Feature #22105 ยป snap_export.patch
fs/ceph/export.c | ||
---|---|---|
u64 ino, parent_ino;
|
||
} __attribute__ ((packed));
|
||
/*
|
||
* fh for snapped inode
|
||
*/
|
||
struct ceph_nfs_snapfh {
|
||
u64 ino;
|
||
u64 snapid;
|
||
u64 parent_ino;
|
||
u32 hash;
|
||
} __attribute__ ((packed));
|
||
static int ceph_encode_snapfh(struct inode *inode, u32 *rawfh, int *max_len,
|
||
struct inode *parent_inode)
|
||
{
|
||
const static int snap_handle_length =
|
||
sizeof(struct ceph_nfs_snapfh) >> 2;
|
||
struct ceph_nfs_snapfh *sfh = (void *)rawfh;
|
||
u64 snapid = ceph_snap(inode);
|
||
bool no_parent = true;
|
||
if (*max_len < snap_handle_length) {
|
||
*max_len = snap_handle_length;
|
||
return FILEID_INVALID;
|
||
}
|
||
if (snapid != CEPH_SNAPDIR) {
|
||
struct inode *dir;
|
||
struct dentry *dentry = d_find_alias(inode);
|
||
if (!dentry)
|
||
return -EINVAL;
|
||
rcu_read_lock();
|
||
dir = d_inode_rcu(dentry->d_parent);
|
||
if (ceph_snap(dir) != CEPH_SNAPDIR) {
|
||
sfh->parent_ino = ceph_ino(dir);
|
||
sfh->hash = ceph_dentry_hash(dir, dentry);
|
||
no_parent = false;
|
||
}
|
||
rcu_read_unlock();
|
||
dput(dentry);
|
||
}
|
||
sfh->ino = ceph_ino(inode);
|
||
sfh->snapid = snapid;
|
||
if (no_parent) {
|
||
sfh->parent_ino = sfh->ino;
|
||
sfh->hash = 0;
|
||
}
|
||
*max_len = snap_handle_length;
|
||
return FILEID_BTRFS_WITH_PARENT;
|
||
}
|
||
static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
|
||
struct inode *parent_inode)
|
||
{
|
||
const static int handle_length =
|
||
sizeof(struct ceph_nfs_fh) >> 2;
|
||
const static int connected_handle_length =
|
||
sizeof(struct ceph_nfs_confh) >> 2;
|
||
int type;
|
||
struct ceph_nfs_fh *fh = (void *)rawfh;
|
||
struct ceph_nfs_confh *cfh = (void *)rawfh;
|
||
int connected_handle_length = sizeof(*cfh)/4;
|
||
int handle_length = sizeof(*fh)/4;
|
||
/* don't re-export snaps */
|
||
if (ceph_snap(inode) != CEPH_NOSNAP)
|
||
return -EINVAL;
|
||
return ceph_encode_snapfh(inode, rawfh, max_len, parent_inode);
|
||
if (parent_inode && (*max_len < connected_handle_length)) {
|
||
*max_len = connected_handle_length;
|
||
... | ... | |
}
|
||
if (parent_inode) {
|
||
struct ceph_nfs_confh *cfh = (void *)rawfh;
|
||
dout("encode_fh %llx with parent %llx\n",
|
||
ceph_ino(inode), ceph_ino(parent_inode));
|
||
cfh->ino = ceph_ino(inode);
|
||
... | ... | |
*max_len = connected_handle_length;
|
||
type = FILEID_INO32_GEN_PARENT;
|
||
} else {
|
||
struct ceph_nfs_fh *fh = (void *)rawfh;
|
||
dout("encode_fh %llx\n", ceph_ino(inode));
|
||
fh->ino = ceph_ino(inode);
|
||
*max_len = handle_length;
|
||
... | ... | |
return d_obtain_alias(inode);
|
||
}
|
||
static struct dentry *__snapfh_to_dentry(struct super_block *sb,
|
||
struct ceph_nfs_snapfh *sfh,
|
||
bool want_parent)
|
||
{
|
||
struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
|
||
struct ceph_mds_request *req;
|
||
struct inode *inode;
|
||
struct ceph_vino vino;
|
||
int mask;
|
||
int err;
|
||
if (want_parent) {
|
||
vino.ino = sfh->parent_ino;
|
||
if (sfh->snapid == CEPH_SNAPDIR)
|
||
vino.snap = CEPH_NOSNAP;
|
||
else if (sfh->ino == sfh->parent_ino)
|
||
vino.snap = CEPH_SNAPDIR;
|
||
else
|
||
vino.snap = sfh->snapid;
|
||
} else {
|
||
vino.ino = sfh->ino;
|
||
vino.snap = sfh->snapid;
|
||
}
|
||
inode = ceph_find_inode(sb, vino);
|
||
if (inode)
|
||
return d_obtain_alias(inode);
|
||
req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
|
||
USE_ANY_MDS);
|
||
if (IS_ERR(req))
|
||
return ERR_CAST(req);
|
||
mask = CEPH_STAT_CAP_INODE;
|
||
if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
|
||
mask |= CEPH_CAP_XATTR_SHARED;
|
||
req->r_args.lookupino.mask = cpu_to_le32(mask);
|
||
if (vino.snap < CEPH_NOSNAP) {
|
||
req->r_args.lookupino.snapid = cpu_to_le64(vino.snap);
|
||
if (!want_parent && sfh->ino != sfh->parent_ino) {
|
||
req->r_args.lookupino.parent = cpu_to_le64(sfh->parent_ino);
|
||
req->r_args.lookupino.hash = cpu_to_le32(sfh->hash);
|
||
}
|
||
}
|
||
req->r_ino1 = vino;
|
||
req->r_num_caps = 1;
|
||
err = ceph_mdsc_do_request(mdsc, NULL, req);
|
||
inode = req->r_target_inode;
|
||
if (inode) {
|
||
if (vino.snap == CEPH_SNAPDIR)
|
||
inode = ceph_get_snapdir(inode);
|
||
else if (ceph_snap(inode) == vino.snap)
|
||
ihold(inode);
|
||
else
|
||
inode = NULL;
|
||
}
|
||
ceph_mdsc_put_request(req);
|
||
if (!inode)
|
||
return ERR_PTR(-ESTALE);
|
||
if (inode->i_nlink == 0) {
|
||
iput(inode);
|
||
return ERR_PTR(-ESTALE);
|
||
}
|
||
return d_obtain_alias(inode);
|
||
}
|
||
/*
|
||
* convert regular fh to dentry
|
||
*/
|
||
... | ... | |
{
|
||
struct ceph_nfs_fh *fh = (void *)fid->raw;
|
||
if (fh_type == FILEID_BTRFS_WITH_PARENT) {
|
||
struct ceph_nfs_snapfh *sfh = (void *)fid->raw;
|
||
return __snapfh_to_dentry(sb, sfh, false);
|
||
}
|
||
if (fh_type != FILEID_INO32_GEN &&
|
||
fh_type != FILEID_INO32_GEN_PARENT)
|
||
return NULL;
|
||
... | ... | |
static struct dentry *ceph_get_parent(struct dentry *child)
|
||
{
|
||
/* don't re-export snaps */
|
||
if (ceph_snap(d_inode(child)) != CEPH_NOSNAP)
|
||
return ERR_PTR(-EINVAL);
|
||
struct inode *inode = d_inode(child);
|
||
if (ceph_snap(inode) == CEPH_SNAPDIR)
|
||
return __fh_to_dentry(inode->i_sb, ceph_ino(inode));
|
||
if (ceph_snap(inode) < CEPH_NOSNAP) {
|
||
struct dentry *dn;
|
||
struct inode* dir;
|
||
if (!d_is_dir(child))
|
||
return ERR_PTR(-EINVAL);
|
||
dn = __fh_to_dentry(inode->i_sb, ceph_ino(inode));
|
||
if (IS_ERR_OR_NULL(dn))
|
||
return dn;
|
||
dir = ceph_get_snapdir(d_inode(dn));
|
||
dput(dn);
|
||
return d_obtain_alias(dir);
|
||
}
|
||
dout("get_parent %p ino %llx.%llx\n",
|
||
child, ceph_vinop(d_inode(child)));
|
||
dout("get_parent %p ino %llx.%llx\n", child, ceph_vinop(inode));
|
||
return __get_parent(child->d_sb, child, 0);
|
||
}
|
||
... | ... | |
struct ceph_nfs_confh *cfh = (void *)fid->raw;
|
||
struct dentry *dentry;
|
||
if (fh_type == FILEID_BTRFS_WITH_PARENT) {
|
||
struct ceph_nfs_snapfh *sfh = (void *)fid->raw;
|
||
return __snapfh_to_dentry(sb, sfh, true);
|
||
}
|
||
if (fh_type != FILEID_INO32_GEN_PARENT)
|
||
return NULL;
|
||
if (fh_len < sizeof(*cfh) / 4)
|
||
... | ... | |
return dentry;
|
||
}
|
||
struct getdents_callback {
|
||
struct dir_context ctx;
|
||
struct file *file;
|
||
u64 ino;
|
||
u64 snap;
|
||
char *name;
|
||
unsigned sequence;
|
||
bool found;
|
||
};
|
||
static int filldir_one(struct dir_context *ctx, const char *name, int len,
|
||
loff_t pos, u64 ino, unsigned int d_type)
|
||
{
|
||
struct getdents_callback *buf =
|
||
container_of(ctx, struct getdents_callback, ctx);
|
||
struct ceph_file_info *fi = buf->file->private_data;
|
||
struct ceph_mds_reply_info_parsed *rinfo;
|
||
if (!fi->last_readdir)
|
||
return -1;
|
||
rinfo = &fi->last_readdir->r_reply_info;
|
||
if (!rinfo || !rinfo->dir_nr)
|
||
return true;
|
||
chunk_offset = rinfo->dir_entries[0].offset;
|
||
return new_pos < chunk_offset ||
|
||
is_hash_order(new_pos) != is_hash_order(chunk_offset);
|
||
int result = 0;
|
||
buf->sequence++;
|
||
}
|
||
static int __get_snap_name(struct dentry *parent, char *name,
|
||
struct dentry *child)
|
||
{
|
||
struct inode *inode = d_inode(child);
|
||
struct inode *dir = d_inode(child);
|
||
struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
|
||
struct dir_context ctx = {
|
||
.actor = filldir_noop,
|
||
};
|
||
struct file *file;
|
||
if (ceph_ino(inode) != ceph_ino(dir))
|
||
return -EINVAL;
|
||
if (ceph_snap(inode) == CEPH_SNAPDIR) {
|
||
if (ceph_ino(dir) == CEPH_NOSNAP) {
|
||
return -EINVAL;
|
||
strcpy(name, fsc->mount_options->snapdir_name);
|
||
return 0;
|
||
}
|
||
if (ceph_snap(dir) != CEPH_SNAPDIR)
|
||
return -EINVAL;
|
||
file = dentry_open(path, O_RDONLY, cred);
|
||
if (IS_ERR(file))
|
||
goto out;
|
||
iterate_dir(file, &ctx)
|
||
}
|
||
static int ceph_get_name(struct dentry *parent, char *name,
|
||
struct dentry *child)
|
||
{
|
||
struct ceph_mds_client *mdsc;
|
||
struct ceph_mds_request *req;
|
||
struct inode *inode = d_inode(child);
|
||
int err;
|
||
mdsc = ceph_inode_to_client(d_inode(child))->mdsc;
|
||
if (ceph_snap(inode) != CEPH_NOSNAP)
|
||
return __get_snap_name(parent, name, child);
|
||
mdsc = ceph_inode_to_client(inode)->mdsc;
|
||
req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPNAME,
|
||
USE_ANY_MDS);
|
||
if (IS_ERR(req))
|
||
... | ... | |
inode_lock(d_inode(parent));
|
||
req->r_inode = d_inode(child);
|
||
ihold(d_inode(child));
|
||
req->r_inode = inode;
|
||
ihold(inode);
|
||
req->r_ino2 = ceph_vino(d_inode(parent));
|
||
req->r_parent = d_inode(parent);
|
||
set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
|
||
... | ... | |
memcpy(name, rinfo->dname, rinfo->dname_len);
|
||
name[rinfo->dname_len] = 0;
|
||
dout("get_name %p ino %llx.%llx name %s\n",
|
||
child, ceph_vinop(d_inode(child)), name);
|
||
child, ceph_vinop(inode), name);
|
||
} else {
|
||
dout("get_name %p ino %llx.%llx err %d\n",
|
||
child, ceph_vinop(d_inode(child)), err);
|
||
child, ceph_vinop(inode), err);
|
||
}
|
||
ceph_mdsc_put_request(req);
|
include/linux/ceph/ceph_fs.h | ||
---|---|---|
__le64 length; /* num bytes to lock from start */
|
||
__u8 wait; /* will caller wait for lock to become available? */
|
||
} __attribute__ ((packed)) filelock_change;
|
||
struct {
|
||
__le32 mask; /* CEPH_CAP_* */
|
||
__le64 snapid;
|
||
__le64 parent;
|
||
__le32 hash;
|
||
} __attribute__ ((packed)) lookupino;
|
||
} __attribute__ ((packed));
|
||
#define CEPH_MDS_FLAG_REPLAY 1 /* this is a replayed op */
|