aboutsummaryrefslogtreecommitdiffstats
path: root/fuse_sideload.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'fuse_sideload.cpp')
-rw-r--r--fuse_sideload.cpp106
1 files changed, 105 insertions, 1 deletions
diff --git a/fuse_sideload.cpp b/fuse_sideload.cpp
index ce0ecc3e..dd325569 100644
--- a/fuse_sideload.cpp
+++ b/fuse_sideload.cpp
@@ -72,6 +72,8 @@ static constexpr int NO_STATUS = 1;
using SHA256Digest = std::array<uint8_t, SHA256_DIGEST_LENGTH>;
+#define INSTALL_REQUIRED_MEMORY (100 * 1024 * 1024)
+
struct fuse_data {
android::base::unique_fd ffd; // file descriptor for the fuse socket
@@ -85,15 +87,84 @@ struct fuse_data {
uid_t uid;
gid_t gid;
- uint32_t curr_block; // cache the block most recently read from the host
+ uint32_t curr_block; // cache the block most recently used
uint8_t* block_data;
uint8_t* extra_block; // another block of storage for reads that span two blocks
std::vector<SHA256Digest>
hashes; // SHA-256 hash of each block (all zeros if block hasn't been read yet)
+
+ // Block cache
+ uint32_t block_cache_max_size; // Max allowed block cache size
+ uint32_t block_cache_size; // Current block cache size
+ uint8_t** block_cache; // Block cache data
};
+static uint64_t free_memory() {
+ uint64_t mem = 0;
+ FILE* fp = fopen("/proc/meminfo", "r");
+ if (fp) {
+ char buf[256];
+ char* linebuf = buf;
+ size_t buflen = sizeof(buf);
+ while (getline(&linebuf, &buflen, fp) > 0) {
+ char* key = buf;
+ char* val = strchr(buf, ':');
+ *val = '\0';
+ ++val;
+ if (strcmp(key, "MemFree") == 0) {
+ mem += strtoul(val, nullptr, 0) * 1024;
+ }
+ if (strcmp(key, "Buffers") == 0) {
+ mem += strtoul(val, nullptr, 0) * 1024;
+ }
+ if (strcmp(key, "Cached") == 0) {
+ mem += strtoul(val, nullptr, 0) * 1024;
+ }
+ }
+ fclose(fp);
+ }
+ return mem;
+}
+
+static int block_cache_fetch(struct fuse_data* fd, uint32_t block) {
+ if (fd->block_cache == nullptr) {
+ return -1;
+ }
+ if (fd->block_cache[block] == nullptr) {
+ return -1;
+ }
+ memcpy(fd->block_data, fd->block_cache[block], fd->block_size);
+ return 0;
+}
+
+static void block_cache_enter(struct fuse_data* fd, uint32_t block) {
+ if (!fd->block_cache) return;
+ if (fd->block_cache_size == fd->block_cache_max_size) {
+ // Evict a block from the cache. Since the file is typically read
+ // sequentially, start looking from the block behind the current
+ // block and proceed backward.
+ int n;
+ for (n = fd->curr_block - 1; n != (int)fd->curr_block; --n) {
+ if (n < 0) {
+ n = fd->file_blocks - 1;
+ }
+ if (fd->block_cache[n]) {
+ free(fd->block_cache[n]);
+ fd->block_cache[n] = nullptr;
+ fd->block_cache_size--;
+ break;
+ }
+ }
+ }
+
+ fd->block_cache[block] = (uint8_t*)malloc(fd->block_size);
+ memcpy(fd->block_cache[block], fd->block_data, fd->block_size);
+
+ fd->block_cache_size++;
+}
+
static void fuse_reply(const fuse_data* fd, uint64_t unique, const void* data, size_t len) {
fuse_out_header hdr;
hdr.len = len + sizeof(hdr);
@@ -228,6 +299,11 @@ static int fetch_block(fuse_data* fd, uint32_t block) {
return 0;
}
+ if (block_cache_fetch(fd, block) == 0) {
+ fd->curr_block = block;
+ return 0;
+ }
+
size_t fetch_size = fd->block_size;
if (block * fd->block_size + fetch_size > fd->file_size) {
// If we're reading the last (partial) block of the file, expect a shorter response from the
@@ -264,6 +340,7 @@ static int fetch_block(fuse_data* fd, uint32_t block) {
}
fd->hashes[block] = hash;
+ block_cache_enter(fd, block);
return 0;
}
@@ -359,6 +436,9 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl
fd.block_size = block_size;
fd.file_blocks = (file_size == 0) ? 0 : (((file_size - 1) / block_size) + 1);
+ uint64_t mem = free_memory();
+ uint64_t avail = mem - (INSTALL_REQUIRED_MEMORY + fd.file_blocks * sizeof(uint8_t*));
+
int result;
if (fd.file_blocks > (1 << 18)) {
fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks);
@@ -385,6 +465,22 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl
goto done;
}
+ fd.block_cache_max_size = 0;
+ fd.block_cache_size = 0;
+ fd.block_cache = nullptr;
+ if (mem > avail) {
+ uint32_t max_size = avail / fd.block_size;
+ if (max_size > fd.file_blocks) {
+ max_size = fd.file_blocks;
+ }
+ // The cache must be at least 1% of the file size or two blocks,
+ // whichever is larger.
+ if (max_size >= fd.file_blocks / 100 && max_size >= 2) {
+ fd.block_cache_max_size = max_size;
+ fd.block_cache = (uint8_t**)calloc(fd.file_blocks, sizeof(uint8_t*));
+ }
+ }
+
signal(SIGTERM, sig_term);
fd.ffd.reset(open("/dev/fuse", O_RDWR));
@@ -489,6 +585,14 @@ done:
fprintf(stderr, "fuse_sideload umount failed: %s\n", strerror(errno));
}
+ if (fd.block_cache) {
+ uint32_t n;
+ for (n = 0; n < fd.file_blocks; ++n) {
+ free(fd.block_cache[n]);
+ }
+ free(fd.block_cache);
+ }
+
free(fd.block_data);
free(fd.extra_block);