From 939d55fd5ceccf012f4131ae79529c57ff8b54e3 Mon Sep 17 00:00:00 2001 From: hpirila <37920282+hpirila@users.noreply.github.com> Date: Wed, 25 Jan 2023 17:31:52 +0700 Subject: [PATCH] Initial commit --- .gitattributes | 2 + .gitignore | 5 + .vscode/extensions.json | 10 ++ README.md | 211 +++++++++++++++++++++++++++ platformio.ini | 16 +++ src/main.cpp | 75 ++++++++++ src/ssh.cpp | 307 ++++++++++++++++++++++++++++++++++++++++ src/ssh.hpp | 52 +++++++ src/storage.cpp | 174 +++++++++++++++++++++++ src/storage.hpp | 29 ++++ 10 files changed, 881 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 README.md create mode 100644 platformio.ini create mode 100644 src/main.cpp create mode 100644 src/ssh.cpp create mode 100644 src/ssh.hpp create mode 100644 src/storage.cpp create mode 100644 src/storage.hpp diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..beb251c --- /dev/null +++ b/README.md @@ -0,0 +1,211 @@ +# ESP32 Arduino SSH wrapper class + +This is a wrapper class and example program for [LibSSH-ESP32] Arduino SSH library. +This program features are: +- ssh connection to the server using + - Password authentication + - Public key authentication + - Public key authentication with passphrase encrypted private key +- execute a command over ssh connection (sshCommand) +- copy file from ESP32 to the server using scp (scpPut) +- copy file from the server to ESP32 using scp (scpGet) +- different file systems in ESP32 + - SPIFFS (path prefix is /spiffs/) + - SD card (path prefix is /sd/) + - LittleFS (path prefix is /littlefs/) + +## Usage +You can compile this project in platformIO and upload it to ESP32. Modify main.cpp as per testing instructions below. + +Alternatively, you can add this library to your platformIO project by adding these two dependencies in platformio.ini under lib_dep. + +``` +lib_deps = + ewpa/LibSSH-ESP32@^3.0.1 + https://github.com/hpirila/ESP32-Arduino-SSH.git +``` + +## Testing +Have a Linux server ready where you can ssh using a password or public key authentication. Password authentication may be easier to do first. +### Linux server +You need a Linux server where to access using ssh. You can create one, for example, to Google or Amazon cloud. This program follows an example where the Linux server is Ubuntu in the Amazon cloud, but any Linux distribution shall work. +To access the Linux server, you need to know its +- IP address or domain name +- Username and password +or +- Username and have the public and private key available for connection + +### Configure Wifi +You need to set your WiFi SSID and password to this part in main.cpp. + +```WiFi.begin("ssid", "Wifi_password");``` + +### Configure SSH connection for password authentication +#### In Linux server +By default, Ubuntu in Amazon AWS does not allow ssh using password authentication. You need to do three things to enable password authentication. + +Set password for ubuntu user: + +```sudo passwd ubuntu``` + +In this example, I set the password to ```System#1``` + +Enable ssh password authentication + +```sudo nano /etc/ssh/sshd_config``` + +Change this line from + +```PasswordAuthentication no``` + +to + +```PasswordAuthentication yes``` + +Restart sshd process + +```sudo systemctl restart sshd``` + +#### In main.cpp file + +Set your Linux server domain name or IP address in this line. You can find the domain name in the Amazon AWS EC2 console, Instance summary, Public IPv4 DNS. + +```ssh.connectWithPassword("ec2-111-112-113-114.ap-southeast-1.compute.amazonaws.com","ubuntu","System#1");``` + +That's all. You can now compile and upload the program to ESP32. +- It should connect to the server using username ubuntu and password System#1. +- create a file in the server called testFile1 +- copy that file to the ESP32 SPIFFS file system +- copy the file back to server using different filename testFile2 +- compares testFile1 and testFile2 and writes the result to result.txt + +Login to the server and see if the files exist and the content of the result file. +``` +ubuntu@my-server:~$ ls -l testFile* result.txt +-rw-rw-r-- 1 ubuntu ubuntu 44 Jan 22 10:25 result.txt +-rw-rw-r-- 1 ubuntu ubuntu 56 Jan 22 10:25 testFile1 +-rw-rw-r-- 1 ubuntu ubuntu 56 Jan 22 10:25 testFile2 +ubuntu@my-server:~$ cat result.txt +Files testFile1 and testFile2 are identical +ubuntu@my-server:~$ +``` + +### Configure SSH connection for public key authentication +Now that we can copy files to ESP32, it is easy to configure public key authentication. We need first to generate the keys, add the public key to authorized_keys and copy the key files to ESP32 +#### In Linux server +Generate key pair using ssh-keygen without a passphrase. Just press enter when it asks to enter a passphrase. +``` +ubuntu@my-server:~$ ssh-keygen -f key1 +Generating public/private rsa key pair. +Enter passphrase (empty for no passphrase): +Enter same passphrase again: +Your identification has been saved in key1 +Your public key has been saved in key1.pub +The key fingerprint is: +SHA256:g21tszp4PlPBZp9ToOxgyWOog+HbahI8d5ABj7FbaAE ubuntu@my-server +The key's randomart image is: ++---[RSA 3072]----+ +|E+. | +| B. . | +| = oo o + . . | +|. +o .oB.B . | +|.o o...oS*+o o | +|.oo.o. . ooo+ | +| .oo.. . .. . | +|. o . . =. | +| o.. oo+ | ++----[SHA256]-----+ +ubuntu@my-server:~$ +``` +And another key pair with a passphrase. Set passphrase to MyPassPhrase. +``` +ubuntu@my-server:~$ ssh-keygen -f key2 +Generating public/private rsa key pair. +Enter passphrase (empty for no passphrase): +Enter same passphrase again: +Your identification has been saved in key2 +Your public key has been saved in key2.pub +The key fingerprint is: +SHA256:GL7UTforSMOQ2N1mSHmcdCM7NBPPnoc7jpsgvKPNVG4 ubuntu@my-server +The key's randomart image is: ++---[RSA 3072]----+ +| +Boo | +| o.+O . | +| o +.+o + | +| . +.o++* o | +| o=oS = . | +| . ++. . o | +| +.Eo + | +| +.+...+ o | +| ..+. +oo | ++----[SHA256]-----+ +``` +Now add the two public keys to .ssh/authorized_keys file +``` +cat key1.pub >> ~/.ssh/authorized_keys +cat key2.pub >> ~/.ssh/authorized_keys +chmod 600 ~/.ssh/authorized_keys +``` +#### In main.cpp file +Uncomment these lines to copy the key files to ESP32 +``` +ssh.scpGetFile("key1", "/spiffs/key1"); +ssh.scpGetFile("key1.pub", "/spiffs/key1.pub"); +ssh.scpGetFile("key2", "/spiffs/key2"); +ssh.scpGetFile("key2.pub", "/spiffs/key2.pub"); +``` +Upload the program to ESP32 and run it. + +#### In Linux server +Now the key files are copied to ESP32, and we can remove the password from the ubuntu user and disable password authentication. + +Remove password from user ubuntu + +```sudo passwd -d ubuntu``` + +Disable ssh password authentication + +```sudo nano /etc/ssh/sshd_config``` + +Change this line from + +```PasswordAuthentication yes``` + +to + +```PasswordAuthentication no``` + +Restart sshd process + +```sudo systemctl restart sshd``` + +Remove files testFile1, testFile2 and result.txt + +```rm ~/testFile1 ~/testFile2 ~/result.txt``` + +### Test public key authentication, no passphrase +#### In main.cpp file +Comment password authentication line and uncomment and edit public key authentication without passphrase line. +``` +// ssh.connectWithPassword("ec2-111-112-113-114.ap-southeast-1.compute.amazonaws.com", "ubuntu","System#1"); +ssh.connectWithKey("ec2-111-112-113-114.ap-southeast-1.compute.amazonaws.com", "ubuntu","/spiffs/key1.pub","/spiffs/key1"); +``` +Upload the program to ESP32 and run it. Check again on the Linux server that testFile1, testFile2 and result.txt are back with the correct content. Remove testFile1, testFile2 and result.txt again. +### Test public key authentication with a passphrase +#### In main.cpp file +Comment public key authentication without passphrase line and uncomment public key authentication with passphrase line +``` +// ssh.connectWithKey("ec2-111-112-113-114.ap-southeast-1.compute.amazonaws.com", "ubuntu","/spiffs/key1.pub","/spiffs/key1"); +ssh.connectWithKey("ec2-111-112-113-114.ap-southeast-1.compute.amazonaws.com", "ubuntu","/spiffs/key2.pub","/spiffs/key2","MyPassPhrase"); +``` +Upload the program to ESP32 and run it. Check again on the Linux server that testFile1, testFile2 and result.txt are back with the correct content. +## Troubleshooting +SPIFFS and LittleFS may not be formatted when you try the first time. You can see this error `E (3332) SPIFFS: mount failed`. Just press the ESP32 board reset button or re-upload to try again. + +It can be helpful to download files from ESP32 back to the computer using Visual Studio Code and platformIO. It is possible using this [file system downloader plugin]. + + + [LibSSH-ESP32]: + [file system downloader plugin]: + + diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..368414e --- /dev/null +++ b/platformio.ini @@ -0,0 +1,16 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +monitor_speed = 115200 +lib_deps = ewpa/LibSSH-ESP32@^3.0.1 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..125deef --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,75 @@ +#include +#include "ssh.hpp" + +const unsigned int configSTACK = 40960; +TaskHandle_t sshHandle = NULL; + +void sshTask(void* pvParameter) { + SSH ssh{}; + + // Pick one of the ssh authentication methods to connect + Serial.println("SSH Connecting to server..."); + + // With password (in server side create password for the user and allow + // password authentication in /etc/ssh/sshd_config) + ssh.connectWithPassword("ec2-111-112-113-114.ap-southeast-1.compute.amazonaws.com", "ubuntu","System#1"); + + // With public key + // ssh.connectWithKey("101.102.103.104", "ubuntu","/spiffs/key1.pub","/spiffs/key1"); + // With public key, encrypted private key + // ssh.connectWithKey("192.168.1.200", "hpirila","/spiffs/key2.pub","/spiffs/key2","MyPassPhrase"); + + if (ssh.isConnected) { + Serial.println("SSH is connected!\n"); + + Serial.println("Lets create a test file in server"); + ssh.sendCommand("echo \"This is a test file for ESP32 Arduino SSH wrapper class\" > testFile1"); + + Serial.println("Copying testFile1 from server to ESP32 spiffs file system"); + ssh.scpGetFile("testFile1", "/spiffs/testFile1"); + + Serial.println("Copying testFile1 from ESP32 back to server with new name testFile2"); + ssh.scpPutFile("/spiffs/testFile1", "testFile2"); + + Serial.println("Compare testFile1 and testFile2 and print the result to result.txt\n"); + Serial.println("Login to server and cat result.txt"); + Serial.println("It should say testFile1 and testFile2 are identical.\n"); + ssh.sendCommand("diff -s testFile1 testFile2 > result.txt"); + + // ssh.scpGetFile("key1", "/spiffs/key1"); + // ssh.scpGetFile("key1.pub", "/spiffs/key1.pub"); + // ssh.scpGetFile("key2", "/spiffs/key2"); + // ssh.scpGetFile("key2.pub", "/spiffs/key2.pub"); + + } else { + Serial.println("SSH connection failed."); + } + + Serial.println("Close ssh connection"); + ssh.end(); + Serial.println("Kill ssh task"); + vTaskDelete(NULL); +} + +void setup(void) { + Serial.begin(115200); + + WiFi.begin("ssid", "Wifi_password"); + + Serial.println("Connecting to WiFi..."); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(100); + } + Serial.println("\nConnected to the WiFi network"); + Serial.print("Local ESP32 IP: "); + Serial.println(WiFi.localIP()); + + xTaskCreatePinnedToCore(sshTask, "ctl", configSTACK, NULL, + (tskIDLE_PRIORITY + 3), &sshHandle, + portNUM_PROCESSORS - 1); +} + +void loop(void) { + delay(1); +} diff --git a/src/ssh.cpp b/src/ssh.cpp new file mode 100644 index 0000000..fe6093d --- /dev/null +++ b/src/ssh.cpp @@ -0,0 +1,307 @@ +#include "ssh.hpp" +#include "libssh_esp32.h" +#include "storage.hpp" + +SSH::SSH() { + isConnected = false; +} + +SSH::SSH(std::string host, + std::string user, + std::string publicKey, + std::string privateKey) { + connectWithKey(host, user, publicKey, privateKey); +} + +SSH::SSH(std::string host, + std::string user, + std::string publicKey, + std::string privateKey, + std::string passPhrase) { + connectWithKey(host, user, publicKey, privateKey, passPhrase); +} + +SSH::SSH(std::string host, std::string user, std::string password) { + connectWithPassword(host, user, password); +} + +SSH::~SSH() { + end(); +} + +void SSH::connectWithPassword(std::string host, + std::string user, + std::string password) { + if (init()) { + if (connect(host, user)) { + if (authenticatePassword(password)) { + isConnected = true; + } + } + } +} + +void SSH::connectWithKey(std::string host, + std::string user, + std::string publicKey, + std::string privateKey) { + isConnected = false; + if (init()) { + if (connect(host, user)) { + if (authenticateKey(publicKey, privateKey, NULL)) { + isConnected = true; + } + } + } +} + +void SSH::connectWithKey(std::string host, + std::string user, + std::string publicKey, + std::string privateKey, + std::string passPhrase) { + isConnected = false; + if (init()) { + if (connect(host, user)) { + if (authenticateKey(publicKey, privateKey, + passPhrase.empty() ? NULL : passPhrase.c_str())) { + isConnected = true; + } + } + } +} + +void SSH::end() { + ssh_disconnect(session); + ssh_free(session); + ssh_finalize(); + isConnected = false; +} + +bool SSH::init() { + libssh_begin(); + ssh_init(); + session = ssh_new(); + if (session == NULL) { + return false; + } + return true; +} + +bool SSH::connect(std::string host, std::string user) { + ssh_options_set(session, SSH_OPTIONS_HOST, host.c_str()); + ssh_options_set(session, SSH_OPTIONS_USER, user.c_str()); + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, 0); + + int rc = ssh_connect(session); + if (rc != SSH_OK) { + return false; + } + return true; +} + +bool SSH::authenticateKey(std::string publicKey, + std::string privateKey, + const char* passPhrase) { + ssh_key key = NULL; + int rc; + + storage::FileLocation publicKeyLocation; + storage::FileLocation privateKeyLocation; + + publicKeyLocation = storage::parseFsType(publicKey); + privateKeyLocation = storage::parseFsType(privateKey); + + if (!storage::fileSystemBegin(publicKeyLocation.fsType)) { + return false; + } + + rc = ssh_pki_import_pubkey_file(publicKey.c_str(), &key); + + if (rc != SSH_OK) { + return false; + } + + rc = ssh_userauth_try_publickey(session, NULL, key); + if (rc != SSH_AUTH_SUCCESS) { + return false; + } + + ssh_key_free(key); + storage::fileSystemEnd(publicKeyLocation.fsType); + storage::fileSystemBegin(privateKeyLocation.fsType); + + rc = ssh_pki_import_privkey_file(privateKey.c_str(), passPhrase, NULL, NULL, + &key); + + if (rc != SSH_AUTH_SUCCESS) { + return false; + } + + rc = ssh_userauth_publickey(session, NULL, key); + if (rc != SSH_AUTH_SUCCESS) { + return false; + } + + ssh_key_free(key); + fileSystemEnd(privateKeyLocation.fsType); + + return true; +} + +bool SSH::authenticatePassword(std::string password) { + int rc = ssh_userauth_password(session, NULL, password.c_str()); + if (rc != SSH_AUTH_SUCCESS) { + return false; + } + + return true; +} + +bool SSH::sendCommand(std::string cmd) { + ssh_channel channel; + channel = ssh_channel_new(session); + + if (channel == NULL) { + return false; + } + + int rc = ssh_channel_open_session(channel); + if (rc < 0) { + return false; + } + + ssh_channel_request_exec(channel, cmd.c_str()); + ssh_channel_close(channel); + ssh_channel_free(channel); + + return true; +} + +bool SSH::scpGetFile(std::string sourceFile, std::string destinationFile) { + int bytesRead; + long fileSize; + long freeSpace; + int resultCode; + u_int8_t buffer[BUFFER_SIZE]; + storage::FileLocation fl; + File file; + + fl = storage::parseFsType(destinationFile); + + if (!storage::fileSystemBegin(fl.fsType)) { + return false; + } + + ssh_scp scp = ssh_scp_new(session, SSH_SCP_READ, sourceFile.c_str()); + resultCode = ssh_scp_init(scp); + if (resultCode != SSH_OK) { + storage::fileSystemEnd(fl.fsType); + ssh_scp_free(scp); + return false; + } + + resultCode = ssh_scp_pull_request(scp); + if (resultCode != SSH_SCP_REQUEST_NEWFILE) { + storage::fileSystemEnd(fl.fsType); + ssh_scp_free(scp); + return false; + } + + fileSize = ssh_scp_request_get_size(scp); + freeSpace = storage::fileSystemTotalBytes(fl.fsType) - + storage::fileSystemUsedBytes(fl.fsType); + + if (fileSize > (freeSpace - std::numeric_limits::max())) { + storage::fileSystemEnd(fl.fsType); + ssh_scp_free(scp); + return false; + } + + if (storage::fileExists(fl)) { + storage::fileRemove(fl); + } + + file = storage::fileOpen(fl, "wb", true); + if (!file) { + storage::fileSystemEnd(fl.fsType); + ssh_scp_free(scp); + return false; + } + + ssh_scp_accept_request(scp); + + long counter = 0; + long total = 0; + while (true) { + bytesRead = ssh_scp_read(scp, buffer, BUFFER_SIZE); + total += bytesRead; + counter++; + + if (bytesRead < 0) { + break; + } + for (int i = 0; i < bytesRead; i++) { + file.write(buffer[i]); + } + } + + file.close(); + storage::fileSystemEnd(fl.fsType); + ssh_scp_free(scp); + + return true; +} + +bool SSH::scpPutFile(std::string sourceFile, std::string destinationFile) { + int bytesRead; + long fileSize; + int resultCode; + char buffer[BUFFER_SIZE]; + storage::FileLocation fl; + File file; + + fl = storage::parseFsType(sourceFile); + + if (!storage::fileSystemBegin(fl.fsType)) { + return false; + } + + file = storage::fileOpen(fl, "r", false); + if (!file) { + return false; + } + + fileSize = file.size(); + ssh_scp scp = ssh_scp_new(session, SSH_SCP_WRITE, destinationFile.c_str()); + resultCode = ssh_scp_init(scp); + + if (resultCode != SSH_OK) { + file.close(); + storage::fileSystemEnd(fl.fsType); + ssh_scp_free(scp); + return false; + } + + resultCode = + ssh_scp_push_file(scp, sourceFile.c_str(), fileSize, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + + if (resultCode != SSH_OK) { + file.close(); + storage::fileSystemEnd(fl.fsType); + ssh_scp_free(scp); + return false; + } + + while (file.available()) { + bytesRead = file.readBytes(buffer, BUFFER_SIZE); + ssh_scp_write(scp, buffer, bytesRead); + } + + file.close(); + storage::fileSystemEnd(fl.fsType); + ssh_scp_free(scp); + + return true; +} diff --git a/src/ssh.hpp b/src/ssh.hpp new file mode 100644 index 0000000..c146c36 --- /dev/null +++ b/src/ssh.hpp @@ -0,0 +1,52 @@ +#ifndef SSH_hpp +#define SSH_hpp + +#include +#include + +#define BUFFER_SIZE 4096 + +class SSH { + private: + ssh_session session; + ssh_channel channel; + bool init(); + bool connect(std::string host, std::string user); + bool authenticateKey(std::string publicKey, + std::string privateKey, + const char* passPhrase); + bool authenticatePassword(std::string password); + + public: + bool isConnected; + SSH(); + SSH(std::string hostIn, + std::string userIn, + std::string privateKeyIn, + std::string publicKeyIn); + SSH(std::string hostIn, + std::string userIn, + std::string privateKeyIn, + std::string publicKeyIn, + std::string passPhraseIn); + SSH(std::string host, std::string user, std::string password); + ~SSH(); + void connectWithPassword(std::string hostIn, + std::string userIn, + std::string passwordIn); + void connectWithKey(std::string hostIn, + std::string userIn, + std::string privateKeyIn, + std::string publicKeyIn); + void connectWithKey(std::string hostIn, + std::string userIn, + std::string privateKeyIn, + std::string publicKeyIn, + std::string passPhraseIn); + bool sendCommand(std::string cmd); + bool scpGetFile(std::string sourceFile, std::string destinationFile); + bool scpPutFile(std::string sourceFile, std::string destinationFile); + void end(); +}; + +#endif diff --git a/src/storage.cpp b/src/storage.cpp new file mode 100644 index 0000000..3a0dfb2 --- /dev/null +++ b/src/storage.cpp @@ -0,0 +1,174 @@ +#include "storage.hpp" +#include +#include +#include +#include + +namespace storage { +File fileOpen(FileLocation fileLocation, + std::string fileMode, + bool fileCreate) { + File file; + const char* fileName = fileLocation.filePath.c_str(); + const char* openingMode = fileMode.c_str(); + + switch (fileLocation.fsType) { + case FSType::sd: + file = SD.open(fileName, openingMode, fileCreate); + break; + case FSType::spiffs: + file = SPIFFS.open(fileName, openingMode, fileCreate); + break; + case FSType::littlefs: + file = LittleFS.open(fileName, openingMode, fileCreate); + break; + default: + break; + } + return file; +} + +FSType findFile(std::string fileName) { + if (SD.begin()) { + if (SD.exists(fileName.c_str())) { + return FSType::sd; + } else { + SD.end(); + } + } + if (SPIFFS.begin()) { + if (SPIFFS.exists(fileName.c_str())) { + return FSType::spiffs; + } else { + SPIFFS.end(); + } + } + if (LittleFS.begin()) { + if (LittleFS.exists(fileName.c_str())) { + return FSType::spiffs; + } else { + LittleFS.end(); + } + } + return FSType::none; +} + +bool fileRemove(FileLocation fileLocation) { + if (fileLocation.fsType == FSType::spiffs) { + if (SPIFFS.remove(fileLocation.filePath.c_str())) { + return true; + } + } else if (fileLocation.fsType == FSType::littlefs) { + if (LittleFS.remove(fileLocation.filePath.c_str())) { + return true; + } + } else if (fileLocation.fsType == FSType::sd) { + if (SD.remove(fileLocation.filePath.c_str())) { + return true; + } + } + return false; +} + +bool fileExists(FileLocation fileLocation) { + if (fileLocation.fsType == FSType::spiffs) { + if (SPIFFS.exists(fileLocation.filePath.c_str())) { + return true; + } + } else if (fileLocation.fsType == FSType::littlefs) { + if (LittleFS.exists(fileLocation.filePath.c_str())) { + return true; + } + } else if (fileLocation.fsType == FSType::sd) { + if (SD.exists(fileLocation.filePath.c_str())) { + return true; + } + } + return false; +} + +long fileSystemTotalBytes(FSType fsType) { + long totalBytes = 0; + + if (fsType == FSType::spiffs) { + totalBytes = SPIFFS.totalBytes(); + } else if (fsType == FSType::littlefs) { + totalBytes = LittleFS.totalBytes(); + } else if (fsType == FSType::sd) { + totalBytes = SD.totalBytes(); + } + return totalBytes; +} + +long fileSystemUsedBytes(FSType fsType) { + long usedBytes = 0; + + if (fsType == FSType::spiffs) { + usedBytes = SPIFFS.usedBytes(); + } else if (fsType == FSType::littlefs) { + usedBytes = LittleFS.usedBytes(); + } else if (fsType == FSType::sd) { + usedBytes = SD.usedBytes(); + } + return usedBytes; +} + +bool fileSystemBegin(FSType fsType) { + if (fsType == FSType::spiffs) { + SPIFFS.begin(true); + SPIFFS.end(); + if (SPIFFS.begin()) { + return true; + } + } else if (fsType == FSType::littlefs) { + if (LittleFS.begin()) { + return true; + } + } else if (fsType == FSType::sd) { + SD.begin(); + SD.end(); + if (SD.begin()) { + return true; + } + } + // printE("File system begin failed"); + // printlnE(fsType); + + return false; +} + +void fileSystemEnd(FSType fsType) { + if (fsType == FSType::spiffs) { + SPIFFS.end(); + } else if (fsType == FSType::littlefs) { + LittleFS.end(); + } else if (fsType == FSType::sd) { + SD.end(); + } +} + +FileLocation parseFsType(std::string fileName) { + FileLocation fl; + fl.fsType = FSType::none; + fl.filePath = fileName; + + if (fileName.rfind("/spiffs/", 0) == 0) { + fl.fsType = FSType::spiffs; + fl.filePath = fileName.replace(0, 7, EMPTY_STRING); + // printlnI("FS is SPIFFS"); + } else if (fileName.rfind("/sd/", 0) == 0) { + fl.fsType = FSType::sd; + fl.filePath = fileName.replace(0, 3, EMPTY_STRING); + // printlnI("FS is SD"); + } else if (fileName.rfind("/littlefs/", 0) == 0) { + fl.fsType = FSType::littlefs; + fl.filePath = fileName.replace(0, 9, EMPTY_STRING); + // printlnI("FS is LITTLEFS"); + } + // printI("File name is: "); + // printlnI(fl.filePath.c_str()); + // debug("Exit"); + return fl; +} + +} // namespace storage diff --git a/src/storage.hpp b/src/storage.hpp new file mode 100644 index 0000000..815042f --- /dev/null +++ b/src/storage.hpp @@ -0,0 +1,29 @@ +#ifndef storage_hpp +#define storage_hpp + +#include +#include + +#define EMPTY_STRING "" + +namespace storage { + +enum FSType { none = 0, spiffs = 1, littlefs = 2, sd = 3 }; +struct FileLocation { + FSType fsType; + std::string filePath; +}; + +File fileOpen(FileLocation fileLocation, std::string fileMode, bool fileCreate) ; +FSType findFile(std::string fileName) ; +bool fileRemove(FileLocation fileLocation); +bool fileExists(FileLocation fileLocation); +long fileSystemTotalBytes(FSType fsType); +long fileSystemUsedBytes(FSType fsType); +bool fileSystemBegin(FSType fsType); +void fileSystemEnd(FSType fsType); +FileLocation parseFsType(std::string fileName); + +} // namespace storage + +#endif