Line data Source code
1 : /*
2 : This program is free software: you can redistribute it and/or modify
3 : it under the terms of the GNU General Public License as published by
4 : the Free Software Foundation, either version 3 of the License, or
5 : (at your option) any later version.
6 :
7 : This program is distributed in the hope that it will be useful,
8 : but WITHOUT ANY WARRANTY; without even the implied warranty of
9 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 : GNU General Public License for more details.
11 :
12 : You should have received a copy of the GNU General Public License
13 : along with this program. If not, see <http://www.gnu.org/licenses/>.
14 : */
15 :
16 : #include "AP_Filesystem.h"
17 :
18 : #include "AP_Filesystem_config.h"
19 :
20 : #if AP_FILESYSTEM_FILE_READING_ENABLED
21 :
22 : #include <AP_HAL/HAL.h>
23 : #include <AP_HAL/Util.h>
24 : #include <AP_Math/AP_Math.h>
25 :
26 : static AP_Filesystem fs;
27 :
28 : // create exactly one "local" filesystem:
29 : #if AP_FILESYSTEM_FATFS_ENABLED
30 : #include "AP_Filesystem_FATFS.h"
31 : static AP_Filesystem_FATFS fs_local;
32 : #elif AP_FILESYSTEM_ESP32_ENABLED
33 : #include "AP_Filesystem_ESP32.h"
34 : static AP_Filesystem_ESP32 fs_local;
35 : #elif AP_FILESYSTEM_LITTLEFS_ENABLED
36 : #include "AP_Filesystem_FlashMemory_LittleFS.h"
37 : static AP_Filesystem_FlashMemory_LittleFS fs_local;
38 : #elif AP_FILESYSTEM_POSIX_ENABLED
39 : #include "AP_Filesystem_posix.h"
40 : static AP_Filesystem_Posix fs_local;
41 : #else
42 : static AP_Filesystem_Backend fs_local;
43 : int errno;
44 : #endif
45 :
46 : #if AP_FILESYSTEM_ROMFS_ENABLED
47 : #include "AP_Filesystem_ROMFS.h"
48 : static AP_Filesystem_ROMFS fs_romfs;
49 : #endif
50 :
51 : #if AP_FILESYSTEM_PARAM_ENABLED
52 : #include "AP_Filesystem_Param.h"
53 : static AP_Filesystem_Param fs_param;
54 : #endif
55 :
56 : #if AP_FILESYSTEM_SYS_ENABLED
57 : #include "AP_Filesystem_Sys.h"
58 : static AP_Filesystem_Sys fs_sys;
59 : #endif
60 :
61 : #if AP_FILESYSTEM_MISSION_ENABLED
62 : #include "AP_Filesystem_Mission.h"
63 : static AP_Filesystem_Mission fs_mission;
64 : #endif
65 :
66 : /*
67 : mapping from filesystem prefix to backend
68 : */
69 : const AP_Filesystem::Backend AP_Filesystem::backends[] = {
70 : { nullptr, fs_local },
71 : #if AP_FILESYSTEM_ROMFS_ENABLED
72 : { "@ROMFS", fs_romfs },
73 : #endif
74 : #if AP_FILESYSTEM_PARAM_ENABLED
75 : { "@PARAM", fs_param },
76 : #endif
77 : #if AP_FILESYSTEM_SYS_ENABLED
78 : { "@SYS", fs_sys },
79 : #endif
80 : #if AP_FILESYSTEM_MISSION_ENABLED
81 : { "@MISSION", fs_mission },
82 : #endif
83 : };
84 :
85 : extern const AP_HAL::HAL& hal;
86 :
87 : #define MAX_FD_PER_BACKEND 256U
88 : #define NUM_BACKENDS ARRAY_SIZE(backends)
89 : #define LOCAL_BACKEND backends[0]
90 : #define BACKEND_IDX(backend) (&(backend) - &backends[0])
91 :
92 : /*
93 : find backend by path
94 : */
95 323260 : const AP_Filesystem::Backend &AP_Filesystem::backend_by_path(const char *&path) const
96 : {
97 : // ignore leading slashes:
98 323260 : const char *path_with_no_leading_slash = path;
99 323260 : if (path_with_no_leading_slash[0] == '/') {
100 8199 : path_with_no_leading_slash = &path_with_no_leading_slash[1];
101 : }
102 1495257 : for (uint8_t i=1; i<NUM_BACKENDS; i++) {
103 1207425 : const uint8_t plen = strlen(backends[i].prefix);
104 1207425 : if (strncmp(path_with_no_leading_slash, backends[i].prefix, plen) == 0) {
105 35428 : path = path_with_no_leading_slash;
106 35428 : path += plen;
107 35428 : if (strlen(path) > 0 && path[0] == '/') {
108 35428 : path++;
109 : }
110 35428 : return backends[i];
111 : }
112 : }
113 : // default to local filesystem
114 287832 : return LOCAL_BACKEND;
115 : }
116 :
117 : /*
118 : return backend by file descriptor
119 : */
120 3535945 : const AP_Filesystem::Backend &AP_Filesystem::backend_by_fd(int &fd) const
121 : {
122 3535945 : if (fd < 0 || uint32_t(fd) >= NUM_BACKENDS*MAX_FD_PER_BACKEND) {
123 0 : return LOCAL_BACKEND;
124 : }
125 3535945 : const uint8_t idx = uint32_t(fd) / MAX_FD_PER_BACKEND;
126 3535945 : fd -= idx * MAX_FD_PER_BACKEND;
127 3535945 : return backends[idx];
128 : }
129 :
130 60392 : int AP_Filesystem::open(const char *fname, int flags, bool allow_absolute_paths)
131 : {
132 60392 : const Backend &backend = backend_by_path(fname);
133 60392 : int fd = backend.fs.open(fname, flags, allow_absolute_paths);
134 60392 : if (fd < 0) {
135 11120 : return -1;
136 : }
137 49272 : if (uint32_t(fd) >= MAX_FD_PER_BACKEND) {
138 0 : backend.fs.close(fd);
139 0 : errno = ERANGE;
140 0 : return -1;
141 : }
142 : // offset fd so we can recognise the backend
143 49272 : const uint8_t idx = (&backend - &backends[0]);
144 49272 : fd += idx * MAX_FD_PER_BACKEND;
145 49272 : return fd;
146 : }
147 :
148 45568 : int AP_Filesystem::close(int fd)
149 : {
150 45568 : const Backend &backend = backend_by_fd(fd);
151 45568 : return backend.fs.close(fd);
152 : }
153 :
154 670320 : int32_t AP_Filesystem::read(int fd, void *buf, uint32_t count)
155 : {
156 670320 : const Backend &backend = backend_by_fd(fd);
157 670320 : return backend.fs.read(fd, buf, count);
158 : }
159 :
160 1028168 : int32_t AP_Filesystem::write(int fd, const void *buf, uint32_t count)
161 : {
162 1028168 : const Backend &backend = backend_by_fd(fd);
163 1028168 : return backend.fs.write(fd, buf, count);
164 : }
165 :
166 5 : int AP_Filesystem::fsync(int fd)
167 : {
168 5 : const Backend &backend = backend_by_fd(fd);
169 5 : return backend.fs.fsync(fd);
170 : }
171 :
172 41578 : int32_t AP_Filesystem::lseek(int fd, int32_t offset, int seek_from)
173 : {
174 41578 : const Backend &backend = backend_by_fd(fd);
175 41578 : return backend.fs.lseek(fd, offset, seek_from);
176 : }
177 :
178 80684 : int AP_Filesystem::stat(const char *pathname, struct stat *stbuf)
179 : {
180 80684 : const Backend &backend = backend_by_path(pathname);
181 80684 : return backend.fs.stat(pathname, stbuf);
182 : }
183 :
184 3 : int AP_Filesystem::unlink(const char *pathname)
185 : {
186 3 : const Backend &backend = backend_by_path(pathname);
187 3 : return backend.fs.unlink(pathname);
188 : }
189 :
190 1782 : int AP_Filesystem::mkdir(const char *pathname)
191 : {
192 1782 : const Backend &backend = backend_by_path(pathname);
193 1782 : return backend.fs.mkdir(pathname);
194 : }
195 :
196 4 : int AP_Filesystem::rename(const char *oldpath, const char *newpath)
197 : {
198 4 : const Backend &oldbackend = backend_by_path(oldpath);
199 :
200 : // Don't need the backend again, but we also need to remove the backend pre-fix from the new path.
201 4 : const Backend &newbackend = backend_by_path(newpath);
202 :
203 : // Don't try and rename between backends.
204 4 : if (&oldbackend != &newbackend) {
205 0 : return -1;
206 : }
207 :
208 4 : return oldbackend.fs.rename(oldpath, newpath);
209 : }
210 :
211 20517 : AP_Filesystem::DirHandle *AP_Filesystem::opendir(const char *pathname)
212 : {
213 : // support reading a list of "@" filesystems (e.g. @SYS) in
214 : // listing of root directory. Note that backend_by_path modifies
215 : // its parameter.
216 20517 : if (strlen(pathname) == 0 ||
217 20517 : (strlen(pathname) == 1 && pathname[0] == '/')) {
218 2 : virtual_dirent.backend_ofs = 0;
219 2 : virtual_dirent.d_off = 0;
220 : #if AP_FILESYSTEM_HAVE_DIRENT_DTYPE
221 2 : virtual_dirent.de.d_type = DT_DIR;
222 : #endif
223 : } else {
224 20515 : virtual_dirent.backend_ofs = 255;
225 : }
226 :
227 20517 : const Backend &backend = backend_by_path(pathname);
228 20517 : DirHandle *h = NEW_NOTHROW DirHandle;
229 20517 : if (!h) {
230 0 : return nullptr;
231 : }
232 20517 : h->dir = backend.fs.opendir(pathname);
233 20517 : if (h->dir == nullptr) {
234 1 : delete h;
235 1 : return nullptr;
236 : }
237 20516 : h->fs_index = BACKEND_IDX(backend);
238 :
239 20516 : return h;
240 : }
241 :
242 1573488 : struct dirent *AP_Filesystem::readdir(DirHandle *dirp)
243 : {
244 1573488 : if (!dirp) {
245 0 : return nullptr;
246 : }
247 1573488 : const Backend &backend = backends[dirp->fs_index];
248 1573488 : struct dirent * ret = backend.fs.readdir(dirp->dir);
249 1573488 : if (ret != nullptr) {
250 1552972 : return ret;
251 : }
252 :
253 : // virtual directory entries in the root directory (e.g. @SYS, @MISSION)
254 20521 : for (; ret == nullptr && virtual_dirent.backend_ofs < ARRAY_SIZE(AP_Filesystem::backends); virtual_dirent.backend_ofs++) {
255 5 : const char *prefix = backends[virtual_dirent.backend_ofs].prefix;
256 5 : if (prefix == nullptr) {
257 1 : continue;
258 : }
259 4 : if (prefix[0] != '@') {
260 0 : continue;
261 : }
262 :
263 : // only return @ entries in root if we can successfully opendir them:
264 4 : auto *d = backends[virtual_dirent.backend_ofs].fs.opendir("");
265 4 : if (d == nullptr) {
266 2 : continue;
267 : }
268 2 : backends[virtual_dirent.backend_ofs].fs.closedir(d);
269 :
270 : // found a virtual directory we haven't returned yet
271 2 : strncpy_noterm(virtual_dirent.de.d_name, prefix, sizeof(virtual_dirent.de.d_name));
272 2 : virtual_dirent.d_off++;
273 2 : ret = &virtual_dirent.de;
274 : }
275 20516 : return ret;
276 : }
277 :
278 20516 : int AP_Filesystem::closedir(DirHandle *dirp)
279 : {
280 20516 : if (!dirp) {
281 0 : return -1;
282 : }
283 20516 : const Backend &backend = backends[dirp->fs_index];
284 20516 : int ret = backend.fs.closedir(dirp->dir);
285 20516 : delete dirp;
286 20516 : return ret;
287 : }
288 :
289 : // return number of bytes that should be written before fsync for optimal
290 : // streaming performance/robustness. if zero, any number can be written.
291 1026028 : uint32_t AP_Filesystem::bytes_until_fsync(int fd)
292 : {
293 1026028 : const Backend &backend = backend_by_fd(fd);
294 1026028 : return backend.fs.bytes_until_fsync(fd);
295 : }
296 :
297 : // return free disk space in bytes
298 135293 : int64_t AP_Filesystem::disk_free(const char *path)
299 : {
300 135293 : const Backend &backend = backend_by_path(path);
301 135293 : return backend.fs.disk_free(path);
302 : }
303 :
304 : // return total disk space in bytes
305 1 : int64_t AP_Filesystem::disk_space(const char *path)
306 : {
307 1 : const Backend &backend = backend_by_path(path);
308 1 : return backend.fs.disk_space(path);
309 : }
310 :
311 :
312 : /*
313 : set mtime on a file
314 : */
315 0 : bool AP_Filesystem::set_mtime(const char *filename, const uint32_t mtime_sec)
316 : {
317 0 : const Backend &backend = backend_by_path(filename);
318 0 : return backend.fs.set_mtime(filename, mtime_sec);
319 : }
320 :
321 : // if filesystem is not running then try a remount
322 0 : bool AP_Filesystem::retry_mount(void)
323 : {
324 0 : return LOCAL_BACKEND.fs.retry_mount();
325 : }
326 :
327 : // unmount filesystem for reboot
328 0 : void AP_Filesystem::unmount(void)
329 : {
330 0 : return LOCAL_BACKEND.fs.unmount();
331 : }
332 :
333 : /*
334 : Load a file's contents into memory. Returned object must be `delete`d to free
335 : the data. The data is guaranteed to be null-terminated such that it can be
336 : treated as a string.
337 : */
338 24580 : FileData *AP_Filesystem::load_file(const char *filename)
339 : {
340 24580 : const Backend &backend = backend_by_path(filename);
341 24580 : return backend.fs.load_file(filename);
342 : }
343 :
344 : // reads a line into buf, guaranteeing null-termination. buflen is
345 : // the full size of buf, including the space required for the null
346 : // terminator, so up to buflen-1 characters are returned. cr or lf
347 : // terminates the line and is consumed but not returned.
348 724278 : bool AP_Filesystem::fgets(char *buf, uint8_t buflen, int fd)
349 : {
350 724278 : if (buflen == 0) {
351 : // we can't null-terminate the buffer, which we guarantee
352 0 : return false;
353 : }
354 :
355 724278 : const Backend &backend = backend_by_fd(fd);
356 :
357 : // we will need to seek back to the right location at the end
358 724278 : auto offset_start = backend.fs.lseek(fd, 0, SEEK_CUR);
359 724278 : if (offset_start < 0) {
360 0 : return false;
361 : }
362 :
363 724278 : auto n = backend.fs.read(fd, buf, buflen-1U);
364 724278 : if (n <= 0) {
365 20210 : return false;
366 : }
367 :
368 704068 : uint8_t i = 0;
369 14966044 : for (; i < n; i++) {
370 14966044 : if (buf[i] == '\r' || buf[i] == '\n') {
371 : break;
372 : }
373 : }
374 704068 : buf[i] = '\0';
375 :
376 : // get back to the right offset, consuming the line terminator if
377 : // we found one. If we did not find one we have either filled the
378 : // buffer or returned an unterminated final line; in both cases
379 : // the seek target must not extend past the data we consumed -
380 : // backends such as ROMFS refuse to seek past the end of the file.
381 704068 : const int32_t new_offset = offset_start + i + (i < n ? 1 : 0);
382 704068 : if (backend.fs.lseek(fd, new_offset, SEEK_SET) != new_offset) {
383 : // we need to fail if we can't seek back or the caller may loop or get corrupt data
384 0 : return false;
385 : }
386 :
387 704068 : return true;
388 : }
389 :
390 : // run crc32 over file with given name, returns true if successful
391 95 : bool AP_Filesystem::crc32(const char *fname, uint32_t& checksum)
392 : {
393 : // Ensure value is initialized
394 95 : checksum = 0;
395 :
396 : // Open file in readonly mode
397 95 : int fd = open(fname, O_RDONLY);
398 95 : if (fd == -1) {
399 1 : return false;
400 : }
401 :
402 : // Buffer to store data temporarily
403 94 : const ssize_t buff_len = 64;
404 : uint8_t buf[buff_len];
405 :
406 : // Read into buffer and run crc
407 : ssize_t read_size;
408 : do {
409 23912 : read_size = read(fd, buf, buff_len);
410 23912 : if (read_size == -1) {
411 : // Read error, note that we have changed the checksum value in this case
412 0 : close(fd);
413 0 : return false;
414 : }
415 23912 : checksum = crc_crc32(checksum, buf, MIN(read_size, buff_len));
416 23912 : } while (read_size > 0);
417 :
418 94 : close(fd);
419 :
420 94 : return true;
421 : }
422 :
423 :
424 : #if AP_FILESYSTEM_FORMAT_ENABLED
425 : // format filesystem
426 0 : bool AP_Filesystem::format(void)
427 : {
428 0 : if (hal.util->get_soft_armed()) {
429 0 : return false;
430 : }
431 0 : return LOCAL_BACKEND.fs.format();
432 : }
433 0 : AP_Filesystem_Backend::FormatStatus AP_Filesystem::get_format_status(void) const
434 : {
435 0 : return LOCAL_BACKEND.fs.get_format_status();
436 : }
437 : #endif
438 :
439 : /*
440 : stat wrapper for scripting
441 : */
442 4 : bool AP_Filesystem::stat(const char *pathname, stat_t &stbuf)
443 : {
444 : struct stat st;
445 4 : if (fs.stat(pathname, &st) != 0) {
446 3 : return false;
447 : }
448 1 : stbuf.size = st.st_size;
449 1 : stbuf.mode = st.st_mode;
450 : // these wrap in 2038
451 1 : stbuf.atime = st.st_atime;
452 1 : stbuf.ctime = st.st_ctime;
453 1 : stbuf.mtime = st.st_mtime;
454 1 : return true;
455 : }
456 :
457 : // get_singleton for scripting
458 4 : AP_Filesystem *AP_Filesystem::get_singleton(void)
459 : {
460 4 : return &fs;
461 : }
462 :
463 : namespace AP
464 : {
465 5429194 : AP_Filesystem &FS()
466 : {
467 5429194 : return fs;
468 : }
469 : }
470 :
471 : #endif // AP_FILESYSTEM_FILE_READING_ENABLED
|