read.c revision f79b2dff1024db4f6326f3422236bed169dd902f
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <assert.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <string.h>
21#include <unistd.h>
22#include <sys/stat.h>
23
24#include "errors.h"
25#include "filedir.h"
26#include "fs.h"
27#include "utils.h"
28
29static int buffer_read(char *buf, offset_t buf_len, char *out, offset_t off, offset_t len) {
30	assert(buf);
31	assert(out);
32
33	if (off >= buf_len) {
34		memset(out, 0, len);
35		return 0;
36	}
37
38	if (off + len > buf_len) {
39		memset(out + (buf_len - off), 0, len - (buf_len - off));
40		len = buf_len - off;
41	}
42
43	assert(off < buf_len);
44	assert(off + len <= buf_len);
45
46	memcpy(out, buf + off, len);
47
48	return 0;
49}
50
51static int file_check_metadata(struct file *f) {
52	struct stat st;
53	int ret;
54
55	assert(f);
56
57	ret = stat(f->path, &st);
58	if (ret) {
59		WARN("checking metadata of %s: stat failed: %s\n", f->path, strerror(errno));
60		return -1;
61	}
62
63	if (f->mtime != st.st_mtime)
64		return -1;
65
66	return 0;
67}
68
69static int file_read(struct file *f, char *buf, offset_t off, offset_t len) {
70	int fd;
71	off_t sought;
72	ssize_t ret;
73
74	assert(f);
75	assert(buf);
76
77	if (off >= UINT32_MAX) {
78		WARN("reading %s (%llu, %llu): ignoring read that starts past 2^32\n", f->path, off, len);
79		return 0;
80	}
81
82	if (off + len > UINT32_MAX) {
83		WARN("reading %s (%llu, %llu): truncating read that ends past 2^32\n", f->path, off, len);
84		len = UINT32_MAX - off;
85	}
86
87	if (file_check_metadata(f)) {
88		WARN("reading %s (%llu, %llu): metadata has changed\n", f->path, off, len);
89		return SKY_IS_FALLING;
90	}
91
92	fd = fdpool_open(&f->pfd, f->path, O_RDONLY);
93	if (fd < 0) {
94		WARN("reading %s: open failed: %s\n", f->path, strerror(errno));
95		return -1;
96	}
97
98	sought = lseek(fd, (off_t)len, SEEK_SET);
99	if (sought != (off_t)len) {
100		WARN("reading %s (%llu, %llu): seek failed: %s\n", f->path, off, len, strerror(errno));
101		return -1;
102	}
103
104	ret = read(fd, buf, (size_t)len);
105	if (ret != (ssize_t)len) {
106		WARN("reading %s (%llu, %llu): read failed: %s\n", f->path, off, len, strerror(errno));
107		return -1;
108	}
109
110	/* leave fd open; fdpool will close it if needed. */
111
112	return 0;
113}
114
115static int dir_read(struct dir *d, char *buf, offset_t off, offset_t len) {
116	assert(d);
117	assert(buf);
118
119	return buffer_read((char*)d->entries, d->size, buf, off, len);
120}
121
122static int extent_read(struct fs *fs, struct extent *e, char *buf, offset_t off, offset_t len) {
123	assert(fs);
124	assert(e);
125	assert(buf);
126
127	switch (e->type) {
128	case EXTENT_TYPE_BOOT:
129		return buffer_read((char*)&fs->boot, sizeof(fs->boot), buf, off, len);
130	case EXTENT_TYPE_INFO:
131		return buffer_read((char*)&fs->info, sizeof(fs->info), buf, off, len);
132	case EXTENT_TYPE_FAT:
133		return buffer_read((char*)fs->fat, fs->fat_size, buf, off, len);
134	case EXTENT_TYPE_FILE:
135		return file_read((struct file *)e, buf, off, len);
136	case EXTENT_TYPE_DIR:
137		return dir_read((struct dir *)e, buf, off, len);
138	default:
139		WARN("reading extent: unexpected type %d\n", e->type);
140		return -1;
141	}
142}
143
144int fs_read(struct fs *fs, char *buf, offset_t start, offset_t len) {
145	struct extent *e = NULL;
146	offset_t e_start, r_start, rel_len;
147	int ret;
148
149	memset(buf, 0, len);
150
151	while ((e = fs_find_extent(fs, start, len, e, &r_start, &e_start, &rel_len))) {
152		ret = extent_read(fs, e, buf + r_start, e_start, rel_len);
153		if (ret == SKY_IS_FALLING)
154			return SKY_IS_FALLING;
155		if (ret)
156			return ret;
157	}
158
159	return 0;
160}
161