From b5398b1958246734b70663a8d900d19bb19979ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 30 Jun 2021 21:18:41 +0200 Subject: [PATCH] Catch EMFILE instead of trying to compute when we'll hit max open files The previous logic was flawed anyways, as reading a range could potentially span multiple bands, but we only checked if we were just below the max limit. --- src/sparsebundlefs.cpp | 82 +++++++++++++++--------------------------- tests/testhelpers.sh | 2 ++ 2 files changed, 31 insertions(+), 53 deletions(-) diff --git a/src/sparsebundlefs.cpp b/src/sparsebundlefs.cpp index 46cec29..fec0986 100644 --- a/src/sparsebundlefs.cpp +++ b/src/sparsebundlefs.cpp @@ -305,6 +305,21 @@ static int sparsebundle_read(const char *path, char *buffer, size_t length, off_ } #if FUSE_SUPPORTS_ZERO_COPY +static void sparsebundle_read_buf_close_files() +{ + sparsebundle_t *sparsebundle = sparsebundle_current(); + + syslog(LOG_DEBUG, "closing %zu open file descriptor(s)", sparsebundle->open_files.size()); + + map::iterator iter; + for(iter = sparsebundle->open_files.begin(); iter != sparsebundle->open_files.end(); ++iter) { + close(iter->second); + syslog(LOG_DEBUG, "closed %s", iter->first.c_str()); + } + + sparsebundle->open_files.clear(); +} + static int sparsebundle_read_buf_prepare_file(const char *path) { sparsebundle_t *sparsebundle = sparsebundle_current(); @@ -315,7 +330,20 @@ static int sparsebundle_read_buf_prepare_file(const char *path) fd = iter->second; } else { syslog(LOG_DEBUG, "file %s not opened yet, opening", path); - fd = open(path, O_RDONLY); + if ((fd = open(path, O_RDONLY)) == -1) { + if (errno == EMFILE) { + struct rlimit fd_limit; + getrlimit(RLIMIT_NOFILE, &fd_limit); + syslog(LOG_DEBUG, "too many open files (%ju)", + uintmax_t(fd_limit.rlim_cur)); + + sparsebundle_read_buf_close_files(); + return sparsebundle_read_buf_prepare_file(path); + } + syslog(LOG_ERR, "failed to open band %s: %s", path, strerror(errno)); + return -errno; + } + sparsebundle->open_files[path] = fd; } @@ -361,21 +389,6 @@ static int sparsebundle_read_buf_pad_with_zeroes(size_t length, void *read_data) return length; } -static void sparsebundle_read_buf_close_files() -{ - sparsebundle_t *sparsebundle = sparsebundle_current(); - - syslog(LOG_DEBUG, "closing %zu open file descriptor(s)", sparsebundle->open_files.size()); - - map::iterator iter; - for(iter = sparsebundle->open_files.begin(); iter != sparsebundle->open_files.end(); ++iter) { - close(iter->second); - syslog(LOG_DEBUG, "closed %s", iter->first.c_str()); - } - - sparsebundle->open_files.clear(); -} - static int sparsebundle_read_buf(const char *path, struct fuse_bufvec **bufp, size_t length, off_t offset, struct fuse_file_info *) { @@ -394,43 +407,6 @@ static int sparsebundle_read_buf(const char *path, struct fuse_bufvec **bufp, syslog(LOG_DEBUG, "asked to read %zu bytes at offset %ju using zero-copy read", length, uintmax_t(offset)); - static rlim_t max_open_files = []() { - struct rlimit fd_limit; - getrlimit(RLIMIT_NOFILE, &fd_limit); - auto fd_max = fd_limit.rlim_cur - 1; - syslog(LOG_DEBUG, "maximum file descriptor number %ju", uintmax_t(fd_max)); - - // Check how many of the file descriptors are available - std::vector file_descriptors(fd_max); - for (unsigned i = 0; i < file_descriptors.size(); ++i) { - file_descriptors[i].fd = i; - // We don't really care about these states, but we have - // to specify something for poll to give us the POLLNVAL - // we do care about. - file_descriptors[i].events = POLLRDNORM | POLLWRNORM; - } - if (poll(file_descriptors.data(), file_descriptors.size(), 0) == -1) { - syslog(LOG_ERR, "failed to resolve available file descriptors: %s", strerror(errno)); - return fd_max; - } - - rlim_t available_fds = 0; - for (auto file_descriptor : file_descriptors) { - if (file_descriptor.revents & POLLNVAL) - ++available_fds; - } - syslog(LOG_DEBUG, "%ju available file descriptors (%ju in use)", - uintmax_t(available_fds), uintmax_t(fd_max - available_fds)); - - return available_fds; - }(); - - sparsebundle_t *sparsebundle = sparsebundle_current(); - if (sparsebundle->open_files.size() + 1 >= max_open_files) { - syslog(LOG_DEBUG, "hit max number of file descriptors"); - sparsebundle_read_buf_close_files(); - } - ret = sparsebundle_iterate_bands(path, length, offset, &read_ops); if (ret < 0) return ret; diff --git a/tests/testhelpers.sh b/tests/testhelpers.sh index 80afc31..93ddf28 100644 --- a/tests/testhelpers.sh +++ b/tests/testhelpers.sh @@ -53,6 +53,8 @@ function _test_can_handle_ulimit() { cat $f > /dev/null done + cat $test_output_file | grep -q "too many open files" + umount $hfs_dir && rm -Rf $hfs_dir umount $mount_dir && rm -Rf $mount_dir }