mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-25 02:09:42 +00:00
dentry name handling fixes from Jeff and a memory leak fix from Zheng.
Both are old issues, marked for stable. -----BEGIN PGP SIGNATURE----- iQFHBAABCAAxFiEEydHwtzie9C7TfviiSn/eOAIR84sFAlzB8RoTHGlkcnlvbW92 QGdtYWlsLmNvbQAKCRBKf944AhHzix4BCACfaFPdToRlxFa0oYP8yeoznkVNjldX yr00KWc1xNZf0G92REk3zZP1+OfL0GIUbbBwe82uT0QLKcHG7l9ioM5qPwZo5uLv DiV6iEN7jf84H0YmzB2iVJAZp6T85veFjcXOJ2sq0AtoqmUpdcDqm9Ov40oCvxUs aKHsxUQnrOkwF5mtQmOG0CZsl9+pq0c/OpCnc2i6f78KgpWKMzH0gsL4mTM7GLzL LxtbvHws3rxG5HtnWcJgPnv1D0bdcEGgckXWTK9NGYZmWYTuZRwROSrx8lNhkant +1bQPrpzyB6W8rN0wSrWlPkmp71Qqo43Eop13r5V34bU04zSr2bI+PUh =8A02 -----END PGP SIGNATURE----- Merge tag 'ceph-for-5.1-rc7' of git://github.com/ceph/ceph-client Pull ceph fixes from Ilya Dryomov: "dentry name handling fixes from Jeff and a memory leak fix from Zheng. Both are old issues, marked for stable" * tag 'ceph-for-5.1-rc7' of git://github.com/ceph/ceph-client: ceph: fix ci->i_head_snapc leak ceph: handle the case where a dentry has been renamed on outstanding req ceph: ensure d_name stability in ceph_dentry_hash() ceph: only use d_name directly when parent is locked
This commit is contained in:
commit
8113a85f87
4 changed files with 85 additions and 14 deletions
|
@ -1766,6 +1766,7 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size,
|
|||
unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn)
|
||||
{
|
||||
struct ceph_inode_info *dci = ceph_inode(dir);
|
||||
unsigned hash;
|
||||
|
||||
switch (dci->i_dir_layout.dl_dir_hash) {
|
||||
case 0: /* for backward compat */
|
||||
|
@ -1773,8 +1774,11 @@ unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn)
|
|||
return dn->d_name.hash;
|
||||
|
||||
default:
|
||||
return ceph_str_hash(dci->i_dir_layout.dl_dir_hash,
|
||||
spin_lock(&dn->d_lock);
|
||||
hash = ceph_str_hash(dci->i_dir_layout.dl_dir_hash,
|
||||
dn->d_name.name, dn->d_name.len);
|
||||
spin_unlock(&dn->d_lock);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1163,6 +1163,19 @@ static int splice_dentry(struct dentry **pdn, struct inode *in)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int d_name_cmp(struct dentry *dentry, const char *name, size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* take d_lock to ensure dentry->d_name stability */
|
||||
spin_lock(&dentry->d_lock);
|
||||
ret = dentry->d_name.len - len;
|
||||
if (!ret)
|
||||
ret = memcmp(dentry->d_name.name, name, len);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Incorporate results into the local cache. This is either just
|
||||
* one inode, or a directory, dentry, and possibly linked-to inode (e.g.,
|
||||
|
@ -1412,7 +1425,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
|
|||
err = splice_dentry(&req->r_dentry, in);
|
||||
if (err < 0)
|
||||
goto done;
|
||||
} else if (rinfo->head->is_dentry) {
|
||||
} else if (rinfo->head->is_dentry &&
|
||||
!d_name_cmp(req->r_dentry, rinfo->dname, rinfo->dname_len)) {
|
||||
struct ceph_vino *ptvino = NULL;
|
||||
|
||||
if ((le32_to_cpu(rinfo->diri.in->cap.caps) & CEPH_CAP_FILE_SHARED) ||
|
||||
|
|
|
@ -1414,6 +1414,15 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
|
|||
list_add(&ci->i_prealloc_cap_flush->i_list, &to_remove);
|
||||
ci->i_prealloc_cap_flush = NULL;
|
||||
}
|
||||
|
||||
if (drop &&
|
||||
ci->i_wrbuffer_ref_head == 0 &&
|
||||
ci->i_wr_ref == 0 &&
|
||||
ci->i_dirty_caps == 0 &&
|
||||
ci->i_flushing_caps == 0) {
|
||||
ceph_put_snap_context(ci->i_head_snapc);
|
||||
ci->i_head_snapc = NULL;
|
||||
}
|
||||
}
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
while (!list_empty(&to_remove)) {
|
||||
|
@ -2161,10 +2170,39 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
|
|||
return path;
|
||||
}
|
||||
|
||||
/* Duplicate the dentry->d_name.name safely */
|
||||
static int clone_dentry_name(struct dentry *dentry, const char **ppath,
|
||||
int *ppathlen)
|
||||
{
|
||||
u32 len;
|
||||
char *name;
|
||||
|
||||
retry:
|
||||
len = READ_ONCE(dentry->d_name.len);
|
||||
name = kmalloc(len + 1, GFP_NOFS);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (dentry->d_name.len != len) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
kfree(name);
|
||||
goto retry;
|
||||
}
|
||||
memcpy(name, dentry->d_name.name, len);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
|
||||
name[len] = '\0';
|
||||
*ppath = name;
|
||||
*ppathlen = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int build_dentry_path(struct dentry *dentry, struct inode *dir,
|
||||
const char **ppath, int *ppathlen, u64 *pino,
|
||||
int *pfreepath)
|
||||
bool *pfreepath, bool parent_locked)
|
||||
{
|
||||
int ret;
|
||||
char *path;
|
||||
|
||||
rcu_read_lock();
|
||||
|
@ -2173,8 +2211,15 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir,
|
|||
if (dir && ceph_snap(dir) == CEPH_NOSNAP) {
|
||||
*pino = ceph_ino(dir);
|
||||
rcu_read_unlock();
|
||||
*ppath = dentry->d_name.name;
|
||||
*ppathlen = dentry->d_name.len;
|
||||
if (parent_locked) {
|
||||
*ppath = dentry->d_name.name;
|
||||
*ppathlen = dentry->d_name.len;
|
||||
} else {
|
||||
ret = clone_dentry_name(dentry, ppath, ppathlen);
|
||||
if (ret)
|
||||
return ret;
|
||||
*pfreepath = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
@ -2182,13 +2227,13 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir,
|
|||
if (IS_ERR(path))
|
||||
return PTR_ERR(path);
|
||||
*ppath = path;
|
||||
*pfreepath = 1;
|
||||
*pfreepath = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int build_inode_path(struct inode *inode,
|
||||
const char **ppath, int *ppathlen, u64 *pino,
|
||||
int *pfreepath)
|
||||
bool *pfreepath)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
char *path;
|
||||
|
@ -2204,7 +2249,7 @@ static int build_inode_path(struct inode *inode,
|
|||
if (IS_ERR(path))
|
||||
return PTR_ERR(path);
|
||||
*ppath = path;
|
||||
*pfreepath = 1;
|
||||
*pfreepath = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2215,7 +2260,7 @@ 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, int *freepath)
|
||||
u64 *ino, bool *freepath, bool parent_locked)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
|
@ -2225,7 +2270,7 @@ static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry,
|
|||
ceph_snap(rinode));
|
||||
} else if (rdentry) {
|
||||
r = build_dentry_path(rdentry, rdiri, ppath, pathlen, ino,
|
||||
freepath);
|
||||
freepath, parent_locked);
|
||||
dout(" dentry %p %llx/%.*s\n", rdentry, *ino, *pathlen,
|
||||
*ppath);
|
||||
} else if (rpath || rino) {
|
||||
|
@ -2251,7 +2296,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
|
|||
const char *path2 = NULL;
|
||||
u64 ino1 = 0, ino2 = 0;
|
||||
int pathlen1 = 0, pathlen2 = 0;
|
||||
int freepath1 = 0, freepath2 = 0;
|
||||
bool freepath1 = false, freepath2 = false;
|
||||
int len;
|
||||
u16 releases;
|
||||
void *p, *end;
|
||||
|
@ -2259,16 +2304,19 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
|
|||
|
||||
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);
|
||||
&path1, &pathlen1, &ino1, &freepath1,
|
||||
test_bit(CEPH_MDS_R_PARENT_LOCKED,
|
||||
&req->r_req_flags));
|
||||
if (ret < 0) {
|
||||
msg = ERR_PTR(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If r_old_dentry is set, then assume that its parent is locked */
|
||||
ret = set_request_path_attr(NULL, req->r_old_dentry,
|
||||
req->r_old_dentry_dir,
|
||||
req->r_path2, req->r_ino2.ino,
|
||||
&path2, &pathlen2, &ino2, &freepath2);
|
||||
&path2, &pathlen2, &ino2, &freepath2, true);
|
||||
if (ret < 0) {
|
||||
msg = ERR_PTR(ret);
|
||||
goto out_free1;
|
||||
|
|
|
@ -572,7 +572,12 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci)
|
|||
old_snapc = NULL;
|
||||
|
||||
update_snapc:
|
||||
if (ci->i_head_snapc) {
|
||||
if (ci->i_wrbuffer_ref_head == 0 &&
|
||||
ci->i_wr_ref == 0 &&
|
||||
ci->i_dirty_caps == 0 &&
|
||||
ci->i_flushing_caps == 0) {
|
||||
ci->i_head_snapc = NULL;
|
||||
} else {
|
||||
ci->i_head_snapc = ceph_get_snap_context(new_snapc);
|
||||
dout(" new snapc is %p\n", new_snapc);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue