read.c revision 0cb61411eadd92b7202e045c1c9ec8df7fb636fe
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 "fatblock.h"
25#include "fs.h"
26#include "utils.h"
27
28static int buffer_read(char *buf, offset_t buf_len, char *out,
29		       offset_t off, offset_t len)
30{
31	assert(buf);
32	assert(out);
33
34	if (off >= buf_len) {
35		memset(out, 0, len);
36		return 0;
37	}
38
39	if (off + len > buf_len) {
40		memset(out + (buf_len - off), 0, len - (buf_len - off));
41		len = buf_len - off;
42	}
43
44	assert(off < buf_len);
45	assert(off + len <= buf_len);
46
47	memcpy(out, buf + off, len);
48
49	return 0;
50}
51
52static int file_check_metadata(struct file *f)
53{
54	struct stat st;
55	int ret;
56
57	assert(f);
58
59	ret = stat(f->path, &st);
60	if (ret) {
61		WARN("checking metadata of %s: stat failed: %s\n",
62		     f->path, strerror(errno));
63		return -1;
64	}
65
66	if (f->mtime != st.st_mtime)
67		return -1;
68
69	return 0;
70}
71
72static int file_read(struct file *f, char *buf, offset_t off, offset_t len)
73{
74	int fd;
75	off_t sought;
76	ssize_t ret;
77
78	assert(f);
79	assert(buf);
80
81	if (off >= UINT32_MAX) {
82		WARN("reading %s (%llu, %llu): "
83		     "ignoring read that starts past 2^32\n",
84		     f->path, off, len);
85		return 0;
86	}
87
88	if (off + len > UINT32_MAX) {
89		WARN("reading %s (%llu, %llu): "
90		     "truncating read that ends past 2^32\n",
91		     f->path, off, len);
92		len = UINT32_MAX - off;
93	}
94
95	if (file_check_metadata(f)) {
96		WARN("reading %s (%llu, %llu): metadata has changed\n",
97		     f->path, off, len);
98		return SKY_IS_FALLING;
99	}
100
101	fd = fdpool_open(&f->pfd, f->path, O_RDONLY);
102	if (fd < 0) {
103		WARN("reading %s: open failed: %s\n", f->path, strerror(errno));
104		return -1;
105	}
106
107	sought = lseek(fd, (off_t)len, SEEK_SET);
108	if (sought != (off_t)len) {
109		WARN("reading %s (%llu, %llu): seek failed: %s\n",
110		     f->path, off, len, strerror(errno));
111		return -1;
112	}
113
114	ret = read(fd, buf, (size_t)len);
115	if (ret != (ssize_t)len) {
116		WARN("reading %s (%llu, %llu): read failed: %s\n",
117		     f->path, off, len, strerror(errno));
118		return -1;
119	}
120
121	/* leave fd open; fdpool will close it if needed. */
122
123	return 0;
124}
125
126static int dir_read(struct dir *d, char *buf, offset_t off, offset_t len)
127{
128	assert(d);
129	assert(buf);
130
131	return buffer_read((char*)d->entries, d->size, buf, off, len);
132}
133
134static int extent_read(struct fs *fs, struct extent *e, char *buf,
135		       offset_t off, offset_t len)
136{
137	assert(fs);
138	assert(e);
139	assert(buf);
140
141	switch (e->type) {
142	case EXTENT_TYPE_BOOT:
143		return buffer_read((char*)&fs->boot, sizeof(fs->boot), buf,
144				   off, len);
145	case EXTENT_TYPE_INFO:
146		return buffer_read((char*)&fs->info, sizeof(fs->info), buf,
147				   off, len);
148	case EXTENT_TYPE_FAT:
149		return buffer_read((char*)fs->fat, fs->fat_size, buf,
150				   off, len);
151	case EXTENT_TYPE_FILE:
152		return file_read((struct file *)e, buf, off, len);
153	case EXTENT_TYPE_DIR:
154		return dir_read((struct dir *)e, buf, off, len);
155	default:
156		WARN("reading extent: unexpected type %d\n", e->type);
157		return -1;
158	}
159}
160
161int fs_read(struct fs *fs, char *buf, offset_t start, offset_t len)
162{
163	struct extent *e = NULL;
164	offset_t e_start, r_start, rel_len;
165	int ret;
166
167	memset(buf, 0, len);
168
169	while ((e = fs_find_extent(fs, start, len, e,
170				   &r_start, &e_start, &rel_len))) {
171		ret = extent_read(fs, e, buf + r_start, e_start, rel_len);
172		if (ret == SKY_IS_FALLING)
173			return SKY_IS_FALLING;
174		if (ret)
175			return ret;
176	}
177
178	return 0;
179}
180