1 /*
2  * Block device emulated in a file
3  *
4  * Copyright (c) 2017, Arm Limited. All rights reserved.
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 #include "bd/lfs_filebd.h"
8 
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <errno.h>
12 
lfs_filebd_createcfg(const struct lfs_config * cfg,const char * path,const struct lfs_filebd_config * bdcfg)13 int lfs_filebd_createcfg(const struct lfs_config *cfg, const char *path,
14         const struct lfs_filebd_config *bdcfg) {
15     LFS_FILEBD_TRACE("lfs_filebd_createcfg(%p {.context=%p, "
16                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
17                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
18                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
19                 "\"%s\", "
20                 "%p {.erase_value=%"PRId32"})",
21             (void*)cfg, cfg->context,
22             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
23             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
24             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
25             path, (void*)bdcfg, bdcfg->erase_value);
26     lfs_filebd_t *bd = cfg->context;
27     bd->cfg = bdcfg;
28 
29     // open file
30     bd->fd = open(path, O_RDWR | O_CREAT, 0666);
31     if (bd->fd < 0) {
32         int err = -errno;
33         LFS_FILEBD_TRACE("lfs_filebd_createcfg -> %d", err);
34         return err;
35     }
36 
37     LFS_FILEBD_TRACE("lfs_filebd_createcfg -> %d", 0);
38     return 0;
39 }
40 
lfs_filebd_create(const struct lfs_config * cfg,const char * path)41 int lfs_filebd_create(const struct lfs_config *cfg, const char *path) {
42     LFS_FILEBD_TRACE("lfs_filebd_create(%p {.context=%p, "
43                 ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
44                 ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
45                 ".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
46                 "\"%s\")",
47             (void*)cfg, cfg->context,
48             (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
49             (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
50             cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
51             path);
52     static const struct lfs_filebd_config defaults = {.erase_value=-1};
53     int err = lfs_filebd_createcfg(cfg, path, &defaults);
54     LFS_FILEBD_TRACE("lfs_filebd_create -> %d", err);
55     return err;
56 }
57 
lfs_filebd_destroy(const struct lfs_config * cfg)58 int lfs_filebd_destroy(const struct lfs_config *cfg) {
59     LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)cfg);
60     lfs_filebd_t *bd = cfg->context;
61     int err = close(bd->fd);
62     if (err < 0) {
63         err = -errno;
64         LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", err);
65         return err;
66     }
67     LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", 0);
68     return 0;
69 }
70 
lfs_filebd_read(const struct lfs_config * cfg,lfs_block_t block,lfs_off_t off,void * buffer,lfs_size_t size)71 int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block,
72         lfs_off_t off, void *buffer, lfs_size_t size) {
73     LFS_FILEBD_TRACE("lfs_filebd_read(%p, "
74                 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
75             (void*)cfg, block, off, buffer, size);
76     lfs_filebd_t *bd = cfg->context;
77 
78     // check if read is valid
79     LFS_ASSERT(off  % cfg->read_size == 0);
80     LFS_ASSERT(size % cfg->read_size == 0);
81     LFS_ASSERT(block < cfg->block_count);
82 
83     // zero for reproducability (in case file is truncated)
84     if (bd->cfg->erase_value != -1) {
85         memset(buffer, bd->cfg->erase_value, size);
86     }
87 
88     // read
89     off_t res1 = lseek(bd->fd,
90             (off_t)block*cfg->block_size + (off_t)off, SEEK_SET);
91     if (res1 < 0) {
92         int err = -errno;
93         LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
94         return err;
95     }
96 
97     ssize_t res2 = read(bd->fd, buffer, size);
98     if (res2 < 0) {
99         int err = -errno;
100         LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
101         return err;
102     }
103 
104     LFS_FILEBD_TRACE("lfs_filebd_read -> %d", 0);
105     return 0;
106 }
107 
lfs_filebd_prog(const struct lfs_config * cfg,lfs_block_t block,lfs_off_t off,const void * buffer,lfs_size_t size)108 int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
109         lfs_off_t off, const void *buffer, lfs_size_t size) {
110     LFS_FILEBD_TRACE("lfs_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
111             (void*)cfg, block, off, buffer, size);
112     lfs_filebd_t *bd = cfg->context;
113 
114     // check if write is valid
115     LFS_ASSERT(off  % cfg->prog_size == 0);
116     LFS_ASSERT(size % cfg->prog_size == 0);
117     LFS_ASSERT(block < cfg->block_count);
118 
119     // check that data was erased? only needed for testing
120     if (bd->cfg->erase_value != -1) {
121         off_t res1 = lseek(bd->fd,
122                 (off_t)block*cfg->block_size + (off_t)off, SEEK_SET);
123         if (res1 < 0) {
124             int err = -errno;
125             LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
126             return err;
127         }
128 
129         for (lfs_off_t i = 0; i < size; i++) {
130             uint8_t c;
131             ssize_t res2 = read(bd->fd, &c, 1);
132             if (res2 < 0) {
133                 int err = -errno;
134                 LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
135                 return err;
136             }
137 
138             LFS_ASSERT(c == bd->cfg->erase_value);
139         }
140     }
141 
142     // program data
143     off_t res1 = lseek(bd->fd,
144             (off_t)block*cfg->block_size + (off_t)off, SEEK_SET);
145     if (res1 < 0) {
146         int err = -errno;
147         LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
148         return err;
149     }
150 
151     ssize_t res2 = write(bd->fd, buffer, size);
152     if (res2 < 0) {
153         int err = -errno;
154         LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
155         return err;
156     }
157 
158     LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", 0);
159     return 0;
160 }
161 
lfs_filebd_erase(const struct lfs_config * cfg,lfs_block_t block)162 int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) {
163     LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block);
164     lfs_filebd_t *bd = cfg->context;
165 
166     // check if erase is valid
167     LFS_ASSERT(block < cfg->block_count);
168 
169     // erase, only needed for testing
170     if (bd->cfg->erase_value != -1) {
171         off_t res1 = lseek(bd->fd, (off_t)block*cfg->block_size, SEEK_SET);
172         if (res1 < 0) {
173             int err = -errno;
174             LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err);
175             return err;
176         }
177 
178         for (lfs_off_t i = 0; i < cfg->block_size; i++) {
179             ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg->erase_value}, 1);
180             if (res2 < 0) {
181                 int err = -errno;
182                 LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err);
183                 return err;
184             }
185         }
186     }
187 
188     LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", 0);
189     return 0;
190 }
191 
lfs_filebd_sync(const struct lfs_config * cfg)192 int lfs_filebd_sync(const struct lfs_config *cfg) {
193     LFS_FILEBD_TRACE("lfs_filebd_sync(%p)", (void*)cfg);
194     // file sync
195     lfs_filebd_t *bd = cfg->context;
196     int err = fsync(bd->fd);
197     if (err) {
198         err = -errno;
199         LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0);
200         return err;
201     }
202 
203     LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0);
204     return 0;
205 }
206