client/Client.cc: prevent segfaulting

The segfaulting in the rmdir function is caused by calling
filepath::last_dentry() function.
last_dentry() function assumes that the bits vector has always at
least one element, which is not the case for the the filepath object
created with "/" input.
This commit also fixes other functions affected by this bug:
link, unlink, rename, mkdir, mknod and symlink.

Fixes: http://tracker.ceph.com/issues/9935
Signed-off-by: Michal Jarzabek <stiopa@gmail.com>
This commit is contained in:
Michal Jarzabek 2017-01-12 21:22:20 +00:00
parent a30d90cdc6
commit 6ed7f2364a
3 changed files with 72 additions and 9 deletions

View File

@ -6162,31 +6162,35 @@ int Client::link(const char *relexisting, const char *relpath, const UserPerm& p
tout(cct) << relpath << std::endl;
filepath existing(relexisting);
filepath path(relpath);
string name = path.last_dentry();
path.pop_dentry();
InodeRef in, dir;
int r = path_walk(existing, &in, perm, true);
if (r < 0)
goto out;
return r;
if (std::string(relpath) == "/") {
r = -EEXIST;
return r;
}
filepath path(relpath);
string name = path.last_dentry();
path.pop_dentry();
r = path_walk(path, &dir, perm, true);
if (r < 0)
goto out;
return r;
if (cct->_conf->client_permissions) {
if (S_ISDIR(in->mode)) {
r = -EPERM;
goto out;
return r;
}
r = may_hardlink(in.get(), perm);
if (r < 0)
goto out;
return r;
r = may_create(dir.get(), perm);
if (r < 0)
goto out;
return r;
}
r = _link(in.get(), dir.get(), name.c_str(), perm);
out:
return r;
}
@ -6196,6 +6200,9 @@ int Client::unlink(const char *relpath, const UserPerm& perm)
tout(cct) << "unlink" << std::endl;
tout(cct) << relpath << std::endl;
if (std::string(relpath) == "/")
return -EISDIR;
filepath path(relpath);
string name = path.last_dentry();
path.pop_dentry();
@ -6218,6 +6225,9 @@ int Client::rename(const char *relfrom, const char *relto, const UserPerm& perm)
tout(cct) << relfrom << std::endl;
tout(cct) << relto << std::endl;
if (std::string(relfrom) == "/" || std::string(relto) == "/")
return -EBUSY;
filepath from(relfrom);
filepath to(relto);
string fromname = from.last_dentry();
@ -6256,6 +6266,9 @@ int Client::mkdir(const char *relpath, mode_t mode, const UserPerm& perm)
tout(cct) << mode << std::endl;
ldout(cct, 10) << "mkdir: " << relpath << dendl;
if (std::string(relpath) == "/")
return -EEXIST;
filepath path(relpath);
string name = path.last_dentry();
path.pop_dentry();
@ -6325,6 +6338,10 @@ int Client::rmdir(const char *relpath, const UserPerm& perms)
Mutex::Locker lock(client_lock);
tout(cct) << "rmdir" << std::endl;
tout(cct) << relpath << std::endl;
if (std::string(relpath) == "/")
return -EBUSY;
filepath path(relpath);
string name = path.last_dentry();
path.pop_dentry();
@ -6347,6 +6364,10 @@ int Client::mknod(const char *relpath, mode_t mode, const UserPerm& perms, dev_t
tout(cct) << relpath << std::endl;
tout(cct) << mode << std::endl;
tout(cct) << rdev << std::endl;
if (std::string(relpath) == "/")
return -EEXIST;
filepath path(relpath);
string name = path.last_dentry();
path.pop_dentry();
@ -6371,6 +6392,9 @@ int Client::symlink(const char *target, const char *relpath, const UserPerm& per
tout(cct) << target << std::endl;
tout(cct) << relpath << std::endl;
if (std::string(relpath) == "/")
return -EEXIST;
filepath path(relpath);
string name = path.last_dentry();
path.pop_dentry();

View File

@ -130,6 +130,7 @@ class filepath {
const string& last_dentry() const {
if (bits.empty() && path.length() > 0) parse_bits();
assert(!bits.empty());
return bits[ bits.size()-1 ];
}

View File

@ -1739,3 +1739,41 @@ TEST(LibCephFS, ClearSetuid) {
ceph_shutdown(cmount);
}
TEST(LibCephFS, OperationsOnRoot)
{
struct ceph_mount_info *cmount;
ASSERT_EQ(ceph_create(&cmount, NULL), 0);
ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
ASSERT_EQ(ceph_mount(cmount, "/"), 0);
char dirname[32];
sprintf(dirname, "/somedir%x", getpid());
ASSERT_EQ(ceph_mkdir(cmount, dirname, 0755), 0);
ASSERT_EQ(ceph_rmdir(cmount, "/"), -EBUSY);
ASSERT_EQ(ceph_link(cmount, "/", "/"), -EEXIST);
ASSERT_EQ(ceph_link(cmount, dirname, "/"), -EEXIST);
ASSERT_EQ(ceph_link(cmount, "nonExisitingDir", "/"), -ENOENT);
ASSERT_EQ(ceph_unlink(cmount, "/"), -EISDIR);
ASSERT_EQ(ceph_rename(cmount, "/", "/"), -EBUSY);
ASSERT_EQ(ceph_rename(cmount, dirname, "/"), -EBUSY);
ASSERT_EQ(ceph_rename(cmount, "nonExistingDir", "/"), -EBUSY);
ASSERT_EQ(ceph_rename(cmount, "/", dirname), -EBUSY);
ASSERT_EQ(ceph_rename(cmount, "/", "nonExistingDir"), -EBUSY);
ASSERT_EQ(ceph_mkdir(cmount, "/", 0777), -EEXIST);
ASSERT_EQ(ceph_mknod(cmount, "/", 0, 0), -EEXIST);
ASSERT_EQ(ceph_symlink(cmount, "/", "/"), -EEXIST);
ASSERT_EQ(ceph_symlink(cmount, dirname, "/"), -EEXIST);
ASSERT_EQ(ceph_symlink(cmount, "nonExistingDir", "/"), -EEXIST);
ceph_shutdown(cmount);
}