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