mirror of
https://github.com/torarnv/sparsebundlefs.git
synced 2026-02-26 18:35:50 +01:00
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:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user