blob: 8e7a7ec0722a39d26a9b5a264acf183295121d48 [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);
51 if (fstab != NULL) return fstab;
52
53 fstab = fs_mgr_read_fstab("/fstab.device");
54 return fstab;
55}
56
57static int open_partition(const char* name, int flags) {
58 char* path;
59 int fd;
60 struct fstab* fstab;
61 struct fstab_rec* record;
62
63 /* We can't use fs_mgr to look up |name| because fstab doesn't list
64 * every slot partition (it uses the slotselect option to mask the
65 * suffix) and |slot| is expected to be of that form, e.g. boot_a.
66 *
67 * We can however assume that there's an entry for the /misc mount
68 * point and use that to get the device file for the misc
69 * partition. From there we'll assume that a by-name scheme is used
70 * so we can just replace the trailing "misc" by the given |name|,
71 * e.g.
72 *
73 * /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
74 * /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a
75 *
76 * If needed, it's possible to relax this assumption in the future
77 * by trawling /sys/block looking for the appropriate sibling of
78 * misc and then finding an entry in /dev matching the sysfs entry.
79 */
80
81 fstab = open_fstab();
82 if (fstab == NULL) {
83 return -1;
84 }
85 record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
86 if (record == NULL) {
87 fs_mgr_free_fstab(fstab);
88 return -1;
89 }
90 if (strcmp(name, "misc") == 0) {
91 path = strdup(record->blk_device);
92 } else {
93 size_t trimmed_len, name_len;
94 const char* end_slash = strrchr(record->blk_device, '/');
95 if (end_slash == NULL) {
96 fs_mgr_free_fstab(fstab);
97 return -1;
98 }
99 trimmed_len = end_slash - record->blk_device + 1;
100 name_len = strlen(name);
101 path = calloc(trimmed_len + name_len + 1, 1);
102 strncpy(path, record->blk_device, trimmed_len);
103 strncpy(path + trimmed_len, name, name_len);
104 }
105 fs_mgr_free_fstab(fstab);
106
107 fd = open(path, flags);
108 free(path);
109
110 return fd;
111}
112
113static AvbIOResult read_from_partition(AvbOps* ops, const char* partition,
114 int64_t offset, size_t num_bytes,
115 void* buffer, size_t* out_num_read) {
116 int fd;
117 off_t where;
118 ssize_t num_read;
119 AvbIOResult ret;
120
121 fd = open_partition(partition, O_RDONLY);
122 if (fd == -1) {
123 avb_errorv("Error opening \"", partition, "\" partition.\n", NULL);
124 ret = AVB_IO_RESULT_ERROR_IO;
125 goto out;
126 }
127
128 where = lseek(fd, offset, SEEK_SET);
129 if (where == -1) {
130 avb_error("Error seeking to offset.\n");
131 ret = AVB_IO_RESULT_ERROR_IO;
132 goto out;
133 }
134 if (where != offset) {
135 avb_error("Error seeking to offset.\n");
136 ret = AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION;
137 goto out;
138 }
139
140 /* On Linux, we never get partial reads from block devices (except
141 * for EOF).
142 */
143 num_read = read(fd, buffer, num_bytes);
144 if (num_read == -1) {
145 avb_error("Error reading data.\n");
146 ret = AVB_IO_RESULT_ERROR_IO;
147 goto out;
148 }
149 if (out_num_read != NULL) {
150 *out_num_read = num_read;
151 }
152
153 ret = AVB_IO_RESULT_OK;
154
155out:
156 if (fd != -1) {
157 if (close(fd) != 0) {
158 avb_error("Error closing file descriptor.\n");
159 }
160 }
161 return ret;
162}
163
164static AvbIOResult write_to_partition(AvbOps* ops, const char* partition,
165 int64_t offset, size_t num_bytes,
166 const void* buffer) {
167 int fd;
168 off_t where;
169 ssize_t num_written;
170 AvbIOResult ret;
171
172 fd = open_partition(partition, O_WRONLY);
173 if (fd == -1) {
174 avb_errorv("Error opening \"", partition, "\" partition.\n", NULL);
175 ret = AVB_IO_RESULT_ERROR_IO;
176 goto out;
177 }
178
179 where = lseek(fd, offset, SEEK_SET);
180 if (where == -1) {
181 avb_error("Error seeking to offset.\n");
182 ret = AVB_IO_RESULT_ERROR_IO;
183 goto out;
184 }
185 if (where != offset) {
186 avb_error("Error seeking to offset.\n");
187 ret = AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION;
188 goto out;
189 }
190
191 /* On Linux, we never get partial writes on block devices. */
192 num_written = write(fd, buffer, num_bytes);
193 if (num_written == -1) {
194 avb_error("Error writing data.\n");
195 ret = AVB_IO_RESULT_ERROR_IO;
196 goto out;
197 }
198
199 ret = AVB_IO_RESULT_OK;
200
201out:
202 if (fd != -1) {
203 if (close(fd) != 0) {
204 avb_error("Error closing file descriptor.\n");
205 }
206 }
207 return ret;
208}
209
David Zeuthenbaf59e22016-11-14 15:39:43 -0500210AvbABOps* avb_ops_device_new(void) {
211 AvbABOps* ab_ops;
David Zeuthen8b6973b2016-09-20 12:39:49 -0400212
David Zeuthenbaf59e22016-11-14 15:39:43 -0500213 ab_ops = calloc(1, sizeof(AvbABOps));
214 if (ab_ops == NULL) {
David Zeuthen8b6973b2016-09-20 12:39:49 -0400215 avb_error("Error allocating memory for AvbOps.\n");
216 goto out;
217 }
218
David Zeuthenbaf59e22016-11-14 15:39:43 -0500219 /* We only need these operations since that's all what is being used
220 * by the A/B routines.
David Zeuthen8b6973b2016-09-20 12:39:49 -0400221 */
David Zeuthenbaf59e22016-11-14 15:39:43 -0500222 ab_ops->ops.read_from_partition = read_from_partition;
223 ab_ops->ops.write_to_partition = write_to_partition;
224 ab_ops->read_ab_metadata = avb_ab_data_read;
225 ab_ops->write_ab_metadata = avb_ab_data_write;
David Zeuthen8b6973b2016-09-20 12:39:49 -0400226
227out:
David Zeuthenbaf59e22016-11-14 15:39:43 -0500228 return ab_ops;
David Zeuthen8b6973b2016-09-20 12:39:49 -0400229}
230
David Zeuthenbaf59e22016-11-14 15:39:43 -0500231void avb_ops_device_free(AvbABOps* ab_ops) { free(ab_ops); }