blob: a95ce4d31d66ebcbe39db8c159aa61f05e20d93b
1 | /* |
2 | * Copyright (C) 2017 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 "avb_ops_user.h" |
26 | |
27 | #include <errno.h> |
28 | #include <fcntl.h> |
29 | #include <linux/fs.h> |
30 | #include <stdlib.h> |
31 | #include <string.h> |
32 | #include <sys/ioctl.h> |
33 | #include <sys/stat.h> |
34 | #include <sys/types.h> |
35 | #include <unistd.h> |
36 | |
37 | #include <cutils/properties.h> |
38 | #include <fs_mgr.h> |
39 | |
40 | #include <libavb_ab/libavb_ab.h> |
41 | |
42 | /* Open the appropriate fstab file and fallback to /fstab.device if |
43 | * that's what's being used. |
44 | */ |
45 | static struct fstab* open_fstab(void) { |
46 | struct fstab* fstab = fs_mgr_read_fstab_default(); |
47 | |
48 | if (fstab != NULL) { |
49 | return fstab; |
50 | } |
51 | |
52 | fstab = fs_mgr_read_fstab("/fstab.device"); |
53 | return fstab; |
54 | } |
55 | |
56 | static int open_partition(const char* name, int flags) { |
57 | char* path; |
58 | int fd; |
59 | struct fstab* fstab; |
60 | struct fstab_rec* record; |
61 | |
62 | /* We can't use fs_mgr to look up |name| because fstab doesn't list |
63 | * every slot partition (it uses the slotselect option to mask the |
64 | * suffix) and |slot| is expected to be of that form, e.g. boot_a. |
65 | * |
66 | * We can however assume that there's an entry for the /misc mount |
67 | * point and use that to get the device file for the misc |
68 | * partition. From there we'll assume that a by-name scheme is used |
69 | * so we can just replace the trailing "misc" by the given |name|, |
70 | * e.g. |
71 | * |
72 | * /dev/block/platform/soc.0/7824900.sdhci/by-name/misc -> |
73 | * /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a |
74 | * |
75 | * If needed, it's possible to relax this assumption in the future |
76 | * by trawling /sys/block looking for the appropriate sibling of |
77 | * misc and then finding an entry in /dev matching the sysfs entry. |
78 | */ |
79 | |
80 | fstab = open_fstab(); |
81 | if (fstab == NULL) { |
82 | return -1; |
83 | } |
84 | record = fs_mgr_get_entry_for_mount_point(fstab, "/misc"); |
85 | if (record == NULL) { |
86 | fs_mgr_free_fstab(fstab); |
87 | return -1; |
88 | } |
89 | if (strcmp(name, "misc") == 0) { |
90 | path = strdup(record->blk_device); |
91 | } else { |
92 | size_t trimmed_len, name_len; |
93 | const char* end_slash = strrchr(record->blk_device, '/'); |
94 | if (end_slash == NULL) { |
95 | fs_mgr_free_fstab(fstab); |
96 | return -1; |
97 | } |
98 | trimmed_len = end_slash - record->blk_device + 1; |
99 | name_len = strlen(name); |
100 | path = calloc(trimmed_len + name_len + 1, 1); |
101 | strncpy(path, record->blk_device, trimmed_len); |
102 | strncpy(path + trimmed_len, name, name_len); |
103 | } |
104 | fs_mgr_free_fstab(fstab); |
105 | |
106 | fd = open(path, flags); |
107 | free(path); |
108 | |
109 | return fd; |
110 | } |
111 | |
112 | static AvbIOResult read_from_partition(AvbOps* ops, |
113 | const char* partition, |
114 | int64_t offset, |
115 | size_t num_bytes, |
116 | void* buffer, |
117 | size_t* out_num_read) { |
118 | int fd; |
119 | off_t where; |
120 | ssize_t num_read; |
121 | AvbIOResult ret; |
122 | |
123 | fd = open_partition(partition, O_RDONLY); |
124 | if (fd == -1) { |
125 | ret = AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
126 | goto out; |
127 | } |
128 | |
129 | if (offset < 0) { |
130 | uint64_t partition_size; |
131 | if (ioctl(fd, BLKGETSIZE64, &partition_size) != 0) { |
132 | avb_errorv( |
133 | "Error getting size of \"", partition, "\" partition.\n", NULL); |
134 | ret = AVB_IO_RESULT_ERROR_IO; |
135 | goto out; |
136 | } |
137 | offset = partition_size - (-offset); |
138 | } |
139 | |
140 | where = lseek(fd, offset, SEEK_SET); |
141 | if (where == -1) { |
142 | avb_error("Error seeking to offset.\n"); |
143 | ret = AVB_IO_RESULT_ERROR_IO; |
144 | goto out; |
145 | } |
146 | if (where != offset) { |
147 | avb_error("Error seeking to offset.\n"); |
148 | ret = AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; |
149 | goto out; |
150 | } |
151 | |
152 | /* On Linux, we never get partial reads from block devices (except |
153 | * for EOF). |
154 | */ |
155 | num_read = read(fd, buffer, num_bytes); |
156 | if (num_read == -1) { |
157 | avb_error("Error reading data.\n"); |
158 | ret = AVB_IO_RESULT_ERROR_IO; |
159 | goto out; |
160 | } |
161 | if (out_num_read != NULL) { |
162 | *out_num_read = num_read; |
163 | } |
164 | |
165 | ret = AVB_IO_RESULT_OK; |
166 | |
167 | out: |
168 | if (fd != -1) { |
169 | if (close(fd) != 0) { |
170 | avb_error("Error closing file descriptor.\n"); |
171 | } |
172 | } |
173 | return ret; |
174 | } |
175 | |
176 | static AvbIOResult write_to_partition(AvbOps* ops, |
177 | const char* partition, |
178 | int64_t offset, |
179 | size_t num_bytes, |
180 | const void* buffer) { |
181 | int fd; |
182 | off_t where; |
183 | ssize_t num_written; |
184 | AvbIOResult ret; |
185 | |
186 | fd = open_partition(partition, O_WRONLY); |
187 | if (fd == -1) { |
188 | avb_errorv("Error opening \"", partition, "\" partition.\n", NULL); |
189 | ret = AVB_IO_RESULT_ERROR_IO; |
190 | goto out; |
191 | } |
192 | |
193 | where = lseek(fd, offset, SEEK_SET); |
194 | if (where == -1) { |
195 | avb_error("Error seeking to offset.\n"); |
196 | ret = AVB_IO_RESULT_ERROR_IO; |
197 | goto out; |
198 | } |
199 | if (where != offset) { |
200 | avb_error("Error seeking to offset.\n"); |
201 | ret = AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; |
202 | goto out; |
203 | } |
204 | |
205 | /* On Linux, we never get partial writes on block devices. */ |
206 | num_written = write(fd, buffer, num_bytes); |
207 | if (num_written == -1) { |
208 | avb_error("Error writing data.\n"); |
209 | ret = AVB_IO_RESULT_ERROR_IO; |
210 | goto out; |
211 | } |
212 | |
213 | ret = AVB_IO_RESULT_OK; |
214 | |
215 | out: |
216 | if (fd != -1) { |
217 | if (close(fd) != 0) { |
218 | avb_error("Error closing file descriptor.\n"); |
219 | } |
220 | } |
221 | return ret; |
222 | } |
223 | |
224 | AvbOps* avb_ops_user_new(void) { |
225 | AvbOps* ops; |
226 | |
227 | ops = calloc(1, sizeof(AvbOps)); |
228 | if (ops == NULL) { |
229 | avb_error("Error allocating memory for AvbOps.\n"); |
230 | goto out; |
231 | } |
232 | |
233 | ops->ab_ops = calloc(1, sizeof(AvbABOps)); |
234 | if (ops->ab_ops == NULL) { |
235 | avb_error("Error allocating memory for AvbABOps.\n"); |
236 | free(ops); |
237 | goto out; |
238 | } |
239 | ops->ab_ops->ops = ops; |
240 | |
241 | ops->read_from_partition = read_from_partition; |
242 | ops->write_to_partition = write_to_partition; |
243 | ops->ab_ops->read_ab_metadata = avb_ab_data_read; |
244 | ops->ab_ops->write_ab_metadata = avb_ab_data_write; |
245 | |
246 | out: |
247 | return ops; |
248 | } |
249 | |
250 | void avb_ops_user_free(AvbOps* ops) { |
251 | free(ops->ab_ops); |
252 | free(ops); |
253 | } |
254 |