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.
This commit is contained in:
Tor Arne Vestbø
2021-06-30 21:18:41 +02:00
parent a52497c2b6
commit b5398b1958
2 changed files with 31 additions and 53 deletions

View File

@@ -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<string, int>::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<string, int>::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<pollfd> 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;

View File

@@ -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
}