diff options
author | Thomas Tuttle <ttuttle@google.com> | 2010-07-19 10:28:04 -0700 |
---|---|---|
committer | Thomas Tuttle <ttuttle@google.com> | 2010-08-02 16:37:36 -0700 |
commit | f79b2dff1024db4f6326f3422236bed169dd902f (patch) | |
tree | f07dbc63c11eaf0abbb355f90aadf947b195376e /fatblock/read.c | |
parent | 2045e6e39d6dbfed1e46c333c2910c66e788cd36 (diff) | |
download | extras-f79b2dff1024db4f6326f3422236bed169dd902f.tar.gz |
fatblock: Program to offer dir as FAT32 filesystem using ublock
Change-Id: I6712e062e17b02c453ce89a52000cd8bc3ee810d
Diffstat (limited to 'fatblock/read.c')
-rw-r--r-- | fatblock/read.c | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/fatblock/read.c b/fatblock/read.c new file mode 100644 index 00000000..ca7deead --- /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; +} |