LCOV - code coverage report
Current view: top level - AP_Filesystem - AP_Filesystem.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 149 176 84.7 %
Date: 2026-09-09 11:02:08 Functions: 24 29 82.8 %

          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

Generated by: LCOV version 1.14