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
file_release(struct blocklevel_device * bl)32 static int file_release(struct blocklevel_device *bl)
33 {
34 struct file_data *file_data = container_of(bl, struct file_data, bl);
35 close(file_data->fd);
36 file_data->fd = -1;
37 return 0;
38 }
39
file_reacquire(struct blocklevel_device * bl)40 static int file_reacquire(struct blocklevel_device *bl)
41 {
42 struct file_data *file_data = container_of(bl, struct file_data, bl);
43 int fd;
44
45 fd = open(file_data->path, O_RDWR);
46 if (fd == -1)
47 return FLASH_ERR_PARM_ERROR;
48 file_data->fd = fd;
49 return 0;
50 }
51
file_read(struct blocklevel_device * bl,uint64_t pos,void * buf,uint64_t len)52 static int file_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len)
53 {
54 struct file_data *file_data = container_of(bl, struct file_data, bl);
55 int rc, count = 0;
56
57 rc = lseek(file_data->fd, pos, SEEK_SET);
58 /* errno should remain set */
59 if (rc != pos)
60 return FLASH_ERR_PARM_ERROR;
61
62 while (count < len) {
63 rc = read(file_data->fd, buf, len - count);
64 /* errno should remain set */
65 if (rc == -1 || rc == 0)
66 return FLASH_ERR_BAD_READ;
67
68 buf += rc;
69 count += rc;
70 }
71
72 return 0;
73 }
74
file_write(struct blocklevel_device * bl,uint64_t dst,const void * src,uint64_t len)75 static int file_write(struct blocklevel_device *bl, uint64_t dst, const void *src,
76 uint64_t len)
77 {
78 struct file_data *file_data = container_of(bl, struct file_data, bl);
79 int rc, count = 0;
80
81 rc = lseek(file_data->fd, dst, SEEK_SET);
82 /* errno should remain set */
83 if (rc != dst)
84 return FLASH_ERR_PARM_ERROR;
85
86 while (count < len) {
87 rc = write(file_data->fd, src, len - count);
88 /* errno should remain set */
89 if (rc == -1)
90 return FLASH_ERR_VERIFY_FAILURE;
91
92 src += rc;
93 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 */
file_erase(struct blocklevel_device * bl,uint64_t dst,uint64_t len)106 static int file_erase(struct blocklevel_device *bl, uint64_t dst, uint64_t len)
107 {
108 static char buf[4096];
109 int i = 0;
110 int rc;
111
112 memset(buf, ~0, sizeof(buf));
113
114 while (len - i > 0) {
115 rc = file_write(bl, dst + i, buf, len - i > sizeof(buf) ? sizeof(buf) : len - i);
116 if (rc)
117 return rc;
118 i += (len - i > sizeof(buf)) ? sizeof(buf) : len - i;
119 }
120
121 return 0;
122 }
123
mtd_erase(struct blocklevel_device * bl,uint64_t dst,uint64_t len)124 static int mtd_erase(struct blocklevel_device *bl, uint64_t dst, uint64_t len)
125 {
126 struct file_data *file_data = container_of(bl, struct file_data, bl);
127 int err;
128
129 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 while (len) {
147 if (dst > UINT_MAX || len > UINT_MAX) {
148 struct erase_info_user64 erase_info = {
149 .start = dst,
150 .length = file_data->bl.erase_mask + 1
151 };
152
153 if (ioctl(file_data->fd, MEMERASE64, &erase_info) == -1) {
154 err = errno;
155 if (err == 25) /* Kernel doesn't do 64bit MTD erase ioctl() */
156 FL_DBG("Attempted a 64bit erase on a kernel which doesn't support it\n");
157 FL_ERR("%s: IOCTL to kernel failed! %s\n", __func__, strerror(err));
158 errno = err;
159 return FLASH_ERR_PARM_ERROR;
160 }
161 } else {
162 struct erase_info_user erase_info = {
163 .start = dst,
164 .length = file_data->bl.erase_mask + 1
165 };
166 if (ioctl(file_data->fd, MEMERASE, &erase_info) == -1) {
167 err = errno;
168 FL_ERR("%s: IOCTL to kernel failed! %s\n", __func__, strerror(err));
169 errno = err;
170 return FLASH_ERR_PARM_ERROR;
171 }
172 }
173 dst += file_data->bl.erase_mask + 1;
174 len -= file_data->bl.erase_mask + 1;
175 }
176 return 0;
177 }
178
get_info_name(struct file_data * file_data,char ** name)179 static int get_info_name(struct file_data *file_data, char **name)
180 {
181 char *path, *lpath;
182 int len;
183 struct stat st;
184
185 if (asprintf(&path, "/proc/self/fd/%d", file_data->fd) == -1)
186 return FLASH_ERR_MALLOC_FAILED;
187
188 if (lstat(path, &st)) {
189 free(path);
190 return FLASH_ERR_PARM_ERROR;
191 }
192
193 lpath = malloc(st.st_size + 1);
194 if (!lpath) {
195 free(path);
196 return FLASH_ERR_MALLOC_FAILED;
197 }
198
199 len = readlink(path, lpath, st.st_size +1);
200 if (len == -1) {
201 free(path);
202 free(lpath);
203 return FLASH_ERR_PARM_ERROR;
204 }
205 lpath[len] = '\0';
206
207 *name = lpath;
208
209 free(path);
210 return 0;
211 }
212
213
mtd_get_info(struct blocklevel_device * bl,const char ** name,uint64_t * total_size,uint32_t * erase_granule)214 static int mtd_get_info(struct blocklevel_device *bl, const char **name,
215 uint64_t *total_size, uint32_t *erase_granule)
216 {
217 struct file_data *file_data = container_of(bl, struct file_data, bl);
218 struct mtd_info_user mtd_info;
219 int rc;
220
221 rc = ioctl(file_data->fd, MEMGETINFO, &mtd_info);
222 if (rc == -1)
223 return FLASH_ERR_BAD_READ;
224
225 if (total_size)
226 *total_size = mtd_info.size;
227
228 if (erase_granule)
229 *erase_granule = mtd_info.erasesize;
230
231 if (name) {
232 rc = get_info_name(file_data, &(file_data->name));
233 if (rc)
234 return rc;
235 *name = file_data->name;
236 }
237
238 return 0;
239 }
240
file_get_info(struct blocklevel_device * bl,const char ** name,uint64_t * total_size,uint32_t * erase_granule)241 static int file_get_info(struct blocklevel_device *bl, const char **name,
242 uint64_t *total_size, uint32_t *erase_granule)
243 {
244 struct file_data *file_data = container_of(bl, struct file_data, bl);
245 struct stat st;
246 int rc;
247
248 if (fstat(file_data->fd, &st))
249 return FLASH_ERR_PARM_ERROR;
250
251 if (total_size)
252 *total_size = st.st_size;
253
254 if (erase_granule)
255 *erase_granule = 1;
256
257 if (name) {
258 rc = get_info_name(file_data, &(file_data->name));
259 if (rc)
260 return rc;
261 *name = file_data->name;
262 }
263
264 return 0;
265 }
266
file_init(int fd,struct blocklevel_device ** bl)267 int file_init(int fd, struct blocklevel_device **bl)
268 {
269 struct file_data *file_data;
270 struct stat sbuf;
271
272 if (!bl)
273 return FLASH_ERR_PARM_ERROR;
274
275 *bl = NULL;
276
277 file_data = calloc(1, sizeof(struct file_data));
278 if (!file_data)
279 return FLASH_ERR_MALLOC_FAILED;
280
281 file_data->fd = fd;
282 file_data->bl.reacquire = &file_reacquire;
283 file_data->bl.release = &file_release;
284 file_data->bl.read = &file_read;
285 file_data->bl.write = &file_write;
286 file_data->bl.erase = &file_erase;
287 file_data->bl.get_info = &file_get_info;
288 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 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 if (fstat(file_data->fd, &sbuf) == -1)
303 goto out;
304
305 /* Won't be able to handle other than MTD devices for now */
306 if (S_ISCHR(sbuf.st_mode)) {
307 file_data->bl.erase = &mtd_erase;
308 file_data->bl.get_info = &mtd_get_info;
309 file_data->bl.flags = WRITE_NEED_ERASE;
310 mtd_get_info(&file_data->bl, NULL, NULL, &(file_data->bl.erase_mask));
311 file_data->bl.erase_mask--;
312 } else if (!S_ISREG(sbuf.st_mode)) {
313 /* If not a char device or a regular file something went wrong */
314 goto out;
315 }
316
317 *bl = &(file_data->bl);
318 return 0;
319 out:
320 free(file_data);
321 return FLASH_ERR_PARM_ERROR;
322 }
323
file_init_path(const char * path,int * r_fd,bool keep_alive,struct blocklevel_device ** bl)324 int file_init_path(const char *path, int *r_fd, bool keep_alive,
325 struct blocklevel_device **bl)
326 {
327 int fd, rc;
328 char *path_ptr = NULL;
329 struct file_data *file_data;
330
331 if (!path || !bl)
332 return FLASH_ERR_PARM_ERROR;
333
334 fd = open(path, O_RDWR);
335 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 path_ptr = strdup(path);
343 if (!path_ptr) {
344 rc = FLASH_ERR_MALLOC_FAILED;
345 goto out;
346 }
347
348 rc = file_init(fd, bl);
349 if (rc)
350 goto out;
351
352 file_data = container_of(*bl, struct file_data, bl);
353 file_data->bl.keep_alive = keep_alive;
354 file_data->path = path_ptr;
355
356 if (r_fd)
357 *r_fd = fd;
358
359 return rc;
360 out:
361 free(path_ptr);
362 close(fd);
363 return rc;
364 }
365
file_exit(struct blocklevel_device * bl)366 void file_exit(struct blocklevel_device *bl)
367 {
368 struct file_data *file_data;
369 if (bl) {
370 free(bl->ecc_prot.prot);
371 file_data = container_of(bl, struct file_data, bl);
372 free(file_data->name);
373 free(file_data->path);
374 free(file_data);
375 }
376 }
377
file_exit_close(struct blocklevel_device * bl)378 void file_exit_close(struct blocklevel_device *bl)
379 {
380 struct file_data *file_data;
381 if (bl) {
382 file_data = container_of(bl, struct file_data, bl);
383 close(file_data->fd);
384 file_exit(bl);
385 }
386 }
387