Merge: ceph: fix client race condition validating r_parent before applying state

MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/7303

ceph: fix client race condition validating r_parent before applying state

JIRA: https://issues.redhat.com/browse/RHEL-109212

Signed-off-by: Alex Markuze <amarkuze@redhat.com>

Approved-by: Ilya Dryomov <idryomov@redhat.com>
Approved-by: Xiubo Li <xiubli@redhat.com>
Approved-by: Venky Shankar <vshankar@redhat.com>
Approved-by: David Howells <dhowells@redhat.com>
Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com>

Merged-by: CKI GitLab Kmaint Pipeline Bot <26919896-cki-kmaint-pipeline-bot@users.noreply.gitlab.com>
This commit is contained in:
CKI KWF Bot 2025-09-15 20:47:41 +00:00
commit bb65ee2ca1
6 changed files with 201 additions and 101 deletions

View File

@ -55,8 +55,6 @@ static int mdsc_show(struct seq_file *s, void *p)
struct ceph_mds_client *mdsc = fsc->mdsc;
struct ceph_mds_request *req;
struct rb_node *rp;
int pathlen = 0;
u64 pathbase;
char *path;
mutex_lock(&mdsc->mutex);
@ -81,8 +79,8 @@ static int mdsc_show(struct seq_file *s, void *p)
if (req->r_inode) {
seq_printf(s, " #%llx", ceph_ino(req->r_inode));
} else if (req->r_dentry) {
path = ceph_mdsc_build_path(req->r_dentry, &pathlen,
&pathbase, 0);
struct ceph_path_info path_info;
path = ceph_mdsc_build_path(req->r_dentry, &path_info, 0);
if (IS_ERR(path))
path = NULL;
spin_lock(&req->r_dentry->d_lock);
@ -91,7 +89,7 @@ static int mdsc_show(struct seq_file *s, void *p)
req->r_dentry,
path ? path : "");
spin_unlock(&req->r_dentry->d_lock);
ceph_mdsc_free_path(path, pathlen);
ceph_mdsc_free_path_info(&path_info);
} else if (req->r_path1) {
seq_printf(s, " #%llx/%s", req->r_ino1.ino,
req->r_path1);
@ -100,8 +98,8 @@ static int mdsc_show(struct seq_file *s, void *p)
}
if (req->r_old_dentry) {
path = ceph_mdsc_build_path(req->r_old_dentry, &pathlen,
&pathbase, 0);
struct ceph_path_info path_info;
path = ceph_mdsc_build_path(req->r_old_dentry, &path_info, 0);
if (IS_ERR(path))
path = NULL;
spin_lock(&req->r_old_dentry->d_lock);
@ -111,7 +109,7 @@ static int mdsc_show(struct seq_file *s, void *p)
req->r_old_dentry,
path ? path : "");
spin_unlock(&req->r_old_dentry->d_lock);
ceph_mdsc_free_path(path, pathlen);
ceph_mdsc_free_path_info(&path_info);
} else if (req->r_path2 && req->r_op != CEPH_MDS_OP_SYMLINK) {
if (req->r_ino2.ino)
seq_printf(s, " #%llx/%s", req->r_ino2.ino,

View File

@ -1224,10 +1224,8 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc,
/* If op failed, mark everyone involved for errors */
if (result) {
int pathlen = 0;
u64 base = 0;
char *path = ceph_mdsc_build_path(dentry, &pathlen,
&base, 0);
struct ceph_path_info path_info = {0};
char *path = ceph_mdsc_build_path(dentry, &path_info, 0);
/* mark error on parent + clear complete */
mapping_set_error(req->r_parent->i_mapping, result);
@ -1241,8 +1239,8 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc,
mapping_set_error(req->r_old_inode->i_mapping, result);
pr_warn("async unlink failure path=(%llx)%s result=%d!\n",
base, IS_ERR(path) ? "<<bad>>" : path, result);
ceph_mdsc_free_path(path, pathlen);
path_info.vino.ino, IS_ERR(path) ? "<<bad>>" : path, result);
ceph_mdsc_free_path_info(&path_info);
}
out:
iput(req->r_old_inode);

View File

@ -576,14 +576,12 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc,
mapping_set_error(req->r_parent->i_mapping, result);
if (result) {
int pathlen = 0;
u64 base = 0;
char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen,
&base, 0);
struct ceph_path_info path_info = {0};
char *path = ceph_mdsc_build_path(req->r_dentry, &path_info, 0);
pr_warn("async create failure path=(%llx)%s result=%d!\n",
base, IS_ERR(path) ? "<<bad>>" : path, result);
ceph_mdsc_free_path(path, pathlen);
path_info.vino.ino, IS_ERR(path) ? "<<bad>>" : path, result);
ceph_mdsc_free_path_info(&path_info);
ceph_dir_clear_complete(req->r_parent);
if (!d_unhashed(dentry))

