From: Xavi Hernandez Date: Fri, 16 Feb 2024 18:14:07 +0000 (+0100) Subject: client: fix leak of file handles X-Git-Tag: v20.0.0~2470^2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=fe5c13d2e207473d48aa818484584d67dc3d23b5;p=ceph.git 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 --- diff --git a/src/client/Client.cc b/src/client/Client.cc index 6ee5e742b9b2c..5290351778cd0 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() @@ -9122,7 +9122,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; @@ -9130,11 +9132,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; @@ -9162,6 +9164,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 712583aa0d3c5..61098481b06e9 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -163,7 +163,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) { @@ -240,6 +240,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 { @@ -1563,7 +1565,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);