Branch data Line data Source code
1 : : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2 : : /* Copyright 2013-2019 IBM Corp. */
3 : :
4 : : #define _GNU_SOURCE
5 : : #include <errno.h>
6 : : #include <inttypes.h>
7 : : #include <stdio.h>
8 : : #include <stdlib.h>
9 : : #include <string.h>
10 : : #include <sys/ioctl.h>
11 : : #include <sys/types.h>
12 : : #include <sys/stat.h>
13 : : #include <fcntl.h>
14 : : #include <unistd.h>
15 : : #include <limits.h>
16 : :
17 : : #include <ccan/container_of/container_of.h>
18 : :
19 : : #include <mtd/mtd-abi.h>
20 : :
21 : : #include "libflash.h"
22 : : #include "libflash/file.h"
23 : : #include "blocklevel.h"
24 : :
25 : : struct file_data {
26 : : int fd;
27 : : char *name;
28 : : char *path;
29 : : struct blocklevel_device bl;
30 : : };
31 : :
32 : 0 : static int file_release(struct blocklevel_device *bl)
33 : : {
34 : 0 : struct file_data *file_data = container_of(bl, struct file_data, bl);
35 : 0 : close(file_data->fd);
36 : 0 : file_data->fd = -1;
37 : 0 : return 0;
38 : : }
39 : :
40 : 0 : static int file_reacquire(struct blocklevel_device *bl)
41 : : {
42 : 0 : struct file_data *file_data = container_of(bl, struct file_data, bl);
43 : 0 : int fd;
44 : :
45 : 0 : fd = open(file_data->path, O_RDWR);
46 : 0 : if (fd == -1)
47 : : return FLASH_ERR_PARM_ERROR;
48 : 0 : file_data->fd = fd;
49 : 0 : return 0;
50 : : }
51 : :
52 : 0 : static int file_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len)
53 : : {
54 : 0 : struct file_data *file_data = container_of(bl, struct file_data, bl);
55 : 0 : int rc, count = 0;
56 : :
57 : 0 : rc = lseek(file_data->fd, pos, SEEK_SET);
58 : : /* errno should remain set */
59 : 0 : if (rc != pos)
60 : : return FLASH_ERR_PARM_ERROR;
61 : :
62 : 0 : while (count < len) {
63 : 0 : rc = read(file_data->fd, buf, len - count);
64 : : /* errno should remain set */
65 : 0 : if (rc == -1 || rc == 0)
66 : : return FLASH_ERR_BAD_READ;
67 : :
68 : 0 : buf += rc;
69 : 0 : count += rc;
70 : : }
71 : :
72 : : return 0;
73 : : }
74 : :
75 : 58 : static int file_write(struct blocklevel_device *bl, uint64_t dst, const void *src,
76 : : uint64_t len)
77 : : {
78 : 58 : struct file_data *file_data = container_of(bl, struct file_data, bl);
79 : 58 : int rc, count = 0;
80 : :
81 : 58 : rc = lseek(file_data->fd, dst, SEEK_SET);
82 : : /* errno should remain set */
83 : 58 : if (rc != dst)
84 : : return FLASH_ERR_PARM_ERROR;
85 : :
86 : 116 : while (count < len) {
87 : 58 : rc = write(file_data->fd, src, len - count);
88 : : /* errno should remain set */
89 : 58 : if (rc == -1)
90 : : return FLASH_ERR_VERIFY_FAILURE;
91 : :
92 : 58 : src += rc;
93 : 58 : count += rc;
94 : : }
95 : :
96 : : return 0;
97 : : }
98 : :
99 : : /*
100 : : * Due to to the fact these interfaces are ultimately supposed to deal with
101 : : * flash, an erase function must be implemented even when the flash images
102 : : * are backed by regular files.
103 : : * Also, erasing flash leaves all the bits set to 1. This may be expected
104 : : * by higher level functions so this function should also emulate that
105 : : */
106 : 12 : static int file_erase(struct blocklevel_device *bl, uint64_t dst, uint64_t len)
107 : : {
108 : 12 : static char buf[4096];
109 : 12 : int i = 0;
110 : 12 : int rc;
111 : :
112 : 12 : memset(buf, ~0, sizeof(buf));
113 : :
114 : 36 : while (len - i > 0) {
115 : 24 : rc = file_write(bl, dst + i, buf, len - i > sizeof(buf) ? sizeof(buf) : len - i);
116 : 24 : if (rc)
117 : 0 : return rc;
118 : 24 : i += (len - i > sizeof(buf)) ? sizeof(buf) : len - i;
119 : : }
120 : :
121 : : return 0;
122 : : }
123 : :
124 : 0 : static int mtd_erase(struct blocklevel_device *bl, uint64_t dst, uint64_t len)
125 : : {
126 : 0 : struct file_data *file_data = container_of(bl, struct file_data, bl);
127 : 0 : int err;
128 : :
129 : 0 : FL_DBG("%s: dst: 0x%" PRIx64 ", len: 0x%" PRIx64 "\n", __func__, dst, len);
130 : :
131 : : /*
132 : : * Some kernels that pflash supports do not know about the 64bit
133 : : * version of the ioctl() therefore we'll just use the 32bit (which
134 : : * should always be supported...) unless we MUST use the 64bit and
135 : : * then lets just hope the kernel knows how to deal with it. If it
136 : : * is unsupported the ioctl() will fail and we'll report that -
137 : : * there is no other option.
138 : : *
139 : : * Furthermore, even very recent MTD layers and drivers aren't
140 : : * particularly good at not blocking in the kernel. This creates
141 : : * unexpected behaviour in userspace tools using these functions.
142 : : * In the absence of significant work inside the kernel, we'll just
143 : : * split stuff up here for convenience.
144 : : * We can assume everything is aligned here.
145 : : */
146 : 0 : while (len) {
147 : 0 : if (dst > UINT_MAX || len > UINT_MAX) {
148 : 0 : struct erase_info_user64 erase_info = {
149 : : .start = dst,
150 : 0 : .length = file_data->bl.erase_mask + 1
151 : : };
152 : :
153 : 0 : if (ioctl(file_data->fd, MEMERASE64, &erase_info) == -1) {
154 : 0 : err = errno;
155 : 0 : if (err == 25) /* Kernel doesn't do 64bit MTD erase ioctl() */
156 : 0 : FL_DBG("Attempted a 64bit erase on a kernel which doesn't support it\n");
157 : 0 : FL_ERR("%s: IOCTL to kernel failed! %s\n", __func__, strerror(err));
158 : 0 : errno = err;
159 : 0 : return FLASH_ERR_PARM_ERROR;
160 : : }
161 : : } else {
162 : 0 : struct erase_info_user erase_info = {
163 : : .start = dst,
164 : 0 : .length = file_data->bl.erase_mask + 1
165 : : };
166 : 0 : if (ioctl(file_data->fd, MEMERASE, &erase_info) == -1) {
167 : 0 : err = errno;
168 : 0 : FL_ERR("%s: IOCTL to kernel failed! %s\n", __func__, strerror(err));
169 : 0 : errno = err;
170 : 0 : return FLASH_ERR_PARM_ERROR;
171 : : }
172 : : }
173 : 0 : dst += file_data->bl.erase_mask + 1;
174 : 0 : len -= file_data->bl.erase_mask + 1;
175 : : }
176 : : return 0;
177 : : }
178 : :
179 : 0 : static int get_info_name(struct file_data *file_data, char **name)
180 : : {
181 : 0 : char *path, *lpath;
182 : 0 : int len;
183 : 0 : struct stat st;
184 : :
185 : 0 : if (asprintf(&path, "/proc/self/fd/%d", file_data->fd) == -1)
186 : : return FLASH_ERR_MALLOC_FAILED;
187 : :
188 : 0 : if (lstat(path, &st)) {
189 : 0 : free(path);
190 : 0 : return FLASH_ERR_PARM_ERROR;
191 : : }
192 : :
193 : 0 : lpath = malloc(st.st_size + 1);
194 : 0 : if (!lpath) {
195 : 0 : free(path);
196 : 0 : return FLASH_ERR_MALLOC_FAILED;
197 : : }
198 : :
199 : 0 : len = readlink(path, lpath, st.st_size +1);
200 : 0 : if (len == -1) {
201 : 0 : free(path);
202 : 0 : free(lpath);
203 : 0 : return FLASH_ERR_PARM_ERROR;
204 : : }
205 : 0 : lpath[len] = '\0';
206 : :
207 : 0 : *name = lpath;
208 : :
209 : 0 : free(path);
210 : 0 : return 0;
211 : : }
212 : :
213 : :
214 : 0 : static int mtd_get_info(struct blocklevel_device *bl, const char **name,
215 : : uint64_t *total_size, uint32_t *erase_granule)
216 : : {
217 : 0 : struct file_data *file_data = container_of(bl, struct file_data, bl);
218 : 0 : struct mtd_info_user mtd_info;
219 : 0 : int rc;
220 : :
221 : 0 : rc = ioctl(file_data->fd, MEMGETINFO, &mtd_info);
222 : 0 : if (rc == -1)
223 : : return FLASH_ERR_BAD_READ;
224 : :
225 : 0 : if (total_size)
226 : 0 : *total_size = mtd_info.size;
227 : :
228 : 0 : if (erase_granule)
229 : 0 : *erase_granule = mtd_info.erasesize;
230 : :
231 : 0 : if (name) {
232 : 0 : rc = get_info_name(file_data, &(file_data->name));
233 : 0 : if (rc)
234 : : return rc;
235 : 0 : *name = file_data->name;
236 : : }
237 : :
238 : : return 0;
239 : : }
240 : :
241 : 0 : static int file_get_info(struct blocklevel_device *bl, const char **name,
242 : : uint64_t *total_size, uint32_t *erase_granule)
243 : : {
244 : 0 : struct file_data *file_data = container_of(bl, struct file_data, bl);
245 : 0 : struct stat st;
246 : 0 : int rc;
247 : :
248 : 0 : if (fstat(file_data->fd, &st))
249 : : return FLASH_ERR_PARM_ERROR;
250 : :
251 : 0 : if (total_size)
252 : 0 : *total_size = st.st_size;
253 : :
254 : 0 : if (erase_granule)
255 : 0 : *erase_granule = 1;
256 : :
257 : 0 : if (name) {
258 : 0 : rc = get_info_name(file_data, &(file_data->name));
259 : 0 : if (rc)
260 : : return rc;
261 : 0 : *name = file_data->name;
262 : : }
263 : :
264 : : return 0;
265 : : }
266 : :
267 : 6 : int file_init(int fd, struct blocklevel_device **bl)
268 : : {
269 : 6 : struct file_data *file_data;
270 : 6 : struct stat sbuf;
271 : :
272 : 6 : if (!bl)
273 : : return FLASH_ERR_PARM_ERROR;
274 : :
275 : 6 : *bl = NULL;
276 : :
277 : 6 : file_data = calloc(1, sizeof(struct file_data));
278 : 6 : if (!file_data)
279 : : return FLASH_ERR_MALLOC_FAILED;
280 : :
281 : 6 : file_data->fd = fd;
282 : 6 : file_data->bl.reacquire = &file_reacquire;
283 : 6 : file_data->bl.release = &file_release;
284 : 6 : file_data->bl.read = &file_read;
285 : 6 : file_data->bl.write = &file_write;
286 : 6 : file_data->bl.erase = &file_erase;
287 : 6 : file_data->bl.get_info = &file_get_info;
288 : 6 : file_data->bl.erase_mask = 0;
289 : :
290 : : /*
291 : : * If the blocklevel_device is only inited with file_init() then keep
292 : : * alive is assumed, as fd will change otherwise and this may break
293 : : * callers assumptions.
294 : : */
295 : 6 : file_data->bl.keep_alive = 1;
296 : :
297 : : /*
298 : : * Unfortunately not all file descriptors are created equal...
299 : : * Here we check to see if the file descriptor is to an MTD device, in
300 : : * which case we have to erase and get the size of it differently.
301 : : */
302 : 6 : if (fstat(file_data->fd, &sbuf) == -1)
303 : 0 : goto out;
304 : :
305 : : /* Won't be able to handle other than MTD devices for now */
306 : 6 : if (S_ISCHR(sbuf.st_mode)) {
307 : 0 : file_data->bl.erase = &mtd_erase;
308 : 0 : file_data->bl.get_info = &mtd_get_info;
309 : 0 : file_data->bl.flags = WRITE_NEED_ERASE;
310 : 0 : mtd_get_info(&file_data->bl, NULL, NULL, &(file_data->bl.erase_mask));
311 : 0 : file_data->bl.erase_mask--;
312 : 6 : } else if (!S_ISREG(sbuf.st_mode)) {
313 : : /* If not a char device or a regular file something went wrong */
314 : 0 : goto out;
315 : : }
316 : :
317 : 6 : *bl = &(file_data->bl);
318 : 6 : return 0;
319 : 0 : out:
320 : 0 : free(file_data);
321 : 0 : return FLASH_ERR_PARM_ERROR;
322 : : }
323 : :
324 : 6 : int file_init_path(const char *path, int *r_fd, bool keep_alive,
325 : : struct blocklevel_device **bl)
326 : : {
327 : 6 : int fd, rc;
328 : 6 : char *path_ptr = NULL;
329 : 6 : struct file_data *file_data;
330 : :
331 : 6 : if (!path || !bl)
332 : : return FLASH_ERR_PARM_ERROR;
333 : :
334 : 6 : fd = open(path, O_RDWR);
335 : 6 : if (fd == -1)
336 : : return FLASH_ERR_PARM_ERROR;
337 : :
338 : : /*
339 : : * strdup() first so don't have to deal with malloc failure after
340 : : * file_init()
341 : : */
342 : 6 : path_ptr = strdup(path);
343 : 6 : if (!path_ptr) {
344 : 0 : rc = FLASH_ERR_MALLOC_FAILED;
345 : 0 : goto out;
346 : : }
347 : :
348 : 6 : rc = file_init(fd, bl);
349 : 6 : if (rc)
350 : 0 : goto out;
351 : :
352 : 6 : file_data = container_of(*bl, struct file_data, bl);
353 : 6 : file_data->bl.keep_alive = keep_alive;
354 : 6 : file_data->path = path_ptr;
355 : :
356 : 6 : if (r_fd)
357 : 0 : *r_fd = fd;
358 : :
359 : : return rc;
360 : 0 : out:
361 : 0 : free(path_ptr);
362 : 0 : close(fd);
363 : 0 : return rc;
364 : : }
365 : :
366 : 6 : void file_exit(struct blocklevel_device *bl)
367 : : {
368 : 6 : struct file_data *file_data;
369 : 6 : if (bl) {
370 : 6 : free(bl->ecc_prot.prot);
371 : 6 : file_data = container_of(bl, struct file_data, bl);
372 : 6 : free(file_data->name);
373 : 6 : free(file_data->path);
374 : 6 : free(file_data);
375 : : }
376 : 6 : }
377 : :
378 : 6 : void file_exit_close(struct blocklevel_device *bl)
379 : : {
380 : 6 : struct file_data *file_data;
381 : 6 : if (bl) {
382 : 6 : file_data = container_of(bl, struct file_data, bl);
383 : 6 : close(file_data->fd);
384 : 6 : file_exit(bl);
385 : : }
386 : 6 : }
|