commit e7eeca1ca087c7f38e07e8da8e55fe10890d31a9
Author: Tomas Hlavaty <tom@logand.com>
Date: Sun, 29 May 2011 16:24:33 +0200
Initial commit
Diffstat:
A | Makefile | | | 20 | ++++++++++++++++++++ |
A | cfbfs.c | | | 681 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | odrawfs.c | | | 205 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 906 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,20 @@
+ALL=cfbfs odrawfs
+#-std=c99
+CFLAGS=-g -Wall
+#CFLAGS=-Wall -O2
+LDFLAGS=
+CFLAGSFUSE=-DFUSE_USE_VERSION=25 $(shell pkg-config fuse --cflags)
+LDFLAGSFUSE=$(shell pkg-config fuse --libs)
+
+all: $(ALL)
+
+cfbfs: cfbfs.c
+ $(CC) $(CFLAGS) $(CFLAGSFUSE) -o $@ $< $(LDFLAGS) $(LDFLAGSFUSE)
+# strip $@
+
+odrawfs: odrawfs.c
+ $(CC) $(CFLAGS) $(CFLAGSFUSE) -o $@ $< $(LDFLAGS) $(LDFLAGSFUSE)
+# strip $@
+
+clean:
+ rm -f $(ALL)
diff --git a/cfbfs.c b/cfbfs.c
@@ -0,0 +1,681 @@
+// TODO version 4 with bigger sector size
+
+#include <fuse.h>
+#include <stdlib.h>
+#include <assert.h> // TODO dont use assert!
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+//#include <iconv.h> // TODO wchar -> utf8 properly
+
+// MS-CFB Compound File Binary File Format
+
+#define UNUSED_SECTOR 0
+#define MAXREGSECT 0xfffffffa
+#define DIFSECT 0xfffffffc
+#define FATSECT 0xfffffffd
+#define ENDOFCHAIN 0xfffffffe
+#define FREESECT 0xffffffff
+
+#define MAXREGSIG 0xfffffffa
+#define NOSTREAM 0xffffffff
+
+#define ENTRY_UNKNOWN 0
+#define ENTRY_STORAGE 1
+#define ENTRY_STREAM 2
+#define ENTRY_ROOT 5
+
+typedef uint8_t byte;
+typedef uint16_t ushort;
+typedef uint16_t wchar;
+typedef uint32_t dword;
+typedef uint64_t filetime;
+typedef uint64_t ulonglong;
+
+struct entry {
+ wchar name[32];
+ ushort name_length;
+ byte object_type;
+ byte color_flag;
+ dword left_sibling_id;
+ dword right_sibling_id;
+ dword child_id;
+ byte clsid[16];
+ dword state_bits;
+ filetime creation_time;
+ filetime modified_time;
+ dword starting_sector_location;
+ ulonglong stream_size;
+};
+
+struct chain {
+ dword location;
+ struct chain *next;
+};
+
+#define PATH_MAX 1024
+
+static char *filename;
+static char cwd[PATH_MAX]; // fuse changes cwd:-{
+//static iconv_t conv;
+static dword difat_length;
+static dword *difat;
+static dword fat_length;
+static dword *fat;
+static struct chain *directory_chain;
+static int directories_length;
+static struct entry *directories;
+static struct chain *mfat_chain;
+static int mfat_length;
+static dword *mfat;
+
+// header
+static byte signature[8];
+static byte clsid[16];
+static ushort minor_version;
+static ushort major_version;
+static ushort byte_order;
+static ushort sector_shift;
+static ushort mini_sector_shift;
+static byte reserved[6];
+static dword number_of_directory_sectors;
+static dword number_of_fat_sectors;
+static dword first_directory_sector_location;
+static dword transaction_signature_number;
+static dword mini_stream_cutoff_size;
+static dword first_mini_fat_sector_location;
+static dword number_of_mini_fat_sectors;
+static dword first_difat_sector_location;
+static dword number_of_difat_sectors;
+
+static dword location_position(dword location) {
+ return (1 + location) * 512;
+}
+
+static void seek_sector(FILE *stream, dword location) {
+ dword position = location_position(location);
+ fseek(stream, position, SEEK_SET);
+}
+
+static void read_byte(FILE *stream, byte *place, int count) {
+ fread(place, sizeof(byte), count, stream);
+}
+
+static void read_wchar(FILE *stream, wchar *place, int count) {
+ fread(place, sizeof(wchar), count, stream);
+}
+
+static void read_ushort(FILE *stream, ushort *place, int count) {
+ fread(place, sizeof(ushort), count, stream);
+}
+
+static void read_dword(FILE *stream, dword *place, int count) {
+ fread(place, sizeof(dword), count, stream);
+}
+
+static void read_filetime(FILE *stream, filetime *place, int count) {
+ fread(place, sizeof(filetime), count, stream);
+}
+
+static void read_ulonglong(FILE *stream, ulonglong *place, int count) {
+ fread(place, sizeof(ulonglong), count, stream);
+}
+
+static void read_header(FILE *stream) {
+ read_byte(stream, signature, 8);
+ read_byte(stream, clsid, 16);
+ read_ushort(stream, &minor_version, 1);
+ read_ushort(stream, &major_version, 1);
+ read_ushort(stream, &byte_order, 1);
+ read_ushort(stream, §or_shift, 1);
+ read_ushort(stream, &mini_sector_shift, 1);
+ read_byte(stream, reserved, 6);
+ read_dword(stream, &number_of_directory_sectors, 1);
+ read_dword(stream, &number_of_fat_sectors, 1);
+ read_dword(stream, &first_directory_sector_location, 1);
+ read_dword(stream, &transaction_signature_number, 1);
+ read_dword(stream, &mini_stream_cutoff_size, 1);
+ read_dword(stream, &first_mini_fat_sector_location, 1);
+ read_dword(stream, &number_of_mini_fat_sectors, 1);
+ read_dword(stream, &first_difat_sector_location, 1);
+ read_dword(stream, &number_of_difat_sectors, 1);
+}
+
+static const byte expected_signature[8] =
+ {0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1};
+
+static const byte expected_reserved[6] = {0, 0, 0, 0, 0, 0};
+
+static void check_header() {
+ assert(!memcmp(signature, expected_signature, 8));
+/* ;;(assert (equalp clsid_null (ole_header.clsid x))) */
+ assert(0xfffe == byte_order);
+ assert(!memcmp(reserved, expected_reserved, 6));
+ assert(3 == major_version);
+ assert(512 == (1 << sector_shift));
+ assert(64 == (1 << mini_sector_shift));
+ assert(0 == number_of_directory_sectors);
+ /* ;;(assert (eql 0xfffffffe (first_directory_sector_location x))) */
+ assert(0 == transaction_signature_number);
+ assert(4096 == mini_stream_cutoff_size);
+ /* ;;(assert (eql 0xfffffffe (first_mini_fat_sector_location x))) */
+ if(number_of_difat_sectors <= 0)
+ assert(0xfffffffe == first_difat_sector_location);
+}
+
+static void read_entry(FILE *stream, struct entry *entry) {
+ read_wchar(stream, &entry->name[0], 32);
+ read_ushort(stream, &entry->name_length, 1);
+ read_byte(stream, &entry->object_type, 1);
+ read_byte(stream, &entry->color_flag, 1);
+ read_dword(stream, &entry->left_sibling_id, 1);
+ read_dword(stream, &entry->right_sibling_id, 1);
+ read_dword(stream, &entry->child_id, 1);
+ read_byte(stream, &entry->clsid[0], 16);
+ read_dword(stream, &entry->state_bits, 1);
+ read_filetime(stream, &entry->creation_time, 1);
+ read_filetime(stream, &entry->modified_time, 1);
+ read_dword(stream, &entry->starting_sector_location, 1);
+ read_ulonglong(stream, &entry->stream_size, 1);
+};
+
+static void print_bytes(FILE *stream, byte *place, int count) {
+ int i;
+ for(i = 0; i < count; i++) {
+ fprintf(stream, "%s%02x", (0 < i ? ":" : ""), place[i]);
+ }
+}
+
+static void print_ushort(FILE *stream, ushort x) {
+ fprintf(stream, "%u 0x%04x", x, x);
+}
+
+static void print_dword(FILE *stream, dword x) {
+ fprintf(stream, "%u 0x%08x", x, x);
+}
+
+static void print_header(FILE *stream) {
+ fprintf(stream, "signature ");
+ print_bytes(stream, signature, 8);
+ fprintf(stream, "\nclsid ");
+ print_bytes(stream, clsid, 16);
+ fprintf(stream, "\nminor_version ");
+ print_ushort(stream, minor_version);
+ fprintf(stream, "\nmajor_version ");
+ print_ushort(stream, major_version);
+ fprintf(stream, "\nbyte_order ");
+ print_ushort(stream, byte_order);
+ fprintf(stream, "\nsector_shift ");
+ print_ushort(stream, sector_shift);
+ fprintf(stream, "\nmini_sector_shift ");
+ print_ushort(stream, mini_sector_shift);
+ fprintf(stream, "\nreserved ");
+ print_bytes(stream, reserved, 6);
+ fprintf(stream, "\nnumber_of_directory_sectors ");
+ print_dword(stream, number_of_directory_sectors);
+ fprintf(stream, "\nnumber_of_fat_sectors ");
+ print_dword(stream, number_of_fat_sectors);
+ fprintf(stream, "\nfirst_directory_sector_location ");
+ print_dword(stream, first_directory_sector_location);
+ fprintf(stream, "\ntransaction_signature_number ");
+ print_dword(stream, transaction_signature_number);
+ fprintf(stream, "\nmini_stream_cutoff_size ");
+ print_dword(stream, mini_stream_cutoff_size);
+ fprintf(stream, "\nfirst_mini_fat_sector_location ");
+ print_dword(stream, first_mini_fat_sector_location);
+ fprintf(stream, "\nnumber_of_mini_fat_sectors ");
+ print_dword(stream, number_of_mini_fat_sectors);
+ fprintf(stream, "\nfirst_difat_sector_location ");
+ print_dword(stream, first_difat_sector_location);
+ fprintf(stream, "\nnumber_of_difat_sectors ");
+ print_dword(stream, number_of_difat_sectors);
+ //fprintf(stream, "\n");
+ fprintf(stream, "\nfilename %s", filename);
+ fprintf(stream, "\ndirectory %s", cwd);
+ fprintf(stream, "\ndifat_length ");
+ print_dword(stream, difat_length);
+ fprintf(stream, "\nfat_length ");
+ print_dword(stream, fat_length);
+ fprintf(stream, "\ndirectories_length %d", directories_length);
+ fprintf(stream, "\nmfat_length %d", mfat_length);
+ fprintf(stream, "\n");
+}
+
+static void read_difat (FILE *stream) {
+ difat_length = 109 + ((512 - 4) / 4) * number_of_difat_sectors;
+ difat = calloc(difat_length, sizeof(dword));
+ read_dword(stream, difat, 109);
+ dword n = first_difat_sector_location, i = 109, m = 512 / 4 - 1;
+ for(; n != ENDOFCHAIN; read_dword(stream, &n, 1), i += m) {
+ seek_sector(stream, n);
+ read_dword(stream, &difat[i], m);
+ }
+}
+
+static void read_fat (FILE *stream) {
+ dword m = 512 / 4;
+ fat_length = m * difat_length;
+ fat = calloc(fat_length, sizeof(dword));
+ int i;
+ for(i = 0; i < difat_length; i++) {
+ dword s = difat[i];
+ if(s != FREESECT) {
+ seek_sector(stream, s);
+ read_dword(stream, &fat[i * m], m);
+ }
+ }
+}
+
+static struct chain *make_chain(dword location, struct chain *next) {
+ struct chain *x = malloc(sizeof(struct chain));
+ x->location = location;
+ x->next = next;
+ return x;
+}
+
+#define FOREACH_CHAIN(chain) for(; chain; chain = chain->next)
+
+static void free_chain(struct chain *x) {
+ struct chain *next;
+ for(; x; x = next) {
+ next = x->next;
+ free(x);
+ }
+}
+
+static int chain_length(struct chain *chain) {
+ int i = 0;
+ FOREACH_CHAIN(chain) i++;
+ return i;
+}
+
+static struct chain *nth_chain(struct chain *chain, int n) {
+ if(0 <= n) {
+ FOREACH_CHAIN(chain) {
+ if(n == 0)
+ return chain;
+ n--;
+ }
+ }
+ return NULL;
+}
+
+static struct chain *sector_chain(dword *fat, dword location) {
+ struct chain *x = NULL;
+ switch(location) {
+ case DIFSECT:
+ case FATSECT:
+ case ENDOFCHAIN:
+ case FREESECT:
+ break;
+ default:
+ assert(0 <= location && location <= MAXREGSECT);
+ x = make_chain(location, sector_chain(fat, fat[location]));
+ }
+ return x;
+}
+
+static void read_directories (FILE *stream) {
+ dword m = 512 / 128;
+ directories_length = m * chain_length(directory_chain);
+ directories = calloc(directories_length, sizeof(struct entry));
+ int i = 0;
+ struct chain *x = directory_chain;
+ FOREACH_CHAIN(x) {
+ seek_sector(stream, x->location);
+ int j;
+ for(j = 0; j < m; j++)
+ read_entry(stream, &directories[i++]);
+ }
+}
+
+static void read_mfat (FILE *stream) {
+ dword m = 512 / 4;
+ mfat_length = m * chain_length(mfat_chain);
+ mfat = calloc(mfat_length, sizeof(dword));
+ int i = 0;
+ struct chain *x = mfat_chain;
+ FOREACH_CHAIN(x) {
+ seek_sector(stream, x->location);
+ read_dword(stream, &mfat[i++ * m], m);
+ }
+}
+
+static const char *header_path = "/header";
+
+static void print_header_to_string(char **str, size_t *size) {
+ FILE *stream = open_memstream(str, size);
+ print_header(stream);
+ fflush(stream);
+ fclose(stream);
+}
+
+static size_t xconv(wchar *iname, char *oname, size_t length) {
+ /* size_t ileft = length, oleft; */
+ /* return iconv(conv, (char **) &iname, &ileft, &oname, &oleft); */
+ int i;
+ for(i = 0; i < length / sizeof(wchar); i++)
+ oname[i] = iname[i];
+ return 0;
+}
+
+typedef int (*walk_directory_cb)(void *env, struct entry *entry, dword id,
+ char *path, char *name, char *parent_path);
+
+static void walk_directory(void *env, walk_directory_cb cb, dword id, char *path) {
+ struct entry *x = &directories[id];
+ if(x->object_type == ENTRY_STORAGE
+ || x->object_type == ENTRY_STREAM
+ || x->object_type == ENTRY_ROOT) {
+ char name[32 * sizeof(wchar)];
+ xconv(x->name, name, x->name_length);
+ size_t xlen = strlen(path) + 1 + strlen(name) + 1;
+ char xpath[xlen];
+ snprintf(xpath, xlen, "%s/%s", path, name);
+ int descend = cb(env, x, id, xpath, name, path);
+ dword n;
+ n = x->left_sibling_id;
+ if(n <= MAXREGSIG)
+ walk_directory(env, cb, n, path);
+ if(descend) {
+ n = x->child_id;
+ if(n <= MAXREGSIG)
+ walk_directory(env, cb, n, xpath);
+ }
+ n = x->right_sibling_id;
+ if(n <= MAXREGSIG)
+ walk_directory(env, cb, n, path);
+ }
+}
+
+struct getattr_walk {
+ const char *path;
+ struct stat *stbuf;
+ int result;
+};
+
+static int getattr_walk_cb(void *env, struct entry *entry, dword id,
+ char *path, char *name, char *parent_path) {
+ struct getattr_walk *e = env;
+ if(!strcmp(path, e->path)) {
+ switch(entry->object_type) {
+ case ENTRY_STORAGE:
+ e->stbuf->st_mode = S_IFDIR | 0755;
+ e->stbuf->st_nlink = 1;
+ e->stbuf->st_size = 0;
+ e->result = 0;
+ return 0;
+ case ENTRY_STREAM:
+ e->stbuf->st_mode = S_IFREG | 0444;
+ e->stbuf->st_nlink = 1;
+ e->stbuf->st_size = entry->stream_size;
+ e->result = 0;
+ return 0;
+ case ENTRY_ROOT:
+ e->stbuf->st_mode = S_IFDIR | 0755;
+ e->stbuf->st_nlink = 1;
+ e->stbuf->st_size = 0;
+ e->result = 0;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int olefs_getattr(const char *path, struct stat *stbuf) {
+ int res = 0;
+ memset(stbuf, 0, sizeof(struct stat));
+ if(strcmp(path, "/") == 0) {
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ }
+ else if(strcmp(path, header_path) == 0) {
+ stbuf->st_mode = S_IFREG | 0444;
+ stbuf->st_nlink = 1;
+ size_t len;
+ char *header_str;
+ print_header_to_string(&header_str, &len);
+ free(header_str);
+ stbuf->st_size = len; //0; //strlen(header_str);
+ }
+ else {
+ struct getattr_walk e = {path, stbuf, -ENOENT};
+ walk_directory(&e, getattr_walk_cb, 0, "");
+ return e.result;
+ }
+ return res;
+}
+
+struct readdir_walk {
+ const char *path;
+ void *buf;
+ fuse_fill_dir_t filler;
+ int result;
+};
+
+static int readdir_walk_cb(void *env, struct entry *entry, dword id,
+ char *path, char *name, char *parent_path) {
+ struct readdir_walk *e = env;
+ if(!strcmp(parent_path, e->path)) {
+ e->filler(e->buf, name, NULL, 0);
+ e->result = 0;
+ return 0;
+ }
+ return 1;
+}
+
+static int olefs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi) {
+ (void) offset;
+ (void) fi;
+ struct entry *root = &directories[0];
+ char name[32 * sizeof(wchar)];
+ xconv(root->name, name, root->name_length);
+ if(strcmp(path, "/") == 0) {
+ filler(buf, ".", NULL, 0);
+ filler(buf, "..", NULL, 0);
+ filler(buf, header_path + 1, NULL, 0);
+ filler(buf, name, NULL, 0);
+ }
+ else {
+ struct readdir_walk e = {path, buf, filler, -ENOENT};
+ walk_directory(&e, readdir_walk_cb, 0, "");
+ return e.result;
+ }
+ return 0;
+}
+
+struct open_walk {
+ const char *path;
+ struct fuse_file_info *fi;
+ int result;
+};
+
+static int open_walk_cb(void *env, struct entry *entry, dword id,
+ char *path, char *name, char *parent_path) {
+ struct open_walk *e = env;
+ if(!strcmp(path, e->path)) {
+ if(entry->object_type == ENTRY_STREAM)
+ e->result = (e->fi->flags & 3) != O_RDONLY ? -EACCES : 0;
+ return 0;
+ }
+ return 1;
+}
+
+static int olefs_open(const char *path, struct fuse_file_info *fi) {
+ if(!strcmp(path, header_path)) {
+ if((fi->flags & 3) != O_RDONLY)
+ return -EACCES;
+ return 0;
+ }
+ struct open_walk e = {path, fi, -ENOENT};
+ walk_directory(&e, open_walk_cb, 0, "");
+ return e.result;
+}
+
+struct entry_stream {
+ FILE *file;
+ dword offset;
+ struct chain *chain;
+ struct chain *mchain;
+ dword sector;
+ char buffer[512];
+ dword size;
+};
+
+static void open_entry_stream(struct entry_stream *s, struct entry *e, FILE *f) {
+ int mini = e->stream_size < mini_stream_cutoff_size;
+ s->file = f;
+ s->offset = 0;
+ s->chain = sector_chain(fat, (mini ? directories : e)->starting_sector_location);
+ s->mchain = mini ? sector_chain(mfat, e->starting_sector_location) : NULL;
+ s->sector = -1;
+ s->size = e->stream_size;
+}
+
+static void close_entry_stream(struct entry_stream *s) {
+ //fclose(s->file);
+ free_chain(s->chain);
+ free_chain(s->mchain);
+}
+
+static void seek_entry_stream(struct entry_stream *stream, off_t off) {
+ stream->offset = off;
+ stream->sector = -1;
+}
+
+static void stream_read_byte_pick(struct entry_stream *x, dword q, dword r,
+ char *byte) {
+ if(x->sector != q) {
+ seek_sector(x->file, nth_chain(x->chain, q)->location);
+ int n = fread(x->buffer, sizeof(char), 512, x->file);
+ assert(512 == n);
+ x->sector = q;
+ }
+ *byte = x->buffer[r];
+ x->offset++;
+}
+
+static int stream_read_byte(struct entry_stream *x, char *byte) {
+ if(x->offset < x->size) {
+ if(x->mchain) {
+ dword mq = x->offset / 64, mr = x->offset % 64;
+ dword s = nth_chain(x->mchain, mq)->location;
+ dword q = s / (512 / 64), r = s % (512 / 64);
+ stream_read_byte_pick(x, q, (64 * r) + mr, byte);
+ }
+ else {
+ dword q = x->offset / 512, r = x->offset % 512;
+ stream_read_byte_pick(x, q, r, byte);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static int copy_entry_data_from(char *path, struct entry *entry,
+ char *buf, size_t size, off_t off) {
+ int n = 0;
+ FILE *f = fopen(path, "r"); // TODO move to open_entry_stream
+ if(f) {
+ struct entry_stream s;
+ open_entry_stream(&s, entry, f);
+ seek_entry_stream(&s, off);
+ for(; 0 < size; size--, n++)
+ if(!stream_read_byte(&s, &buf[n])) // TODO read block instead of bytes
+ break;
+ close_entry_stream(&s);
+ fclose(f); // TODO move to close_entry_stream
+ }
+ return n;
+}
+
+static int copy_entry_data(struct entry *entry, char *buf, size_t size, off_t off) {
+ int n = 0;
+ if(0 < size && 0 <= off && off < entry->stream_size) {
+ if('/' == filename[0])
+ n = copy_entry_data_from(filename, entry, buf, size, off);
+ else {
+ char x[PATH_MAX];
+ snprintf(x, PATH_MAX, "%s/%s", cwd, filename);
+ n = copy_entry_data_from(x, entry, buf, size, off);
+ }
+ }
+ return n;
+}
+
+struct read_walk {
+ const char *path;
+ char *buf;
+ size_t size;
+ off_t offset;
+ struct fuse_file_info *fi;
+ int result;
+};
+
+static int read_walk_cb(void *env, struct entry *entry, dword id,
+ char *path, char *name, char *parent_path) {
+ struct read_walk *e = env;
+ if(!strcmp(path, e->path)) {
+ e->result = copy_entry_data(entry, e->buf, e->size, e->offset);
+ return 0;
+ }
+ return 1;
+}
+
+static int olefs_read(const char *path, char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi) {
+ if(!strcmp(path, header_path)) {
+ size_t len;
+ char *header_str;
+ print_header_to_string(&header_str, &len);
+ /* len = strlen(header_str); */
+ if (offset < len) {
+ if (offset + size > len)
+ size = len - offset;
+ memcpy(buf, header_str + offset, size);
+ } else
+ size = 0;
+ free(header_str);
+ return size;
+ }
+ struct read_walk e = {path, buf, size, offset, fi, -ENOENT};
+ walk_directory(&e, read_walk_cb, 0, "");
+ return e.result;
+}
+
+static struct fuse_operations olefs_operations = {
+ .readdir = olefs_readdir,
+ .getattr = olefs_getattr,
+ .open = olefs_open,
+ .read = olefs_read,
+};
+
+int main(int argc, char *argv[]) {
+ if(argc < 3) {
+ fprintf(stderr, "Usage: %s filename directory\n", argv[0]);
+ exit(-1);
+ }
+ filename = argv[1];
+ argv++;
+ argc--;
+ getcwd(cwd, PATH_MAX);
+ FILE *stream = fopen(filename, "r");
+ if(!stream) {
+ fprintf(stderr, "Unable to open '%s'.\n", filename);
+ exit(-1);
+ }
+ //conv = iconv_open("UTF-8", "UTF-16LE"); //"UCS-2"); //"UCS2-LE");
+ read_header(stream);
+ check_header();
+ read_difat(stream);
+ read_fat(stream);
+ directory_chain = sector_chain(fat, first_directory_sector_location);
+ read_directories(stream);
+ mfat_chain = sector_chain(fat, first_mini_fat_sector_location);
+ read_mfat(stream);
+ fclose(stream);
+ return fuse_main(argc, argv, &olefs_operations);
+}
diff --git a/odrawfs.c b/odrawfs.c
@@ -0,0 +1,205 @@
+// TODO proper little endian read_
+
+#include <fuse.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+typedef uint8_t byte;
+typedef uint16_t ushort;
+typedef uint16_t wchar;
+typedef uint32_t dword;
+typedef uint64_t filetime;
+typedef uint64_t ulonglong;
+
+char *filename;
+char *dir;
+FILE *stream;
+
+struct shorter_stream {
+ FILE *wrap;
+ size_t size;
+ off_t offset;
+};
+
+static int shorter_stream_eof(struct shorter_stream *s) {
+ return !(s->offset < s->size);
+}
+
+static int shorter_stream_read(struct shorter_stream *s, void *buf, size_t size) {
+ int left = s->size - s->offset;
+ if(0 < left) {
+ int n = left < size ? left : size;
+ int m = fread(buf, sizeof(byte), n, s->wrap);
+ if(0 < m)
+ s->offset += m;
+ return m;
+ }
+ return 0;
+}
+
+static int read_guid(struct shorter_stream *s, byte guid[]) {
+ int n = shorter_stream_read(s, guid, 16);
+ if(0 < n)
+ assert(n == 16);
+ return n;
+}
+
+// MS-PPT PowerPoint (.ppt) Binary File Format
+
+struct RecordHeader {
+ ushort recVer: 4; //(logand #x0f %dummy1))
+ ushort recInstance: 12; //(logior (ash %dummy2 4) (ash %dummy1 -4)))
+ ushort recType;
+ dword recLen;
+} __attribute__((__packed__));
+
+static int read_RecordHeader(struct RecordHeader *x) {
+ return fread(x, sizeof(struct RecordHeader), 1, stream);
+}
+
+/* static void print_RecordHeader(struct RecordHeader *x) { */
+/* printf("RecordHeader 0x%x 0x%x 0x%x %d 0x%lx\n", x->recVer, x->recInstance, */
+/* x->recType, x->recLen, ftell(stream)); */
+/* } */
+
+// MS-ODRAW Office Drawing Binary File Format
+
+struct POINT {
+ dword x;
+ dword y;
+} __attribute__((__packed__));
+
+struct RECT {
+ dword left;
+ dword top;
+ dword right;
+ dword bottom;
+} __attribute__((__packed__));
+
+struct OfficeArtMetafileHeader {
+ dword cbSize;
+ struct RECT rcBounds;
+ struct POINT ptSize;
+ dword cbSave;
+ byte compression; // :member '(#x00 #xfe))
+ byte filter; //:always #xfe))
+} __attribute__((__packed__));
+
+static int read_OfficeArtMetafileHeader(struct shorter_stream *s,
+ struct OfficeArtMetafileHeader *x) {
+ return shorter_stream_read(s, x, sizeof(struct OfficeArtMetafileHeader));
+}
+
+static const struct OfficeArtBlip_config {
+ ushort recType;
+ ushort recInstance[4];
+ char *ext;
+ ushort guid2[2];
+ int metafileHeader;
+} OfficeArtBlip_config[] = {
+ {0xF01A, {0x3d4, 0x3d5}, "emf", {0x3d5}, 1},
+ {0xF01B, {0x216, 0x217}, "wmf", {0x217}, 1},
+ {0xF01C, {0x542, 0x543}, "pict", {0x543}, 1},
+ {0xF01D, {0x46A, 0x46B, 0x6E2, 0x6E3}, "jpeg", {0x46B, 0x6E3}, 0},
+ {0xF01E, {0x6e0, 0x6e1}, "png", {0x6e1}, 0},
+ {0xF01F, {0x7a8, 0x7a9}, "dib", {0x7a9}, 0},
+ {0xF029, {0x6e4, 0x6e5}, "tiff", {0x6e5}, 0},
+ {0xF02A, {0x46A, 0x46B, 0x6E2, 0x6E3}, "jpeg", {0x46B, 0x6E3}, 0},
+};
+
+static int member(ushort x, const ushort a[]) { // , size_t n
+ int i;
+ for(i = 0; i < sizeof(a); i++)
+ if(a[i] == x)
+ return 1;
+ return 0;
+}
+
+static void blip(int n, ushort recType, ushort recInstance, dword recLen) {
+ const struct OfficeArtBlip_config *config;
+ int i;
+ for(i = 0; i < sizeof(OfficeArtBlip_config); i++) {
+ config = &OfficeArtBlip_config[i];
+ if(OfficeArtBlip_config[i].recType == recType) break;
+ }
+ assert(member(recInstance, config->recInstance));
+ struct shorter_stream s = {stream, recLen, 0};
+ byte guid[16];
+ read_guid(&s, guid);
+ if(member(recInstance, config->guid2))
+ read_guid(&s, guid);
+ if(config->metafileHeader) {
+ struct OfficeArtMetafileHeader h;
+ read_OfficeArtMetafileHeader(&s, &h);
+ }
+ else {
+ byte b;
+ shorter_stream_read(&s, &b, 1);
+ }
+ char ofile[1024];
+ snprintf(ofile, 1024, "%s/%d.%s", dir, n, config->ext);
+ FILE *o = fopen(ofile, "w");
+ assert(o);
+ while(!shorter_stream_eof(&s)) {
+ byte buf[1024];
+ int n = shorter_stream_read(&s, buf, 1024);
+ if(n <= 0) break;
+ fwrite(buf, sizeof(char), n, o);
+ }
+ fclose(o);
+ //printf("<p><img src=\"%d.%s\">\n", n, config->ext);
+}
+
+int main(int argc, char *argv[]) {
+ if(argc < 3) {
+ fprintf(stderr, "Usage: %s filename directory\n", argv[0]);
+ exit(-1);
+ }
+ filename = argv[1];
+ dir = argv[2];
+ stream = fopen(filename, "r");
+ if(!stream) {
+ fprintf(stderr, "Unable to open '%s'.\n", filename);
+ exit(-1);
+ }
+ int i;
+ for(i = 0;; i++) {
+ struct RecordHeader h;
+ /* struct CurrentUserAtom u; */
+ if(read_RecordHeader(&h) <= 0) break;
+ //print_RecordHeader(&h);
+ switch(h.recType) {
+ /* case RT_CurrentUserAtom: */
+ /* assert(!x->recVer); */
+ /* assert(!x->recInstance); */
+ /* read_CurrentUserAtom(&u); */
+ /* /\* #+nil ;; why recLen too small? *\/ */
+ /* /\* (with-shorter-stream (in stream (RecordHeader.recLen x)) *\/ */
+ /* /\* (list x (read-CurrentUserAtom in)))) *\/ */
+ /* break; */
+ case 0xF01A:
+ case 0xF01B:
+ case 0xF01C:
+ case 0xF01D:
+ case 0xF01E:
+ case 0xF01F:
+ case 0xF029:
+ case 0xF02A:
+ assert(!h.recVer);
+ blip(i, h.recType, h.recInstance, h.recLen);
+ break;
+ default:
+ fprintf(stderr, "Unknown recType 0x%x\n", h.recType);
+ exit(-1);
+ }
+ }
+ fclose(stream);
+ return 0;
+}
+
+// n[0..] -> fpos