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_create(const struct lfs_config * cfg,const char * path,const struct lfs_filebd_config * bdcfg)18 int lfs_filebd_create(const struct lfs_config *cfg, const char *path,
19 const struct lfs_filebd_config *bdcfg) {
20 LFS_FILEBD_TRACE("lfs_filebd_create(%p {.context=%p, "
21 ".read=%p, .prog=%p, .erase=%p, .sync=%p}, "
22 "\"%s\", "
23 "%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", "
24 ".erase_size=%"PRIu32", .erase_count=%"PRIu32"})",
25 (void*)cfg, cfg->context,
26 (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
27 (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
28 path,
29 (void*)bdcfg,
30 bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size,
31 bdcfg->erase_count);
32 lfs_filebd_t *bd = cfg->context;
33 bd->cfg = bdcfg;
34
35 // open file
36 #ifdef _WIN32
37 bd->fd = open(path, O_RDWR | O_CREAT | O_BINARY, 0666);
38 #else
39 bd->fd = open(path, O_RDWR | O_CREAT, 0666);
40 #endif
41
42 if (bd->fd < 0) {
43 int err = -errno;
44 LFS_FILEBD_TRACE("lfs_filebd_create -> %d", err);
45 return err;
46 }
47
48 LFS_FILEBD_TRACE("lfs_filebd_create -> %d", 0);
49 return 0;
50 }
51
lfs_filebd_destroy(const struct lfs_config * cfg)52 int lfs_filebd_destroy(const struct lfs_config *cfg) {
53 LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)cfg);
54 lfs_filebd_t *bd = cfg->context;
55 int err = close(bd->fd);
56 if (err < 0) {
57 err = -errno;
58 LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", err);
59 return err;
60 }
61 LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", 0);
62 return 0;
63 }
64
lfs_filebd_read(const struct lfs_config * cfg,lfs_block_t block,lfs_off_t off,void * buffer,lfs_size_t size)65 int lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block,
66 lfs_off_t off, void *buffer, lfs_size_t size) {
67 LFS_FILEBD_TRACE("lfs_filebd_read(%p, "
68 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
69 (void*)cfg, block, off, buffer, size);
70 lfs_filebd_t *bd = cfg->context;
71
72 // check if read is valid
73 LFS_ASSERT(block < bd->cfg->erase_count);
74 LFS_ASSERT(off % bd->cfg->read_size == 0);
75 LFS_ASSERT(size % bd->cfg->read_size == 0);
76 LFS_ASSERT(off+size <= bd->cfg->erase_size);
77
78 // zero for reproducibility (in case file is truncated)
79 memset(buffer, 0, size);
80
81 // read
82 off_t res1 = lseek(bd->fd,
83 (off_t)block*bd->cfg->erase_size + (off_t)off, SEEK_SET);
84 if (res1 < 0) {
85 int err = -errno;
86 LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
87 return err;
88 }
89
90 ssize_t res2 = read(bd->fd, buffer, size);
91 if (res2 < 0) {
92 int err = -errno;
93 LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
94 return err;
95 }
96
97 LFS_FILEBD_TRACE("lfs_filebd_read -> %d", 0);
98 return 0;
99 }
100
lfs_filebd_prog(const struct lfs_config * cfg,lfs_block_t block,lfs_off_t off,const void * buffer,lfs_size_t size)101 int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
102 lfs_off_t off, const void *buffer, lfs_size_t size) {
103 LFS_FILEBD_TRACE("lfs_filebd_prog(%p, "
104 "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
105 (void*)cfg, block, off, buffer, size);
106 lfs_filebd_t *bd = cfg->context;
107
108 // check if write is valid
109 LFS_ASSERT(block < bd->cfg->erase_count);
110 LFS_ASSERT(off % bd->cfg->prog_size == 0);
111 LFS_ASSERT(size % bd->cfg->prog_size == 0);
112 LFS_ASSERT(off+size <= bd->cfg->erase_size);
113
114 // program data
115 off_t res1 = lseek(bd->fd,
116 (off_t)block*bd->cfg->erase_size + (off_t)off, SEEK_SET);
117 if (res1 < 0) {
118 int err = -errno;
119 LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
120 return err;
121 }
122
123 ssize_t res2 = write(bd->fd, buffer, size);
124 if (res2 < 0) {
125 int err = -errno;
126 LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
127 return err;
128 }
129
130 LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", 0);
131 return 0;
132 }
133
lfs_filebd_erase(const struct lfs_config * cfg,lfs_block_t block)134 int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) {
135 LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
136 (void*)cfg, block, ((lfs_file_t*)cfg->context)->cfg->erase_size);
137 lfs_filebd_t *bd = cfg->context;
138
139 // check if erase is valid
140 LFS_ASSERT(block < bd->cfg->erase_count);
141
142 // erase is a noop
143 (void)block;
144
145 LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", 0);
146 return 0;
147 }
148
lfs_filebd_sync(const struct lfs_config * cfg)149 int lfs_filebd_sync(const struct lfs_config *cfg) {
150 LFS_FILEBD_TRACE("lfs_filebd_sync(%p)", (void*)cfg);
151
152 // file sync
153 lfs_filebd_t *bd = cfg->context;
154 #ifdef _WIN32
155 int err = FlushFileBuffers((HANDLE) _get_osfhandle(bd->fd)) ? 0 : -1;
156 #else
157 int err = fsync(bd->fd);
158 #endif
159 if (err) {
160 err = -errno;
161 LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0);
162 return err;
163 }
164
165 LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0);
166 return 0;
167 }
168