Skip to content

Commit b5398b1

Browse files
committed
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.
1 parent a52497c commit b5398b1

2 files changed

Lines changed: 31 additions & 53 deletions

File tree

src/sparsebundlefs.cpp

Lines changed: 29 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,21 @@ static int sparsebundle_read(const char *path, char *buffer, size_t length, off_
305305
}
306306

307307
#if FUSE_SUPPORTS_ZERO_COPY
308+
static void sparsebundle_read_buf_close_files()
309+
{
310+
sparsebundle_t *sparsebundle = sparsebundle_current();
311+
312+
syslog(LOG_DEBUG, "closing %zu open file descriptor(s)", sparsebundle->open_files.size());
313+
314+
map<string, int>::iterator iter;
315+
for(iter = sparsebundle->open_files.begin(); iter != sparsebundle->open_files.end(); ++iter) {
316+
close(iter->second);
317+
syslog(LOG_DEBUG, "closed %s", iter->first.c_str());
318+
}
319+
320+
sparsebundle->open_files.clear();
321+
}
322+
308323
static int sparsebundle_read_buf_prepare_file(const char *path)
309324
{
310325
sparsebundle_t *sparsebundle = sparsebundle_current();
@@ -315,7 +330,20 @@ static int sparsebundle_read_buf_prepare_file(const char *path)
315330
fd = iter->second;
316331
} else {
317332
syslog(LOG_DEBUG, "file %s not opened yet, opening", path);
318-
fd = open(path, O_RDONLY);
333+
if ((fd = open(path, O_RDONLY)) == -1) {
334+
if (errno == EMFILE) {
335+
struct rlimit fd_limit;
336+
getrlimit(RLIMIT_NOFILE, &fd_limit);
337+
syslog(LOG_DEBUG, "too many open files (%ju)",
338+
uintmax_t(fd_limit.rlim_cur));
339+
340+
sparsebundle_read_buf_close_files();
341+
return sparsebundle_read_buf_prepare_file(path);
342+
}
343+
syslog(LOG_ERR, "failed to open band %s: %s", path, strerror(errno));
344+
return -errno;
345+
}
346+
319347
sparsebundle->open_files[path] = fd;
320348
}
321349

@@ -361,21 +389,6 @@ static int sparsebundle_read_buf_pad_with_zeroes(size_t length, void *read_data)
361389
return length;
362390
}
363391

364-
static void sparsebundle_read_buf_close_files()
365-
{
366-
sparsebundle_t *sparsebundle = sparsebundle_current();
367-
368-
syslog(LOG_DEBUG, "closing %zu open file descriptor(s)", sparsebundle->open_files.size());
369-
370-
map<string, int>::iterator iter;
371-
for(iter = sparsebundle->open_files.begin(); iter != sparsebundle->open_files.end(); ++iter) {
372-
close(iter->second);
373-
syslog(LOG_DEBUG, "closed %s", iter->first.c_str());
374-
}
375-
376-
sparsebundle->open_files.clear();
377-
}
378-
379392
static int sparsebundle_read_buf(const char *path, struct fuse_bufvec **bufp,
380393
size_t length, off_t offset, struct fuse_file_info *)
381394
{
@@ -394,43 +407,6 @@ static int sparsebundle_read_buf(const char *path, struct fuse_bufvec **bufp,
394407
syslog(LOG_DEBUG, "asked to read %zu bytes at offset %ju using zero-copy read",
395408
length, uintmax_t(offset));
396409

397-
static rlim_t max_open_files = []() {
398-
struct rlimit fd_limit;
399-
getrlimit(RLIMIT_NOFILE, &fd_limit);
400-
auto fd_max = fd_limit.rlim_cur - 1;
401-
syslog(LOG_DEBUG, "maximum file descriptor number %ju", uintmax_t(fd_max));
402-
403-
// Check how many of the file descriptors are available
404-
std::vector<pollfd> file_descriptors(fd_max);
405-
for (unsigned i = 0; i < file_descriptors.size(); ++i) {
406-
file_descriptors[i].fd = i;
407-
// We don't really care about these states, but we have
408-
// to specify something for poll to give us the POLLNVAL
409-
// we do care about.
410-
file_descriptors[i].events = POLLRDNORM | POLLWRNORM;
411-
}
412-
if (poll(file_descriptors.data(), file_descriptors.size(), 0) == -1) {
413-
syslog(LOG_ERR, "failed to resolve available file descriptors: %s", strerror(errno));
414-
return fd_max;
415-
}
416-
417-
rlim_t available_fds = 0;
418-
for (auto file_descriptor : file_descriptors) {
419-
if (file_descriptor.revents & POLLNVAL)
420-
++available_fds;
421-
}
422-
syslog(LOG_DEBUG, "%ju available file descriptors (%ju in use)",
423-
uintmax_t(available_fds), uintmax_t(fd_max - available_fds));
424-
425-
return available_fds;
426-
}();
427-
428-
sparsebundle_t *sparsebundle = sparsebundle_current();
429-
if (sparsebundle->open_files.size() + 1 >= max_open_files) {
430-
syslog(LOG_DEBUG, "hit max number of file descriptors");
431-
sparsebundle_read_buf_close_files();
432-
}
433-
434410
ret = sparsebundle_iterate_bands(path, length, offset, &read_ops);
435411
if (ret < 0)
436412
return ret;

tests/testhelpers.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ function _test_can_handle_ulimit() {
5353
cat $f > /dev/null
5454
done
5555

56+
cat $test_output_file | grep -q "too many open files"
57+
5658
umount $hfs_dir && rm -Rf $hfs_dir
5759
umount $mount_dir && rm -Rf $mount_dir
5860
}

0 commit comments

Comments
 (0)