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