mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 02:24:24 +02:00
ceph: fix race condition validating r_parent before applying state
Add validation to ensure the cached parent directory inode matches the directory info in MDS replies. This prevents client-side race conditions where concurrent operations (e.g. rename) cause r_parent to become stale between request initiation and reply processing, which could lead to applying state changes to incorrect directory inodes. [ idryomov: folded a kerneldoc fixup and a follow-up fix from Alex to move CEPH_CAP_PIN reference when r_parent is updated: When the parent directory lock is not held, req->r_parent can become stale and is updated to point to the correct inode. However, the associated CEPH_CAP_PIN reference was not being adjusted. The CEPH_CAP_PIN is a reference on an inode that is tracked for accounting purposes. Moving this pin is important to keep the accounting balanced. When the pin was not moved from the old parent to the new one, it created two problems: The reference on the old, stale parent was never released, causing a reference leak. A reference for the new parent was never acquired, creating the risk of a reference underflow later in ceph_mdsc_release_request(). This patch corrects the logic by releasing the pin from the old parent and acquiring it for the new parent when r_parent is switched. This ensures reference accounting stays balanced. ] Cc: stable@vger.kernel.org Signed-off-by: Alex Markuze <amarkuze@redhat.com> Reviewed-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
This commit is contained in:
parent
76eeb9b8de
commit
15f519e9f8
|
|
@ -55,8 +55,6 @@ static int mdsc_show(struct seq_file *s, void *p)
|
||||||
struct ceph_mds_client *mdsc = fsc->mdsc;
|
struct ceph_mds_client *mdsc = fsc->mdsc;
|
||||||
struct ceph_mds_request *req;
|
struct ceph_mds_request *req;
|
||||||
struct rb_node *rp;
|
struct rb_node *rp;
|
||||||
int pathlen = 0;
|
|
||||||
u64 pathbase;
|
|
||||||
char *path;
|
char *path;
|
||||||
|
|
||||||
mutex_lock(&mdsc->mutex);
|
mutex_lock(&mdsc->mutex);
|
||||||
|
|
@ -81,8 +79,8 @@ static int mdsc_show(struct seq_file *s, void *p)
|
||||||
if (req->r_inode) {
|
if (req->r_inode) {
|
||||||
seq_printf(s, " #%llx", ceph_ino(req->r_inode));
|
seq_printf(s, " #%llx", ceph_ino(req->r_inode));
|
||||||
} else if (req->r_dentry) {
|
} else if (req->r_dentry) {
|
||||||
path = ceph_mdsc_build_path(mdsc, req->r_dentry, &pathlen,
|
struct ceph_path_info path_info;
|
||||||
&pathbase, 0);
|
path = ceph_mdsc_build_path(mdsc, req->r_dentry, &path_info, 0);
|
||||||
if (IS_ERR(path))
|
if (IS_ERR(path))
|
||||||
path = NULL;
|
path = NULL;
|
||||||
spin_lock(&req->r_dentry->d_lock);
|
spin_lock(&req->r_dentry->d_lock);
|
||||||
|
|
@ -91,7 +89,7 @@ static int mdsc_show(struct seq_file *s, void *p)
|
||||||
req->r_dentry,
|
req->r_dentry,
|
||||||
path ? path : "");
|
path ? path : "");
|
||||||
spin_unlock(&req->r_dentry->d_lock);
|
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) {
|
} else if (req->r_path1) {
|
||||||
seq_printf(s, " #%llx/%s", req->r_ino1.ino,
|
seq_printf(s, " #%llx/%s", req->r_ino1.ino,
|
||||||
req->r_path1);
|
req->r_path1);
|
||||||
|
|
@ -100,8 +98,8 @@ static int mdsc_show(struct seq_file *s, void *p)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req->r_old_dentry) {
|
if (req->r_old_dentry) {
|
||||||
path = ceph_mdsc_build_path(mdsc, req->r_old_dentry, &pathlen,
|
struct ceph_path_info path_info;
|
||||||
&pathbase, 0);
|
path = ceph_mdsc_build_path(mdsc, req->r_old_dentry, &path_info, 0);
|
||||||
if (IS_ERR(path))
|
if (IS_ERR(path))
|
||||||
path = NULL;
|
path = NULL;
|
||||||
spin_lock(&req->r_old_dentry->d_lock);
|
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,
|
req->r_old_dentry,
|
||||||
path ? path : "");
|
path ? path : "");
|
||||||
spin_unlock(&req->r_old_dentry->d_lock);
|
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) {
|
} else if (req->r_path2 && req->r_op != CEPH_MDS_OP_SYMLINK) {
|
||||||
if (req->r_ino2.ino)
|
if (req->r_ino2.ino)
|
||||||
seq_printf(s, " #%llx/%s", req->r_ino2.ino,
|
seq_printf(s, " #%llx/%s", req->r_ino2.ino,
|
||||||
|
|
|
||||||
|
|
@ -1271,10 +1271,8 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc,
|
||||||
|
|
||||||
/* If op failed, mark everyone involved for errors */
|
/* If op failed, mark everyone involved for errors */
|
||||||
if (result) {
|
if (result) {
|
||||||
int pathlen = 0;
|
struct ceph_path_info path_info = {0};
|
||||||
u64 base = 0;
|
char *path = ceph_mdsc_build_path(mdsc, dentry, &path_info, 0);
|
||||||
char *path = ceph_mdsc_build_path(mdsc, dentry, &pathlen,
|
|
||||||
&base, 0);
|
|
||||||
|
|
||||||
/* mark error on parent + clear complete */
|
/* mark error on parent + clear complete */
|
||||||
mapping_set_error(req->r_parent->i_mapping, result);
|
mapping_set_error(req->r_parent->i_mapping, result);
|
||||||
|
|
@ -1288,8 +1286,8 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc,
|
||||||
mapping_set_error(req->r_old_inode->i_mapping, result);
|
mapping_set_error(req->r_old_inode->i_mapping, result);
|
||||||
|
|
||||||
pr_warn_client(cl, "failure path=(%llx)%s result=%d!\n",
|
pr_warn_client(cl, "failure path=(%llx)%s result=%d!\n",
|
||||||
base, IS_ERR(path) ? "<<bad>>" : path, result);
|
path_info.vino.ino, IS_ERR(path) ? "<<bad>>" : path, result);
|
||||||
ceph_mdsc_free_path(path, pathlen);
|
ceph_mdsc_free_path_info(&path_info);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
iput(req->r_old_inode);
|
iput(req->r_old_inode);
|
||||||
|
|
@ -1347,8 +1345,6 @@ static int ceph_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
int err = -EROFS;
|
int err = -EROFS;
|
||||||
int op;
|
int op;
|
||||||
char *path;
|
char *path;
|
||||||
int pathlen;
|
|
||||||
u64 pathbase;
|
|
||||||
|
|
||||||
if (ceph_snap(dir) == CEPH_SNAPDIR) {
|
if (ceph_snap(dir) == CEPH_SNAPDIR) {
|
||||||
/* rmdir .snap/foo is RMSNAP */
|
/* rmdir .snap/foo is RMSNAP */
|
||||||
|
|
@ -1367,14 +1363,15 @@ static int ceph_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
if (!dn) {
|
if (!dn) {
|
||||||
try_async = false;
|
try_async = false;
|
||||||
} else {
|
} else {
|
||||||
path = ceph_mdsc_build_path(mdsc, dn, &pathlen, &pathbase, 0);
|
struct ceph_path_info path_info;
|
||||||
|
path = ceph_mdsc_build_path(mdsc, dn, &path_info, 0);
|
||||||
if (IS_ERR(path)) {
|
if (IS_ERR(path)) {
|
||||||
try_async = false;
|
try_async = false;
|
||||||
err = 0;
|
err = 0;
|
||||||
} else {
|
} else {
|
||||||
err = ceph_mds_check_access(mdsc, path, MAY_WRITE);
|
err = ceph_mds_check_access(mdsc, path, MAY_WRITE);
|
||||||
}
|
}
|
||||||
ceph_mdsc_free_path(path, pathlen);
|
ceph_mdsc_free_path_info(&path_info);
|
||||||
dput(dn);
|
dput(dn);
|
||||||
|
|
||||||
/* For none EACCES cases will let the MDS do the mds auth check */
|
/* For none EACCES cases will let the MDS do the mds auth check */
|
||||||
|
|
|
||||||
|
|
@ -368,8 +368,6 @@ int ceph_open(struct inode *inode, struct file *file)
|
||||||
int flags, fmode, wanted;
|
int flags, fmode, wanted;
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
char *path;
|
char *path;
|
||||||
int pathlen;
|
|
||||||
u64 pathbase;
|
|
||||||
bool do_sync = false;
|
bool do_sync = false;
|
||||||
int mask = MAY_READ;
|
int mask = MAY_READ;
|
||||||
|
|
||||||
|
|
@ -399,14 +397,15 @@ int ceph_open(struct inode *inode, struct file *file)
|
||||||
if (!dentry) {
|
if (!dentry) {
|
||||||
do_sync = true;
|
do_sync = true;
|
||||||
} else {
|
} else {
|
||||||
path = ceph_mdsc_build_path(mdsc, dentry, &pathlen, &pathbase, 0);
|
struct ceph_path_info path_info;
|
||||||
|
path = ceph_mdsc_build_path(mdsc, dentry, &path_info, 0);
|
||||||
if (IS_ERR(path)) {
|
if (IS_ERR(path)) {
|
||||||
do_sync = true;
|
do_sync = true;
|
||||||
err = 0;
|
err = 0;
|
||||||
} else {
|
} else {
|
||||||
err = ceph_mds_check_access(mdsc, path, mask);
|
err = ceph_mds_check_access(mdsc, path, mask);
|
||||||
}
|
}
|
||||||
ceph_mdsc_free_path(path, pathlen);
|
ceph_mdsc_free_path_info(&path_info);
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
|
|
||||||
/* For none EACCES cases will let the MDS do the mds auth check */
|
/* For none EACCES cases will let the MDS do the mds auth check */
|
||||||
|
|
@ -614,15 +613,13 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc,
|
||||||
mapping_set_error(req->r_parent->i_mapping, result);
|
mapping_set_error(req->r_parent->i_mapping, result);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
int pathlen = 0;
|
struct ceph_path_info path_info = {0};
|
||||||
u64 base = 0;
|
char *path = ceph_mdsc_build_path(mdsc, req->r_dentry, &path_info, 0);
|
||||||
char *path = ceph_mdsc_build_path(mdsc, req->r_dentry, &pathlen,
|
|
||||||
&base, 0);
|
|
||||||
|
|
||||||
pr_warn_client(cl,
|
pr_warn_client(cl,
|
||||||
"async create failure path=(%llx)%s result=%d!\n",
|
"async create failure path=(%llx)%s result=%d!\n",
|
||||||
base, IS_ERR(path) ? "<<bad>>" : path, result);
|
path_info.vino.ino, IS_ERR(path) ? "<<bad>>" : path, result);
|
||||||
ceph_mdsc_free_path(path, pathlen);
|
ceph_mdsc_free_path_info(&path_info);
|
||||||
|
|
||||||
ceph_dir_clear_complete(req->r_parent);
|
ceph_dir_clear_complete(req->r_parent);
|
||||||
if (!d_unhashed(dentry))
|
if (!d_unhashed(dentry))
|
||||||
|
|
@ -791,8 +788,6 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
|
||||||
int mask;
|
int mask;
|
||||||
int err;
|
int err;
|
||||||
char *path;
|
char *path;
|
||||||
int pathlen;
|
|
||||||
u64 pathbase;
|
|
||||||
|
|
||||||
doutc(cl, "%p %llx.%llx dentry %p '%pd' %s flags %d mode 0%o\n",
|
doutc(cl, "%p %llx.%llx dentry %p '%pd' %s flags %d mode 0%o\n",
|
||||||
dir, ceph_vinop(dir), dentry, dentry,
|
dir, ceph_vinop(dir), dentry, dentry,
|
||||||
|
|
@ -814,7 +809,8 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
|
||||||
if (!dn) {
|
if (!dn) {
|
||||||
try_async = false;
|
try_async = false;
|
||||||
} else {
|
} else {
|
||||||
path = ceph_mdsc_build_path(mdsc, dn, &pathlen, &pathbase, 0);
|
struct ceph_path_info path_info;
|
||||||
|
path = ceph_mdsc_build_path(mdsc, dn, &path_info, 0);
|
||||||
if (IS_ERR(path)) {
|
if (IS_ERR(path)) {
|
||||||
try_async = false;
|
try_async = false;
|
||||||
err = 0;
|
err = 0;
|
||||||
|
|
@ -826,7 +822,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
|
||||||
mask |= MAY_WRITE;
|
mask |= MAY_WRITE;
|
||||||
err = ceph_mds_check_access(mdsc, path, mask);
|
err = ceph_mds_check_access(mdsc, path, mask);
|
||||||
}
|
}
|
||||||
ceph_mdsc_free_path(path, pathlen);
|
ceph_mdsc_free_path_info(&path_info);
|
||||||
dput(dn);
|
dput(dn);
|
||||||
|
|
||||||
/* For none EACCES cases will let the MDS do the mds auth check */
|
/* For none EACCES cases will let the MDS do the mds auth check */
|
||||||
|
|
|
||||||
|
|
@ -2487,22 +2487,21 @@ int __ceph_setattr(struct mnt_idmap *idmap, struct inode *inode,
|
||||||
int truncate_retry = 20; /* The RMW will take around 50ms */
|
int truncate_retry = 20; /* The RMW will take around 50ms */
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
char *path;
|
char *path;
|
||||||
int pathlen;
|
|
||||||
u64 pathbase;
|
|
||||||
bool do_sync = false;
|
bool do_sync = false;
|
||||||
|
|
||||||
dentry = d_find_alias(inode);
|
dentry = d_find_alias(inode);
|
||||||
if (!dentry) {
|
if (!dentry) {
|
||||||
do_sync = true;
|
do_sync = true;
|
||||||
} else {
|
} else {
|
||||||
path = ceph_mdsc_build_path(mdsc, dentry, &pathlen, &pathbase, 0);
|
struct ceph_path_info path_info;
|
||||||
|
path = ceph_mdsc_build_path(mdsc, dentry, &path_info, 0);
|
||||||
if (IS_ERR(path)) {
|
if (IS_ERR(path)) {
|
||||||
do_sync = true;
|
do_sync = true;
|
||||||
err = 0;
|
err = 0;
|
||||||
} else {
|
} else {
|
||||||
err = ceph_mds_check_access(mdsc, path, MAY_WRITE);
|
err = ceph_mds_check_access(mdsc, path, MAY_WRITE);
|
||||||
}
|
}
|
||||||
ceph_mdsc_free_path(path, pathlen);
|
ceph_mdsc_free_path_info(&path_info);
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
|
|
||||||
/* For none EACCES cases will let the MDS do the mds auth check */
|
/* For none EACCES cases will let the MDS do the mds auth check */
|
||||||
|
|
|
||||||
|
|
@ -2681,8 +2681,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
|
* ceph_mdsc_build_path - build a path string to a given dentry
|
||||||
* @mdsc: mds client
|
* @mdsc: mds client
|
||||||
* @dentry: dentry to which path should be built
|
* @dentry: dentry to which path should be built
|
||||||
* @plen: returned length of string
|
* @path_info: output path, length, base ino+snap, and freepath ownership flag
|
||||||
* @pbase: returned base inode number
|
|
||||||
* @for_wire: is this path going to be sent to the MDS?
|
* @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
|
* Build a string that represents the path to the dentry. This is mostly called
|
||||||
|
|
@ -2700,7 +2699,7 @@ static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen)
|
||||||
* foo/.snap/bar -> foo//bar
|
* foo/.snap/bar -> foo//bar
|
||||||
*/
|
*/
|
||||||
char *ceph_mdsc_build_path(struct ceph_mds_client *mdsc, struct dentry *dentry,
|
char *ceph_mdsc_build_path(struct ceph_mds_client *mdsc, struct dentry *dentry,
|
||||||
int *plen, u64 *pbase, int for_wire)
|
struct ceph_path_info *path_info, int for_wire)
|
||||||
{
|
{
|
||||||
struct ceph_client *cl = mdsc->fsc->client;
|
struct ceph_client *cl = mdsc->fsc->client;
|
||||||
struct dentry *cur;
|
struct dentry *cur;
|
||||||
|
|
@ -2810,16 +2809,28 @@ char *ceph_mdsc_build_path(struct ceph_mds_client *mdsc, struct dentry *dentry,
|
||||||
return ERR_PTR(-ENAMETOOLONG);
|
return ERR_PTR(-ENAMETOOLONG);
|
||||||
}
|
}
|
||||||
|
|
||||||
*pbase = base;
|
/* Initialize the output structure */
|
||||||
*plen = PATH_MAX - 1 - pos;
|
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;
|
||||||
|
|
||||||
doutc(cl, "on %p %d built %llx '%.*s'\n", dentry, d_count(dentry),
|
doutc(cl, "on %p %d built %llx '%.*s'\n", dentry, d_count(dentry),
|
||||||
base, *plen, path + pos);
|
base, PATH_MAX - 1 - pos, path + pos);
|
||||||
return path + pos;
|
return path + pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int build_dentry_path(struct ceph_mds_client *mdsc, struct dentry *dentry,
|
static int build_dentry_path(struct ceph_mds_client *mdsc, struct dentry *dentry,
|
||||||
struct inode *dir, const char **ppath, int *ppathlen,
|
struct inode *dir, struct ceph_path_info *path_info,
|
||||||
u64 *pino, bool *pfreepath, bool parent_locked)
|
bool parent_locked)
|
||||||
{
|
{
|
||||||
char *path;
|
char *path;
|
||||||
|
|
||||||
|
|
@ -2828,41 +2839,47 @@ static int build_dentry_path(struct ceph_mds_client *mdsc, struct dentry *dentry
|
||||||
dir = d_inode_rcu(dentry->d_parent);
|
dir = d_inode_rcu(dentry->d_parent);
|
||||||
if (dir && parent_locked && ceph_snap(dir) == CEPH_NOSNAP &&
|
if (dir && parent_locked && ceph_snap(dir) == CEPH_NOSNAP &&
|
||||||
!IS_ENCRYPTED(dir)) {
|
!IS_ENCRYPTED(dir)) {
|
||||||
*pino = ceph_ino(dir);
|
path_info->vino.ino = ceph_ino(dir);
|
||||||
|
path_info->vino.snap = ceph_snap(dir);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
*ppath = dentry->d_name.name;
|
path_info->path = dentry->d_name.name;
|
||||||
*ppathlen = dentry->d_name.len;
|
path_info->pathlen = dentry->d_name.len;
|
||||||
|
path_info->freepath = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
path = ceph_mdsc_build_path(mdsc, dentry, ppathlen, pino, 1);
|
path = ceph_mdsc_build_path(mdsc, dentry, path_info, 1);
|
||||||
if (IS_ERR(path))
|
if (IS_ERR(path))
|
||||||
return PTR_ERR(path);
|
return PTR_ERR(path);
|
||||||
*ppath = path;
|
/*
|
||||||
*pfreepath = true;
|
* ceph_mdsc_build_path already fills path_info, including snap handling.
|
||||||
|
*/
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int build_inode_path(struct inode *inode,
|
static int build_inode_path(struct inode *inode, struct ceph_path_info *path_info)
|
||||||
const char **ppath, int *ppathlen, u64 *pino,
|
|
||||||
bool *pfreepath)
|
|
||||||
{
|
{
|
||||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
|
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
char *path;
|
char *path;
|
||||||
|
|
||||||
if (ceph_snap(inode) == CEPH_NOSNAP) {
|
if (ceph_snap(inode) == CEPH_NOSNAP) {
|
||||||
*pino = ceph_ino(inode);
|
path_info->vino.ino = ceph_ino(inode);
|
||||||
*ppathlen = 0;
|
path_info->vino.snap = ceph_snap(inode);
|
||||||
|
path_info->pathlen = 0;
|
||||||
|
path_info->freepath = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
dentry = d_find_alias(inode);
|
dentry = d_find_alias(inode);
|
||||||
path = ceph_mdsc_build_path(mdsc, dentry, ppathlen, pino, 1);
|
path = ceph_mdsc_build_path(mdsc, dentry, path_info, 1);
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
if (IS_ERR(path))
|
if (IS_ERR(path))
|
||||||
return PTR_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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2872,26 +2889,32 @@ static int build_inode_path(struct inode *inode,
|
||||||
*/
|
*/
|
||||||
static int set_request_path_attr(struct ceph_mds_client *mdsc, struct inode *rinode,
|
static int set_request_path_attr(struct ceph_mds_client *mdsc, struct inode *rinode,
|
||||||
struct dentry *rdentry, struct inode *rdiri,
|
struct dentry *rdentry, struct inode *rdiri,
|
||||||
const char *rpath, u64 rino, const char **ppath,
|
const char *rpath, u64 rino,
|
||||||
int *pathlen, u64 *ino, bool *freepath,
|
struct ceph_path_info *path_info,
|
||||||
bool parent_locked)
|
bool parent_locked)
|
||||||
{
|
{
|
||||||
struct ceph_client *cl = mdsc->fsc->client;
|
struct ceph_client *cl = mdsc->fsc->client;
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
|
||||||
|
/* Initialize the output structure */
|
||||||
|
memset(path_info, 0, sizeof(*path_info));
|
||||||
|
|
||||||
if (rinode) {
|
if (rinode) {
|
||||||
r = build_inode_path(rinode, ppath, pathlen, ino, freepath);
|
r = build_inode_path(rinode, path_info);
|
||||||
doutc(cl, " inode %p %llx.%llx\n", rinode, ceph_ino(rinode),
|
doutc(cl, " inode %p %llx.%llx\n", rinode, ceph_ino(rinode),
|
||||||
ceph_snap(rinode));
|
ceph_snap(rinode));
|
||||||
} else if (rdentry) {
|
} else if (rdentry) {
|
||||||
r = build_dentry_path(mdsc, rdentry, rdiri, ppath, pathlen, ino,
|
r = build_dentry_path(mdsc, rdentry, rdiri, path_info, parent_locked);
|
||||||
freepath, parent_locked);
|
doutc(cl, " dentry %p %llx/%.*s\n", rdentry, path_info->vino.ino,
|
||||||
doutc(cl, " dentry %p %llx/%.*s\n", rdentry, *ino, *pathlen, *ppath);
|
path_info->pathlen, path_info->path);
|
||||||
} else if (rpath || rino) {
|
} else if (rpath || rino) {
|
||||||
*ino = rino;
|
path_info->vino.ino = rino;
|
||||||
*ppath = rpath;
|
path_info->vino.snap = CEPH_NOSNAP;
|
||||||
*pathlen = rpath ? strlen(rpath) : 0;
|
path_info->path = rpath;
|
||||||
doutc(cl, " path %.*s\n", *pathlen, rpath);
|
path_info->pathlen = rpath ? strlen(rpath) : 0;
|
||||||
|
path_info->freepath = false;
|
||||||
|
|
||||||
|
doutc(cl, " path %.*s\n", path_info->pathlen, rpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
|
|
@ -2968,11 +2991,8 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
|
||||||
struct ceph_client *cl = mdsc->fsc->client;
|
struct ceph_client *cl = mdsc->fsc->client;
|
||||||
struct ceph_msg *msg;
|
struct ceph_msg *msg;
|
||||||
struct ceph_mds_request_head_legacy *lhead;
|
struct ceph_mds_request_head_legacy *lhead;
|
||||||
const char *path1 = NULL;
|
struct ceph_path_info path_info1 = {0};
|
||||||
const char *path2 = NULL;
|
struct ceph_path_info path_info2 = {0};
|
||||||
u64 ino1 = 0, ino2 = 0;
|
|
||||||
int pathlen1 = 0, pathlen2 = 0;
|
|
||||||
bool freepath1 = false, freepath2 = false;
|
|
||||||
struct dentry *old_dentry = NULL;
|
struct dentry *old_dentry = NULL;
|
||||||
int len;
|
int len;
|
||||||
u16 releases;
|
u16 releases;
|
||||||
|
|
@ -2982,25 +3002,49 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
|
||||||
u16 request_head_version = mds_supported_head_version(session);
|
u16 request_head_version = mds_supported_head_version(session);
|
||||||
kuid_t caller_fsuid = req->r_cred->fsuid;
|
kuid_t caller_fsuid = req->r_cred->fsuid;
|
||||||
kgid_t caller_fsgid = req->r_cred->fsgid;
|
kgid_t caller_fsgid = req->r_cred->fsgid;
|
||||||
|
bool parent_locked = test_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
|
||||||
|
|
||||||
ret = set_request_path_attr(mdsc, req->r_inode, req->r_dentry,
|
ret = set_request_path_attr(mdsc, req->r_inode, req->r_dentry,
|
||||||
req->r_parent, req->r_path1, req->r_ino1.ino,
|
req->r_parent, req->r_path1, req->r_ino1.ino,
|
||||||
&path1, &pathlen1, &ino1, &freepath1,
|
&path_info1, parent_locked);
|
||||||
test_bit(CEPH_MDS_R_PARENT_LOCKED,
|
|
||||||
&req->r_req_flags));
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
msg = ERR_PTR(ret);
|
msg = ERR_PTR(ret);
|
||||||
goto out;
|
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 r_old_dentry is set, then assume that its parent is locked */
|
||||||
if (req->r_old_dentry &&
|
if (req->r_old_dentry &&
|
||||||
!(req->r_old_dentry->d_flags & DCACHE_DISCONNECTED))
|
!(req->r_old_dentry->d_flags & DCACHE_DISCONNECTED))
|
||||||
old_dentry = req->r_old_dentry;
|
old_dentry = req->r_old_dentry;
|
||||||
ret = set_request_path_attr(mdsc, NULL, old_dentry,
|
ret = set_request_path_attr(mdsc, NULL, old_dentry,
|
||||||
req->r_old_dentry_dir,
|
req->r_old_dentry_dir,
|
||||||
req->r_path2, req->r_ino2.ino,
|
req->r_path2, req->r_ino2.ino,
|
||||||
&path2, &pathlen2, &ino2, &freepath2, true);
|
&path_info2, true);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
msg = ERR_PTR(ret);
|
msg = ERR_PTR(ret);
|
||||||
goto out_free1;
|
goto out_free1;
|
||||||
|
|
@ -3031,7 +3075,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
|
||||||
|
|
||||||
/* filepaths */
|
/* filepaths */
|
||||||
len += 2 * (1 + sizeof(u32) + sizeof(u64));
|
len += 2 * (1 + sizeof(u32) + sizeof(u64));
|
||||||
len += pathlen1 + pathlen2;
|
len += path_info1.pathlen + path_info2.pathlen;
|
||||||
|
|
||||||
/* cap releases */
|
/* cap releases */
|
||||||
len += sizeof(struct ceph_mds_request_release) *
|
len += sizeof(struct ceph_mds_request_release) *
|
||||||
|
|
@ -3039,9 +3083,9 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
|
||||||
!!req->r_old_inode_drop + !!req->r_old_dentry_drop);
|
!!req->r_old_inode_drop + !!req->r_old_dentry_drop);
|
||||||
|
|
||||||
if (req->r_dentry_drop)
|
if (req->r_dentry_drop)
|
||||||
len += pathlen1;
|
len += path_info1.pathlen;
|
||||||
if (req->r_old_dentry_drop)
|
if (req->r_old_dentry_drop)
|
||||||
len += pathlen2;
|
len += path_info2.pathlen;
|
||||||
|
|
||||||
/* MClientRequest tail */
|
/* MClientRequest tail */
|
||||||
|
|
||||||
|
|
@ -3154,8 +3198,8 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
|
||||||
lhead->ino = cpu_to_le64(req->r_deleg_ino);
|
lhead->ino = cpu_to_le64(req->r_deleg_ino);
|
||||||
lhead->args = req->r_args;
|
lhead->args = req->r_args;
|
||||||
|
|
||||||
ceph_encode_filepath(&p, end, ino1, path1);
|
ceph_encode_filepath(&p, end, path_info1.vino.ino, path_info1.path);
|
||||||
ceph_encode_filepath(&p, end, ino2, path2);
|
ceph_encode_filepath(&p, end, path_info2.vino.ino, path_info2.path);
|
||||||
|
|
||||||
/* make note of release offset, in case we need to replay */
|
/* make note of release offset, in case we need to replay */
|
||||||
req->r_request_release_offset = p - msg->front.iov_base;
|
req->r_request_release_offset = p - msg->front.iov_base;
|
||||||
|
|
@ -3218,11 +3262,9 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
|
||||||
msg->hdr.data_off = cpu_to_le16(0);
|
msg->hdr.data_off = cpu_to_le16(0);
|
||||||
|
|
||||||
out_free2:
|
out_free2:
|
||||||
if (freepath2)
|
ceph_mdsc_free_path_info(&path_info2);
|
||||||
ceph_mdsc_free_path((char *)path2, pathlen2);
|
|
||||||
out_free1:
|
out_free1:
|
||||||
if (freepath1)
|
ceph_mdsc_free_path_info(&path_info1);
|
||||||
ceph_mdsc_free_path((char *)path1, pathlen1);
|
|
||||||
out:
|
out:
|
||||||
return msg;
|
return msg;
|
||||||
out_err:
|
out_err:
|
||||||
|
|
@ -4579,24 +4621,20 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg)
|
||||||
struct ceph_pagelist *pagelist = recon_state->pagelist;
|
struct ceph_pagelist *pagelist = recon_state->pagelist;
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
struct ceph_cap *cap;
|
struct ceph_cap *cap;
|
||||||
char *path;
|
struct ceph_path_info path_info = {0};
|
||||||
int pathlen = 0, err;
|
int err;
|
||||||
u64 pathbase;
|
|
||||||
u64 snap_follows;
|
u64 snap_follows;
|
||||||
|
|
||||||
dentry = d_find_primary(inode);
|
dentry = d_find_primary(inode);
|
||||||
if (dentry) {
|
if (dentry) {
|
||||||
/* set pathbase to parent dir when msg_version >= 2 */
|
/* set pathbase to parent dir when msg_version >= 2 */
|
||||||
path = ceph_mdsc_build_path(mdsc, dentry, &pathlen, &pathbase,
|
char *path = ceph_mdsc_build_path(mdsc, dentry, &path_info,
|
||||||
recon_state->msg_version >= 2);
|
recon_state->msg_version >= 2);
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
if (IS_ERR(path)) {
|
if (IS_ERR(path)) {
|
||||||
err = PTR_ERR(path);
|
err = PTR_ERR(path);
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
path = NULL;
|
|
||||||
pathbase = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock(&ci->i_ceph_lock);
|
spin_lock(&ci->i_ceph_lock);
|
||||||
|
|
@ -4629,7 +4667,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.wanted = cpu_to_le32(__ceph_caps_wanted(ci));
|
||||||
rec.v2.issued = cpu_to_le32(cap->issued);
|
rec.v2.issued = cpu_to_le32(cap->issued);
|
||||||
rec.v2.snaprealm = cpu_to_le64(ci->i_snap_realm->ino);
|
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)
|
rec.v2.flock_len = (__force __le32)
|
||||||
((ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) ? 0 : 1);
|
((ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) ? 0 : 1);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -4644,7 +4682,7 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg)
|
||||||
ts = inode_get_atime(inode);
|
ts = inode_get_atime(inode);
|
||||||
ceph_encode_timespec64(&rec.v1.atime, &ts);
|
ceph_encode_timespec64(&rec.v1.atime, &ts);
|
||||||
rec.v1.snaprealm = cpu_to_le64(ci->i_snap_realm->ino);
|
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)) {
|
if (list_empty(&ci->i_cap_snaps)) {
|
||||||
|
|
@ -4706,7 +4744,7 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg)
|
||||||
sizeof(struct ceph_filelock);
|
sizeof(struct ceph_filelock);
|
||||||
rec.v2.flock_len = cpu_to_le32(struct_len);
|
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)
|
if (struct_v >= 2)
|
||||||
struct_len += sizeof(u64); /* snap_follows */
|
struct_len += sizeof(u64); /* snap_follows */
|
||||||
|
|
@ -4730,7 +4768,7 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg)
|
||||||
ceph_pagelist_encode_8(pagelist, 1);
|
ceph_pagelist_encode_8(pagelist, 1);
|
||||||
ceph_pagelist_encode_32(pagelist, struct_len);
|
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_pagelist_append(pagelist, &rec, sizeof(rec.v2));
|
||||||
ceph_locks_to_pagelist(flocks, pagelist,
|
ceph_locks_to_pagelist(flocks, pagelist,
|
||||||
num_fcntl_locks, num_flock_locks);
|
num_fcntl_locks, num_flock_locks);
|
||||||
|
|
@ -4741,17 +4779,17 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg)
|
||||||
} else {
|
} else {
|
||||||
err = ceph_pagelist_reserve(pagelist,
|
err = ceph_pagelist_reserve(pagelist,
|
||||||
sizeof(u64) + sizeof(u32) +
|
sizeof(u64) + sizeof(u32) +
|
||||||
pathlen + sizeof(rec.v1));
|
path_info.pathlen + sizeof(rec.v1));
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
ceph_pagelist_encode_64(pagelist, ceph_ino(inode));
|
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));
|
ceph_pagelist_append(pagelist, &rec, sizeof(rec.v1));
|
||||||
}
|
}
|
||||||
|
|
||||||
out_err:
|
out_err:
|
||||||
ceph_mdsc_free_path(path, pathlen);
|
ceph_mdsc_free_path_info(&path_info);
|
||||||
if (!err)
|
if (!err)
|
||||||
recon_state->nr_caps++;
|
recon_state->nr_caps++;
|
||||||
return err;
|
return err;
|
||||||
|
|
|
||||||
|
|
@ -617,14 +617,24 @@ extern int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath,
|
||||||
|
|
||||||
extern void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc);
|
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))
|
if (path_info && path_info->freepath && !IS_ERR_OR_NULL(path_info->path))
|
||||||
__putname(path - (PATH_MAX - 1 - len));
|
__putname((char *)path_info->path - (PATH_MAX - 1 - path_info->pathlen));
|
||||||
}
|
}
|
||||||
|
|
||||||
extern char *ceph_mdsc_build_path(struct ceph_mds_client *mdsc,
|
extern char *ceph_mdsc_build_path(struct ceph_mds_client *mdsc,
|
||||||
struct dentry *dentry, int *plen, u64 *base,
|
struct dentry *dentry, struct ceph_path_info *path_info,
|
||||||
int for_wire);
|
int for_wire);
|
||||||
|
|
||||||
extern void __ceph_mdsc_drop_dentry_lease(struct dentry *dentry);
|
extern void __ceph_mdsc_drop_dentry_lease(struct dentry *dentry);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user