diff options
author | Nikolaus Rath <Nikolaus@rath.org> | 2018-11-27 20:58:36 +0000 |
---|---|---|
committer | Nikolaus Rath <Nikolaus@rath.org> | 2018-11-27 20:58:36 +0000 |
commit | d4a7ba44b022e3b63fc215374d87ed9e930d9974 (patch) | |
tree | 7b99187a746092c72b39e9e323bbe9a7eecb4750 | |
parent | d1ab94c3551ebdeb4fbfbd12db930f4c20a3a889 (diff) | |
download | libfuse-d4a7ba44b022e3b63fc215374d87ed9e930d9974.tar.gz |
Fix fd/inode leak
If do_readdir() calls do_lookup(), but the latter fails, we still have
to return any entries that we already stored in the readdir buffer to
avoid leaking inodes.
do_lookup() may fail if e.g. we reach the file descriptor limit.
-rw-r--r-- | example/passthrough_ll.c | 32 |
1 files changed, 19 insertions, 13 deletions
diff --git a/example/passthrough_ll.c b/example/passthrough_ll.c index eadad3a..5364b97 100644 --- a/example/passthrough_ll.c +++ b/example/passthrough_ll.c @@ -666,22 +666,23 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, struct lo_dirp *d = lo_dirp(fi); char *buf; char *p; - size_t rem; + size_t rem = size; int err; (void) ino; buf = calloc(1, size); - if (!buf) - return (void) fuse_reply_err(req, ENOMEM); + if (!buf) { + err = ENOMEM; + goto error; + } + p = buf; if (offset != d->offset) { seekdir(d->dp, offset); d->entry = NULL; d->offset = offset; } - p = buf; - rem = size; while (1) { size_t entsize; off_t nextoff; @@ -691,11 +692,12 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, errno = 0; d->entry = readdir(d->dp); if (!d->entry) { - if (errno && rem == size) { + if (errno) { // Error err = errno; goto error; + } else { // End of stream + break; } - break; } } nextoff = d->entry->d_off; @@ -738,13 +740,17 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, d->offset = nextoff; } - fuse_reply_buf(req, buf, size - rem); - free(buf); - return; - + err = 0; error: - free(buf); - fuse_reply_err(req, err); + // If there's an error, we can only signal it if we haven't stored + // any entries yet - otherwise we'd end up with wrong lookup + // counts for the entries that are already in the buffer. So we + // return what we've collected until that point. + if (err && rem == size) + fuse_reply_err(req, err); + else + fuse_reply_buf(req, buf, size - rem); + free(buf); } static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, |