Dawn Framework 1.0
Universal data acquisition framework for embedded systems
fileio.cxx
1// dawn/src/io/fileio.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "dawn/io/fileio.hxx"
7
8#include <fcntl.h>
9#include <sys/stat.h>
10#include <unistd.h>
11
12#include <cstring>
13
14using namespace dawn;
15
18static const char g_prefix_data[] = "/data/";
19static const char g_prefix_tmp[] = "/tmp/";
20static const char g_prefix_dev[] = CONFIG_DAWN_IO_FILE_DEV_PREFIX;
21
22// Device-node prefix is board-configurable; an empty one disables it.
23
24static bool pathIsDev(const char *path)
25{
26 return sizeof(g_prefix_dev) > 1 &&
27 std::strncmp(path, g_prefix_dev, sizeof(g_prefix_dev) - 1) == 0;
28}
29
30int CIOFile::configureDesc(const CDescObject &desc)
31{
32 const SObjectCfg::SObjectCfgItem *item = nullptr;
33 size_t offset = 0;
34 size_t nbytes = 0;
35 size_t i = 0;
36
37 for (i = 0; i < desc.getSize(); i++)
38 {
39 item = desc.objectCfgItemAtOffset(offset);
40
42 {
43 offset += cfgCmnOffset(item);
44 continue;
45 }
46
48 {
49 DAWNERR("unsupported fileio cfg 0x%08" PRIx32 "\n", item->cfgid.v);
50 return -EINVAL;
51 }
52
53 switch (item->cfgid.s.id)
54 {
56 {
57 nbytes = (size_t)item->cfgid.s.size * 4u;
58
59 if (nbytes >= PATH_MAX)
60 {
61 DAWNERR("file path too long: %zu bytes\n", nbytes);
62 return -ENAMETOOLONG;
63 }
64
65 std::memcpy(path, &item->data, nbytes);
66 path[nbytes] = '\0';
67
68 if (std::strstr(path, "..") != nullptr)
69 {
70 DAWNERR("path traversal not allowed: %s\n", path);
71 return -EACCES;
72 }
73
74 if (std::strncmp(path, g_prefix_data, sizeof(g_prefix_data) - 1) != 0 &&
75 std::strncmp(path, g_prefix_tmp, sizeof(g_prefix_tmp) - 1) != 0 &&
76 !pathIsDev(path))
77 {
78 DAWNERR("path not in allowed directory: %s\n", path);
79 return -EACCES;
80 }
81
82 offset += (1 + (size_t)item->cfgid.s.size);
83 break;
84 }
85
87 {
88 const uint8_t *tmp = reinterpret_cast<const uint8_t *>(&item->data);
89
90 perm = *tmp;
91
92 if (perm > IO_FILE_PERM_WRITE_ONCE)
93 {
94 DAWNERR("invalid file permission: %u\n", (unsigned)perm);
95 return -EINVAL;
96 }
97
98 offset += 2;
99 break;
100 }
101
102 default:
103 {
104 DAWNERR("unsupported fileio cfg 0x%08" PRIx32 "\n", item->cfgid.v);
105 return -EINVAL;
106 }
107 }
108 }
109
110 return OK;
111}
112
113CIOFile::~CIOFile()
114{
115 deinit();
116}
117
119{
120 struct stat st;
121 int flags;
122 int ret;
123
124 ret = configureDesc(getDesc());
125 if (ret != OK)
126 {
127 return ret;
128 }
129
130 writeOnceLocked = false;
131
132 // Select open flags based on permission
133
134 if (perm == IO_FILE_PERM_READ)
135 {
136 flags = O_RDONLY;
137 }
138 else if (perm == IO_FILE_PERM_WRITE || perm == IO_FILE_PERM_WRITE_ONCE)
139 {
140 flags = O_WRONLY | O_CREAT;
141 }
142 else
143 {
144 // IO_FILE_PERM_RW
145
146 flags = O_RDWR | O_CREAT;
147 }
148
149 fd = open(path, flags, 0666);
150 if (fd < 0)
151 {
152 DAWNERR("failed to open %s: %d\n", path, errno);
153 return -errno;
154 }
155
156 ret = fstat(fd, &st);
157 if (ret < 0)
158 {
159 DAWNERR("fstat failed for %s: %d\n", path, errno);
160 close(fd);
161 fd = -1;
162 return -errno;
163 }
164
165 // Under the device prefix only char or block device nodes are allowed
166
167 devNode = S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode);
168
169 if (!devNode && pathIsDev(path))
170 {
171 DAWNERR("not a device node: %s\n", path);
172 close(fd);
173 fd = -1;
174 return -EACCES;
175 }
176
177 // Device nodes report a zero st_size, so probe with SEEK_END; they have
178 // a fixed size and cannot be truncated.
179
180 fsize = (size_t)st.st_size;
181
182 if (fsize == 0 && devNode)
183 {
184 off_t end = lseek(fd, 0, SEEK_END);
185 if (end > 0)
186 {
187 fsize = (size_t)end;
188 }
189
190 lseek(fd, 0, SEEK_SET);
191 }
192
193 // A device node has a fixed size, so it cannot tell whether it was
194 // written before - write-once is then enforced per session only.
195
196 writeOnceLocked = perm == IO_FILE_PERM_WRITE_ONCE && !devNode && fsize > 0;
197
198 // A write-only regular file starts empty from the IO point of view
199
200 if (perm == IO_FILE_PERM_WRITE && !devNode)
201 {
202 fsize = 0;
203 }
204
205 return OK;
206}
207
208int CIOFile::truncateFile()
209{
210 int ret;
211
212 // Device nodes have no truncate operation
213
214 if (devNode)
215 {
216 return OK;
217 }
218
219 ret = ftruncate(fd, 0);
220 if (ret < 0)
221 {
222 DAWNERR("ftruncate failed: %d\n", errno);
223 return -EIO;
224 }
225
226 return OK;
227}
228
230{
231 if (fd >= 0)
232 {
233 close(fd);
234 fd = -1;
235 }
236
237 return OK;
238}
239
240int CIOFile::getDataImpl(IODataCmn &data, size_t len)
241{
242 ssize_t nread;
243
244 if (len != 1)
245 {
246 return -EINVAL;
247 }
248
249 if (data.getDataSize() < fsize)
250 {
251 return -ENOMEM;
252 }
253
254 if (lseek(fd, 0, SEEK_SET) < 0)
255 {
256 DAWNERR("lseek failed: %d\n", errno);
257 return -EIO;
258 }
259
260 nread = read(fd, data.getDataPtr(0), fsize);
261 if (nread < 0)
262 {
263 DAWNERR("read failed: %d\n", errno);
264 return -EIO;
265 }
266
267 return OK;
268}
269
271{
272 ssize_t nwritten;
273 int ret;
274
275 if (perm == IO_FILE_PERM_WRITE_ONCE && writeOnceLocked)
276 {
277 DAWNERR("write-once file already written: %s\n", path);
278 return -EPERM;
279 }
280
281 ret = truncateFile();
282 if (ret < 0)
283 {
284 return ret;
285 }
286
287 if (lseek(fd, 0, SEEK_SET) < 0)
288 {
289 DAWNERR("lseek failed: %d\n", errno);
290 return -EIO;
291 }
292
293 nwritten = write(fd, data.getDataPtr(0), data.getDataSize());
294 if (nwritten < 0)
295 {
296 DAWNERR("write failed: %d\n", errno);
297 return -EIO;
298 }
299
300 // A device node keeps its fixed size
301
302 if (!devNode)
303 {
304 fsize = (size_t)nwritten;
305 }
306
307 if (perm == IO_FILE_PERM_WRITE_ONCE)
308 {
309 writeOnceLocked = true;
310 }
311
312 return OK;
313}
314
315int CIOFile::getDataAtImpl(IODataCmn &data, size_t len, size_t offset)
316{
317 ssize_t nread;
318
319 if (len != 1)
320 {
321 return -EINVAL;
322 }
323
324 if (offset >= fsize)
325 {
326 return -EINVAL;
327 }
328
329 if (lseek(fd, (off_t)offset, SEEK_SET) < 0)
330 {
331 DAWNERR("lseek failed: %d\n", errno);
332 return -EIO;
333 }
334
335 nread = read(fd, data.getDataPtr(0), data.getDataSize());
336 if (nread < 0)
337 {
338 DAWNERR("read failed: %d\n", errno);
339 return -EIO;
340 }
341
342 return OK;
343}
344
345int CIOFile::setDataAtImpl(IODataCmn &data, size_t offset)
346{
347 ssize_t nwritten;
348 int ret;
349 size_t endpos;
350
351 if (perm == IO_FILE_PERM_WRITE_ONCE && writeOnceLocked)
352 {
353 DAWNERR("write-once file already written: %s\n", path);
354 return -EPERM;
355 }
356
357 if (offset == 0)
358 {
359 ret = truncateFile();
360 if (ret < 0)
361 {
362 return ret;
363 }
364 }
365
366 if (lseek(fd, (off_t)offset, SEEK_SET) < 0)
367 {
368 DAWNERR("lseek failed: %d\n", errno);
369 return -EIO;
370 }
371
372 nwritten = write(fd, data.getDataPtr(0), data.getDataSize());
373 if (nwritten < 0)
374 {
375 DAWNERR("write failed: %d\n", errno);
376 return -EIO;
377 }
378
379 endpos = offset + (size_t)nwritten;
380 if (!devNode && (offset == 0 || endpos > fsize))
381 {
382 fsize = endpos;
383 }
384
385 if (perm == IO_FILE_PERM_WRITE_ONCE)
386 {
387 writeOnceLocked = true;
388 }
389
390 return OK;
391}
392
393#ifdef CONFIG_DAWN_IO_NOTIFY
394int CIOFile::getFd() const
395{
396 return -1;
397}
398#endif
399
401{
402 return fsize;
403}
404
406{
407 // Block payload uses 1-byte data units.
408
409 return fsize;
410}
411
412bool CIOFile::isRead() const
413{
414 return perm == IO_FILE_PERM_READ || perm == IO_FILE_PERM_RW;
415}
416
418{
419 return perm == IO_FILE_PERM_WRITE || perm == IO_FILE_PERM_RW || perm == IO_FILE_PERM_WRITE_ONCE;
420}
Descriptor wrapper for individual object configuration.
size_t getSize() const
Get number of configuration items for this object.
SObjectCfg::SObjectCfgItem * objectCfgItemAtOffset(size_t offset) const
Get configuration item at specified offset.
virtual int getFd() const
Get file descriptor for notifications.
Definition common.hxx:451
size_t cfgCmnOffset(const SObjectCfg::SObjectCfgItem *cfg)
Get offset of configuration item in descriptor.
Definition common.cxx:144
@ IO_CLASS_ANY
Any I/O class.
Definition common.hxx:86
@ IO_CLASS_FILE
File system I/O.
Definition common.hxx:109
int setDataImpl(IODataCmn &data)
Set data implementation (override in derived classes).
Definition fileio.cxx:270
@ IO_FILE_PERM_RW
Read-write.
Definition fileio.hxx:34
@ IO_FILE_PERM_WRITE
Write-only.
Definition fileio.hxx:33
@ IO_FILE_PERM_WRITE_ONCE
Write-once (per session on device nodes)
Definition fileio.hxx:35
@ IO_FILE_PERM_READ
Read-only.
Definition fileio.hxx:32
int getDataImpl(IODataCmn &data, size_t len)
Get data implementation (override in derived classes).
Definition fileio.cxx:240
size_t getDataDim() const
Get data vector dimension.
Definition fileio.cxx:405
int getDataAtImpl(IODataCmn &data, size_t len, size_t offset)
Get data at byte offset (override in seekable IOs).
Definition fileio.cxx:315
int deinit()
De-initialize object.
Definition fileio.cxx:229
@ IO_FILE_CFG_PATH
File path (null-terminated string, DTYPE_CHAR).
Definition fileio.hxx:41
@ IO_FILE_CFG_PERM
Permission mode (EIOFilePerm, DTYPE_UINT8).
Definition fileio.hxx:42
size_t getDataSize() const
Get data size in bytes.
Definition fileio.cxx:400
bool isWrite() const
Check if IO supports write operations.
Definition fileio.cxx:417
bool isRead() const
Check if IO supports read operations.
Definition fileio.cxx:412
int setDataAtImpl(IODataCmn &data, size_t offset)
Set data at byte offset (override in seekable IOs).
Definition fileio.cxx:345
int configure()
Configure object from descriptor data.
Definition fileio.cxx:118
CDescObject & getDesc()
Get descriptor object for this object.
Definition object.cxx:190
Out-of-tree user-extension hooks for Dawn.
Definition bindable.hxx:13
Base interface for I/O data buffers (static and dynamic).
Definition idata.hxx:21
virtual void * getDataPtr(size_t batch=0)=0
Get pointer to data only (skips timestamp if present).
virtual size_t getDataSize()=0
Get data size in bytes.
Single configuration item within object.
ObjectCfgData_t data[]
Configuration data array (flexible, size from cfgid.s.size).
UObjectCfgId cfgid
Configuration ID header (type, class, id, size, rw, dtype).
ObjectCfgId v
Raw 32-bit ConfigID value (for storage, comparison).
Definition objectcfg.hxx:82
uint32_t cls
Object class (bits 21-29, max 511).
uint32_t id
Configuration identifier (bits 0-4, max 31).
Definition objectcfg.hxx:94
uint32_t size
Configuration data size in 32-bit words (bits 5-14, max 1023).
struct dawn::SObjectCfg::UObjectCfgId::@10 s
Bit-field structure for named member access.