/* * 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 "fatblock.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; }