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