summaryrefslogtreecommitdiff
path: root/fs/ceph
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ceph')
-rw-r--r--fs/ceph/crypto.c50
-rw-r--r--fs/ceph/mds_client.c12
-rw-r--r--fs/ceph/mds_client.h1
3 files changed, 50 insertions, 13 deletions
diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c
index 64d240759277..1cc11f77413c 100644
--- a/fs/ceph/crypto.c
+++ b/fs/ceph/crypto.c
@@ -298,6 +298,11 @@ out:
* Otherwise, base64 decode the string, and then ask fscrypt to format it
* for userland presentation.
*
+ * Though the fscrypt/crypto subsystems broadly expect all buffers to be in the
+ * linear-mapped region, this function slightly relaxes those requirements:
+ * fname->ctext, fname->name, and oname->name may be vmalloc(), but not
+ * tname->name.
+ *
* Returns 0 on success or negative error code on error.
*/
int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
@@ -305,11 +310,15 @@ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
{
struct inode *dir = fname->dir;
struct fscrypt_str _tname = FSTR_INIT(NULL, 0);
+ struct fscrypt_str _oname;
struct fscrypt_str iname;
char *name = fname->name;
int name_len = fname->name_len;
int ret;
+ if (WARN_ON_ONCE(tname && is_vmalloc_addr(tname->name)))
+ return -EIO;
+
/* Sanity check that the resulting name will fit in the buffer */
if (fname->name_len > NAME_MAX || fname->ctext_len > NAME_MAX)
return -EIO;
@@ -350,31 +359,50 @@ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
goto out_inode;
}
+ if ((!tname || !tname->name) &&
+ (fname->ctext_len == 0 ||
+ unlikely(is_vmalloc_addr(fname->ctext)) ||
+ unlikely(is_vmalloc_addr(oname->name)))) {
+ ret = fscrypt_fname_alloc_buffer(NAME_MAX, &_tname);
+ if (ret)
+ goto out_inode;
+ tname = &_tname;
+ }
+
if (fname->ctext_len == 0) {
int declen;
- if (!tname) {
- ret = fscrypt_fname_alloc_buffer(NAME_MAX, &_tname);
- if (ret)
- goto out_inode;
- tname = &_tname;
- }
-
- declen = base64_decode(name, name_len,
- tname->name, false, BASE64_IMAP);
+ declen = base64_decode(name, name_len, tname->name, false,
+ BASE64_IMAP);
if (declen <= 0) {
ret = -EIO;
goto out;
}
iname.name = tname->name;
iname.len = declen;
+ } else if (unlikely(is_vmalloc_addr(fname->ctext))) {
+ memcpy(tname->name, fname->ctext, fname->ctext_len);
+
+ iname.name = tname->name;
+ iname.len = fname->ctext_len;
} else {
iname.name = fname->ctext;
iname.len = fname->ctext_len;
}
- ret = fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, oname);
- if (!ret && (dir != fname->dir)) {
+ _oname.name = unlikely(is_vmalloc_addr(oname->name)) ?
+ tname->name : oname->name;
+ _oname.len = oname->len;
+
+ ret = fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, &_oname);
+ if (ret)
+ goto out;
+
+ if (unlikely(is_vmalloc_addr(oname->name)))
+ memcpy(oname->name, _oname.name, _oname.len);
+ oname->len = _oname.len;
+
+ if (dir != fname->dir) {
char tmp_buf[BASE64_CHARS(NAME_MAX)];
name_len = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%llu",
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index ec886ca9b526..0b6ff5b704f6 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -541,9 +541,13 @@ static int parse_reply_info_readdir(void **p, void *end,
* to do the base64_decode in-place. It's
* safe because the decoded string should
* always be shorter, which is 3/4 of origin
- * string.
+ * string. If this message was allocated with
+ * vmalloc() (happens, but rarely), leave it
+ * NULL and let ceph_fname_to_usr() allocate
+ * suitable temporary working space instead.
*/
- tname.name = _name;
+ if (likely(!is_vmalloc_addr(_name)))
+ tname.name = _name;
/*
* Set oname to _name too, and this will be
@@ -6589,11 +6593,13 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask)
doutc(cl, "tpath '%s', mask %d, caller_uid %d, caller_gid %d\n",
tpath, mask, caller_uid, caller_gid);
+ mutex_lock(&mdsc->mutex);
for (i = 0; i < mdsc->s_cap_auths_num; i++) {
struct ceph_mds_cap_auth *s = &mdsc->s_cap_auths[i];
err = ceph_mds_auth_match(mdsc, s, cred, tpath);
if (err < 0) {
+ mutex_unlock(&mdsc->mutex);
put_cred(cred);
return err;
} else if (err > 0) {
@@ -6615,6 +6621,7 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask)
doutc(cl, "root_squash_perms %d, rw_perms_s %p\n", root_squash_perms,
rw_perms_s);
if (root_squash_perms && rw_perms_s == NULL) {
+ mutex_unlock(&mdsc->mutex);
doutc(cl, "access allowed\n");
return 0;
}
@@ -6629,6 +6636,7 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask)
!!(mask & MAY_READ), !!(mask & MAY_WRITE));
}
doutc(cl, "access denied\n");
+ mutex_unlock(&mdsc->mutex);
return -EACCES;
}
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index 3c62e3c3530b..e7a262c9c2ab 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -604,6 +604,7 @@ struct ceph_mds_client {
struct rw_semaphore pool_perm_rwsem;
struct rb_root pool_perm_tree;
+ /* protected by mutex */
u32 s_cap_auths_num;
struct ceph_mds_cap_auth *s_cap_auths;