blob: 325bdac62bdd00852d74dce111c78ef5d567ec12 [file] [log] [blame]
From 69b7010995534bef058d2a7e2b2c7e18bb760222 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Degros?= <fdegros@chromium.org>
Date: Thu, 18 Jun 2020 23:35:55 +1000
Subject: [PATCH] Lazy caching of files
Only cache a file in memory if it needs to be cached and if it is less than 1GB.
---
lib/Makefile | 4 +-
lib/bigBuffer.cpp | 362 ++++++++++++++++++++++++----------------------
lib/bigBuffer.h | 89 +++++++-----
lib/dataNode.cpp | 7 +-
lib/dataNode.h | 3 +-
lib/fileNode.cpp | 2 +-
lib/fileNode.h | 2 +-
lib/fuse-zip.cpp | 4 +-
8 files changed, 254 insertions(+), 219 deletions(-)
diff --git a/lib/Makefile b/lib/Makefile
index d092e0e..af2df55 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,8 +1,8 @@
DEST=libfusezip.a
PKG_CONFIG?=pkg-config
LIBS=$(shell $(PKG_CONFIG) fuse --libs) $(shell $(PKG_CONFIG) libzip --libs)
-CXXFLAGS=-g -O0 -Wall -Wextra -Wconversion -Wsign-conversion -Wlogical-op -Wshadow -pedantic -Werror -std=c++11
-RELEASE_CXXFLAGS=-O2 -Wall -Wextra -Wconversion -Wsign-conversion -Wlogical-op -Wshadow -pedantic -Werror -std=c++11
+CXXFLAGS=-g -O0 -Wall -Wextra -Wconversion -Wno-sign-compare -Wlogical-op -Wshadow -pedantic -Werror -std=c++11
+RELEASE_CXXFLAGS=-O2 -Wall -Wextra -Wconversion -Wno-sign-compare -Wlogical-op -Wshadow -pedantic -Werror -std=c++11
FUSEFLAGS=$(shell $(PKG_CONFIG) fuse --cflags)
ZIPFLAGS=$(shell $(PKG_CONFIG) libzip --cflags)
SOURCES=$(sort $(wildcard *.cpp))
diff --git a/lib/bigBuffer.cpp b/lib/bigBuffer.cpp
index 109d2f0..268807a 100644
--- a/lib/bigBuffer.cpp
+++ b/lib/bigBuffer.cpp
@@ -17,6 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.//
////////////////////////////////////////////////////////////////////////////
+#include "bigBuffer.h"
+
#include <cassert>
#include <cerrno>
#include <cstdlib>
@@ -28,67 +30,54 @@
#include <limits.h>
#include <syslog.h>
-#include "bigBuffer.h"
+#include "util.h"
/**
- * Class that keep chunk of file data.
+ * Operate with chunks of 4 KiB.
*/
-class BigBuffer::ChunkWrapper {
-private:
- /**
- * Pointer that keeps data for chunk. Can be NULL.
- */
- char *m_ptr;
+static const unsigned int chunkBits = 12;
+static const unsigned int chunkSize = 1u << chunkBits;
-public:
- /**
- * By default internal buffer is NULL, so this can be used for creating
- * sparse files.
- */
- ChunkWrapper(): m_ptr(NULL) {
- }
+/**
+ * Return number of chunks needed to keep 'size' bytes.
+ */
+inline size_t chunksCount(size_t size) {
+ return (size + (chunkSize - 1)) >> chunkBits;
+}
- /**
- * Take ownership on internal pointer from 'other' object.
- */
- ChunkWrapper(const ChunkWrapper &other) {
- m_ptr = other.m_ptr;
- const_cast<ChunkWrapper*>(&other)->m_ptr = NULL;
- }
+/**
+ * Return number of chunk where 'offset'-th byte is located.
+ */
+inline size_t chunkNumber(size_t offset) { return offset >> chunkBits; }
- /**
- * Free pointer if allocated.
- */
- ~ChunkWrapper() {
- if (m_ptr != NULL) {
- free(m_ptr);
- }
- }
+/**
+ * Return offset inside chunk to 'offset'-th byte.
+ */
+inline unsigned int chunkOffset(size_t offset) {
+ return offset & (chunkSize - 1);
+}
+/**
+ * Class that keep chunk of file data.
+ */
+class BigBuffer::Chunk {
+ private:
/**
- * Take ownership on internal pointer from 'other' object.
+ * Pointer that keeps data for chunk. Can be NULL.
*/
- ChunkWrapper &operator=(const ChunkWrapper &other) {
- if (&other != this) {
- m_ptr = other.m_ptr;
- const_cast<ChunkWrapper*>(&other)->m_ptr = NULL;
- }
- return *this;
- }
+ std::unique_ptr<char[]> m_ptr;
+ public:
/**
* Return pointer to internal storage and initialize it if needed.
* @throws
* std::bad_alloc If memory can not be allocated
*/
- char *ptr(bool init = false) {
- if (init && m_ptr == NULL) {
- m_ptr = (char *)malloc(chunkSize);
- if (m_ptr == NULL) {
- throw std::bad_alloc();
- }
+ char *ptr() {
+ if (!m_ptr) {
+ m_ptr.reset(new char[chunkSize]{});
}
- return m_ptr;
+ return m_ptr.get();
}
/**
@@ -106,8 +95,8 @@ public:
if (offset + count > chunkSize) {
count = chunkSize - offset;
}
- if (m_ptr != NULL) {
- memcpy(dest, m_ptr + offset, count);
+ if (m_ptr) {
+ memcpy(dest, m_ptr.get() + offset, count);
} else {
memset(dest, 0, count);
}
@@ -132,16 +121,7 @@ public:
if (offset + count > chunkSize) {
count = chunkSize - offset;
}
- if (m_ptr == NULL) {
- m_ptr = (char *)malloc(chunkSize);
- if (m_ptr == NULL) {
- throw std::bad_alloc();
- }
- if (offset > 0) {
- memset(m_ptr, 0, offset);
- }
- }
- memcpy(m_ptr + offset, src, count);
+ memcpy(ptr() + offset, src, count);
return count;
}
@@ -149,192 +129,230 @@ public:
* Clear tail of internal buffer with zeroes starting from 'offset'.
*/
void clearTail(unsigned int offset) {
- if (m_ptr != NULL && offset < chunkSize) {
- memset(m_ptr + offset, 0, chunkSize - offset);
+ if (m_ptr && offset < chunkSize) {
+ memset(m_ptr.get() + offset, 0, chunkSize - offset);
}
}
-
};
-BigBuffer::BigBuffer(): len(0) {
+BigBuffer::~BigBuffer() {}
+
+BigBuffer::BigBuffer() {}
+
+BigBuffer::BigBuffer(zip *archive, zip_uint64_t nodeId, off_t length)
+ : archive_(archive), nodeId_(nodeId), file_(OpenFile()), len(length) {}
+
+ZipFile BigBuffer::OpenFile() const {
+ if (zip_file *file = zip_fopen_index(archive_, nodeId_, 0))
+ return ZipFile(file);
+
+ throw ZipError("Cannot open file at index " + std::to_string(nodeId_),
+ archive_);
}
-BigBuffer::BigBuffer(struct zip *z, zip_uint64_t nodeId, size_t length):
- len(length) {
- struct zip_file *zf = zip_fopen_index(z, nodeId, 0);
- if (zf == NULL) {
- syslog(LOG_WARNING, "%s", zip_strerror(z));
- throw std::runtime_error(zip_strerror(z));
- }
- size_t ccount = chunksCount(length);
- chunks.resize(ccount, ChunkWrapper());
- size_t chunk = 0;
- while (length > 0) {
- size_t readSize = chunkSize;
- if (readSize > length) {
- readSize = length;
- }
- zip_int64_t nr = zip_fread(zf, chunks[chunk].ptr(true), readSize);
- if (nr < 0) {
- std::string err = zip_file_strerror(zf);
- syslog(LOG_WARNING, "%s", err.c_str());
- zip_fclose(zf);
- throw std::runtime_error(err);
+void BigBuffer::CacheInMemory() {
+ if (!archive_)
+ return;
+
+ // Don't cache files bigger than 1 GiB.
+ if ((len >> 30) > 0)
+ throw std::bad_alloc();
+
+ file_.reset();
+ chunks_.clear();
+ chunks_.reserve(len / chunkSize + 1);
+ const ZipFile file = OpenFile();
+ archive_ = nullptr;
+ len = 0;
+ Chunk chunk;
+ zip_int64_t nr;
+ do {
+ nr = zip_fread(file.get(), chunk.ptr(), chunkSize);
+ if (nr < 0)
+ throw ZipError("Cannot read file " + std::to_string(nodeId_),
+ file.get());
+
+ len += nr;
+ chunks_.push_back(std::move(chunk));
+ } while (nr == chunkSize);
+
+ if (nr == 0)
+ chunks_.pop_back();
+}
+
+int BigBuffer::read(char *buf, size_t size, off_t offset) {
+ size = std::min<size_t>(size, std::numeric_limits<int>::max());
+
+ if (offset < 0)
+ throw std::runtime_error("Negative offset");
+
+ if (file_) {
+ if (pos_ != offset) {
+ // Try to adjust the actual position in the file.
+ if (zip_fseek(file_.get(), offset, SEEK_SET) < 0) {
+ // Cannot adjust position. The file is probably compressed.
+ // We'll have to cache it in memory.
+ file_.reset();
+ } else {
+ // Adjust recorded position.
+ pos_ = offset;
+ }
}
- ++chunk;
- length -= static_cast<size_t>(nr);
- if ((nr == 0 || chunk == ccount) && length != 0) {
- // Allocated memory are exhausted, but there are unread bytes (or
- // file is longer that given length). Possibly CRC error.
- zip_fclose(zf);
- syslog(LOG_WARNING, "length of file %s differ from data length",
- zip_get_name(z, nodeId, ZIP_FL_ENC_GUESS));
- throw std::runtime_error("data length differ");
+
+ if (file_) {
+ // Read from file.
+ assert(pos_ == offset);
+ const zip_int64_t n = zip_fread(file_.get(), buf, size);
+ if (n < 0) {
+ throw ZipError("Cannot read file " + std::to_string(nodeId_),
+ file_.get());
+ }
+
+ // Adjust recorded position.
+ pos_ += n;
+ return static_cast<int>(n);
}
}
- if (zip_fclose(zf)) {
- syslog(LOG_WARNING, "%s", zip_strerror(z));
- throw std::runtime_error(zip_strerror(z));
- }
-}
-BigBuffer::~BigBuffer() {
-}
+ assert(!file_);
+ CacheInMemory();
-int BigBuffer::read(char *buf, size_t size, size_t offset) const {
- if (offset > len) {
+ if (offset >= len)
return 0;
- }
- size_t chunk = chunkNumber(offset);
- unsigned int pos = chunkOffset(offset);
- if (size > len - offset) {
- size = len - offset;
- }
- if (size > INT_MAX)
- size = INT_MAX;
- int nread = static_cast<int>(size);
- while (size > 0) {
- size_t r = chunks[chunk].read(buf, pos, size);
+ // Read from in-memory cache.
+ size = std::min<off_t>(size, len - offset);
+ Chunks::const_iterator chunk = chunks_.cbegin() + chunkNumber(offset);
+ unsigned int pos_in_chunk = chunkOffset(offset);
+ const int nread = static_cast<int>(size);
+
+ while (size > 0) {
+ const size_t r = chunk->read(buf, pos_in_chunk, size);
+ assert(r <= size);
size -= r;
buf += r;
++chunk;
- pos = 0;
+ pos_in_chunk = 0;
}
+
return nread;
}
int BigBuffer::write(const char *buf, size_t size, size_t offset) {
+ CacheInMemory();
+ size = std::min<size_t>(size, std::numeric_limits<int>::max());
size_t chunk = chunkNumber(offset);
unsigned int pos = chunkOffset(offset);
- if (size > INT_MAX)
- size = INT_MAX;
- int nwritten = static_cast<int>(size);
+ const int nwritten = static_cast<int>(size);
if (offset > len) {
if (chunkNumber(len) < chunksCount(len)) {
- chunks[chunkNumber(len)].clearTail(chunkOffset(len));
+ chunks_[chunkNumber(len)].clearTail(chunkOffset(len));
}
len = size + offset;
- } else if (size > unsigned(len - offset)) {
+ } else if (size > len - offset) {
len = size + offset;
}
- chunks.resize(chunksCount(len));
+ chunks_.resize(chunksCount(len));
while (size > 0) {
- size_t w = chunks[chunk].write(buf, pos, size);
+ size_t w = chunks_[chunk].write(buf, pos, size);
size -= w;
buf += w;
- ++ chunk;
+ ++chunk;
pos = 0;
}
return nwritten;
}
void BigBuffer::truncate(size_t offset) {
- chunks.resize(chunksCount(offset));
+ CacheInMemory();
+ chunks_.resize(chunksCount(offset));
if (offset > len && chunkNumber(len) < chunksCount(len)) {
// Fill end of last non-empty chunk with zeroes
- chunks[chunkNumber(len)].clearTail(chunkOffset(len));
+ chunks_[chunkNumber(len)].clearTail(chunkOffset(len));
}
len = offset;
}
zip_int64_t BigBuffer::zipUserFunctionCallback(void *state, void *data,
- zip_uint64_t len, enum zip_source_cmd cmd) {
- CallBackStruct *b = (CallBackStruct*)state;
+ zip_uint64_t len,
+ enum zip_source_cmd cmd) {
+ CallBackStruct *b = static_cast<CallBackStruct *>(state);
switch (cmd) {
- case ZIP_SOURCE_OPEN: {
- b->pos = 0;
- return 0;
- }
- case ZIP_SOURCE_READ: {
- size_t rlen = std::numeric_limits<size_t>::max();
- if (len < rlen)
- rlen = static_cast<size_t>(len);
- int r = b->buf->read((char*)data, rlen, b->pos);
- b->pos += static_cast<unsigned int>(r);
- return r;
- }
- case ZIP_SOURCE_STAT: {
- struct zip_stat *st = (struct zip_stat*)data;
- zip_stat_init(st);
- st->valid = ZIP_STAT_SIZE | ZIP_STAT_MTIME;
- st->size = b->buf->len;
- st->mtime = b->mtime;
- return sizeof(struct zip_stat);
- }
- case ZIP_SOURCE_FREE: {
- delete b;
- return 0;
- }
- case ZIP_SOURCE_CLOSE:
- return 0;
- case ZIP_SOURCE_ERROR: {
- // This code should not be called in normal case because none of
- // implemented functions raises error flag.
- int *errs = static_cast<int *>(data);
- errs[0] = ZIP_ER_OPNOTSUPP;
- errs[1] = EINVAL;
- return 2 * sizeof(int);
- }
- case ZIP_SOURCE_SUPPORTS:
- return ZIP_SOURCE_SUPPORTS_READABLE;
- default:
- // indicate unsupported operation
- return -1;
+ case ZIP_SOURCE_OPEN: {
+ b->pos = 0;
+ return 0;
+ }
+ case ZIP_SOURCE_READ: {
+ size_t rlen = std::numeric_limits<size_t>::max();
+ if (len < rlen)
+ rlen = static_cast<size_t>(len);
+ const int r = b->buf->read(static_cast<char *>(data), rlen, b->pos);
+ b->pos += r;
+ return r;
+ }
+ case ZIP_SOURCE_STAT: {
+ struct zip_stat *st = static_cast<struct zip_stat *>(data);
+ zip_stat_init(st);
+ st->valid = ZIP_STAT_SIZE | ZIP_STAT_MTIME;
+ st->size = b->buf->len;
+ st->mtime = b->mtime;
+ return sizeof(struct zip_stat);
+ }
+ case ZIP_SOURCE_FREE: {
+ delete b;
+ return 0;
+ }
+ case ZIP_SOURCE_CLOSE:
+ return 0;
+ case ZIP_SOURCE_ERROR: {
+ // This code should not be called in normal case because none of
+ // implemented functions raises error flag.
+ int *errs = static_cast<int *>(data);
+ errs[0] = ZIP_ER_OPNOTSUPP;
+ errs[1] = EINVAL;
+ return 2 * sizeof(int);
+ }
+ case ZIP_SOURCE_SUPPORTS:
+ return ZIP_SOURCE_SUPPORTS_READABLE;
+ default:
+ // indicate unsupported operation
+ return -1;
}
}
int BigBuffer::saveToZip(time_t mtime, struct zip *z, const char *fname,
- bool newFile, zip_int64_t &index) {
- struct zip_source *s;
- struct CallBackStruct *cbs = new CallBackStruct();
+ bool newFile, zip_int64_t &index) {
+ CallBackStruct *const cbs = new CallBackStruct();
cbs->buf = this;
cbs->mtime = mtime;
- if ((s=zip_source_function(z, zipUserFunctionCallback, cbs)) == NULL) {
+ zip_source *const s = zip_source_function(z, zipUserFunctionCallback, cbs);
+ if (!s) {
delete cbs;
return -ENOMEM;
}
+
if (newFile) {
- zip_int64_t nid = zip_file_add(z, fname, s, ZIP_FL_ENC_GUESS);
+ const zip_int64_t nid = zip_file_add(z, fname, s, ZIP_FL_ENC_GUESS);
if (nid < 0) {
delete cbs;
zip_source_free(s);
return -ENOMEM;
- } else {
- // indices are actually in range [0..2^63-1]
- index = nid;
}
+
+ // indices are actually in range [0..2^63-1]
+ index = nid;
} else {
assert(index >= 0);
- if (zip_file_replace(z, static_cast<zip_uint64_t>(index), s, ZIP_FL_ENC_GUESS) < 0) {
+ if (zip_file_replace(z, index, s, ZIP_FL_ENC_GUESS) < 0) {
delete cbs;
zip_source_free(s);
return -ENOMEM;
}
}
+
return 0;
}
diff --git a/lib/bigBuffer.h b/lib/bigBuffer.h
index 2ec92d6..b5ad3fc 100644
--- a/lib/bigBuffer.h
+++ b/lib/bigBuffer.h
@@ -20,60 +20,84 @@
#ifndef BIG_BUFFER_H
#define BIG_BUFFER_H
-#include <zip.h>
#include <unistd.h>
+#include <zip.h>
+#include <memory>
#include <vector>
#include "types.h"
-class BigBuffer {
-private:
- //TODO: use >> and <<
- static const unsigned int chunkSize = 4*1024; //4 Kilobytes
-
- class ChunkWrapper;
+struct ZipClose {
+ void operator()(zip_file_t *const file) const { zip_fclose(file); }
+};
- typedef std::vector<ChunkWrapper> chunks_t;
+using ZipFile = std::unique_ptr<zip_file_t, ZipClose>;
+class BigBuffer {
+ private:
struct CallBackStruct {
- size_t pos;
- const BigBuffer *buf;
+ off_t pos;
+ BigBuffer *buf;
time_t mtime;
};
- chunks_t chunks;
-
/**
* Callback for zip_source_function.
* See zip_source_function(3) for details.
*/
static zip_int64_t zipUserFunctionCallback(void *state, void *data,
- zip_uint64_t len, enum zip_source_cmd cmd);
+ zip_uint64_t len,
+ enum zip_source_cmd cmd);
/**
- * Return number of chunks needed to keep 'size' bytes.
+ * Opens the file at index nodeId_ in archive_.
+ * @throws std::runtime_error on error.
*/
- inline static size_t chunksCount(size_t size) {
- return (size + chunkSize - 1) / chunkSize;
- }
+ ZipFile OpenFile() const;
/**
- * Return number of chunk where 'offset'-th byte is located.
+ * Cache file in memory if it isn't cached yet.
+ * @throws
+ * std::bad_alloc if there is not enough memory.
+ * std::runtime_error on file read error.
*/
- inline static size_t chunkNumber(size_t offset) {
- return offset / chunkSize;
- }
+ void CacheInMemory();
+
+ class Chunk;
+ using Chunks = std::vector<Chunk>;
/**
- * Return offset inside chunk to 'offset'-th byte.
+ * Pointer to the ZIP archive.
+ * Becomes null when file is cached in memory.
*/
- inline static unsigned int chunkOffset(size_t offset) {
- return offset % chunkSize;
- }
+ zip *archive_ = nullptr;
-public:
- size_t len;
+ /**
+ * Index of the file in the ZIP archive.
+ */
+ const zip_uint64_t nodeId_ = 0;
+
+ /**
+ * File being streamed.
+ * Becomes null when file is cached in memory.
+ */
+ ZipFile file_ = nullptr;
+
+ /**
+ * Current position of the file being streamed.
+ * Not used when file is cached in memory.
+ */
+ off_t pos_ = 0;
+
+ /**
+ * Cached file in memory.
+ * Used only when archive_ is null.
+ */
+ Chunks chunks_;
+
+ public:
+ off_t len = 0;
/**
* Create new file buffer without mapping to file in a zip archive
@@ -86,11 +110,11 @@ public:
* @param z Zip file
* @param nodeId Node index inside zip file
* @param length File length
- * @throws
+ * @throws
* std::exception On file read error
* std::bad_alloc On memory insufficiency
*/
- BigBuffer(struct zip *z, zip_uint64_t nodeId, size_t length);
+ BigBuffer(struct zip *z, zip_uint64_t nodeId, off_t length);
~BigBuffer();
@@ -105,7 +129,7 @@ public:
* @param offset offset to start reading from
* @return number of bytes read
*/
- int read(char *buf, size_t size, size_t offset) const;
+ int read(char *buf, size_t size, off_t offset);
/**
* Dispatch write request to chunks of a file and grow 'chunks' vector if
@@ -135,8 +159,8 @@ public:
* 0 If successfull
* -ENOMEM If there are no memory
*/
- int saveToZip(time_t mtime, struct zip *z, const char *fname,
- bool newFile, zip_int64_t &index);
+ int saveToZip(time_t mtime, struct zip *z, const char *fname, bool newFile,
+ zip_int64_t &index);
/**
* Truncate buffer at position offset.
@@ -151,4 +175,3 @@ public:
};
#endif
-
diff --git a/lib/dataNode.cpp b/lib/dataNode.cpp
index 0efd7bc..07f0f84 100644
--- a/lib/dataNode.cpp
+++ b/lib/dataNode.cpp
@@ -116,17 +116,14 @@ int DataNode::open(struct zip *zip) {
if (_state == NodeState::CLOSED) {
_open_count = 1;
assert(zip != NULL);
- if (_size > std::numeric_limits<size_t>::max()) {
- return -ENOMEM;
- }
assert(_id != FAKE_ID);
- _buffer.reset(new BigBuffer(zip, _id, static_cast<size_t>(_size)));
+ _buffer.reset(new BigBuffer(zip, _id, _size));
_state = NodeState::OPENED;
}
return 0;
}
-int DataNode::read(char *buf, size_t sz, size_t offset) {
+int DataNode::read(char *buf, size_t sz, off_t offset) {
_atime = currentTime();
return _buffer->read(buf, sz, offset);
}
diff --git a/lib/dataNode.h b/lib/dataNode.h
index 7489ec9..550593c 100644
--- a/lib/dataNode.h
+++ b/lib/dataNode.h
@@ -73,7 +73,7 @@ public:
static std::shared_ptr<DataNode> createExisting(struct zip *zip, zip_uint64_t id, mode_t mode);
int open(struct zip *zip);
- int read(char *buf, size_t size, size_t offset);
+ int read(char *buf, size_t size, off_t offset);
int write(const char *buf, size_t size, size_t offset);
int close();
@@ -166,4 +166,3 @@ public:
zip_uint64_t size() const;
};
#endif
-
diff --git a/lib/fileNode.cpp b/lib/fileNode.cpp
index efe1105..62723d6 100644
--- a/lib/fileNode.cpp
+++ b/lib/fileNode.cpp
@@ -189,7 +189,7 @@ int FileNode::open() {
return _data->open(zip);
}
-int FileNode::read(char *buf, size_t sz, size_t offset) {
+int FileNode::read(char *buf, size_t sz, off_t offset) {
return _data->read(buf, sz, offset);
}
diff --git a/lib/fileNode.h b/lib/fileNode.h
index 998bc9e..ab07cd9 100644
--- a/lib/fileNode.h
+++ b/lib/fileNode.h
@@ -111,7 +111,7 @@ public:
void rename (const char *new_name);
int open();
- int read(char *buf, size_t size, size_t offset);
+ int read(char *buf, size_t size, off_t offset);
int write(const char *buf, size_t size, size_t offset);
int close();
diff --git a/lib/fuse-zip.cpp b/lib/fuse-zip.cpp
index 061cc6d..113065c 100644
--- a/lib/fuse-zip.cpp
+++ b/lib/fuse-zip.cpp
@@ -262,8 +262,7 @@ int fusezip_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi) try {
if (offset < 0)
return -EINVAL;
- return reinterpret_cast<FileNode *>(fi->fh)->read(
- buf, size, static_cast<size_t>(offset));
+ return reinterpret_cast<FileNode *>(fi->fh)->read(buf, size, offset);
} catch (...) {
return exceptionToError("read file", path);
}
@@ -655,4 +654,3 @@ int fusezip_symlink(const char *dest, const char *path) {
node->close();
return (res < 0) ? -ENOMEM : 0;
}
-
--
2.29.1.341.ge80a0c044ae-goog