From a1d404f16094799aafd759234b2506725a7b499f Mon Sep 17 00:00:00 2001 From: Xavi Hernandez Date: Fri, 16 Feb 2024 19:14:07 +0100 Subject: [PATCH] client: fix leak of file handles Based on posix specification, the fd passed to fdopendir() will be closed by closedir(). However CephFS client wasn't doing that. If the user opened a directory using ceph_openat(), for example, and then passed the returned fd to ceph_fdopendir(), the created Fh associated with the new open was never destroyed. This patch records the fd used in ceph_fdopendir() so that it can be closed when ceph_closedir() is called. Fixes: https://tracker.ceph.com/issues/64479 Signed-off-by: Xavi Hernandez (cherry picked from commit fe5c13d2e207473d48aa818484584d67dc3d23b5) --- src/client/Client.cc | 18 +++++++++++++----- src/client/Client.h | 6 ++++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/client/Client.cc b/src/client/Client.cc index 42ca406f84c..2e23dbf7915 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -257,10 +257,10 @@ int Client::get_fd_inode(int fd, InodeRef *in) { return r; } -dir_result_t::dir_result_t(Inode *in, const UserPerm& perms) +dir_result_t::dir_result_t(Inode *in, const UserPerm& perms, int fd) : inode(in), offset(0), next_offset(2), release_count(0), ordered_count(0), cache_index(0), start_shared_gen(0), - perms(perms) + perms(perms), fd(fd) { } void Client::_reset_faked_inos() @@ -9014,7 +9014,9 @@ int Client::fdopendir(int dirfd, dir_result_t **dirpp, const UserPerm &perms) { return r; } } - r = _opendir(dirinode.get(), dirpp, perms); + // Posix says that closedir will also close the file descriptor passed to fdopendir, so we associate + // dirfd to the new dir_result_t so that it can be closed later. + r = _opendir(dirinode.get(), dirpp, perms, dirfd); /* if ENOTDIR, dirpp will be an uninitialized point and it's very dangerous to access its value */ if (r != -CEPHFS_ENOTDIR) { tout(cct) << (uintptr_t)*dirpp << std::endl; @@ -9022,11 +9024,11 @@ int Client::fdopendir(int dirfd, dir_result_t **dirpp, const UserPerm &perms) { return r; } -int Client::_opendir(Inode *in, dir_result_t **dirpp, const UserPerm& perms) +int Client::_opendir(Inode *in, dir_result_t **dirpp, const UserPerm& perms, int fd) { if (!in->is_dir()) return -CEPHFS_ENOTDIR; - *dirpp = new dir_result_t(in, perms); + *dirpp = new dir_result_t(in, perms, fd); opened_dirs.insert(*dirpp); ldout(cct, 8) << __func__ << "(" << in->ino << ") = " << 0 << " (" << *dirpp << ")" << dendl; return 0; @@ -9054,6 +9056,12 @@ void Client::_closedir(dir_result_t *dirp) } _readdir_drop_dirp_buffer(dirp); opened_dirs.erase(dirp); + + /* Close the associated fd if this dir_result_t comes from an fdopendir request. */ + if (dirp->fd >= 0) { + _close(dirp->fd); + } + delete dirp; } diff --git a/src/client/Client.h b/src/client/Client.h index 911a8b460df..87593007fff 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -161,7 +161,7 @@ struct dir_result_t { }; - explicit dir_result_t(Inode *in, const UserPerm& perms); + explicit dir_result_t(Inode *in, const UserPerm& perms, int fd); static uint64_t make_fpos(unsigned h, unsigned l, bool hash) { @@ -238,6 +238,8 @@ struct dir_result_t { std::vector buffer; struct dirent de; + + int fd; // fd attached using fdopendir (-1 if none) }; class Client : public Dispatcher, public md_config_obs_t { @@ -1296,7 +1298,7 @@ private: void fill_dirent(struct dirent *de, const char *name, int type, uint64_t ino, loff_t next_off); - int _opendir(Inode *in, dir_result_t **dirpp, const UserPerm& perms); + int _opendir(Inode *in, dir_result_t **dirpp, const UserPerm& perms, int fd = -1); void _readdir_drop_dirp_buffer(dir_result_t *dirp); bool _readdir_have_frag(dir_result_t *dirp); void _readdir_next_frag(dir_result_t *dirp); -- 2.39.5