ceph-fuse: return EINVAL if get invalid fino instead of assert

All the snap ids of the finos returned to libfuse from libcephfs
will be recorded in the map of 'stag_snap_map', and will never be
erased before unmounting. So if libfuse passes invalid fino the
ceph-fuse should return EINVAL errno instead of crash itself.

Fixes: https://tracker.ceph.com/issues/54653
Signed-off-by: Xiubo Li <xiubli@redhat.com>
This commit is contained in:
Xiubo Li 2022-03-24 10:01:57 +08:00
parent 2349083a9d
commit a6e83d8dec

View File

@ -351,7 +351,9 @@ static void fuse_ll_forget(fuse_req_t req, fuse_ino_t ino,
#endif
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
cfuse->client->ll_forget(cfuse->iget(ino), nlookup+1);
Inode *in = cfuse->iget(ino);
if (in)
cfuse->client->ll_forget(in, nlookup+1);
fuse_reply_none(req);
}
@ -360,11 +362,16 @@ static void fuse_ll_getattr(fuse_req_t req, fuse_ino_t ino,
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *in = cfuse->iget(ino);
struct stat stbuf;
UserPerm perms(ctx->uid, ctx->gid);
Inode *in = cfuse->iget(ino);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
(void) fi; // XXX
if (cfuse->client->ll_getattr(in, &stbuf, perms)
@ -383,8 +390,13 @@ static void fuse_ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *in = cfuse->iget(ino);
UserPerm perms(ctx->uid, ctx->gid);
Inode *in = cfuse->iget(ino);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
int mask = 0;
@ -420,8 +432,13 @@ static void fuse_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *in = cfuse->iget(ino);
UserPerm perms(ctx->uid, ctx->gid);
Inode *in = cfuse->iget(ino);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
int r = cfuse->client->ll_setxattr(in, name, value, size, flags, perms);
@ -434,9 +451,14 @@ static void fuse_ll_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *in = cfuse->iget(ino);
char buf[size];
UserPerm perms(ctx->uid, ctx->gid);
Inode *in = cfuse->iget(ino);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
int r = cfuse->client->ll_listxattr(in, buf, size, perms);
@ -459,9 +481,14 @@ static void fuse_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *in = cfuse->iget(ino);
char buf[size];
UserPerm perms(ctx->uid, ctx->gid);
Inode *in = cfuse->iget(ino);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
int r = cfuse->client->ll_getxattr(in, name, buf, size, perms);
@ -480,8 +507,13 @@ static void fuse_ll_removexattr(fuse_req_t req, fuse_ino_t ino,
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *in = cfuse->iget(ino);
UserPerm perms(ctx->uid, ctx->gid);
Inode *in = cfuse->iget(ino);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
int r = cfuse->client->ll_removexattr(in, name, perms);
@ -495,10 +527,14 @@ static void fuse_ll_opendir(fuse_req_t req, fuse_ino_t ino,
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *in = cfuse->iget(ino);
void *dirp;
UserPerm perms(ctx->uid, ctx->gid);
void *dirp;
Inode *in = cfuse->iget(ino);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
int r = cfuse->client->ll_opendir(in, fi->flags, (dir_result_t **)&dirp,
@ -517,11 +553,15 @@ static void fuse_ll_readlink(fuse_req_t req, fuse_ino_t ino)
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *in = cfuse->iget(ino);
char buf[PATH_MAX + 1]; // leave room for a null terminator
UserPerm perms(ctx->uid, ctx->gid);
get_fuse_groups(perms, req);
Inode *in = cfuse->iget(ino);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
int r = cfuse->client->ll_readlink(in, buf, sizeof(buf) - 1, perms);
if (r >= 0) {
buf[r] = '\0';
@ -538,9 +578,14 @@ static void fuse_ll_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *i2, *i1 = cfuse->iget(parent);
struct fuse_entry_param fe;
UserPerm perms(ctx->uid, ctx->gid);
Inode *i2, *i1 = cfuse->iget(parent);
if (!i1) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
memset(&fe, 0, sizeof(fe));
@ -600,6 +645,11 @@ static void fuse_ll_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
#endif
i1 = cfuse->iget(parent);
if (!i1) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
int r = cfuse->client->ll_mkdir(i1, name, mode, &fe.attr, &i2, perm);
if (r == 0) {
fe.ino = cfuse->make_fake_ino(fe.attr.st_ino, fe.attr.st_dev);
@ -618,8 +668,13 @@ static void fuse_ll_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *in = cfuse->iget(parent);
UserPerm perm(ctx->uid, ctx->gid);
Inode *in = cfuse->iget(parent);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perm, req);
int r = cfuse->client->ll_unlink(in, name, perm);
@ -632,8 +687,13 @@ static void fuse_ll_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *in = cfuse->iget(parent);
UserPerm perms(ctx->uid, ctx->gid);
Inode *in = cfuse->iget(parent);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
int r = cfuse->client->ll_rmdir(in, name, perms);
@ -647,9 +707,14 @@ static void fuse_ll_symlink(fuse_req_t req, const char *existing,
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *i2, *i1 = cfuse->iget(parent);
struct fuse_entry_param fe;
UserPerm perms(ctx->uid, ctx->gid);
Inode *i2, *i1 = cfuse->iget(parent);
if (!i1) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
memset(&fe, 0, sizeof(fe));
@ -677,9 +742,14 @@ static void fuse_ll_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
UserPerm perm(ctx->uid, ctx->gid);
Inode *in = cfuse->iget(parent);
Inode *nin = cfuse->iget(newparent);
UserPerm perm(ctx->uid, ctx->gid);
if (!in || !nin) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perm, req);
int r = cfuse->client->ll_rename(in, name, nin, newname, perm);
@ -694,14 +764,18 @@ static void fuse_ll_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
struct fuse_entry_param fe;
Inode *in = cfuse->iget(ino);
Inode *nin = cfuse->iget(newparent);
struct fuse_entry_param fe;
if (!in || !nin) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
memset(&fe, 0, sizeof(fe));
UserPerm perm(ctx->uid, ctx->gid);
get_fuse_groups(perm, req);
/*
* Note that we could successfully link, but then fail the subsequent
* getattr and return an error. Perhaps we should ignore getattr errors,
@ -736,9 +810,14 @@ static void fuse_ll_open(fuse_req_t req, fuse_ino_t ino,
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *in = cfuse->iget(ino);
Fh *fh = NULL;
UserPerm perms(ctx->uid, ctx->gid);
Inode *in = cfuse->iget(ino);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
int r = cfuse->client->ll_open(in, fi->flags, &fh, perms);
@ -930,10 +1009,14 @@ static void fuse_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
struct readdir_context rc;
rc.req = req;
rc.snap = cfuse->fino_snap(ino);
if (rc.snap == CEPH_MAXSNAP) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
rc.buf = new char[size];
rc.size = size;
rc.pos = 0;
rc.snap = cfuse->fino_snap(ino);
int r = cfuse->client->readdir_r_cb(dirp, fuse_ll_add_dirent, &rc);
if (r == 0 || r == -CEPHFS_ENOSPC) /* ignore ENOSPC from our callback */
@ -965,8 +1048,13 @@ static void fuse_ll_access(fuse_req_t req, fuse_ino_t ino, int mask)
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *in = cfuse->iget(ino);
UserPerm perms(ctx->uid, ctx->gid);
Inode *in = cfuse->iget(ino);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
int r = cfuse->client->inode_permission(in, perms, mask);
@ -979,10 +1067,15 @@ static void fuse_ll_create(fuse_req_t req, fuse_ino_t parent, const char *name,
{
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
Inode *i1 = cfuse->iget(parent), *i2;
struct fuse_entry_param fe;
Fh *fh = NULL;
UserPerm perms(ctx->uid, ctx->gid);
Inode *i1 = cfuse->iget(parent), *i2;
if (!i1) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
memset(&fe, 0, sizeof(fe));
@ -1015,9 +1108,14 @@ static void fuse_ll_statfs(fuse_req_t req, fuse_ino_t ino)
{
struct statvfs stbuf;
CephFuse::Handle *cfuse = fuse_ll_req_prepare(req);
Inode *in = cfuse->iget(ino);
const struct fuse_ctx *ctx = fuse_req_ctx(req);
UserPerm perms(ctx->uid, ctx->gid);
Inode *in = cfuse->iget(ino);
if (!in) {
fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL));
return;
}
get_fuse_groups(perms, req);
int r = cfuse->client->ll_statfs(in, &stbuf, perms);
@ -1534,7 +1632,8 @@ uint64_t CephFuse::Handle::fino_snap(uint64_t fino)
} else {
std::lock_guard l(stag_lock);
uint64_t stag = FINO_STAG(fino);
ceph_assert(stag_snap_map.count(stag));
if (!stag_snap_map.count(stag))
return CEPH_MAXSNAP;
return stag_snap_map[stag];
}
}
@ -1547,7 +1646,10 @@ Inode * CephFuse::Handle::iget(fuse_ino_t fino)
if (client->use_faked_inos()) {
return client->ll_get_inode((ino_t)fino);
} else {
vinodeno_t vino(FINO_INO(fino), fino_snap(fino));
uint64_t snap = fino_snap(fino);
if (snap == CEPH_MAXSNAP)
return NULL;
vinodeno_t vino(FINO_INO(fino), snap);
return client->ll_get_inode(vino);
}
}