[FL-3662] Do not remove file when renaming to itself (#3193)
* Do not allow overwriting a file with dir and support renaming file to itself * Fix operator precedence error * Add support for storage-specific path equivalence checks * Fix typo * Fix updater compilation * Update Doxygen comments in storage.h Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
co-authored by
Aleksandr Kutuzov
parent
615a147973
commit
ba074068b0
@@ -165,6 +165,13 @@ typedef struct {
|
|||||||
* @param total_space pointer to total space value
|
* @param total_space pointer to total space value
|
||||||
* @param free_space pointer to free space value
|
* @param free_space pointer to free space value
|
||||||
* @return FS_Error error info
|
* @return FS_Error error info
|
||||||
|
*
|
||||||
|
* @var FS_Common_Api::equivalent_path
|
||||||
|
* @brief Test whether two paths are equivalent (e.g differing case on a case-insensitive fs)
|
||||||
|
* @param path1 first path to be compared
|
||||||
|
* @param path2 second path to be compared
|
||||||
|
* @param truncate if set to true, compare only up to the path1's length
|
||||||
|
* @return true if path1 and path2 are considered equivalent
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FS_Error (*const stat)(void* context, const char* path, FileInfo* fileinfo);
|
FS_Error (*const stat)(void* context, const char* path, FileInfo* fileinfo);
|
||||||
@@ -175,6 +182,7 @@ typedef struct {
|
|||||||
const char* fs_path,
|
const char* fs_path,
|
||||||
uint64_t* total_space,
|
uint64_t* total_space,
|
||||||
uint64_t* free_space);
|
uint64_t* free_space);
|
||||||
|
bool (*const equivalent_path)(const char* path1, const char* path2);
|
||||||
} FS_Common_Api;
|
} FS_Common_Api;
|
||||||
|
|
||||||
/** Full filesystem api structure */
|
/** Full filesystem api structure */
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* @file storage.h
|
||||||
|
* @brief APIs for working with storages, directories and files.
|
||||||
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "filesystem_api_defines.h"
|
#include "filesystem_api_defines.h"
|
||||||
#include "storage_sd_api.h"
|
#include "storage_sd_api.h"
|
||||||
@@ -23,43 +28,62 @@ extern "C" {
|
|||||||
|
|
||||||
typedef struct Storage Storage;
|
typedef struct Storage Storage;
|
||||||
|
|
||||||
/** Allocates and initializes a file descriptor
|
/**
|
||||||
* @return File*
|
* @brief Allocate and initialize a file instance.
|
||||||
|
*
|
||||||
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @return pointer to the created instance.
|
||||||
*/
|
*/
|
||||||
File* storage_file_alloc(Storage* storage);
|
File* storage_file_alloc(Storage* storage);
|
||||||
|
|
||||||
/** Frees the file descriptor. Closes the file if it was open.
|
/**
|
||||||
|
* @brief Free the file instance.
|
||||||
|
*
|
||||||
|
* If the file was open, calling this function will close it automatically.
|
||||||
|
* @param file pointer to the file instance to be freed.
|
||||||
*/
|
*/
|
||||||
void storage_file_free(File* file);
|
void storage_file_free(File* file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enumeration of events emitted by the storage through the PubSub system.
|
||||||
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
StorageEventTypeCardMount,
|
StorageEventTypeCardMount, /**< SD card was mounted. */
|
||||||
StorageEventTypeCardUnmount,
|
StorageEventTypeCardUnmount, /**< SD card was unmounted. */
|
||||||
StorageEventTypeCardMountError,
|
StorageEventTypeCardMountError, /**< An error occurred during mounting of an SD card. */
|
||||||
StorageEventTypeFileClose,
|
StorageEventTypeFileClose, /**< A file was closed. */
|
||||||
StorageEventTypeDirClose,
|
StorageEventTypeDirClose, /**< A directory was closed. */
|
||||||
} StorageEventType;
|
} StorageEventType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Storage event (passed to the PubSub callback).
|
||||||
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
StorageEventType type;
|
StorageEventType type; /**< Type of the event. */
|
||||||
} StorageEvent;
|
} StorageEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get storage pubsub.
|
* @brief Get the storage pubsub instance.
|
||||||
|
*
|
||||||
* Storage will send StorageEvent messages.
|
* Storage will send StorageEvent messages.
|
||||||
* @param storage
|
*
|
||||||
* @return FuriPubSub*
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @return pointer to the pubsub instance.
|
||||||
*/
|
*/
|
||||||
FuriPubSub* storage_get_pubsub(Storage* storage);
|
FuriPubSub* storage_get_pubsub(Storage* storage);
|
||||||
|
|
||||||
/******************* File Functions *******************/
|
/******************* File Functions *******************/
|
||||||
|
|
||||||
/** Opens an existing file or create a new one.
|
/**
|
||||||
* @param file pointer to file object.
|
* @brief Open an existing file or create a new one.
|
||||||
* @param path path to file
|
*
|
||||||
* @param access_mode access mode from FS_AccessMode
|
* @warning The calling code MUST call storage_file_close() even if the open operation had failed.
|
||||||
|
*
|
||||||
|
* @param file pointer to the file instance to be opened.
|
||||||
|
* @param path pointer to a zero-terminated string containing the path to the file to be opened.
|
||||||
|
* @param access_mode access mode from FS_AccessMode.
|
||||||
* @param open_mode open mode from FS_OpenMode
|
* @param open_mode open mode from FS_OpenMode
|
||||||
* @return success flag. You need to close the file even if the open operation failed.
|
* @return true if the file was successfully opened, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_file_open(
|
bool storage_file_open(
|
||||||
File* file,
|
File* file,
|
||||||
@@ -67,202 +91,267 @@ bool storage_file_open(
|
|||||||
FS_AccessMode access_mode,
|
FS_AccessMode access_mode,
|
||||||
FS_OpenMode open_mode);
|
FS_OpenMode open_mode);
|
||||||
|
|
||||||
/** Close the file.
|
/**
|
||||||
* @param file pointer to a file object, the file object will be freed.
|
* @brief Close the file.
|
||||||
* @return success flag
|
*
|
||||||
|
* @param file pointer to the file instance to be closed.
|
||||||
|
* @return true if the file was successfully closed, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_file_close(File* file);
|
bool storage_file_close(File* file);
|
||||||
|
|
||||||
/** Tells if the file is open
|
/**
|
||||||
* @param file pointer to a file object
|
* @brief Check whether the file is open.
|
||||||
* @return bool true if file is open
|
*
|
||||||
|
* @param file pointer to the file instance in question.
|
||||||
|
* @return true if the file is open, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_file_is_open(File* file);
|
bool storage_file_is_open(File* file);
|
||||||
|
|
||||||
/** Tells if the file is a directory
|
/**
|
||||||
* @param file pointer to a file object
|
* @brief Check whether a file instance represents a directory.
|
||||||
* @return bool true if file is a directory
|
*
|
||||||
|
* @param file pointer to the file instance in question.
|
||||||
|
* @return true if the file instance represents a directory, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_file_is_dir(File* file);
|
bool storage_file_is_dir(File* file);
|
||||||
|
|
||||||
/** Reads bytes from a file into a buffer
|
/**
|
||||||
* @param file pointer to file object.
|
* @brief Read bytes from a file into a buffer.
|
||||||
* @param buff pointer to a buffer, for reading
|
*
|
||||||
* @param bytes_to_read how many bytes to read. Must be less than or equal to the size of the buffer.
|
* @param file pointer to the file instance to read from.
|
||||||
* @return uint16_t how many bytes were actually read
|
* @param buff pointer to the buffer to be filled with read data.
|
||||||
|
* @param bytes_to_read number of bytes to read. Must be less than or equal to the size of the buffer.
|
||||||
|
* @return actual number of bytes read (may be fewer than requested).
|
||||||
*/
|
*/
|
||||||
uint16_t storage_file_read(File* file, void* buff, uint16_t bytes_to_read);
|
uint16_t storage_file_read(File* file, void* buff, uint16_t bytes_to_read);
|
||||||
|
|
||||||
/** Writes bytes from a buffer to a file
|
/**
|
||||||
* @param file pointer to file object.
|
* @brief Write bytes from a buffer to a file.
|
||||||
* @param buff pointer to buffer, for writing
|
*
|
||||||
* @param bytes_to_write how many bytes to write. Must be less than or equal to the size of the buffer.
|
* @param file pointer to the file instance to write into.
|
||||||
* @return uint16_t how many bytes were actually written
|
* @param buff pointer to the buffer containing the data to be written.
|
||||||
|
* @param bytes_to_write number of bytes to write. Must be less than or equal to the size of the buffer.
|
||||||
|
* @return actual number of bytes written (may be fewer than requested).
|
||||||
*/
|
*/
|
||||||
uint16_t storage_file_write(File* file, const void* buff, uint16_t bytes_to_write);
|
uint16_t storage_file_write(File* file, const void* buff, uint16_t bytes_to_write);
|
||||||
|
|
||||||
/** Moves the r/w pointer
|
/**
|
||||||
* @param file pointer to file object.
|
* @brief Change the current access position in a file.
|
||||||
* @param offset offset to move the r/w pointer
|
*
|
||||||
* @param from_start set an offset from the start or from the current position
|
* @param file pointer to the file instance in question.
|
||||||
|
* @param offset access position offset (meaning depends on from_start parameter).
|
||||||
|
* @param from_start if true, set the access position relative to the file start, otherwise relative to the current position.
|
||||||
* @return success flag
|
* @return success flag
|
||||||
*/
|
*/
|
||||||
bool storage_file_seek(File* file, uint32_t offset, bool from_start);
|
bool storage_file_seek(File* file, uint32_t offset, bool from_start);
|
||||||
|
|
||||||
/** Gets the position of the r/w pointer
|
/**
|
||||||
* @param file pointer to file object.
|
* @brief Get the current access position.
|
||||||
* @return uint64_t position of the r/w pointer
|
*
|
||||||
|
* @param file pointer to the file instance in question.
|
||||||
|
* @return current access position.
|
||||||
*/
|
*/
|
||||||
uint64_t storage_file_tell(File* file);
|
uint64_t storage_file_tell(File* file);
|
||||||
|
|
||||||
/** Truncates the file size to the current position of the r/w pointer
|
/**
|
||||||
* @param file pointer to file object.
|
* @brief Truncate the file size to the current access position.
|
||||||
* @return bool success flag
|
*
|
||||||
|
* @param file pointer to the file instance to be truncated.
|
||||||
|
* @return true if the file was successfully truncated, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_file_truncate(File* file);
|
bool storage_file_truncate(File* file);
|
||||||
|
|
||||||
/** Gets the size of the file
|
/**
|
||||||
* @param file pointer to file object.
|
* @brief Get the file size.
|
||||||
* @return uint64_t size of the file
|
*
|
||||||
|
* @param file pointer to the file instance in question.
|
||||||
|
* @return size of the file, in bytes.
|
||||||
*/
|
*/
|
||||||
uint64_t storage_file_size(File* file);
|
uint64_t storage_file_size(File* file);
|
||||||
|
|
||||||
/** Writes file cache to storage
|
/**
|
||||||
* @param file pointer to file object.
|
* @brief Synchronise the file cache with the actual storage.
|
||||||
* @return bool success flag
|
*
|
||||||
|
* @param file pointer to the file instance in question.
|
||||||
|
* @return true if the file was successfully synchronised, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_file_sync(File* file);
|
bool storage_file_sync(File* file);
|
||||||
|
|
||||||
/** Checks that the r/w pointer is at the end of the file
|
/**
|
||||||
* @param file pointer to file object.
|
* @brief Check whether the current access position is at the end of the file.
|
||||||
* @return bool success flag
|
*
|
||||||
|
* @param file pointer to a file instance in question.
|
||||||
|
* @return bool true if the current access position is at the end of the file, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_file_eof(File* file);
|
bool storage_file_eof(File* file);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check that file exists
|
* @brief Check whether a file exists.
|
||||||
*
|
*
|
||||||
* @param storage
|
* @param storage pointer to a storage API instance.
|
||||||
* @param path
|
* @param path pointer to a zero-terminated string containing the path to the file in question.
|
||||||
* @return true if file exists
|
* @return true if the file exists, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_file_exists(Storage* storage, const char* path);
|
bool storage_file_exists(Storage* storage, const char* path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copy data from one opened file to another opened file
|
* @brief Copy data from a source file to the destination file.
|
||||||
* Size bytes will be copied from current position of source file to current position of destination file
|
|
||||||
*
|
*
|
||||||
* @param source source file
|
* Both files must be opened prior to calling this function.
|
||||||
* @param destination destination file
|
*
|
||||||
* @param size size of data to copy
|
* The requested amount of bytes will be copied from the current access position
|
||||||
* @return bool success flag
|
* in the source file to the current access position in the destination file.
|
||||||
|
*
|
||||||
|
* @param source pointer to a source file instance.
|
||||||
|
* @param destination pointer to a destination file instance.
|
||||||
|
* @param size data size to be copied, in bytes.
|
||||||
|
* @return true if the data was successfully copied, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_file_copy_to_file(File* source, File* destination, uint32_t size);
|
bool storage_file_copy_to_file(File* source, File* destination, uint32_t size);
|
||||||
|
|
||||||
/******************* Dir Functions *******************/
|
/******************* Directory Functions *******************/
|
||||||
|
|
||||||
/** Opens a directory to get objects from it
|
/**
|
||||||
* @param app pointer to the api
|
* @brief Open a directory.
|
||||||
* @param file pointer to file object.
|
*
|
||||||
* @param path path to directory
|
* Opening a directory is necessary to be able to read its contents with storage_dir_read().
|
||||||
* @return bool success flag. You need to close the directory even if the open operation failed.
|
*
|
||||||
|
* @warning The calling code MUST call storage_dir_close() even if the open operation had failed.
|
||||||
|
*
|
||||||
|
* @param file pointer to a file instance representing the directory in question.
|
||||||
|
* @param path pointer to a zero-terminated string containing the path of the directory in question.
|
||||||
|
* @return true if the directory was successfully opened, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_dir_open(File* file, const char* path);
|
bool storage_dir_open(File* file, const char* path);
|
||||||
|
|
||||||
/** Close the directory. Also free file handle structure and point it to the NULL.
|
/**
|
||||||
* @param file pointer to a file object.
|
* @brief Close the directory.
|
||||||
* @return bool success flag
|
*
|
||||||
|
* @param file pointer to a file instance representing the directory in question.
|
||||||
|
* @return true if the directory was successfully closed, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_dir_close(File* file);
|
bool storage_dir_close(File* file);
|
||||||
|
|
||||||
/** Reads the next object in the directory
|
/**
|
||||||
* @param file pointer to file object.
|
* @brief Get the next item in the directory.
|
||||||
* @param fileinfo pointer to the read FileInfo, may be NULL
|
*
|
||||||
* @param name pointer to name buffer, may be NULL
|
* If the next object does not exist, this function returns false as well
|
||||||
* @param name_length name buffer length
|
* and sets the file error id to FSE_NOT_EXIST.
|
||||||
* @return success flag (if the next object does not exist, it also returns false and sets the file error id to FSE_NOT_EXIST)
|
*
|
||||||
|
* @param file pointer to a file instance representing the directory in question.
|
||||||
|
* @param fileinfo pointer to the FileInfo structure to contain the info (may be NULL).
|
||||||
|
* @param name pointer to the buffer to contain the name (may be NULL).
|
||||||
|
* @param name_length maximum capacity of the name buffer, in bytes.
|
||||||
|
* @return true if the next item was successfully read, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_length);
|
bool storage_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_length);
|
||||||
|
|
||||||
/** Rewinds the read pointer to first item in the directory
|
/**
|
||||||
* @param file pointer to file object.
|
* @brief Change the access position to first item in the directory.
|
||||||
* @return bool success flag
|
*
|
||||||
|
* @param file pointer to a file instance representing the directory in question.
|
||||||
|
* @return true if the access position was successfully changed, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_dir_rewind(File* file);
|
bool storage_dir_rewind(File* file);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check that dir exists
|
* @brief Check whether a directory exists.
|
||||||
*
|
*
|
||||||
* @param storage
|
* @param storage pointer to a storage API instance.
|
||||||
* @param path
|
* @param path pointer to a zero-terminated string containing the path of the directory in question.
|
||||||
* @return bool
|
* @return true if the directory exists, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_dir_exists(Storage* storage, const char* path);
|
bool storage_dir_exists(Storage* storage, const char* path);
|
||||||
|
|
||||||
/******************* Common Functions *******************/
|
/******************* Common Functions *******************/
|
||||||
|
|
||||||
/** Retrieves unix timestamp of last access
|
/**
|
||||||
|
* @brief Get the last access time in UNIX format.
|
||||||
*
|
*
|
||||||
* @param storage The storage instance
|
* @param storage pointer to a storage API instance.
|
||||||
* @param path path to file/directory
|
* @param path pointer to a zero-terminated string containing the path of the item in question.
|
||||||
* @param timestamp the timestamp pointer
|
* @param timestamp pointer to a value to contain the timestamp.
|
||||||
*
|
* @return FSE_OK if the timestamp has been successfully received, any other error code on failure.
|
||||||
* @return FS_Error operation result
|
|
||||||
*/
|
*/
|
||||||
FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp);
|
FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp);
|
||||||
|
|
||||||
/** Retrieves information about a file/directory
|
/**
|
||||||
* @param app pointer to the api
|
* @brief Get information about a file or a directory.
|
||||||
* @param path path to file/directory
|
*
|
||||||
* @param fileinfo pointer to the read FileInfo, may be NULL
|
* @param storage pointer to a storage API instance.
|
||||||
* @return FS_Error operation result
|
* @param path pointer to a zero-terminated string containing the path of the item in question.
|
||||||
|
* @param fileinfo pointer to the FileInfo structure to contain the info (may be NULL).
|
||||||
|
* @return FSE_OK if the info has been successfully received, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo);
|
FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo);
|
||||||
|
|
||||||
/** Removes a file/directory from the repository, the directory must be empty and the file/directory must not be open
|
/**
|
||||||
* @param app pointer to the api
|
* @brief Remove a file or a directory.
|
||||||
* @param path
|
*
|
||||||
* @return FS_Error operation result
|
* The directory must be empty.
|
||||||
|
* The file or the directory must NOT be open.
|
||||||
|
*
|
||||||
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @param path pointer to a zero-terminated string containing the path of the item to be removed.
|
||||||
|
* @return FSE_OK if the file or directory has been successfully removed, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_common_remove(Storage* storage, const char* path);
|
FS_Error storage_common_remove(Storage* storage, const char* path);
|
||||||
|
|
||||||
/** Renames file/directory, file/directory must not be open. Will overwrite existing file.
|
/**
|
||||||
* @param app pointer to the api
|
* @brief Rename a file or a directory.
|
||||||
* @param old_path old path
|
*
|
||||||
* @param new_path new path
|
* The file or the directory must NOT be open.
|
||||||
* @return FS_Error operation result
|
* Will overwrite the destination file if it already exists.
|
||||||
|
*
|
||||||
|
* Renaming a regular file to itself does nothing and always succeeds.
|
||||||
|
* Renaming a directory to itself or to a subdirectory of itself always fails.
|
||||||
|
*
|
||||||
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @param old_path pointer to a zero-terminated string containing the source path.
|
||||||
|
* @param new_path pointer to a zero-terminated string containing the destination path.
|
||||||
|
* @return FSE_OK if the file or directory has been successfully renamed, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path);
|
FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path);
|
||||||
|
|
||||||
/** Copy file, file must not be open
|
/**
|
||||||
* @param app pointer to the api
|
* @brief Copy the file to a new location.
|
||||||
* @param old_path old path
|
*
|
||||||
* @param new_path new path
|
* The file must NOT be open at the time of calling this function.
|
||||||
* @return FS_Error operation result
|
*
|
||||||
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @param old_path pointer to a zero-terminated string containing the source path.
|
||||||
|
* @param new_path pointer to a zero-terminated string containing the destination path.
|
||||||
|
* @return FSE_OK if the file has been successfully copied, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path);
|
FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path);
|
||||||
|
|
||||||
/** Copy one folder contents into another with rename of all conflicting files
|
/**
|
||||||
* @param app pointer to the api
|
* @brief Copy the contents of one directory into another and rename all conflicting files.
|
||||||
* @param old_path old path
|
*
|
||||||
* @param new_path new path
|
* @param storage pointer to a storage API instance.
|
||||||
* @return FS_Error operation result
|
* @param old_path pointer to a zero-terminated string containing the source path.
|
||||||
|
* @param new_path pointer to a zero-terminated string containing the destination path.
|
||||||
|
* @return FSE_OK if the directories have been successfully merged, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path);
|
FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path);
|
||||||
|
|
||||||
/** Creates a directory
|
/**
|
||||||
* @param app pointer to the api
|
* @brief Create a directory.
|
||||||
* @param path directory path
|
*
|
||||||
* @return FS_Error operation result
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @param fs_path pointer to a zero-terminated string containing the directory path.
|
||||||
|
* @return FSE_OK if the directory has been successfully created, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_common_mkdir(Storage* storage, const char* path);
|
FS_Error storage_common_mkdir(Storage* storage, const char* path);
|
||||||
|
|
||||||
/** Gets general information about the storage
|
/**
|
||||||
* @param app pointer to the api
|
* @brief Get the general information about the storage.
|
||||||
* @param fs_path the path to the storage of interest
|
*
|
||||||
* @param total_space pointer to total space record, will be filled
|
* @param storage pointer to a storage API instance.
|
||||||
* @param free_space pointer to free space record, will be filled
|
* @param fs_path pointer to a zero-terminated string containing the path to the storage question.
|
||||||
* @return FS_Error operation result
|
* @param total_space pointer to the value to contain the total capacity, in bytes.
|
||||||
|
* @param free_space pointer to the value to contain the available space, in bytes.
|
||||||
|
* @return FSE_OK if the information has been successfully received, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_common_fs_info(
|
FS_Error storage_common_fs_info(
|
||||||
Storage* storage,
|
Storage* storage,
|
||||||
@@ -271,150 +360,242 @@ FS_Error storage_common_fs_info(
|
|||||||
uint64_t* free_space);
|
uint64_t* free_space);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Parse aliases in path and replace them with real path
|
* @brief Parse aliases in a path and replace them with the real path.
|
||||||
* Also will create special folders if they are not exist
|
|
||||||
*
|
*
|
||||||
* @param storage
|
* Necessary special directories will be created automatically if they did not exist.
|
||||||
* @param path
|
*
|
||||||
* @return bool
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @param path pointer to a zero-terminated string containing the path in question.
|
||||||
|
* @return true if the path was successfully resolved, false otherwise.
|
||||||
*/
|
*/
|
||||||
void storage_common_resolve_path_and_ensure_app_directory(Storage* storage, FuriString* path);
|
void storage_common_resolve_path_and_ensure_app_directory(Storage* storage, FuriString* path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move content of one folder to another, with rename of all conflicting files.
|
* @brief Move the contents of source folder to destination one and rename all conflicting files.
|
||||||
* Source folder will be deleted if the migration is successful.
|
|
||||||
*
|
*
|
||||||
* @param storage
|
* Source folder will be deleted if the migration was successful.
|
||||||
* @param source
|
*
|
||||||
* @param dest
|
* @param storage pointer to a storage API instance.
|
||||||
* @return FS_Error
|
* @param source pointer to a zero-terminated string containing the source path.
|
||||||
|
* @param dest pointer to a zero-terminated string containing the destination path.
|
||||||
|
* @return FSE_OK if the migration was successfull completed, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_common_migrate(Storage* storage, const char* source, const char* dest);
|
FS_Error storage_common_migrate(Storage* storage, const char* source, const char* dest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check that file or dir exists
|
* @brief Check whether a file or a directory exists.
|
||||||
*
|
*
|
||||||
* @param storage
|
* @param storage pointer to a storage API instance.
|
||||||
* @param path
|
* @param path pointer to a zero-terminated string containing the path in question.
|
||||||
* @return bool
|
* @return true if a file or a directory exists, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_common_exists(Storage* storage, const char* path);
|
bool storage_common_exists(Storage* storage, const char* path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether two paths are equivalent.
|
||||||
|
*
|
||||||
|
* This function will resolve aliases and apply filesystem-specific
|
||||||
|
* rules to determine whether the two given paths are equivalent.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* - /int/text and /ext/test -> false (Different storages),
|
||||||
|
* - /int/Test and /int/test -> false (Case-sensitive storage),
|
||||||
|
* - /ext/Test and /ext/test -> true (Case-insensitive storage).
|
||||||
|
*
|
||||||
|
* If the truncate parameter is set to true, the second path will be
|
||||||
|
* truncated to be no longer than the first one. It is useful to determine
|
||||||
|
* whether path2 is a subdirectory of path1.
|
||||||
|
*
|
||||||
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @param path1 pointer to a zero-terminated string containing the first path.
|
||||||
|
* @param path2 pointer to a zero-terminated string containing the second path.
|
||||||
|
* @param truncate whether to truncate path2 to be no longer than path1.
|
||||||
|
* @return true if paths are equivalent, false otherwise.
|
||||||
|
*/
|
||||||
|
bool storage_common_equivalent_path(
|
||||||
|
Storage* storage,
|
||||||
|
const char* path1,
|
||||||
|
const char* path2,
|
||||||
|
bool truncate);
|
||||||
|
|
||||||
/******************* Error Functions *******************/
|
/******************* Error Functions *******************/
|
||||||
|
|
||||||
/** Retrieves the error text from the error id
|
/**
|
||||||
* @param error_id error id
|
* @brief Get the textual description of a numeric error identifer.
|
||||||
* @return const char* error text
|
*
|
||||||
|
* @param error_id numeric identifier of the error in question.
|
||||||
|
* @return pointer to a statically allocated zero-terminated string containing the respective error text.
|
||||||
*/
|
*/
|
||||||
const char* storage_error_get_desc(FS_Error error_id);
|
const char* storage_error_get_desc(FS_Error error_id);
|
||||||
|
|
||||||
/** Retrieves the error id from the file object
|
/**
|
||||||
* @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETRIEVE THE ERROR ID IF THE FILE HAS BEEN CLOSED
|
* @brief Get the numeric error identifier from a file instance.
|
||||||
* @return FS_Error error id
|
*
|
||||||
|
* @warning It is not possible to get the error identifier after the file has been closed.
|
||||||
|
*
|
||||||
|
* @param file pointer to the file instance in question (must NOT be NULL).
|
||||||
|
* @return numeric identifier of the last error associated with the file instance.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_file_get_error(File* file);
|
FS_Error storage_file_get_error(File* file);
|
||||||
|
|
||||||
/** Retrieves the internal (storage-specific) error id from the file object
|
/**
|
||||||
* @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETRIEVE THE INTERNAL ERROR ID IF THE FILE HAS BEEN CLOSED
|
* @brief Get the internal (storage-specific) numeric error identifier from a file instance.
|
||||||
* @return FS_Error error id
|
*
|
||||||
|
* @warning It is not possible to get the internal error identifier after the file has been closed.
|
||||||
|
*
|
||||||
|
* @param file pointer to the file instance in question (must NOT be NULL).
|
||||||
|
* @return numeric identifier of the last internal error associated with the file instance.
|
||||||
*/
|
*/
|
||||||
int32_t storage_file_get_internal_error(File* file);
|
int32_t storage_file_get_internal_error(File* file);
|
||||||
|
|
||||||
/** Retrieves the error text from the file object
|
/**
|
||||||
* @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETRIEVE THE ERROR TEXT IF THE FILE HAS BEEN CLOSED
|
* @brief Get the textual description of a the last error associated with a file instance.
|
||||||
* @return const char* error text
|
*
|
||||||
|
* @warning It is not possible to get the error text after the file has been closed.
|
||||||
|
*
|
||||||
|
* @param file pointer to the file instance in question (must NOT be NULL).
|
||||||
|
* @return pointer to a statically allocated zero-terminated string containing the respective error text.
|
||||||
*/
|
*/
|
||||||
const char* storage_file_get_error_desc(File* file);
|
const char* storage_file_get_error_desc(File* file);
|
||||||
|
|
||||||
/******************* SD Card Functions *******************/
|
/******************* SD Card Functions *******************/
|
||||||
|
|
||||||
/** Formats SD Card
|
/**
|
||||||
* @param api pointer to the api
|
* @brief Format the SD Card.
|
||||||
* @return FS_Error operation result
|
*
|
||||||
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @return FSE_OK if the card was successfully formatted, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_sd_format(Storage* api);
|
FS_Error storage_sd_format(Storage* storage);
|
||||||
|
|
||||||
/** Will unmount the SD card.
|
/**
|
||||||
* Will return FSE_NOT_READY if the SD card is not mounted.
|
* @brief Unmount the SD card.
|
||||||
* Will return FSE_DENIED if there are open files on the SD card.
|
*
|
||||||
* @param api pointer to the api
|
* These return values have special meaning:
|
||||||
* @return FS_Error operation result
|
* - FSE_NOT_READY if the SD card is not mounted.
|
||||||
|
* - FSE_DENIED if there are open files on the SD card.
|
||||||
|
*
|
||||||
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @return FSE_OK if the card was successfully formatted, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_sd_unmount(Storage* api);
|
FS_Error storage_sd_unmount(Storage* storage);
|
||||||
|
|
||||||
/** Will mount the SD card
|
/**
|
||||||
* @param api pointer to the api
|
* @brief Mount the SD card.
|
||||||
* @return FS_Error operation result
|
*
|
||||||
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @return FSE_OK if the card was successfully mounted, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_sd_mount(Storage* api);
|
FS_Error storage_sd_mount(Storage* storage);
|
||||||
|
|
||||||
/** Retrieves SD card information
|
/**
|
||||||
* @param api pointer to the api
|
* @brief Get SD card information.
|
||||||
* @param info pointer to the info
|
*
|
||||||
* @return FS_Error operation result
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @param info pointer to the info object to contain the requested information.
|
||||||
|
* @return FSE_OK if the info was successfully received, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_sd_info(Storage* api, SDInfo* info);
|
FS_Error storage_sd_info(Storage* storage, SDInfo* info);
|
||||||
|
|
||||||
/** Retrieves SD card status
|
/**
|
||||||
* @param api pointer to the api
|
* @brief Get SD card status.
|
||||||
* @return FS_Error operation result
|
*
|
||||||
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @return storage status in the form of a numeric error identifier.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_sd_status(Storage* api);
|
FS_Error storage_sd_status(Storage* storage);
|
||||||
|
|
||||||
/******************* Internal LFS Functions *******************/
|
/******************* Internal LFS Functions *******************/
|
||||||
|
|
||||||
typedef void (*Storage_name_converter)(FuriString*);
|
typedef void (*Storage_name_converter)(FuriString*);
|
||||||
|
|
||||||
/** Backs up internal storage to a tar archive
|
/**
|
||||||
* @param api pointer to the api
|
* @brief Back up the internal storage contents to a *.tar archive.
|
||||||
* @param dstmane destination archive path
|
*
|
||||||
* @return FS_Error operation result
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @param dstname pointer to a zero-terminated string containing the archive file path.
|
||||||
|
* @return FSE_OK if the storage was successfully backed up, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_int_backup(Storage* api, const char* dstname);
|
FS_Error storage_int_backup(Storage* storage, const char* dstname);
|
||||||
|
|
||||||
/** Restores internal storage from a tar archive
|
/**
|
||||||
* @param api pointer to the api
|
* @brief Restore the internal storage contents from a *.tar archive.
|
||||||
* @param dstmane archive path
|
*
|
||||||
* @param converter pointer to filename conversion function, may be NULL
|
* @param storage pointer to a storage API instance.
|
||||||
* @return FS_Error operation result
|
* @param dstname pointer to a zero-terminated string containing the archive file path.
|
||||||
|
* @param converter pointer to a filename conversion function (may be NULL).
|
||||||
|
* @return FSE_OK if the storage was successfully restored, any other error code on failure.
|
||||||
*/
|
*/
|
||||||
FS_Error storage_int_restore(Storage* api, const char* dstname, Storage_name_converter converter);
|
FS_Error storage_int_restore(Storage* api, const char* dstname, Storage_name_converter converter);
|
||||||
|
|
||||||
/***************** Simplified Functions ******************/
|
/***************** Simplified Functions ******************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a file/directory, the directory must be empty and the file/directory must not be open
|
* @brief Remove a file or a directory.
|
||||||
* @param storage pointer to the api
|
*
|
||||||
* @param path
|
* The following conditions must be met:
|
||||||
* @return true on success or if file/dir is not exist
|
* - the directory must be empty.
|
||||||
|
* - the file or the directory must NOT be open.
|
||||||
|
*
|
||||||
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @param path pointer to a zero-terminated string containing the item path.
|
||||||
|
* @return true on success or if the item does not exist, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_simply_remove(Storage* storage, const char* path);
|
bool storage_simply_remove(Storage* storage, const char* path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively removes a file/directory, the directory can be not empty
|
* @brief Recursively remove a file or a directory.
|
||||||
* @param storage pointer to the api
|
*
|
||||||
* @param path
|
* Unlike storage_simply_remove(), the directory does not need to be empty.
|
||||||
* @return true on success or if file/dir is not exist
|
*
|
||||||
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @param path pointer to a zero-terminated string containing the item path.
|
||||||
|
* @return true on success or if the item does not exist, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_simply_remove_recursive(Storage* storage, const char* path);
|
bool storage_simply_remove_recursive(Storage* storage, const char* path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a directory
|
* @brief Create a directory.
|
||||||
* @param storage
|
*
|
||||||
* @param path
|
* @param storage pointer to a storage API instance.
|
||||||
* @return true on success or if directory is already exist
|
* @param path pointer to a zero-terminated string containing the directory path.
|
||||||
|
* @return true on success or if directory does already exist, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool storage_simply_mkdir(Storage* storage, const char* path);
|
bool storage_simply_mkdir(Storage* storage, const char* path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get next free filename.
|
* @brief Get the next free filename in a directory.
|
||||||
*
|
*
|
||||||
* @param storage
|
* Usage example:
|
||||||
* @param dirname
|
* ```c
|
||||||
* @param filename
|
* FuriString* file_name = furi_string_alloc();
|
||||||
* @param fileextension
|
* Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
* @param nextfilename return name
|
*
|
||||||
* @param max_len max len name
|
* storage_get_next_filename(storage,
|
||||||
|
* "/ext/test",
|
||||||
|
* "cookies",
|
||||||
|
* ".yum",
|
||||||
|
* 20);
|
||||||
|
*
|
||||||
|
* furi_record_close(RECORD_STORAGE);
|
||||||
|
*
|
||||||
|
* use_file_name(file_name);
|
||||||
|
*
|
||||||
|
* furi_string_free(file_name);
|
||||||
|
* ```
|
||||||
|
* Possible file_name values after calling storage_get_next_filename():
|
||||||
|
* "cookies", "cookies1", "cookies2", ... etc depending on whether any of
|
||||||
|
* these files have already existed in the directory.
|
||||||
|
*
|
||||||
|
* @note If the resulting next file name length is greater than set by the max_len
|
||||||
|
* parameter, the original filename will be returned instead.
|
||||||
|
*
|
||||||
|
* @param storage pointer to a storage API instance.
|
||||||
|
* @param dirname pointer to a zero-terminated string containing the directory path.
|
||||||
|
* @param filename pointer to a zero-terminated string containing the file name.
|
||||||
|
* @param fileextension pointer to a zero-terminated string containing the file extension.
|
||||||
|
* @param nextfilename pointer to a dynamic string containing the resulting file name.
|
||||||
|
* @param max_len maximum length of the new name.
|
||||||
*/
|
*/
|
||||||
void storage_get_next_filename(
|
void storage_get_next_filename(
|
||||||
Storage* storage,
|
Storage* storage,
|
||||||
|
|||||||
@@ -431,17 +431,22 @@ FS_Error storage_common_rename(Storage* storage, const char* old_path, const cha
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(storage_dir_exists(storage, old_path)) {
|
if(storage_dir_exists(storage, old_path)) {
|
||||||
FuriString* dir_path = furi_string_alloc_set_str(old_path);
|
// Cannot overwrite a file with a directory
|
||||||
if(!furi_string_end_with_str(dir_path, "/")) {
|
if(storage_file_exists(storage, new_path)) {
|
||||||
furi_string_cat_str(dir_path, "/");
|
|
||||||
}
|
|
||||||
const char* dir_path_s = furi_string_get_cstr(dir_path);
|
|
||||||
if(strncmp(new_path, dir_path_s, strlen(dir_path_s)) == 0) {
|
|
||||||
error = FSE_INVALID_NAME;
|
error = FSE_INVALID_NAME;
|
||||||
furi_string_free(dir_path);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
furi_string_free(dir_path);
|
|
||||||
|
// Cannot rename a directory to itself or to a nested directory
|
||||||
|
if(storage_common_equivalent_path(storage, old_path, new_path, true)) {
|
||||||
|
error = FSE_INVALID_NAME;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renaming a regular file to itself does nothing and always succeeds
|
||||||
|
} else if(storage_common_equivalent_path(storage, old_path, new_path, false)) {
|
||||||
|
error = FSE_OK;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(storage_file_exists(storage, new_path)) {
|
if(storage_file_exists(storage, new_path)) {
|
||||||
@@ -742,6 +747,27 @@ bool storage_common_exists(Storage* storage, const char* path) {
|
|||||||
return storage_common_stat(storage, path, &file_info) == FSE_OK;
|
return storage_common_stat(storage, path, &file_info) == FSE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool storage_common_equivalent_path(
|
||||||
|
Storage* storage,
|
||||||
|
const char* path1,
|
||||||
|
const char* path2,
|
||||||
|
bool truncate) {
|
||||||
|
S_API_PROLOGUE;
|
||||||
|
|
||||||
|
SAData data = {
|
||||||
|
.cequivpath = {
|
||||||
|
.path1 = path1,
|
||||||
|
.path2 = path2,
|
||||||
|
.truncate = truncate,
|
||||||
|
.thread_id = furi_thread_get_current_id(),
|
||||||
|
}};
|
||||||
|
|
||||||
|
S_API_MESSAGE(StorageCommandCommonEquivalentPath);
|
||||||
|
S_API_EPILOGUE;
|
||||||
|
|
||||||
|
return S_RETURN_BOOL;
|
||||||
|
}
|
||||||
|
|
||||||
/****************** ERROR ******************/
|
/****************** ERROR ******************/
|
||||||
|
|
||||||
const char* storage_error_get_desc(FS_Error error_id) {
|
const char* storage_error_get_desc(FS_Error error_id) {
|
||||||
|
|||||||
@@ -69,6 +69,13 @@ typedef struct {
|
|||||||
FuriThreadId thread_id;
|
FuriThreadId thread_id;
|
||||||
} SADataCResolvePath;
|
} SADataCResolvePath;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* path1;
|
||||||
|
const char* path2;
|
||||||
|
bool truncate;
|
||||||
|
FuriThreadId thread_id;
|
||||||
|
} SADataCEquivPath;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
} SADataError;
|
} SADataError;
|
||||||
@@ -99,6 +106,7 @@ typedef union {
|
|||||||
SADataCStat cstat;
|
SADataCStat cstat;
|
||||||
SADataCFSInfo cfsinfo;
|
SADataCFSInfo cfsinfo;
|
||||||
SADataCResolvePath cresolvepath;
|
SADataCResolvePath cresolvepath;
|
||||||
|
SADataCEquivPath cequivpath;
|
||||||
|
|
||||||
SADataError error;
|
SADataError error;
|
||||||
|
|
||||||
@@ -142,6 +150,7 @@ typedef enum {
|
|||||||
StorageCommandSDStatus,
|
StorageCommandSDStatus,
|
||||||
StorageCommandCommonResolvePath,
|
StorageCommandCommonResolvePath,
|
||||||
StorageCommandSDMount,
|
StorageCommandSDMount,
|
||||||
|
StorageCommandCommonEquivalentPath,
|
||||||
} StorageCommand;
|
} StorageCommand;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -98,6 +98,12 @@ static FS_Error storage_get_data(Storage* app, FuriString* path, StorageData** s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void storage_path_trim_trailing_slashes(FuriString* path) {
|
||||||
|
while(furi_string_end_with(path, "/")) {
|
||||||
|
furi_string_left(path, furi_string_size(path) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/******************* File Functions *******************/
|
/******************* File Functions *******************/
|
||||||
|
|
||||||
bool storage_process_file_open(
|
bool storage_process_file_open(
|
||||||
@@ -357,6 +363,8 @@ static FS_Error storage_process_common_remove(Storage* app, FuriString* path) {
|
|||||||
FS_Error ret = storage_get_data(app, path, &storage);
|
FS_Error ret = storage_get_data(app, path, &storage);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
if(ret != FSE_OK) break;
|
||||||
|
|
||||||
if(storage_path_already_open(path, storage)) {
|
if(storage_path_already_open(path, storage)) {
|
||||||
ret = FSE_ALREADY_OPEN;
|
ret = FSE_ALREADY_OPEN;
|
||||||
break;
|
break;
|
||||||
@@ -398,6 +406,31 @@ static FS_Error storage_process_common_fs_info(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
storage_process_common_equivalent_path(Storage* app, FuriString* path1, FuriString* path2) {
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
const StorageType storage_type1 = storage_get_type_by_path(path1);
|
||||||
|
const StorageType storage_type2 = storage_get_type_by_path(path2);
|
||||||
|
|
||||||
|
// Paths on different storages are of course not equal
|
||||||
|
if(storage_type1 != storage_type2) break;
|
||||||
|
|
||||||
|
StorageData* storage;
|
||||||
|
const FS_Error status = storage_get_data(app, path1, &storage);
|
||||||
|
|
||||||
|
if(status != FSE_OK) break;
|
||||||
|
|
||||||
|
FS_CALL(
|
||||||
|
storage,
|
||||||
|
common.equivalent_path(furi_string_get_cstr(path1), furi_string_get_cstr(path2)));
|
||||||
|
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/****************** Raw SD API ******************/
|
/****************** Raw SD API ******************/
|
||||||
// TODO FL-3521: think about implementing a custom storage API to split that kind of api linkage
|
// TODO FL-3521: think about implementing a custom storage API to split that kind of api linkage
|
||||||
#include "storages/storage_ext.h"
|
#include "storages/storage_ext.h"
|
||||||
@@ -649,6 +682,23 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
|
|||||||
app, message->data->cresolvepath.path, message->data->cresolvepath.thread_id, true);
|
app, message->data->cresolvepath.path, message->data->cresolvepath.thread_id, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case StorageCommandCommonEquivalentPath: {
|
||||||
|
FuriString* path1 = furi_string_alloc_set(message->data->cequivpath.path1);
|
||||||
|
FuriString* path2 = furi_string_alloc_set(message->data->cequivpath.path2);
|
||||||
|
storage_path_trim_trailing_slashes(path1);
|
||||||
|
storage_path_trim_trailing_slashes(path2);
|
||||||
|
storage_process_alias(app, path1, message->data->cequivpath.thread_id, false);
|
||||||
|
storage_process_alias(app, path2, message->data->cequivpath.thread_id, false);
|
||||||
|
if(message->data->cequivpath.truncate) {
|
||||||
|
furi_string_left(path2, furi_string_size(path1));
|
||||||
|
}
|
||||||
|
message->return_data->bool_value =
|
||||||
|
storage_process_common_equivalent_path(app, path1, path2);
|
||||||
|
furi_string_free(path1);
|
||||||
|
furi_string_free(path2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// SD operations
|
// SD operations
|
||||||
case StorageCommandSDFormat:
|
case StorageCommandSDFormat:
|
||||||
message->return_data->error_value = storage_process_sd_format(app);
|
message->return_data->error_value = storage_process_sd_format(app);
|
||||||
|
|||||||
@@ -596,6 +596,16 @@ static FS_Error storage_ext_common_fs_info(
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool storage_ext_common_equivalent_path(const char* path1, const char* path2) {
|
||||||
|
#ifdef FURI_RAM_EXEC
|
||||||
|
UNUSED(path1);
|
||||||
|
UNUSED(path2);
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
return strcasecmp(path1, path2) == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/******************* Init Storage *******************/
|
/******************* Init Storage *******************/
|
||||||
static const FS_Api fs_api = {
|
static const FS_Api fs_api = {
|
||||||
.file =
|
.file =
|
||||||
@@ -624,6 +634,7 @@ static const FS_Api fs_api = {
|
|||||||
.mkdir = storage_ext_common_mkdir,
|
.mkdir = storage_ext_common_mkdir,
|
||||||
.remove = storage_ext_common_remove,
|
.remove = storage_ext_common_remove,
|
||||||
.fs_info = storage_ext_common_fs_info,
|
.fs_info = storage_ext_common_fs_info,
|
||||||
|
.equivalent_path = storage_ext_common_equivalent_path,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -686,6 +686,10 @@ static FS_Error storage_int_common_fs_info(
|
|||||||
return storage_int_parse_error(result);
|
return storage_int_parse_error(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool storage_int_common_equivalent_path(const char* path1, const char* path2) {
|
||||||
|
return strcmp(path1, path2) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
/******************* Init Storage *******************/
|
/******************* Init Storage *******************/
|
||||||
static const FS_Api fs_api = {
|
static const FS_Api fs_api = {
|
||||||
.file =
|
.file =
|
||||||
@@ -714,6 +718,7 @@ static const FS_Api fs_api = {
|
|||||||
.mkdir = storage_int_common_mkdir,
|
.mkdir = storage_int_common_mkdir,
|
||||||
.remove = storage_int_common_remove,
|
.remove = storage_int_common_remove,
|
||||||
.fs_info = storage_int_common_fs_info,
|
.fs_info = storage_int_common_fs_info,
|
||||||
|
.equivalent_path = storage_int_common_equivalent_path,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,45.0,,
|
Version,+,45.1,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
Header,+,applications/services/cli/cli_vcp.h,,
|
Header,+,applications/services/cli/cli_vcp.h,,
|
||||||
@@ -2071,6 +2071,7 @@ Function,+,st25r3916_write_pttsn_mem,void,"FuriHalSpiBusHandle*, uint8_t*, size_
|
|||||||
Function,+,st25r3916_write_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t"
|
Function,+,st25r3916_write_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t"
|
||||||
Function,+,st25r3916_write_test_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t"
|
Function,+,st25r3916_write_test_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t"
|
||||||
Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*"
|
Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*"
|
||||||
|
Function,+,storage_common_equivalent_path,_Bool,"Storage*, const char*, const char*, _Bool"
|
||||||
Function,+,storage_common_exists,_Bool,"Storage*, const char*"
|
Function,+,storage_common_exists,_Bool,"Storage*, const char*"
|
||||||
Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*"
|
Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*"
|
||||||
Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*"
|
Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*"
|
||||||
|
|||||||
|
@@ -1,5 +1,5 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,45.0,,
|
Version,+,45.1,,
|
||||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
@@ -2698,6 +2698,7 @@ Function,+,st25tb_save,_Bool,"const St25tbData*, FlipperFormat*"
|
|||||||
Function,+,st25tb_set_uid,_Bool,"St25tbData*, const uint8_t*, size_t"
|
Function,+,st25tb_set_uid,_Bool,"St25tbData*, const uint8_t*, size_t"
|
||||||
Function,+,st25tb_verify,_Bool,"St25tbData*, const FuriString*"
|
Function,+,st25tb_verify,_Bool,"St25tbData*, const FuriString*"
|
||||||
Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*"
|
Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*"
|
||||||
|
Function,+,storage_common_equivalent_path,_Bool,"Storage*, const char*, const char*, _Bool"
|
||||||
Function,+,storage_common_exists,_Bool,"Storage*, const char*"
|
Function,+,storage_common_exists,_Bool,"Storage*, const char*"
|
||||||
Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*"
|
Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*"
|
||||||
Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*"
|
Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*"
|
||||||
|
|||||||
|
Reference in New Issue
Block a user