blob: fbee76c0efbde876daac8fb3fc8cb1fd0dc17330 [file] [log] [blame]
David Zeuthen8b6973b2016-09-20 12:39:49 -04001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25#include <errno.h>
26#include <fcntl.h>
27#include <linux/fs.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/ioctl.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <unistd.h>
34
35#include <cutils/properties.h>
36#include <fs_mgr.h>
37
38#include "avb_ops_device.h"
39
40/* Open the appropriate fstab file and fallback to /fstab.device if
41 * that's what's being used.
42 */
43static struct fstab* open_fstab(void) {
44 char propbuf[PROPERTY_VALUE_MAX];
45 char fstab_name[PROPERTY_VALUE_MAX + 32];
46 struct fstab* fstab;
47
48 property_get("ro.hardware", propbuf, "");
49 snprintf(fstab_name, sizeof(fstab_name), "/fstab.%s", propbuf);
50 fstab = fs_mgr_read_fstab(fstab_name);
David Zeuthen4b6a6342017-01-03 15:19:56 -050051 if (fstab != NULL) {
52 return fstab;
53 }
David Zeuthen8b6973b2016-09-20 12:39:49 -040054
55 fstab = fs_mgr_read_fstab("/fstab.device");
56 return fstab;
57}
58
59static int open_partition(const char* name, int flags) {
60 char* path;
61 int fd;
62 struct fstab* fstab;
63 struct fstab_rec* record;
64
65 /* We can't use fs_mgr to look up |name| because fstab doesn't list
66 * every slot partition (it uses the slotselect option to mask the
67 * suffix) and |slot| is expected to be of that form, e.g. boot_a.
68 *
69 * We can however assume that there's an entry for the /misc mount
70 * point and use that to get the device file for the misc
71 * partition. From there we'll assume that a by-name scheme is used
72 * so we can just replace the trailing "misc" by the given |name|,
73 * e.g.
74 *
75 * /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
76 * /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a
77 *
78 * If needed, it's possible to relax this assumption in the future
79 * by trawling /sys/block looking for the appropriate sibling of
80 * misc and then finding an entry in /dev matching the sysfs entry.
81 */
82
83 fstab = open_fstab();
84 if (fstab == NULL) {
85 return -1;
86 }
87 record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
88 if (record == NULL) {
89 fs_mgr_free_fstab(fstab);
90 return -1;
91 }
92 if (strcmp(name, "misc") == 0) {
93 path = strdup(record->blk_device);
94 } else {
95 size_t trimmed_len, name_len;
96 const char* end_slash = strrchr(record->blk_device, '/');
97 if (end_slash == NULL) {
98 fs_mgr_free_fstab(fstab);
99 return -1;
100 }
101 trimmed_len = end_slash - record->blk_device + 1;
102 name_len = strlen(name);
103 path = calloc(trimmed_len + name_len + 1, 1);
104 strncpy(path, record->blk_device, trimmed_len);
105 strncpy(path + trimmed_len, name, name_len);
106 }
107 fs_mgr_free_fstab(fstab);
108
109 fd = open(path, flags);
110 free(path);
111
112 return fd;
113}
114
David Zeuthen4b6a6342017-01-03 15:19:56 -0500115static AvbIOResult read_from_partition(AvbOps* ops,
116 const char* partition,
117 int64_t offset,
118 size_t num_bytes,
119 void* buffer,
120 size_t* out_num_read) {
David Zeuthen8b6973b2016-09-20 12:39:49 -0400121 int fd;
122 off_t where;
123 ssize_t num_read;
124 AvbIOResult ret;
125
126 fd = open_partition(partition, O_RDONLY);
127 if (fd == -1) {
128 avb_errorv("Error opening \"", partition, "\" partition.\n", NULL);
129 ret = AVB_IO_RESULT_ERROR_IO;
130 goto out;
131 }
132
133 where = lseek(fd, offset, SEEK_SET);
134 if (where == -1) {
135 avb_error("Error seeking to offset.\n");
136 ret = AVB_IO_RESULT_ERROR_IO;
137 goto out;
138 }
139 if (where != offset) {
140 avb_error("Error seeking to offset.\n");
141 ret = AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION;
142 goto out;
143 }
144
145 /* On Linux, we never get partial reads from block devices (except
146 * for EOF).
147 */
148 num_read = read(fd, buffer, num_bytes);
149 if (num_read == -1) {
150 avb_error("Error reading data.\n");
151 ret = AVB_IO_RESULT_ERROR_IO;
152 goto out;
153 }
154 if (out_num_read != NULL) {
155 *out_num_read = num_read;
156 }
157
158 ret = AVB_IO_RESULT_OK;
159
160out:
161 if (fd != -1) {
162 if (close(fd) != 0) {
163 avb_error("Error closing file descriptor.\n");
164 }
165 }
166 return ret;
167}
168
David Zeuthen4b6a6342017-01-03 15:19:56 -0500169static AvbIOResult write_to_partition(AvbOps* ops,
170 const char* partition,
171 int64_t offset,
172 size_t num_bytes,
David Zeuthen8b6973b2016-09-20 12:39:49 -0400173 const void* buffer) {
174 int fd;
175 off_t where;
176 ssize_t num_written;
177 AvbIOResult ret;
178
179 fd = open_partition(partition, O_WRONLY);
180 if (fd == -1) {
181 avb_errorv("Error opening \"", partition, "\" partition.\n", NULL);
182 ret = AVB_IO_RESULT_ERROR_IO;
183 goto out;
184 }
185
186 where = lseek(fd, offset, SEEK_SET);
187 if (where == -1) {
188 avb_error("Error seeking to offset.\n");
189 ret = AVB_IO_RESULT_ERROR_IO;
190 goto out;
191 }
192 if (where != offset) {
193 avb_error("Error seeking to offset.\n");
194 ret = AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION;
195 goto out;
196 }
197
198 /* On Linux, we never get partial writes on block devices. */
199 num_written = write(fd, buffer, num_bytes);
200 if (num_written == -1) {
201 avb_error("Error writing data.\n");
202 ret = AVB_IO_RESULT_ERROR_IO;
203 goto out;
204 }
205
206 ret = AVB_IO_RESULT_OK;
207
208out:
209 if (fd != -1) {
210 if (close(fd) != 0) {
211 avb_error("Error closing file descriptor.\n");
212 }
213 }
214 return ret;
215}
216
David Zeuthenbaf59e22016-11-14 15:39:43 -0500217AvbABOps* avb_ops_device_new(void) {
218 AvbABOps* ab_ops;
David Zeuthen8b6973b2016-09-20 12:39:49 -0400219
David Zeuthenbaf59e22016-11-14 15:39:43 -0500220 ab_ops = calloc(1, sizeof(AvbABOps));
221 if (ab_ops == NULL) {
David Zeuthen574ed992017-01-05 15:43:11 -0500222 avb_error("Error allocating memory for AvbABOps.\n");
223 goto out;
224 }
225
226 ab_ops->ops = calloc(1, sizeof(AvbOps));
227 if (ab_ops->ops == NULL) {
David Zeuthen8b6973b2016-09-20 12:39:49 -0400228 avb_error("Error allocating memory for AvbOps.\n");
David Zeuthen574ed992017-01-05 15:43:11 -0500229 free(ab_ops);
David Zeuthen8b6973b2016-09-20 12:39:49 -0400230 goto out;
231 }
232
David Zeuthenbaf59e22016-11-14 15:39:43 -0500233 /* We only need these operations since that's all what is being used
234 * by the A/B routines.
David Zeuthen8b6973b2016-09-20 12:39:49 -0400235 */
David Zeuthen574ed992017-01-05 15:43:11 -0500236 ab_ops->ops->read_from_partition = read_from_partition;
237 ab_ops->ops->write_to_partition = write_to_partition;
David Zeuthenbaf59e22016-11-14 15:39:43 -0500238 ab_ops->read_ab_metadata = avb_ab_data_read;
239 ab_ops->write_ab_metadata = avb_ab_data_write;
David Zeuthen8b6973b2016-09-20 12:39:49 -0400240
241out:
David Zeuthenbaf59e22016-11-14 15:39:43 -0500242 return ab_ops;
David Zeuthen8b6973b2016-09-20 12:39:49 -0400243}
244
David Zeuthen4b6a6342017-01-03 15:19:56 -0500245void avb_ops_device_free(AvbABOps* ab_ops) {
David Zeuthen574ed992017-01-05 15:43:11 -0500246 free(ab_ops->ops);
David Zeuthen4b6a6342017-01-03 15:19:56 -0500247 free(ab_ops);
248}