View File

@ -55,6 +55,52 @@ static int ceph_set_ino_cb(struct inode *inode, void *data)
return 0;
}
/*
* Check if the parent inode matches the vino from directory reply info
*/
static inline bool ceph_vino_matches_parent(struct inode *parent,
struct ceph_vino vino)
{
return ceph_ino(parent) == vino.ino && ceph_snap(parent) == vino.snap;
}
/*
* Validate that the directory inode referenced by @req->r_parent matches the
* inode number and snapshot id contained in the reply's directory record. If
* they do not match which can theoretically happen if the parent dentry was
* moved between the time the request was issued and the reply arrived fall
* back to looking up the correct inode in the inode cache.
*
* A reference is *always* returned. Callers that receive a different inode
* than the original @parent are responsible for dropping the extra reference
* once the reply has been processed.
*/
static struct inode *ceph_get_reply_dir(struct super_block *sb,
struct inode *parent,
struct ceph_mds_reply_info_parsed *rinfo)
{
struct ceph_vino vino;
if (unlikely(!rinfo->diri.in))
return parent; /* nothing to compare against */
/* If we didn't have a cached parent inode to begin with, just bail out. */
if (!parent)
return NULL;
vino.ino = le64_to_cpu(rinfo->diri.in->ino);
vino.snap = le64_to_cpu(rinfo->diri.in->snapid);
if (likely(ceph_vino_matches_parent(parent, vino)))
return parent; /* matches use the original reference */
/* Mismatch this should be rare. Emit a WARN and obtain the correct inode. */
WARN_ONCE(1, "ceph: reply dir mismatch (parent valid %llx.%llx reply %llx.%llx)\n",
ceph_ino(parent), ceph_snap(parent), vino.ino, vino.snap);
return ceph_get_inode(sb, vino, NULL);
}
/**
* ceph_new_inode - allocate a new inode in advance of an expected create
* @dir: parent directory for new inode
@ -1489,6 +1535,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
struct inode *in = NULL;
struct ceph_vino tvino, dvino;
struct ceph_fs_client *fsc = ceph_sb_to_client(sb);
struct inode *parent_dir = NULL;
int err = 0;
dout("fill_trace %p is_dentry %d is_target %d\n", req,
@ -1502,10 +1549,18 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
}
if (rinfo->head->is_dentry) {
struct inode *dir = req->r_parent;
/*
* r_parent may be stale, in cases when R_PARENT_LOCKED is not set,
* so we need to get the correct inode
*/
parent_dir = ceph_get_reply_dir(sb, req->r_parent, rinfo);
if (unlikely(IS_ERR(parent_dir))) {
err = PTR_ERR(parent_dir);
goto done;
}
if (dir) {
err = ceph_fill_inode(dir, NULL, &rinfo->diri,
if (parent_dir) {
err = ceph_fill_inode(parent_dir, NULL, &rinfo->diri,
rinfo->dirfrag, session, -1,
&req->r_caps_reservation);
if (err < 0)
@ -1514,14 +1569,14 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
WARN_ON_ONCE(1);
}
if (dir && req->r_op == CEPH_MDS_OP_LOOKUPNAME &&
if (parent_dir && req->r_op == CEPH_MDS_OP_LOOKUPNAME &&
test_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags) &&
!test_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags)) {
bool is_nokey = false;
struct qstr dname;
struct dentry *dn, *parent;
struct fscrypt_str oname = FSTR_INIT(NULL, 0);
struct ceph_fname fname = { .dir = dir,
struct ceph_fname fname = { .dir = parent_dir,
.name = rinfo->dname,
.ctext = rinfo->altname,
.name_len = rinfo->dname_len,
@ -1530,10 +1585,10 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
BUG_ON(!rinfo->head->is_target);
BUG_ON(req->r_dentry);
parent = d_find_any_alias(dir);
parent = d_find_any_alias(parent_dir);
BUG_ON(!parent);
err = ceph_fname_alloc_buffer(dir, &oname);
err = ceph_fname_alloc_buffer(parent_dir, &oname);
if (err < 0) {
dput(parent);
goto done;
@ -1542,7 +1597,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
err = ceph_fname_to_usr(&fname, NULL, &oname, &is_nokey);
if (err < 0) {
dput(parent);
ceph_fname_free_buffer(dir, &oname);
ceph_fname_free_buffer(parent_dir, &oname);
goto done;
}
dname.name = oname.name;
@ -1550,6 +1605,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
dname.hash = full_name_hash(parent, dname.name, dname.len);
tvino.ino = le64_to_cpu(rinfo->targeti.in->ino);
tvino.snap = le64_to_cpu(rinfo->targeti.in->snapid);
retry_lookup:
dn = d_lookup(parent, &dname);
dout("d_lookup on parent=%p name=%.*s got %p\n",
@ -1561,7 +1617,7 @@ retry_lookup:
dname.len, dname.name, dn);
if (!dn) {
dput(parent);
ceph_fname_free_buffer(dir, &oname);
ceph_fname_free_buffer(parent_dir, &oname);
err = -ENOMEM;
goto done;
}
@ -1576,12 +1632,12 @@ retry_lookup:
ceph_snap(d_inode(dn)) != tvino.snap)) {
dout(" dn %p points to wrong inode %p\n",
dn, d_inode(dn));
ceph_dir_clear_ordered(dir);
ceph_dir_clear_ordered(parent_dir);
d_delete(dn);
dput(dn);
goto retry_lookup;
}
ceph_fname_free_buffer(dir, &oname);
ceph_fname_free_buffer(parent_dir, &oname);
req->r_dentry = dn;
dput(parent);
@ -1763,6 +1819,9 @@ retry_lookup:
&dvino, ptvino);
}
done:
/* Drop extra ref from ceph_get_reply_dir() if it returned a new inode */
if (unlikely(!IS_ERR_OR_NULL(parent_dir) && parent_dir != req->r_parent))
iput(parent_dir);
dout("fill_trace done err=%d\n", err);
return err;
}

