fatblock: Program to offer dir as FAT32 filesystem using ublock
Change-Id: I6712e062e17b02c453ce89a52000cd8bc3ee810d
diff --git a/fatblock/Android.mk b/fatblock/Android.mk
new file mode 100644
index 0000000..07d9cc1
--- /dev/null
+++ b/fatblock/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fatblock
+LOCAL_SRC_FILES := fat.c fatblock.c fs.c import.c read.c utils.c fdpool.c
+# TODO: Why doesn't this work?
+#LOCAL_C_INCLUDES := $(call include-path-for, libublock)/include
+LOCAL_C_INCLUDES := system/extras/libublock/include
+LOCAL_SHARED_LIBRARIES := libublock
+
+include $(BUILD_EXECUTABLE)
diff --git a/fatblock/MODULE_LICENSE_APACHE2 b/fatblock/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fatblock/MODULE_LICENSE_APACHE2
diff --git a/fatblock/errors.h b/fatblock/errors.h
new file mode 100644
index 0000000..addfb18
--- /dev/null
+++ b/fatblock/errors.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ERRORS_H
+#define ERRORS_H
+
+#define MALLOC_FAIL (-41) /* memory allocation failed somewhere. */
+#define SKY_IS_FALLING (-42) /* One of the files changed out from under us. */
+
+#endif
diff --git a/fatblock/extent.h b/fatblock/extent.h
new file mode 100644
index 0000000..bbf32a4
--- /dev/null
+++ b/fatblock/extent.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EXTENT_H
+#define EXTENT_H
+
+#include "types.h"
+
+typedef enum {
+ EXTENT_TYPE_BOOT,
+ EXTENT_TYPE_INFO,
+ EXTENT_TYPE_FAT,
+ EXTENT_TYPE_FILE,
+ EXTENT_TYPE_DIR
+} extent_type;
+
+struct extent {
+ offset_t start;
+ offset_t len;
+ extent_type type;
+
+ struct extent *next;
+};
+
+#endif
diff --git a/fatblock/fat.c b/fatblock/fat.c
new file mode 100644
index 0000000..340f667
--- /dev/null
+++ b/fatblock/fat.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <sys/endian.h>
+
+#include "fat.h"
+
+const char FAT_BOOT_SIG[] = { 0x55, 0xAA };
+const char FAT_INFO_SIG1[4] = { 'R', 'R', 'a', 'A' };
+const char FAT_INFO_SIG2[4] = { 'r', 'r', 'A', 'a' };
+
+void fat_dirent_set_first_cluster(struct fat_dirent *de, cluster_t cluster) {
+ assert(de);
+
+ de->first_cluster_hi = htole16((cluster >> 16) & 0xffff);
+ de->first_cluster_lo = htole16((cluster >> 0) & 0xffff);
+}
+
+void fat_dirent_set(struct fat_dirent *de,
+ char *name, uint8_t attr,
+ cluster_t first_cluster, uint32_t size) {
+ assert(de);
+ assert(name);
+
+ memset(de, 0, sizeof(*de));
+
+ memcpy(de->name, name, 11);
+ de->attr = attr;
+ fat_dirent_set_first_cluster(de, first_cluster);
+ de->size = htole32(size);
+}
diff --git a/fatblock/fat.h b/fatblock/fat.h
new file mode 100644
index 0000000..4c66b3b
--- /dev/null
+++ b/fatblock/fat.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FAT_H
+#define FAT_H
+
+#include <stdint.h>
+
+#include "types.h"
+
+typedef uint64_t sector_t;
+typedef cluster_t fat_entry_t;
+
+struct fat_boot_sector {
+ uint8_t jump[3];
+ char name[8];
+ uint16_t sector_size;
+ uint8_t sectors_per_cluster;
+ uint16_t reserved_sectors;
+ uint8_t fats;
+ uint16_t rootdir_size;
+ uint16_t sectors16;
+ uint8_t media_desc;
+ uint16_t fat_sectors16;
+ uint16_t sectors_per_track;
+ uint16_t heads;
+ uint32_t hidden_sectors;
+ uint32_t sectors32;
+ uint32_t fat_sectors32;
+ uint16_t fat_flags;
+ uint16_t version;
+ cluster_t rootdir_start;
+ uint16_t fs_info_sector;
+ uint16_t backup_boot_sector;
+ uint8_t reserved1[12];
+ uint8_t phys_drive;
+ uint8_t reserved2;
+ uint8_t ext_boot_sig;
+ uint32_t serial;
+ char vol_label[11];
+ char type[8];
+ char boot_code[420];
+ uint8_t boot_sig[2];
+} __attribute__((__packed__));
+
+#define FAT_MEDIA_DESC_FIXED 0xF8
+
+#define FAT_PHYS_DRIVE_REMOVABLE 0x00
+#define FAT_PHYS_DRIVE_FIXED 0x80
+
+#define FAT_EXT_BOOT_SIG 0x29
+
+extern const char FAT_BOOT_SIG[2];
+
+extern const char FAT_INFO_SIG1[4];
+extern const char FAT_INFO_SIG2[4];
+#define FAT_INFO_SIG3 FAT_BOOT_SIG
+
+struct fat_info_sector {
+ char info_sig1[4];
+ char reserved1[480];
+ char info_sig2[4];
+ cluster_t free_clusters;
+ cluster_t last_cluster;
+ char reserved2[14];
+ char info_sig3[2];
+} __attribute__((__packed__));
+
+struct fat_bootinfo {
+ struct fat_boot_sector boot;
+ struct fat_info_sector info;
+} __attribute__((__packed__));
+
+struct fat_dirent {
+ char name[11];
+ uint8_t attr;
+ uint8_t reserved;
+ uint8_t ctime_ms;
+ uint16_t ctime;
+ uint16_t cdate;
+ uint16_t adate;
+ uint16_t first_cluster_hi;
+ uint16_t mtime;
+ uint16_t mdate;
+ uint16_t first_cluster_lo;
+ uint32_t size;
+} __attribute__((__packed__));
+
+#define FAT_ATTR_READONLY 0x01
+#define FAT_ATTR_HIDDEN 0x02
+#define FAT_ATTR_SYSTEM 0x04
+#define FAT_ATTR_VOLLABEL 0x08
+#define FAT_ATTR_SUBDIR 0x10
+#define FAT_ATTR_ARCHIVE 0x20
+#define FAT_ATTR_DEVICE 0x40
+
+#define FAT_ENTRY_FREE 0x00000000
+#define FAT_ENTRY_BAD 0x0FFFFFF7
+#define FAT_ENTRY_EOC 0x0FFFFFF8
+
+#define FAT_SECTOR_SIZE 512
+#define FAT_CLUSTER_ZERO 2
+#define FAT_ENTRIES_PER_SECTOR ((SECTOR_SIZE) / (sizeof(fat_entry_t)))
+
+void fat_dirent_set_first_cluster(struct fat_dirent *de, cluster_t cluster);
+void fat_dirent_set(struct fat_dirent *de,
+ char *name, uint8_t attr,
+ cluster_t first_cluster, uint32_t size);
+
+#endif
diff --git a/fatblock/fatblock.c b/fatblock/fatblock.c
new file mode 100644
index 0000000..dc9f402
--- /dev/null
+++ b/fatblock/fatblock.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <ublock/ublock.h>
+
+#include "errors.h"
+#include "import.h"
+#include "fs.h"
+#include "read.h"
+#include "utils.h"
+
+static struct fs fs;
+static struct ublock_ctx *ub;
+static int ums_lun = 0;
+
+static int fs_import(struct fs *fs, uint16_t cluster_size, offset_t data_size, offset_t *total_size_out) {
+ int ret;
+
+ ret = fs_init(fs, cluster_size, data_size, total_size_out);
+ if (ret)
+ return ret;
+
+ ret = import_tree(fs, ".");
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+
+
+static int read_callback(char *buf, uint64_t length, uint64_t offset) {
+ int result;
+ int i;
+
+ result = fs_read(&fs, buf, offset, length);
+ if (result == SKY_IS_FALLING) {
+ WARN("underlying filesystem has been modified; stopping.\n");
+ ublock_stop(ub);
+ }
+
+ return result ? -EINVAL : 0;
+}
+
+static int write_callback(const char *buf, uint64_t length, uint64_t offset) {
+ DEBUG("writing to (%llu, %llu): we are read-only\n", offset, length);
+
+ return -EINVAL;
+}
+
+static struct ublock_ops ops = {
+ .read = &read_callback,
+ .write = &write_callback
+};
+
+
+
+static int set_ums_file(int index)
+{
+ char filename[PATH_MAX];
+ FILE *file;
+
+ sprintf(filename, "/sys/devices/platform/usb_mass_storage/lun%d/file", ums_lun);
+ file = fopen(filename, "w");
+ if (!file) {
+ WARN("setting USB mass storage file: fopen(%s) failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ WARN("writing '/dev/block/ublock%d' to %s.\n", index, filename);
+
+ fprintf(file, "/dev/block/ublock%d", index);
+
+ fclose(file);
+
+ return 0;
+}
+
+static int clear_ums_file(void)
+{
+ char filename[PATH_MAX];
+ FILE *file;
+
+ sprintf(filename, "/sys/devices/platform/usb_mass_storage/lun%d/file", ums_lun);
+ file = fopen(filename, "w");
+ if (!file) {
+ WARN("clearing USB mass storage file: fopen(%s) failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ fclose(file);
+
+ return 0;
+}
+
+
+
+
+static void cleanup(void)
+{
+ WARN("cleanup: clearing USB mass storage file\n");
+ clear_ums_file();
+ WARN("cleanup: destroying block device\n");
+ ublock_destroy(ub);
+}
+
+static void signal_handler(int sig)
+{
+ WARN("received signal %d\n", sig);
+ cleanup();
+ exit(0);
+}
+
+static int normal_exit = 0;
+
+static void atexit_handler(void)
+{
+ if (normal_exit)
+ return;
+
+ cleanup();
+}
+
+int main(int argc, char *argv[]) {
+ char *path;
+ int mb;
+ offset_t total_size;
+ int index;
+ int ret;
+
+ signal(SIGINT, &signal_handler);
+ signal(SIGTERM, &signal_handler);
+ atexit(&atexit_handler);
+
+ if (argc != 3)
+ DIE("Usage: fatblock <path> <size in MB>\n");
+
+ path = argv[1];
+ mb = atoi(argv[2]);
+
+ INFO("fatblock: importing filesystem from %s (%d MB)\n", path, mb);
+
+ ret = chdir(path);
+ if (ret < 0)
+ DIE("fatblock: chdir(%s) failed: %s; aborting\n", path, strerror(errno));
+
+ ret = fs_import(&fs, 32768, 1048576LL * mb, &total_size);
+ if (ret)
+ DIE("fatblock: couldn't import filesystem; aborting\n");
+
+ INFO("fatblock: filesystem imported (%llu bytes)\n", total_size);
+
+ ret = ublock_init(&ub, &ops, total_size);
+ if (ret)
+ DIE("fatblock: couldn't create block device; aborting\n");
+ index = ublock_index(ub);
+ if (index < 0)
+ DIE("fatblock: invalid ublock index %d; aborting\n", index);
+
+ INFO("fatblock: block device ublock%d created\n", index);
+ set_ums_file(index);
+
+ INFO("fatblock: entering main loop\n");
+ ublock_run(ub);
+
+ INFO("fatblock: destroying block device\n");
+ clear_ums_file();
+ ublock_destroy(ub);
+
+ normal_exit = 1;
+
+ INFO("fatblock: goodbye!\n");
+ return 0;
+}
diff --git a/fatblock/fdpool.c b/fatblock/fdpool.c
new file mode 100644
index 0000000..900aeca
--- /dev/null
+++ b/fatblock/fdpool.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <utils.h>
+
+#include "fdpool.h"
+
+#define INVALID_FD (-1)
+#define FDPOOL_SIZE 4
+
+static struct pooled_fd fdpool_head = {
+ .fd = INVALID_FD,
+ .prev = &fdpool_head,
+ .next = &fdpool_head
+};
+static int fdpool_count = 0;
+
+static void fdpool_insert_head(struct pooled_fd *node) {
+ struct pooled_fd *prev = &fdpool_head;
+ struct pooled_fd *next = prev->next;
+
+ assert(node);
+
+ prev->next = node;
+ node->prev = prev;
+ node->next = next;
+ next->prev = node;
+
+ fdpool_count++;
+}
+
+static void fdpool_remove(struct pooled_fd *node) {
+ struct pooled_fd *prev = node->prev;
+ struct pooled_fd *next = node->next;
+
+ assert(prev);
+ assert(next);
+
+ prev->next = next;
+ next->prev = prev;
+
+ fdpool_count--;
+}
+
+static struct pooled_fd *fdpool_remove_tail(void) {
+ struct pooled_fd *tail = fdpool_head.prev;
+
+ assert(tail != &fdpool_head);
+
+ fdpool_remove(tail);
+
+ return tail;
+}
+
+static void fdpool_clear(struct pooled_fd *pfd) {
+ assert(pfd);
+
+ pfd->fd = INVALID_FD;
+ pfd->prev = pfd->next = NULL;
+}
+
+static void fdpool_unpool(struct pooled_fd *pfd) {
+ close(pfd->fd);
+ fdpool_clear(pfd);
+}
+
+static void fdpool_evict(void) {
+ struct pooled_fd *tail;
+
+ tail = fdpool_remove_tail();
+ fdpool_unpool(tail);
+}
+
+static void fdpool_pool(struct pooled_fd *pfd, int fd) {
+ if (fdpool_count >= FDPOOL_SIZE)
+ fdpool_evict();
+
+ fdpool_insert_head(pfd);
+ pfd->fd = fd;
+}
+
+static void fdpool_touch(struct pooled_fd *pfd) {
+ fdpool_remove(pfd);
+ fdpool_insert_head(pfd);
+}
+
+
+
+void fdpool_init(struct pooled_fd *pfd) {
+ fdpool_clear(pfd);
+}
+
+int fdpool_open(struct pooled_fd *pfd, const char *pathname, int flags) {
+ int open_errno;
+ int fd;
+
+ if (pfd->fd != INVALID_FD) {
+ fdpool_touch(pfd);
+ return pfd->fd;
+ }
+
+ fd = open(pathname, flags);
+ open_errno = errno;
+
+ if (fd >= 0) {
+ fdpool_pool(pfd, fd);
+ }
+
+ errno = open_errno;
+ return fd;
+}
+
+void fdpool_close(struct pooled_fd *pfd) {
+ assert(pfd);
+
+ fdpool_unpool(pfd);
+}
diff --git a/fatblock/fdpool.h b/fatblock/fdpool.h
new file mode 100644
index 0000000..85c7af3
--- /dev/null
+++ b/fatblock/fdpool.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FDPOOL_H
+#define FDPOOL_H
+
+struct pooled_fd {
+ struct pooled_fd *prev;
+ struct pooled_fd *next;
+ int fd;
+};
+
+void fdpool_init(struct pooled_fd *pfd);
+int fdpool_open(struct pooled_fd *pfd, const char *pathname, int flags);
+void fdpool_close(struct pooled_fd *pfd);
+
+#endif
diff --git a/fatblock/filedir.h b/fatblock/filedir.h
new file mode 100644
index 0000000..2c95b50
--- /dev/null
+++ b/fatblock/filedir.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FILEDIR_H
+#define FILEDIR_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "extent.h"
+#include "fdpool.h"
+#include "types.h"
+
+struct file {
+ struct extent extent;
+
+ char *path;
+ uint32_t size;
+ cluster_t first_cluster;
+
+ dev_t dev;
+ ino_t ino;
+ time_t mtime;
+
+ struct pooled_fd pfd;
+};
+
+struct dir {
+ struct extent extent;
+
+ char *path;
+ uint32_t size;
+ cluster_t first_cluster;
+
+ struct fat_dirent *entries;
+};
+
+#endif
diff --git a/fatblock/fs.c b/fatblock/fs.c
new file mode 100644
index 0000000..b603b13
--- /dev/null
+++ b/fatblock/fs.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <sys/endian.h>
+
+#include "extent.h"
+#include "errors.h"
+#include "fat.h"
+#include "fs.h"
+#include "types.h"
+#include "utils.h"
+
+#define DEFAULT_SECTOR_SIZE 512
+
+static void fs_add_extent(struct fs *fs, struct extent *extent,
+ offset_t start, offset_t len, int type)
+{
+ assert(fs);
+ assert(extent);
+
+ extent->start = start;
+ extent->len = len;
+ extent->type = type;
+
+ extent->next = fs->extents;
+ fs->extents = extent;
+}
+
+struct extent *fs_find_extent(struct fs *fs, offset_t start, offset_t len,
+ struct extent *last,
+ offset_t *r_start_out,
+ offset_t *e_start_out,
+ offset_t *len_out)
+{
+ struct extent *e;
+ offset_t end;
+ offset_t e_start, e_end, e_len, e_rel_start, r_rel_start, rel_len;
+
+ assert(fs);
+
+ end = start + len;
+
+ e = last ? last->next : fs->extents;
+ for (; e; e = e->next) {
+ e_start = e->start;
+ e_len = e->len;
+ e_end = e_start + e_len;
+
+ if (start >= e_end)
+ continue;
+
+ if (end <= e_start)
+ continue;
+
+ if (e_start <= start) {
+ r_rel_start = 0;
+ e_rel_start = start - e_start;
+ if (end <= e_end)
+ rel_len = len;
+ else
+ rel_len = e_end - start;
+ } else {
+ e_rel_start = 0;
+ r_rel_start = e_start - start;
+ if (e_end <= end)
+ rel_len = e_len;
+ else
+ rel_len = end - e_start;
+ }
+
+ assert(e_rel_start < e_len);
+ assert(e_rel_start + rel_len <= e_len);
+ assert(r_rel_start < len);
+ assert(r_rel_start + rel_len <= len);
+
+ if (r_start_out)
+ *r_start_out = r_rel_start;
+ if (e_start_out)
+ *e_start_out = e_rel_start;
+ if (len_out)
+ *len_out = rel_len;
+
+ return e;
+ }
+
+ return NULL;
+}
+
+static void fs_set_fat(struct fs *fs, cluster_t cluster, fat_entry_t entry)
+{
+ assert(fs);
+
+ fs->fat[cluster] = htole32(entry);
+}
+
+int fs_alloc_extent(struct fs *fs, struct extent *extent,
+ offset_t len, int type, cluster_t *first_cluster_out)
+{
+ assert(fs);
+ assert(extent);
+
+ cluster_t clusters_needed, start;
+ cluster_t i;
+
+ if (len == 0) {
+ extent->start = 0;
+ extent->len = 0;
+ extent->type = type;
+ *first_cluster_out = 0;
+ return 0;
+ }
+
+ clusters_needed = (len + fs->cluster_size - 1) / fs->cluster_size;
+
+ /* Check for adequate space. */
+ if (fs->next_cluster + clusters_needed > fs->num_clusters) {
+ WARN("allocating extent: filesystem is full!\n");
+ return -1;
+ }
+
+ /* Allocate clusters. */
+ start = fs->next_cluster;
+ fs->next_cluster += clusters_needed;
+
+ /* Update FAT. */
+ for (i = 0; i < clusters_needed - 1; i++) {
+ fs_set_fat(fs, start + i, start + i + 1);
+ }
+ fs_set_fat(fs, start + clusters_needed - 1, FAT_ENTRY_EOC);
+
+ *first_cluster_out = start;
+
+ fs_add_extent(fs,
+ extent,
+ fs->data_offset + (offset_t)(start - FAT_CLUSTER_ZERO)
+ * fs->cluster_size,
+ (offset_t)clusters_needed * fs->cluster_size,
+ type);
+
+ return 0;
+}
+
+int fs_init(struct fs *fs, uint16_t cluster_size, offset_t data_size, offset_t *total_size_out)
+{
+ uint16_t sector_size;
+ cluster_t data_clusters;
+ sector_t reserved_sectors, fat_sectors, data_sectors, total_sectors;
+ sector_t sectors_per_cluster;
+ int fat_entries_per_sector;
+ fat_entry_t *fat;
+ struct fat_boot_sector *bs;
+ struct fat_info_sector *is;
+
+ assert(fs);
+
+ sector_size = DEFAULT_SECTOR_SIZE;
+ fs->cluster_size = cluster_size;
+
+ sectors_per_cluster = cluster_size / DEFAULT_SECTOR_SIZE;
+ fat_entries_per_sector = sector_size / sizeof(fat_entry_t);
+
+ data_clusters = (data_size + cluster_size - 1) / cluster_size;
+ data_sectors = data_clusters * sectors_per_cluster;
+ fat_sectors = ((data_clusters + 2) + fat_entries_per_sector - 1)
+ / fat_entries_per_sector;
+ reserved_sectors = 3;
+ total_sectors = reserved_sectors + fat_sectors + data_sectors;
+
+ memset(&fs->boot, 0, sizeof(fs->boot));
+ bs = &fs->boot;
+
+ strpadcpy(bs->name, "FATBLOCK", ' ', sizeof(bs->name));
+ bs->sector_size = htole16(sector_size);
+ bs->sectors_per_cluster = sectors_per_cluster;
+ bs->reserved_sectors = htole16(reserved_sectors);
+ bs->fats = 1;
+ bs->media_desc = FAT_MEDIA_DESC_FIXED;
+ /* TODO: Calculate geometry? */
+ bs->sectors_per_track = htole16(42);
+ bs->heads = htole16(42);
+ bs->sectors32 = htole32(total_sectors);
+ bs->fat_sectors32 = htole32(fat_sectors);
+ /* bs->rootdir_start will be set later. */
+ bs->fs_info_sector = htole16(1);
+ bs->backup_boot_sector = htole16(2);
+ bs->phys_drive = FAT_PHYS_DRIVE_REMOVABLE;
+ bs->ext_boot_sig = FAT_EXT_BOOT_SIG;
+ bs->serial = 0x42424242;
+ strpadcpy(bs->vol_label, "FATBLOCK", ' ', sizeof(bs->vol_label));
+ strpadcpy(bs->type, "FAT32", ' ', sizeof(bs->type));
+ memcpy(bs->boot_sig, FAT_BOOT_SIG, sizeof(bs->boot_sig));
+
+ memset(&fs->info, 0, sizeof(fs->info));
+ is = &fs->info;
+
+ memcpy(is->info_sig1, FAT_INFO_SIG1, sizeof(is->info_sig1));
+ memcpy(is->info_sig2, FAT_INFO_SIG2, sizeof(is->info_sig2));
+ is->free_clusters = htole32(-1);
+ is->last_cluster = htole32(FAT_CLUSTER_ZERO);
+ memcpy(is->info_sig3, FAT_INFO_SIG3, sizeof(is->info_sig3));
+
+ fs->num_clusters = FAT_CLUSTER_ZERO + data_clusters;
+ fs->next_cluster = FAT_CLUSTER_ZERO;
+
+ fs->fat_size = fat_sectors * sector_size;
+ fs->fat = malloc(fs->fat_size);
+ if (!fs->fat) {
+ WARN("initializing filesystem: couldn't allocate FAT extent: out of memory\n");
+ return MALLOC_FAIL;
+ }
+ memset(fs->fat, 0, fs->fat_size);
+
+ fs->data_offset = (reserved_sectors + fat_sectors) * sector_size;
+
+ fs->extents = NULL;
+ fs_add_extent(fs, &fs->boot_extent,
+ 0, sector_size,
+ EXTENT_TYPE_BOOT);
+ fs_add_extent(fs, &fs->info_extent,
+ sector_size, sector_size,
+ EXTENT_TYPE_INFO);
+ fs_add_extent(fs, &fs->backup_boot_extent,
+ 2 * sector_size, sector_size,
+ EXTENT_TYPE_BOOT);
+ fs_add_extent(fs, &fs->fat_extent,
+ reserved_sectors * sector_size, fs->fat_size,
+ EXTENT_TYPE_FAT);
+
+ *total_size_out = (offset_t)total_sectors * sector_size;
+
+ return 0;
+}
+
+void fs_set_rootdir_start(struct fs *fs, cluster_t rootdir_start)
+{
+ assert(fs);
+
+ fs->boot.rootdir_start = htole32(rootdir_start);
+}
+
+void fs_update_free_clusters(struct fs *fs)
+{
+ assert(fs);
+
+ fs->info.free_clusters = htole32(fs->num_clusters - fs->next_cluster);
+}
diff --git a/fatblock/fs.h b/fatblock/fs.h
new file mode 100644
index 0000000..c58decc
--- /dev/null
+++ b/fatblock/fs.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FS_H
+#define FS_H
+
+#include "extent.h"
+#include "fat.h"
+#include "types.h"
+
+struct fs {
+ uint16_t cluster_size;
+
+ cluster_t num_clusters;
+ cluster_t next_cluster;
+ struct extent *extents;
+
+ struct fat_boot_sector boot;
+ struct extent boot_extent;
+ struct extent backup_boot_extent;
+
+ struct fat_info_sector info;
+ struct extent info_extent;
+
+ struct extent fat_extent;
+ fat_entry_t *fat;
+ offset_t fat_size;
+
+ offset_t data_offset;
+};
+
+
+int fs_alloc_extent(struct fs *fs, struct extent *extent,
+ offset_t len, int type, cluster_t *first_cluster_out);
+struct extent *fs_find_extent(struct fs *fs, offset_t start, offset_t len, struct extent *last,
+ offset_t *r_start_out, offset_t *e_start_out, offset_t *len_out);
+int fs_init(struct fs *fs, uint16_t cluster_size, offset_t data_size, offset_t *total_size_out);
+void fs_set_rootdir_start(struct fs *fs, cluster_t rootdir_start);
+void fs_update_free_clusters(struct fs *fs);
+
+#endif
diff --git a/fatblock/import.c b/fatblock/import.c
new file mode 100644
index 0000000..4697819
--- /dev/null
+++ b/fatblock/import.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "errors.h"
+#include "extent.h"
+#include "fat.h"
+#include "fdpool.h"
+#include "filedir.h"
+#include "fs.h"
+#include "import.h"
+#include "utils.h"
+
+static inline int valid_char(int c)
+{
+ return (isalnum(c) || strchr("!#$%'()-@^_`{}~", c) || ((c >= 128) && (c < 256)));
+}
+
+static int convert_name(char *short_name, const char *long_name)
+{
+ int i;
+
+ const char *s;
+ const char *dot;
+ int c;
+
+ dot = NULL;
+
+ for (s = long_name; *s; s++) {
+ if (*s == '.') {
+ if (dot) {
+ goto short_fail;
+ } else {
+ dot = s;
+ }
+ } else if (!valid_char(*s)) {
+ goto short_fail;
+ }
+ }
+
+ if (dot - long_name > 8) {
+ goto short_fail;
+ }
+
+ if (dot && (s - (dot + 1) > 3)) {
+ goto short_fail;
+ }
+
+ memset(short_name, ' ', 11);
+
+ if (!dot) {
+ dot = s;
+ }
+
+ for (i = 0; i < dot - long_name; i++) {
+ short_name[i] = toupper(long_name[i]);
+ }
+
+ for (i = 0; i < s - dot; i++) {
+ short_name[8 + i] = toupper(dot[1 + i]);
+ }
+
+ return 0;
+
+short_fail:
+ return 1;
+}
+
+struct imported {
+ cluster_t first_cluster;
+ uint32_t size;
+ struct fat_dirent *dot_dot_dirent;
+};
+
+static int import_file(struct fs *fs, char *path, struct imported *out)
+{
+ struct stat st;
+ struct file *f = NULL;
+ char *path_copy = NULL;
+ int ret;
+
+ ret = stat(path, &st);
+ if (ret < 0) {
+ WARN("importing %s: stat failed: %s\n", path, strerror(errno));
+ goto fail;
+ }
+
+ f = malloc(sizeof(struct file));
+ if (!f) {
+ WARN("importing %s: couldn't allocate file struct: out of memory\n", path);
+ ret = MALLOC_FAIL;
+ goto fail;
+ }
+
+ path_copy = strdup(path);
+ if (!path_copy) {
+ WARN("importing %s: couldn't strdup path: out of memory\n", path);
+ ret = MALLOC_FAIL;
+ goto fail;
+ }
+
+ f->path = path_copy;
+ f->size = st.st_size;
+ f->dev = st.st_dev;
+ f->ino = st.st_ino;
+ f->mtime = st.st_mtime;
+ fdpool_init(&f->pfd);
+
+ ret = fs_alloc_extent(fs, &f->extent,
+ f->size, EXTENT_TYPE_FILE, &f->first_cluster);
+ if (ret) {
+ WARN("importing %s: couldn't allocate data extent\n", path);
+ goto fail;
+ }
+
+ out->first_cluster = f->first_cluster;
+ out->size = f->size;
+ out->dot_dot_dirent = NULL;
+
+ return 0;
+
+fail:
+ if (path_copy)
+ free(path_copy);
+ if (f)
+ free(f);
+ return ret;
+}
+
+struct item {
+ char name[11];
+ struct imported imp;
+ struct item *next;
+ int is_dir;
+};
+
+static struct item *free_items_head;
+
+static struct item *alloc_item(void)
+{
+ struct item *item;
+
+ if (free_items_head) {
+ item = free_items_head;
+ free_items_head = item->next;
+ } else {
+ item = malloc(sizeof(struct item));
+ /* May return NULL if item couldn't be allocated. */
+ }
+
+ return item;
+}
+
+static void free_item(struct item *item)
+{
+ item->next = free_items_head;
+ free_items_head = item;
+}
+
+static void free_items(struct item *head)
+{
+ struct item *tail;
+
+ for (tail = head; tail->next; tail = tail->next);
+
+ tail->next = free_items_head;
+ free_items_head = head;
+}
+
+/* TODO: With some work, this can be rewritten so we don't recurse
+ * until all memory is allocated. */
+static int import_dir(struct fs *fs, char *path, int is_root, struct imported *out)
+{
+ struct dir *d;
+ cluster_t first_cluster;
+
+ DIR *dir;
+ struct dirent *de;
+
+ char ch_path[PATH_MAX];
+ struct imported *ch_imp;
+ cluster_t ch_first_cluster;
+ struct fat_dirent *ch_dirent;
+
+ int ret;
+
+ struct item *items;
+ struct item *item;
+ int count;
+
+ int i;
+
+ dir = opendir(path);
+ if (!dir) {
+ WARN("importing %s: opendir failed: %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ d = malloc(sizeof(struct dir));
+ if (!d) {
+ WARN("importing %s: couldn't allocate dir struct: out of memory\n", path);
+ closedir(dir);
+ return MALLOC_FAIL;
+ }
+
+ d->path = strdup(path);
+ if (!d->path) {
+ WARN("importing %s: couldn't strdup path: out of memory\n", path);
+ closedir(dir);
+ free(d);
+ return MALLOC_FAIL;
+ }
+
+ items = NULL;
+ item = NULL;
+ count = 0;
+
+ while ((de = readdir(dir))) {
+ if (de->d_name[0] == '.') {
+ goto skip_item;
+ }
+
+ ret = snprintf(ch_path, PATH_MAX, "%s/%s", path, de->d_name);
+ if (ret < 0 || ret >= PATH_MAX) {
+ goto skip_item;
+ }
+
+ item = alloc_item();
+ if (!item) {
+ WARN("importing %s: couldn't allocate item struct: out of memory\n", path);
+ ret = MALLOC_FAIL;
+ goto free_items;
+ }
+
+ if (convert_name(item->name, de->d_name)) {
+ goto skip_item;
+ }
+
+ switch (de->d_type) {
+ case DT_REG:
+ import_file(fs, ch_path, &item->imp);
+ item->is_dir = 0;
+ break;
+ case DT_DIR:
+ import_dir(fs, ch_path, 0, &item->imp);
+ item->is_dir = 1;
+ break;
+ default:
+ goto skip_item;
+ }
+
+ item->next = items;
+ items = item;
+
+ count++;
+
+ item = NULL;
+
+ continue;
+
+skip_item:
+ if (item)
+ free_item(item);
+ }
+
+ closedir(dir);
+
+ d->size = sizeof(struct fat_dirent) * (count + (is_root ? 0 : 2));
+ ret = fs_alloc_extent(fs, &d->extent, d->size, EXTENT_TYPE_DIR, &d->first_cluster);
+ if (ret) {
+ WARN("importing %s: couldn't allocate directory table extent: out of space\n", path);
+ goto free_items;
+ }
+
+ first_cluster = is_root ? 0 : d->first_cluster;
+
+ d->entries = malloc(sizeof(struct fat_dirent) * (count + (is_root ? 0 : 2)));
+ assert(d->entries);
+ for (i = count - 1; i >= 0; i--) {
+ item = items;
+ items = item->next;
+
+ ch_dirent = &d->entries[i + (is_root ? 0 : 2)];
+
+ fat_dirent_set(ch_dirent,
+ item->name, item->is_dir ? FAT_ATTR_SUBDIR : 0,
+ item->imp.first_cluster, item->imp.size);
+
+ if (item->imp.dot_dot_dirent) {
+ fat_dirent_set_first_cluster(item->imp.dot_dot_dirent, first_cluster);
+ }
+
+ free_item(item);
+ }
+
+ if (!is_root) {
+ fat_dirent_set(&d->entries[0],
+ ".. ", FAT_ATTR_SUBDIR,
+ (cluster_t)-1, 0);
+ out->dot_dot_dirent = &d->entries[0]; /* will set first_cluster */
+
+ fat_dirent_set(&d->entries[1],
+ ". ", FAT_ATTR_SUBDIR,
+ first_cluster, 0);
+ } else {
+ out->dot_dot_dirent = NULL;
+ }
+
+ out->first_cluster = d->first_cluster;
+ out->size = 0;
+
+ return 0;
+
+free_items:
+ free_items(items);
+ free(d->path);
+ free(d);
+
+ return ret;
+}
+
+int import_tree(struct fs *fs, char *path)
+{
+ struct imported imp;
+ int ret;
+
+ ret = import_dir(fs, path, 0, &imp);
+ if (ret)
+ return ret;
+
+ fs_set_rootdir_start(fs, imp.first_cluster);
+ fs_update_free_clusters(fs);
+
+ return 0;
+}
diff --git a/fatblock/import.h b/fatblock/import.h
new file mode 100644
index 0000000..e1f90c4
--- /dev/null
+++ b/fatblock/import.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IMPORT_H
+#define IMPORT_H
+
+#include "types.h"
+
+int import_tree(struct fs *fs, char *path);
+
+#endif
diff --git a/fatblock/read.c b/fatblock/read.c
new file mode 100644
index 0000000..ca7deea
--- /dev/null
+++ b/fatblock/read.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "errors.h"
+#include "filedir.h"
+#include "fs.h"
+#include "utils.h"
+
+static int buffer_read(char *buf, offset_t buf_len, char *out, offset_t off, offset_t len) {
+ assert(buf);
+ assert(out);
+
+ if (off >= buf_len) {
+ memset(out, 0, len);
+ return 0;
+ }
+
+ if (off + len > buf_len) {
+ memset(out + (buf_len - off), 0, len - (buf_len - off));
+ len = buf_len - off;
+ }
+
+ assert(off < buf_len);
+ assert(off + len <= buf_len);
+
+ memcpy(out, buf + off, len);
+
+ return 0;
+}
+
+static int file_check_metadata(struct file *f) {
+ struct stat st;
+ int ret;
+
+ assert(f);
+
+ ret = stat(f->path, &st);
+ if (ret) {
+ WARN("checking metadata of %s: stat failed: %s\n", f->path, strerror(errno));
+ return -1;
+ }
+
+ if (f->mtime != st.st_mtime)
+ return -1;
+
+ return 0;
+}
+
+static int file_read(struct file *f, char *buf, offset_t off, offset_t len) {
+ int fd;
+ off_t sought;
+ ssize_t ret;
+
+ assert(f);
+ assert(buf);
+
+ if (off >= UINT32_MAX) {
+ WARN("reading %s (%llu, %llu): ignoring read that starts past 2^32\n", f->path, off, len);
+ return 0;
+ }
+
+ if (off + len > UINT32_MAX) {
+ WARN("reading %s (%llu, %llu): truncating read that ends past 2^32\n", f->path, off, len);
+ len = UINT32_MAX - off;
+ }
+
+ if (file_check_metadata(f)) {
+ WARN("reading %s (%llu, %llu): metadata has changed\n", f->path, off, len);
+ return SKY_IS_FALLING;
+ }
+
+ fd = fdpool_open(&f->pfd, f->path, O_RDONLY);
+ if (fd < 0) {
+ WARN("reading %s: open failed: %s\n", f->path, strerror(errno));
+ return -1;
+ }
+
+ sought = lseek(fd, (off_t)len, SEEK_SET);
+ if (sought != (off_t)len) {
+ WARN("reading %s (%llu, %llu): seek failed: %s\n", f->path, off, len, strerror(errno));
+ return -1;
+ }
+
+ ret = read(fd, buf, (size_t)len);
+ if (ret != (ssize_t)len) {
+ WARN("reading %s (%llu, %llu): read failed: %s\n", f->path, off, len, strerror(errno));
+ return -1;
+ }
+
+ /* leave fd open; fdpool will close it if needed. */
+
+ return 0;
+}
+
+static int dir_read(struct dir *d, char *buf, offset_t off, offset_t len) {
+ assert(d);
+ assert(buf);
+
+ return buffer_read((char*)d->entries, d->size, buf, off, len);
+}
+
+static int extent_read(struct fs *fs, struct extent *e, char *buf, offset_t off, offset_t len) {
+ assert(fs);
+ assert(e);
+ assert(buf);
+
+ switch (e->type) {
+ case EXTENT_TYPE_BOOT:
+ return buffer_read((char*)&fs->boot, sizeof(fs->boot), buf, off, len);
+ case EXTENT_TYPE_INFO:
+ return buffer_read((char*)&fs->info, sizeof(fs->info), buf, off, len);
+ case EXTENT_TYPE_FAT:
+ return buffer_read((char*)fs->fat, fs->fat_size, buf, off, len);
+ case EXTENT_TYPE_FILE:
+ return file_read((struct file *)e, buf, off, len);
+ case EXTENT_TYPE_DIR:
+ return dir_read((struct dir *)e, buf, off, len);
+ default:
+ WARN("reading extent: unexpected type %d\n", e->type);
+ return -1;
+ }
+}
+
+int fs_read(struct fs *fs, char *buf, offset_t start, offset_t len) {
+ struct extent *e = NULL;
+ offset_t e_start, r_start, rel_len;
+ int ret;
+
+ memset(buf, 0, len);
+
+ while ((e = fs_find_extent(fs, start, len, e, &r_start, &e_start, &rel_len))) {
+ ret = extent_read(fs, e, buf + r_start, e_start, rel_len);
+ if (ret == SKY_IS_FALLING)
+ return SKY_IS_FALLING;
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/fatblock/read.h b/fatblock/read.h
new file mode 100644
index 0000000..f5f68a1
--- /dev/null
+++ b/fatblock/read.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef READ_H
+#define READ_H
+
+#include "types.h"
+
+struct fs;
+
+int fs_read(struct fs *fs, char *buf, offset_t start, offset_t len);
+
+#endif
diff --git a/fatblock/types.h b/fatblock/types.h
new file mode 100644
index 0000000..4119039
--- /dev/null
+++ b/fatblock/types.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPES_H
+#define TYPES_H
+
+#include <stdint.h>
+
+struct fs;
+
+typedef uint64_t offset_t;
+typedef uint32_t cluster_t;
+
+#endif
diff --git a/fatblock/utils.c b/fatblock/utils.c
new file mode 100644
index 0000000..20fa83b
--- /dev/null
+++ b/fatblock/utils.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+void strpadcpy(char *d, const char *s, char p, size_t l)
+{
+ while (*s && l-- > 0)
+ *d++ = *s++;
+
+ while (l-- > 0)
+ *d++ = ' ';
+}
+
+void warn(char *msg)
+{
+ fprintf(stderr, "%s", msg);
+}
+
+void die(char *msg)
+{
+ fprintf(stderr, "%s", msg);
+ exit(EXIT_FAILURE);
+}
diff --git a/fatblock/utils.h b/fatblock/utils.h
new file mode 100644
index 0000000..7fe4b88
--- /dev/null
+++ b/fatblock/utils.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define EPRINT(...) fprintf(stderr, __VA_ARGS__)
+#define DIE(...) do { EPRINT(__VA_ARGS__); exit(EXIT_FAILURE); } while (0)
+#define WARN(...) EPRINT(__VA_ARGS__)
+#define INFO(...) EPRINT(__VA_ARGS__)
+#define DEBUG(...) EPRINT(__VA_ARGS__)
+
+void strpadcpy(char *d, const char *s, char p, size_t l);
+void warn(char *msg);
+void die(char *msg);
+
+#endif