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