View File

@ -2631,8 +2631,7 @@ static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen)
/**
* ceph_mdsc_build_path - build a path string to a given dentry
* @dentry: dentry to which path should be built
* @plen: returned length of string
* @pbase: returned base inode number
* @path_info: output path, length, base ino+snap, and freepath ownership flag
* @for_wire: is this path going to be sent to the MDS?
*
* Build a string that represents the path to the dentry. This is mostly called
@ -2649,7 +2648,7 @@ static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen)
* Encode hidden .snap dirs as a double /, i.e.
* foo/.snap/bar -> foo//bar
*/
char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase,
char *ceph_mdsc_build_path(struct dentry *dentry, struct ceph_path_info *path_info,
int for_wire)
{
struct dentry *cur;
@ -2761,16 +2760,28 @@ retry:
goto retry;
}
*pbase = base;
*plen = PATH_MAX - 1 - pos;
/* Initialize the output structure */
memset(path_info, 0, sizeof(*path_info));
path_info->vino.ino = base;
path_info->pathlen = PATH_MAX - 1 - pos;
path_info->path = path + pos;
path_info->freepath = true;
/* Set snap from dentry if available */
if (d_inode(dentry))
path_info->vino.snap = ceph_snap(d_inode(dentry));
else
path_info->vino.snap = CEPH_NOSNAP;
dout("build_path on %p %d built %llx '%.*s'\n",
dentry, d_count(dentry), base, *plen, path + pos);
dentry, d_count(dentry), base, PATH_MAX - 1 - pos, path + pos);
return path + pos;
}
static int build_dentry_path(struct dentry *dentry, struct inode *dir,
const char **ppath, int *ppathlen, u64 *pino,
bool *pfreepath, bool parent_locked)
struct ceph_path_info *path_info,
bool parent_locked)
{
char *path;
@ -2779,40 +2790,46 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir,
dir = d_inode_rcu(dentry->d_parent);
if (dir && parent_locked && ceph_snap(dir) == CEPH_NOSNAP &&
!IS_ENCRYPTED(dir)) {
*pino = ceph_ino(dir);
path_info->vino.ino = ceph_ino(dir);
path_info->vino.snap = ceph_snap(dir);
rcu_read_unlock();
*ppath = dentry->d_name.name;
*ppathlen = dentry->d_name.len;
path_info->path = dentry->d_name.name;
path_info->pathlen = dentry->d_name.len;
path_info->freepath = false;
return 0;
}
rcu_read_unlock();
path = ceph_mdsc_build_path(dentry, ppathlen, pino, 1);
path = ceph_mdsc_build_path(dentry, path_info, 1);
if (IS_ERR(path))
return PTR_ERR(path);
*ppath = path;
*pfreepath = true;
/*
* ceph_mdsc_build_path already fills path_info, including snap handling.
*/
return 0;
}
static int build_inode_path(struct inode *inode,
const char **ppath, int *ppathlen, u64 *pino,
bool *pfreepath)
static int build_inode_path(struct inode *inode, struct ceph_path_info *path_info)
{
struct dentry *dentry;
char *path;
if (ceph_snap(inode) == CEPH_NOSNAP) {
*pino = ceph_ino(inode);
*ppathlen = 0;
path_info->vino.ino = ceph_ino(inode);
path_info->vino.snap = ceph_snap(inode);
path_info->pathlen = 0;
path_info->freepath = false;
return 0;
}
dentry = d_find_alias(inode);
path = ceph_mdsc_build_path(dentry, ppathlen, pino, 1);
path = ceph_mdsc_build_path(dentry, path_info, 1);
dput(dentry);
if (IS_ERR(path))
return PTR_ERR(path);
*ppath = path;
*pfreepath = true;
/*
* ceph_mdsc_build_path already fills path_info, including snap from dentry.
* Override with inode's snap since that's what this function is for.
*/
path_info->vino.snap = ceph_snap(inode);
return 0;
}
@ -2822,25 +2839,30 @@ static int build_inode_path(struct inode *inode,
*/
static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry,
struct inode *rdiri, const char *rpath,
u64 rino, const char **ppath, int *pathlen,
u64 *ino, bool *freepath, bool parent_locked)
u64 rino, struct ceph_path_info *path_info,
bool parent_locked)
{
int r = 0;
/* Initialize the output structure */
memset(path_info, 0, sizeof(*path_info));
if (rinode) {
r = build_inode_path(rinode, ppath, pathlen, ino, freepath);
r = build_inode_path(rinode, path_info);
dout(" inode %p %llx.%llx\n", rinode, ceph_ino(rinode),
ceph_snap(rinode));
} else if (rdentry) {
r = build_dentry_path(rdentry, rdiri, ppath, pathlen, ino,
freepath, parent_locked);
dout(" dentry %p %llx/%.*s\n", rdentry, *ino, *pathlen,
*ppath);
r = build_dentry_path(rdentry, rdiri, path_info, parent_locked);
dout(" dentry %p %llx/%.*s\n", rdentry, path_info->vino.ino,
path_info->pathlen, path_info->path);
} else if (rpath || rino) {
*ino = rino;
*ppath = rpath;
*pathlen = rpath ? strlen(rpath) : 0;
dout(" path %.*s\n", *pathlen, rpath);
path_info->vino.ino = rino;
path_info->vino.snap = CEPH_NOSNAP;
path_info->path = rpath;
path_info->pathlen = rpath ? strlen(rpath) : 0;
path_info->freepath = false;
dout(" path %.*s\n", path_info->pathlen, rpath);
}
return r;
@ -2893,28 +2915,49 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
struct ceph_mds_client *mdsc = session->s_mdsc;
struct ceph_msg *msg;
struct ceph_mds_request_head_old *head;
const char *path1 = NULL;
const char *path2 = NULL;
u64 ino1 = 0, ino2 = 0;
int pathlen1 = 0, pathlen2 = 0;
bool freepath1 = false, freepath2 = false;
struct ceph_path_info path_info1 = {0};
struct ceph_path_info path_info2 = {0};
struct dentry *old_dentry = NULL;
int len;
u16 releases;
void *p, *end;
int ret;
bool legacy = !(session->s_con.peer_features & CEPH_FEATURE_FS_BTIME);
bool parent_locked = test_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
ret = set_request_path_attr(req->r_inode, req->r_dentry,
req->r_parent, req->r_path1, req->r_ino1.ino,
&path1, &pathlen1, &ino1, &freepath1,
test_bit(CEPH_MDS_R_PARENT_LOCKED,
&req->r_req_flags));
&path_info1, parent_locked);
if (ret < 0) {
msg = ERR_PTR(ret);
goto out;
}
/*
* When the parent directory's i_rwsem is *not* locked, req->r_parent may
* have become stale (e.g. after a concurrent rename) between the time the
* dentry was looked up and now. If we detect that the stored r_parent
* does not match the inode number we just encoded for the request, switch
* to the correct inode so that the MDS receives a valid parent reference.
*/
if (!parent_locked && req->r_parent && path_info1.vino.ino &&
ceph_ino(req->r_parent) != path_info1.vino.ino) {
struct inode *old_parent = req->r_parent;
struct inode *correct_dir = ceph_get_inode(mdsc->fsc->sb, path_info1.vino, NULL);
if (!IS_ERR(correct_dir)) {
WARN_ONCE(1, "ceph: r_parent mismatch (had %llx wanted %llx) - updating\n",
ceph_ino(old_parent), path_info1.vino.ino);
/*
* Transfer CEPH_CAP_PIN from the old parent to the new one.
* The pin was taken earlier in ceph_mdsc_submit_request().
*/
ceph_put_cap_refs(ceph_inode(old_parent), CEPH_CAP_PIN);
iput(old_parent);
req->r_parent = correct_dir;
ceph_get_cap_refs(ceph_inode(req->r_parent), CEPH_CAP_PIN);
}
}
/* If r_old_dentry is set, then assume that its parent is locked */
if (req->r_old_dentry &&
!(req->r_old_dentry->d_flags & DCACHE_DISCONNECTED))
@ -2922,7 +2965,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
ret = set_request_path_attr(NULL, old_dentry,
req->r_old_dentry_dir,
req->r_path2, req->r_ino2.ino,
&path2, &pathlen2, &ino2, &freepath2, true);
&path_info2, true);
if (ret < 0) {
msg = ERR_PTR(ret);
goto out_free1;
@ -2939,7 +2982,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
/* filepaths */
len += 2 * (1 + sizeof(u32) + sizeof(u64));
len += pathlen1 + pathlen2;
len += path_info1.pathlen + path_info2.pathlen;
/* cap releases */
len += sizeof(struct ceph_mds_request_release) *
@ -2947,9 +2990,9 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
!!req->r_old_inode_drop + !!req->r_old_dentry_drop);
if (req->r_dentry_drop)
len += pathlen1;
len += path_info1.pathlen;
if (req->r_old_dentry_drop)
len += pathlen2;
len += path_info2.pathlen;
/* MClientRequest tail */
@ -3008,8 +3051,8 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
head->ino = cpu_to_le64(req->r_deleg_ino);
head->args = req->r_args;
ceph_encode_filepath(&p, end, ino1, path1);
ceph_encode_filepath(&p, end, ino2, path2);
ceph_encode_filepath(&p, end, path_info1.vino.ino, path_info1.path);
ceph_encode_filepath(&p, end, path_info2.vino.ino, path_info2.path);
/* make note of release offset, in case we need to replay */
req->r_request_release_offset = p - msg->front.iov_base;
@ -3072,11 +3115,9 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
msg->hdr.data_off = cpu_to_le16(0);
out_free2:
if (freepath2)
ceph_mdsc_free_path((char *)path2, pathlen2);
ceph_mdsc_free_path_info(&path_info2);
out_free1:
if (freepath1)
ceph_mdsc_free_path((char *)path1, pathlen1);
ceph_mdsc_free_path_info(&path_info1);
out:
return msg;
out_err:
@ -4323,24 +4364,20 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg)
struct ceph_pagelist *pagelist = recon_state->pagelist;
struct dentry *dentry;
struct ceph_cap *cap;
char *path;
int pathlen = 0, err;
u64 pathbase;
struct ceph_path_info path_info = {0};
int err;
u64 snap_follows;
dentry = d_find_primary(inode);
if (dentry) {
/* set pathbase to parent dir when msg_version >= 2 */
path = ceph_mdsc_build_path(dentry, &pathlen, &pathbase,
recon_state->msg_version >= 2);
char *path = ceph_mdsc_build_path(dentry, &path_info,
recon_state->msg_version >= 2);
dput(dentry);
if (IS_ERR(path)) {
err = PTR_ERR(path);
goto out_err;
}
} else {
path = NULL;
pathbase = 0;
}
spin_lock(&ci->i_ceph_lock);
@ -4373,7 +4410,7 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg)
rec.v2.wanted = cpu_to_le32(__ceph_caps_wanted(ci));
rec.v2.issued = cpu_to_le32(cap->issued);
rec.v2.snaprealm = cpu_to_le64(ci->i_snap_realm->ino);
rec.v2.pathbase = cpu_to_le64(pathbase);
rec.v2.pathbase = cpu_to_le64(path_info.vino.ino);
rec.v2.flock_len = (__force __le32)
((ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) ? 0 : 1);
} else {
@ -4384,7 +4421,7 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg)
ceph_encode_timespec64(&rec.v1.mtime, &inode->i_mtime);
ceph_encode_timespec64(&rec.v1.atime, &inode->i_atime);
rec.v1.snaprealm = cpu_to_le64(ci->i_snap_realm->ino);
rec.v1.pathbase = cpu_to_le64(pathbase);
rec.v1.pathbase = cpu_to_le64(path_info.vino.ino);
}
if (list_empty(&ci->i_cap_snaps)) {
@ -4446,7 +4483,7 @@ encode_again:
sizeof(struct ceph_filelock);
rec.v2.flock_len = cpu_to_le32(struct_len);
struct_len += sizeof(u32) + pathlen + sizeof(rec.v2);
struct_len += sizeof(u32) + path_info.pathlen + sizeof(rec.v2);
if (struct_v >= 2)
struct_len += sizeof(u64); /* snap_follows */
@ -4470,7 +4507,7 @@ encode_again:
ceph_pagelist_encode_8(pagelist, 1);
ceph_pagelist_encode_32(pagelist, struct_len);
}
ceph_pagelist_encode_string(pagelist, path, pathlen);
ceph_pagelist_encode_string(pagelist, (char *)path_info.path, path_info.pathlen);
ceph_pagelist_append(pagelist, &rec, sizeof(rec.v2));
ceph_locks_to_pagelist(flocks, pagelist,
num_fcntl_locks, num_flock_locks);
@ -4481,17 +4518,17 @@ out_freeflocks:
} else {
err = ceph_pagelist_reserve(pagelist,
sizeof(u64) + sizeof(u32) +
pathlen + sizeof(rec.v1));
path_info.pathlen + sizeof(rec.v1));
if (err)
goto out_err;
ceph_pagelist_encode_64(pagelist, ceph_ino(inode));
ceph_pagelist_encode_string(pagelist, path, pathlen);
ceph_pagelist_encode_string(pagelist, (char *)path_info.path, path_info.pathlen);
ceph_pagelist_append(pagelist, &rec, sizeof(rec.v1));
}
out_err:
ceph_mdsc_free_path(path, pathlen);
ceph_mdsc_free_path_info(&path_info);
if (!err)
recon_state->nr_caps++;
return err;

View File

@ -577,13 +577,23 @@ extern int ceph_iterate_session_caps(struct ceph_mds_session *session,
void *arg);
extern void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc);
static inline void ceph_mdsc_free_path(char *path, int len)
/*
* Structure to group path-related output parameters for build_*_path functions
*/
struct ceph_path_info {
const char *path;
int pathlen;
struct ceph_vino vino;
bool freepath;
};
static inline void ceph_mdsc_free_path_info(const struct ceph_path_info *path_info)
{
if (!IS_ERR_OR_NULL(path))
__putname(path - (PATH_MAX - 1 - len));
if (path_info && path_info->freepath && !IS_ERR_OR_NULL(path_info->path))
__putname((char *)path_info->path - (PATH_MAX - 1 - path_info->pathlen));
}
extern char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
extern char *ceph_mdsc_build_path(struct dentry *dentry, struct ceph_path_info *path_info,
int for_wire);
extern void __ceph_mdsc_drop_dentry_lease(struct dentry *dentry);