Skip to content

Commit

Permalink
Add --nocache option to disable caching
Browse files Browse the repository at this point in the history
Bug: #7
  • Loading branch information
fdegros committed Aug 27, 2022
1 parent 45edd02 commit 6ad3c00
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 28 deletions.
3 changes: 2 additions & 1 deletion lib/data_node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ DataNode DataNode::Make(zip_t* const zip,

Reader::Ptr DataNode::GetReader(zip_t* zip) const {
if (cached_reader) {
Log(LOG_DEBUG, *cached_reader, ": Reused for File [", id, "]");
Log(LOG_DEBUG, *cached_reader, ": Cached ", *cached_reader,
" reused for File [", id, "]");
return cached_reader->AddRef();
}

Expand Down
15 changes: 11 additions & 4 deletions lib/reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/syslog.h>
#include <sys/types.h>
#include <unistd.h>

#include "error.h"
#include "scoped_file.h"

bool Reader::may_cache_ = true;
std::string Reader::cache_dir_ = "/tmp";
zip_int64_t Reader::reader_count_ = 0;

static void LimitSize(ssize_t* const a, off_t b) {
Expand Down Expand Up @@ -102,11 +105,15 @@ class CacheFileReader : public UnbufferedReader {
// Creates a new, empty and hidden cache file.
// Throws std::system_error in case of error.
static ScopedFile CreateCacheFile() {
ScopedFile file(open("/tmp", O_TMPFILE | O_RDWR | O_EXCL, 0));
if (!may_cache_)
throw std::runtime_error("Option --nocache is in use");

ScopedFile file(open(cache_dir_.c_str(), O_TMPFILE | O_RDWR | O_EXCL, 0));

if (!file.IsValid())
ThrowSystemError("Cannot create cache file");
ThrowSystemError("Cannot create cache file in ", cache_dir_);

Log(LOG_DEBUG, "Created cache file in ", cache_dir_);
return file;
}

Expand Down Expand Up @@ -227,7 +234,7 @@ class CacheFileReader : public UnbufferedReader {
};

// Exception thrown by BufferedReader::Advance() when the decompression engine
// has to jump too far and a cached reader is reader to be used instead.
// has to jump too far and a cached reader is to be used instead.
class TooFar : public std::exception {
public:
const char* what() const noexcept override { return "Too far"; }
Expand All @@ -253,7 +260,7 @@ bool BufferedReader::CreateCachedReader() const noexcept {
Log(LOG_DEBUG, *this, ": Created Cached ", *cached_reader_);
return true;
} catch (const std::exception& e) {
Log(LOG_ERR, *this, ": Cannot create cached reader: ", e.what());
Log(LOG_ERR, *this, ": Cannot create Cached Reader: ", e.what());
return false;
}
}
Expand Down
6 changes: 6 additions & 0 deletions lib/reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ class Reader {
return out << "Reader " << reader.reader_id_;
}

// Whether a cache file may be created if needed.
static bool may_cache_;

// Directory in which the cache file is created if needed.
static std::string cache_dir_;

protected:
virtual ~Reader() = default;

Expand Down
46 changes: 26 additions & 20 deletions main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "error.h"
#include "log.h"
#include "path.h"
#include "reader.h"
#include "tree.h"

#if (LIBZIP_VERSION_MAJOR < 1)
Expand All @@ -52,26 +53,25 @@

// Prints usage information.
void print_usage() {
fprintf(
stderr,
"Mounts a ZIP archive as a FUSE filesystem\n"
"\n"
"Usage: %s [options] <ZIP-file> [mount-point]\n"
"\n"
"General options:\n"
" --help -h print help\n"
" --version print version\n"
" --quiet -q print fewer log messages\n"
" --verbose print more log messages\n"
" --redact redact file names from log messages\n"
" --force mount ZIP even if password is wrong\n"
" --encoding=CHARSET original encoding of file names\n"
"\n"
" -o nospecials no special files (FIFOs, sockets, devices)\n"
" -o nosymlinks no symbolic links\n"
" -o nohardlinks no hard links\n"
"\n",
PROGRAM);
fprintf(stderr,
R"(Mounts a ZIP archive as a FUSE filesystem
Usage: %s [options] <ZIP-file> [mount-point]
General options:
--help -h print help
--version print version
--quiet -q print fewer log messages
--verbose print more log messages
--redact redact file names from log messages
--force mount ZIP even if password is wrong
--encoding=CHARSET original encoding of file names
--nocache no caching of uncompressed data
-o nospecials no special files (FIFOs, sockets, devices)
-o nosymlinks no symbolic links
-o nohardlinks no hard links
)",
PROGRAM);
}

// Prints version information.
Expand Down Expand Up @@ -244,6 +244,7 @@ enum {
KEY_NO_SPECIALS,
KEY_NO_SYMLINKS,
KEY_NO_HARDLINKS,
KEY_NO_CACHE,
};

// Processes command line arguments.
Expand Down Expand Up @@ -315,6 +316,10 @@ static int ProcessArg(void* data,
param.opts.check_password = false;
return DISCARD;

case KEY_NO_CACHE:
Reader::may_cache_ = false;
return DISCARD;

case KEY_NO_SPECIALS:
param.opts.include_special_files = false;
return DISCARD;
Expand Down Expand Up @@ -384,6 +389,7 @@ int main(int argc, char* argv[]) try {
FUSE_OPT_KEY("-v", KEY_VERBOSE),
FUSE_OPT_KEY("--redact", KEY_REDACT),
FUSE_OPT_KEY("--force", KEY_FORCE),
FUSE_OPT_KEY("--nocache", KEY_NO_CACHE),
FUSE_OPT_KEY("nospecials", KEY_NO_SPECIALS),
FUSE_OPT_KEY("nosymlinks", KEY_NO_SYMLINKS),
FUSE_OPT_KEY("nohardlinks", KEY_NO_HARDLINKS),
Expand Down
42 changes: 39 additions & 3 deletions tests/blackbox/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1466,7 +1466,7 @@ def TestZipWithDefaultOptions():


# Tests the ZIP with lots of files.
def TestBigZip():
def TestZipWithManyFiles():
# Only check a few files: the first one, the last one, and one in the middle.
want_tree = {
'1': {
Expand Down Expand Up @@ -1548,7 +1548,7 @@ def TestBigZip():


# Tests that a big file can be accessed in random order.
def TestBigZip2():
def TestBigZip():
zip_name = 'big.zip'
logging.info(f'Checking {zip_name!r}...')
with tempfile.TemporaryDirectory() as mount_point:
Expand Down Expand Up @@ -1589,6 +1589,41 @@ def TestBigZip2():
subprocess.run(['fusermount', '-u', '-z', mount_point], check=True)
logging.debug(f'Unmounted {zip_path!r} from {mount_point!r}')

# Tests that a big file can be accessed in somewhat random order even with no
# cache file.
def TestBigZipNoCache():
zip_name = 'big.zip'
logging.info(f'Checking {zip_name!r}...')
with tempfile.TemporaryDirectory() as mount_point:
zip_path = os.path.join(script_dir, 'data', zip_name)
logging.debug(f'Mounting {zip_path!r} on {mount_point!r}...')
subprocess.run([mount_program, '--nocache', zip_path, mount_point],
check=True,
capture_output=True,
input='',
encoding='UTF-8')
try:
logging.debug(f'Mounted ZIP {zip_path!r} on {mount_point!r}')
tree = GetTree(mount_point, use_md5=False)
fd = os.open(os.path.join(mount_point, 'big.txt'), os.O_RDONLY)
try:
random.seed()
n = 100000000
for j in sorted([random.randrange(n) for i in range(50)]) + [n - 1, 0] + sorted([random.randrange(n) for i in range(50)]) + [n - 1, 0]:
logging.debug(f'Getting line {j}...')
want_line = b'%08d The quick brown fox jumps over the lazy dog.\n' % j
got_line = os.pread(fd, len(want_line), j * len(want_line))
if (got_line != want_line):
LogError(
f'Want line: {want_line!r}, Got line: {got_line!r}'
)
finally:
os.close(fd)
finally:
logging.debug(f'Unmounting {zip_path!r} from {mount_point!r}...')
subprocess.run(['fusermount', '-u', '-z', mount_point], check=True)
logging.debug(f'Unmounted {zip_path!r} from {mount_point!r}')


# Tests encrypted ZIP.
def TestEncryptedZip():
Expand Down Expand Up @@ -2310,8 +2345,9 @@ def TestInvalidZip():
TestZipWithSpecialFiles()
TestEncryptedZip()
TestInvalidZip()
TestZipWithManyFiles()
TestBigZip()
TestBigZip2()
TestBigZipNoCache()

if error_count:
LogError(f'There were {error_count} errors')
Expand Down

0 comments on commit 6ad3c00

Please sign in to comment.