commit 09f2c0e39a5ddbf9f3c1a36646d75776e7642d7f Author: Tor Arne Vestbø Date: Fri Sep 28 13:16:17 2012 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f87216f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +tests +sparsebundlefs +*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b7849f3 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +TARGET = sparsebundlefs + +PKG_CONFIG = pkg-config +CFLAGS = -Wall -O3 + +ifeq ($(shell uname), Darwin) + # Pick up OSXFUSE, even with pkg-config from MacPorts + PKG_CONFIG := PKG_CONFIG_PATH=/usr/local/lib/pkgconfig $(PKG_CONFIG) + DEFINES = -DFUSE_USE_VERSION=26 +endif + +FUSE_FLAGS := $(shell $(PKG_CONFIG) fuse --cflags --libs) + +$(TARGET): sparsebundlefs.cpp + $(CXX) $(CFLAGS) $(FUSE_FLAGS) $(DEFINES) $< -o $@ + +all: $(TARGET) + +clean: + rm -f $(TARGET) diff --git a/sparsebundlefs.cpp b/sparsebundlefs.cpp new file mode 100644 index 0000000..29aef45 --- /dev/null +++ b/sparsebundlefs.cpp @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static const char *image_path = "/sparsebundle.dmg"; + +struct sparsebundle_data { + char *path; + int band_size; + int size; + FILE* logfile; +}; + +#define SB_DATA_CAST(ptr) ((struct sparsebundle_data *) ptr) +#define SB_DATA (SB_DATA_CAST(fuse_get_context()->private_data)) + +static int sparsebundle_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + if (strcmp(path, "/") != 0) + return -ENOENT; + + filler(buf, ".", 0, 0); + filler(buf, "..", 0, 0); + filler(buf, image_path + 1, 0, 0); + + return 0; +} + +static int sparsebundle_getattr(const char *path, struct stat *stbuf) +{ + memset(stbuf, 0, sizeof(struct stat)); + + if (strcmp(path, "/") == 0) { + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 3; + } else if (strcmp(path, image_path) == 0) { + stbuf->st_mode = S_IFREG | 0444; + stbuf->st_nlink = 1; + stbuf->st_size = SB_DATA->size; + } else + return -ENOENT; + + return 0; +} + +static int sparsebundle_open(const char *path, struct fuse_file_info *fi) +{ + if (strcmp(path, image_path) != 0) + return -ENOENT; + + if ((fi->flags & O_ACCMODE) != O_RDONLY) + return -EACCES; + + return 0; +} + +static void log(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + + vfprintf(SB_DATA->logfile, format, ap); +} + +static int sparsebundle_read(const char *path, char *buffer, size_t length, off_t offset, + struct fuse_file_info *fi) +{ + if (strcmp(path, image_path) != 0) + return -ENOENT; + + if (offset >= SB_DATA->size) + return 0; + + if (offset + length > SB_DATA->size) + length = SB_DATA->size - offset; + + log("sparsebundle_read(length: %zu offfset: %llu\n", length, offset); + + size_t bytes_read = 0; + while (bytes_read < length) { + off_t band_number = (offset + bytes_read) / SB_DATA->band_size; + off_t band_offset = (offset + bytes_read) % SB_DATA->band_size; + + size_t to_read = length - bytes_read; + + char *band_name; + asprintf(&band_name, "%s/bands/%jd", SB_DATA->path, intmax_t(band_number)); + + log("\tReading band %llu at offet %llu (bytes_read: %zu to_read: %zu\n", + band_number, band_offset, bytes_read, to_read); + + size_t read = 0; + int band_file = open(band_name, O_RDONLY); + if (band_file != -1) { + read = pread(band_file, buffer + bytes_read, to_read, band_offset); + close(band_file); + + if (read == -1) { + log("! Failed to read %zu bytes from band file %s at offset %llu\n", + to_read, band_name, band_offset); + free(band_name); + return -1; + } + } else if (errno != ENOENT) { + log("! Failed to open band file %s %d\n", band_name, errno); + free(band_name); + return -1; + } + + free(band_name); + + if (read < to_read) { + log("! EOB, read: %zu, padding with zeroes\n", read); + // Hit missing band or end of band, pad with zeroes + memset(buffer + bytes_read + read, 0, to_read - read); + } + + bytes_read += to_read; + } + + assert(bytes_read == length); + return bytes_read; +} + +static int sparsebundle_show_usage(char* program_name) +{ + fprintf(stderr, "usage: %s [-o options] sparsebundle mountpoint\n", program_name); + return 1; +} + +static struct fuse_operations sparsebundle_filesystem_operations = { + .getattr = sparsebundle_getattr, + .open = sparsebundle_open, + .read = sparsebundle_read, + .readdir = sparsebundle_readdir, +}; + +static int sparsebundle_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) +{ + if (key == FUSE_OPT_KEY_NONOPT && !SB_DATA_CAST(data)->path) { + SB_DATA_CAST(data)->path = strdup(arg); + return 0; + } + + return 1; +} + +using namespace std; + +int main(int argc, char **argv) +{ + struct sparsebundle_data data = {}; + + data.logfile = fopen("/tmp/sparesebundlefs.log", "w"); + setvbuf(data.logfile, 0, _IOLBF, 0); + + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + fuse_opt_parse(&args, &data, 0, sparsebundle_opt_proc); + + if (!data.path) + return sparsebundle_show_usage(argv[0]); + + char *abs_path = realpath(data.path, 0); + if (!abs_path) { + perror("Could not resolve absolute path"); + return -1; + } + + free(data.path); + data.path = abs_path; + + char *plist_path; + asprintf(&plist_path, "%s/Info.plist", data.path); + + ifstream plist_file(plist_path); + stringstream plist_data; + plist_data << plist_file.rdbuf(); + + string key, line; + while (getline(plist_data, line)) { + static const char *whitespace_chars = " \n\r\t"; + line.erase(0, line.find_first_not_of(whitespace_chars)); + line.erase(line.find_last_not_of(whitespace_chars) + 1); + + if (line.compare(0, 5, "") == 0) { + key = line.substr(5, line.length() - 11); + } else if (!key.empty()) { + line.erase(0, line.find_first_of('>') + 1); + line.erase(line.find_first_of('<')); + + if (key == "band-size") { + data.band_size = atoi(line.c_str()); + } else if (key == "size") { + data.size = atoi(line.c_str()); + } + + key.clear(); + } + } + + int ret = fuse_main(args.argc, args.argv, &sparsebundle_filesystem_operations, &data); + + fuse_opt_free_args(&args); + return ret; +}