Przeglądaj źródła

Storage: tree timestamps (#1971)

* Storage: tree timestamps
* Rpc: add storage timestamp
* Storage: correct timestamp owner
* Storage: update timestamp at sd mount

Co-authored-by: SG <who.just.the.doctor@gmail.com>
あく 3 lat temu
rodzic
commit
d68ac50efd

+ 38 - 0
applications/services/rpc/rpc_storage.c

@@ -138,6 +138,41 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex
     furi_record_close(RECORD_STORAGE);
 }
 
+static void rpc_system_storage_timestamp_process(const PB_Main* request, void* context) {
+    furi_assert(request);
+    furi_assert(context);
+    furi_assert(request->which_content == PB_Main_storage_timestamp_request_tag);
+
+    FURI_LOG_D(TAG, "Timestamp");
+
+    RpcStorageSystem* rpc_storage = context;
+    RpcSession* session = rpc_storage->session;
+    furi_assert(session);
+
+    rpc_system_storage_reset_state(rpc_storage, session, true);
+
+    PB_Main* response = malloc(sizeof(PB_Main));
+    response->command_id = request->command_id;
+
+    Storage* fs_api = furi_record_open(RECORD_STORAGE);
+
+    const char* path = request->content.storage_timestamp_request.path;
+    uint32_t timestamp = 0;
+    FS_Error error = storage_common_timestamp(fs_api, path, &timestamp);
+
+    response->command_status = rpc_system_storage_get_error(error);
+    response->which_content = PB_Main_empty_tag;
+
+    if(error == FSE_OK) {
+        response->which_content = PB_Main_storage_timestamp_response_tag;
+        response->content.storage_timestamp_response.timestamp = timestamp;
+    }
+
+    rpc_send_and_release(session, response);
+    free(response);
+    furi_record_close(RECORD_STORAGE);
+}
+
 static void rpc_system_storage_stat_process(const PB_Main* request, void* context) {
     furi_assert(request);
     furi_assert(context);
@@ -672,6 +707,9 @@ void* rpc_system_storage_alloc(RpcSession* session) {
     rpc_handler.message_handler = rpc_system_storage_info_process;
     rpc_add_handler(session, PB_Main_storage_info_request_tag, &rpc_handler);
 
+    rpc_handler.message_handler = rpc_system_storage_timestamp_process;
+    rpc_add_handler(session, PB_Main_storage_timestamp_request_tag, &rpc_handler);
+
     rpc_handler.message_handler = rpc_system_storage_stat_process;
     rpc_add_handler(session, PB_Main_storage_stat_request_tag, &rpc_handler);
 

+ 1 - 0
applications/services/storage/storage.c

@@ -39,6 +39,7 @@ Storage* storage_app_alloc() {
 
     for(uint8_t i = 0; i < STORAGE_COUNT; i++) {
         storage_data_init(&app->storage[i]);
+        storage_data_timestamp(&app->storage[i]);
     }
 
 #ifndef FURI_RAM_EXEC

+ 10 - 0
applications/services/storage/storage.h

@@ -177,6 +177,16 @@ bool storage_dir_rewind(File* file);
 
 /******************* Common Functions *******************/
 
+/** Retrieves unix timestamp of last access
+ *
+ * @param      storage    The storage instance
+ * @param      path       path to file/directory
+ * @param      timestamp  the timestamp pointer
+ *
+ * @return     FS_Error operation result
+ */
+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
  * @param path path to file/directory

+ 22 - 0
applications/services/storage/storage_cli.c

@@ -32,6 +32,7 @@ static void storage_cli_print_usage() {
     printf("\tmkdir\t - creates a new directory\r\n");
     printf("\tmd5\t - md5 hash of the file\r\n");
     printf("\tstat\t - info about file or dir\r\n");
+    printf("\ttimestamp\t - last modification timestamp\r\n");
 };
 
 static void storage_cli_print_error(FS_Error error) {
@@ -386,6 +387,22 @@ static void storage_cli_stat(Cli* cli, FuriString* path) {
     furi_record_close(RECORD_STORAGE);
 }
 
+static void storage_cli_timestamp(Cli* cli, FuriString* path) {
+    UNUSED(cli);
+    Storage* api = furi_record_open(RECORD_STORAGE);
+
+    uint32_t timestamp = 0;
+    FS_Error error = storage_common_timestamp(api, furi_string_get_cstr(path), &timestamp);
+
+    if(error != FSE_OK) {
+        printf("Invalid arguments\r\n");
+    } else {
+        printf("Timestamp %lu\r\n", timestamp);
+    }
+
+    furi_record_close(RECORD_STORAGE);
+}
+
 static void storage_cli_copy(Cli* cli, FuriString* old_path, FuriString* args) {
     UNUSED(cli);
     Storage* api = furi_record_open(RECORD_STORAGE);
@@ -578,6 +595,11 @@ void storage_cli(Cli* cli, FuriString* args, void* context) {
             break;
         }
 
+        if(furi_string_cmp_str(cmd, "timestamp") == 0) {
+            storage_cli_timestamp(cli, path);
+            break;
+        }
+
         storage_cli_print_usage();
     } while(false);
 

+ 10 - 0
applications/services/storage/storage_external_api.c

@@ -354,6 +354,16 @@ bool storage_dir_rewind(File* file) {
 
 /****************** COMMON ******************/
 
+FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp) {
+    S_API_PROLOGUE;
+
+    SAData data = {.ctimestamp = {.path = path, .timestamp = timestamp}};
+
+    S_API_MESSAGE(StorageCommandCommonTimestamp);
+    S_API_EPILOGUE;
+    return S_RETURN_ERROR;
+}
+
 FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo) {
     S_API_PROLOGUE;
 

+ 8 - 0
applications/services/storage/storage_glue.c

@@ -82,6 +82,14 @@ const char* storage_data_status_text(StorageData* storage) {
     return result;
 }
 
+void storage_data_timestamp(StorageData* storage) {
+    storage->timestamp = furi_hal_rtc_get_timestamp();
+}
+
+uint32_t storage_data_get_timestamp(StorageData* storage) {
+    return storage->timestamp;
+}
+
 /****************** storage glue ******************/
 
 bool storage_has_file(const File* file, StorageData* storage_data) {

+ 3 - 0
applications/services/storage/storage_glue.h

@@ -42,6 +42,8 @@ bool storage_data_lock(StorageData* storage);
 bool storage_data_unlock(StorageData* storage);
 StorageStatus storage_data_status(StorageData* storage);
 const char* storage_data_status_text(StorageData* storage);
+void storage_data_timestamp(StorageData* storage);
+uint32_t storage_data_get_timestamp(StorageData* storage);
 
 LIST_DEF(
     StorageFileList,
@@ -58,6 +60,7 @@ struct StorageData {
     FuriMutex* mutex;
     StorageStatus status;
     StorageFileList_t files;
+    uint32_t timestamp;
 };
 
 bool storage_has_file(const File* file, StorageData* storage_data);

+ 1 - 0
applications/services/storage/storage_i.h

@@ -1,5 +1,6 @@
 #pragma once
 #include <furi.h>
+#include <furi_hal.h>
 #include <gui/gui.h>
 #include "storage_glue.h"
 #include "storage_sd_api.h"

+ 7 - 0
applications/services/storage/storage_message.h

@@ -42,6 +42,11 @@ typedef struct {
     uint16_t name_length;
 } SADataDRead;
 
+typedef struct {
+    const char* path;
+    uint32_t* timestamp;
+} SADataCTimestamp;
+
 typedef struct {
     const char* path;
     FileInfo* fileinfo;
@@ -78,6 +83,7 @@ typedef union {
     SADataDOpen dopen;
     SADataDRead dread;
 
+    SADataCTimestamp ctimestamp;
     SADataCStat cstat;
     SADataCFSInfo cfsinfo;
 
@@ -112,6 +118,7 @@ typedef enum {
     StorageCommandDirClose,
     StorageCommandDirRead,
     StorageCommandDirRewind,
+    StorageCommandCommonTimestamp,
     StorageCommandCommonStat,
     StorageCommandCommonRemove,
     StorageCommandCommonMkDir,

+ 29 - 0
applications/services/storage/storage_processing.c

@@ -114,6 +114,9 @@ bool storage_process_file_open(
         if(storage_path_already_open(real_path, storage->files)) {
             file->error_id = FSE_ALREADY_OPEN;
         } else {
+            if(access_mode & FSAM_WRITE) {
+                storage_data_timestamp(storage);
+            }
             storage_push_storage_file(file, real_path, type, storage);
             FS_CALL(storage, file.open(storage, file, remove_vfs(path), access_mode, open_mode));
         }
@@ -166,6 +169,7 @@ static uint16_t storage_process_file_write(
     if(storage == NULL) {
         file->error_id = FSE_INVALID_PARAMETER;
     } else {
+        storage_data_timestamp(storage);
         FS_CALL(storage, file.write(storage, file, buff, bytes_to_write));
     }
 
@@ -209,6 +213,7 @@ static bool storage_process_file_truncate(Storage* app, File* file) {
     if(storage == NULL) {
         file->error_id = FSE_INVALID_PARAMETER;
     } else {
+        storage_data_timestamp(storage);
         FS_CALL(storage, file.truncate(storage, file));
     }
 
@@ -222,6 +227,7 @@ static bool storage_process_file_sync(Storage* app, File* file) {
     if(storage == NULL) {
         file->error_id = FSE_INVALID_PARAMETER;
     } else {
+        storage_data_timestamp(storage);
         FS_CALL(storage, file.sync(storage, file));
     }
 
@@ -332,6 +338,21 @@ bool storage_process_dir_rewind(Storage* app, File* file) {
 
 /******************* Common FS Functions *******************/
 
+static FS_Error
+    storage_process_common_timestamp(Storage* app, const char* path, uint32_t* timestamp) {
+    FS_Error ret = FSE_OK;
+    StorageType type = storage_get_type_by_path(app, path);
+
+    if(storage_type_is_not_valid(type)) {
+        ret = FSE_INVALID_NAME;
+    } else {
+        StorageData* storage = storage_get_storage_by_type(app, type);
+        *timestamp = storage_data_get_timestamp(storage);
+    }
+
+    return ret;
+}
+
 static FS_Error storage_process_common_stat(Storage* app, const char* path, FileInfo* fileinfo) {
     FS_Error ret = FSE_OK;
     StorageType type = storage_get_type_by_path(app, path);
@@ -366,6 +387,7 @@ static FS_Error storage_process_common_remove(Storage* app, const char* path) {
             break;
         }
 
+        storage_data_timestamp(storage);
         FS_CALL(storage, common.remove(storage, remove_vfs(path)));
     } while(false);
 
@@ -382,6 +404,7 @@ static FS_Error storage_process_common_mkdir(Storage* app, const char* path) {
         ret = FSE_INVALID_NAME;
     } else {
         StorageData* storage = storage_get_storage_by_type(app, type);
+        storage_data_timestamp(storage);
         FS_CALL(storage, common.mkdir(storage, remove_vfs(path)));
     }
 
@@ -417,6 +440,7 @@ static FS_Error storage_process_sd_format(Storage* app) {
         ret = FSE_NOT_READY;
     } else {
         ret = sd_format_card(&app->storage[ST_EXT]);
+        storage_data_timestamp(&app->storage[ST_EXT]);
     }
 
     return ret;
@@ -429,6 +453,7 @@ static FS_Error storage_process_sd_unmount(Storage* app) {
         ret = FSE_NOT_READY;
     } else {
         sd_unmount_card(&app->storage[ST_EXT]);
+        storage_data_timestamp(&app->storage[ST_EXT]);
     }
 
     return ret;
@@ -541,6 +566,10 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
         message->return_data->bool_value =
             storage_process_dir_rewind(app, message->data->file.file);
         break;
+    case StorageCommandCommonTimestamp:
+        message->return_data->error_value = storage_process_common_timestamp(
+            app, message->data->ctimestamp.path, message->data->ctimestamp.timestamp);
+        break;
     case StorageCommandCommonStat:
         message->return_data->error_value = storage_process_common_stat(
             app, message->data->cstat.path, message->data->cstat.fileinfo);

+ 1 - 0
applications/services/storage/storages/storage_ext.c

@@ -90,6 +90,7 @@ static bool sd_mount_card(StorageData* storage, bool notify) {
         }
     }
 
+    storage_data_timestamp(storage);
     storage_data_unlock(storage);
 
     return result;

+ 1 - 1
assets/protobuf

@@ -1 +1 @@
-Subproject commit 6727eaf287db077dcd28719cd764f5804712223e
+Subproject commit e5af96e08fea8351898f7b8c6d1e34ce5fd6cdef

+ 3 - 1
firmware/targets/f7/api_symbols.csv

@@ -1,5 +1,5 @@
 entry,status,name,type,params
-Version,+,7.2,,
+Version,+,7.3,,
 Header,+,applications/services/bt/bt_service/bt.h,,
 Header,+,applications/services/cli/cli.h,,
 Header,+,applications/services/cli/cli_vcp.h,,
@@ -1251,6 +1251,7 @@ Function,+,furi_hal_rtc_get_fault_data,uint32_t,
 Function,+,furi_hal_rtc_get_log_level,uint8_t,
 Function,+,furi_hal_rtc_get_pin_fails,uint32_t,
 Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister
+Function,+,furi_hal_rtc_get_timestamp,uint32_t,
 Function,-,furi_hal_rtc_init,void,
 Function,-,furi_hal_rtc_init_early,void,
 Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag
@@ -2235,6 +2236,7 @@ Function,+,storage_common_mkdir,FS_Error,"Storage*, const char*"
 Function,+,storage_common_remove,FS_Error,"Storage*, const char*"
 Function,+,storage_common_rename,FS_Error,"Storage*, const char*, const char*"
 Function,+,storage_common_stat,FS_Error,"Storage*, const char*, FileInfo*"
+Function,+,storage_common_timestamp,FS_Error,"Storage*, const char*, uint32_t*"
 Function,+,storage_dir_close,_Bool,File*
 Function,+,storage_dir_open,_Bool,"File*, const char*"
 Function,+,storage_dir_read,_Bool,"File*, FileInfo*, char*, uint16_t"

+ 6 - 0
firmware/targets/f7/furi_hal/furi_hal_rtc.c

@@ -318,6 +318,12 @@ uint32_t furi_hal_rtc_get_pin_fails() {
     return furi_hal_rtc_get_register(FuriHalRtcRegisterPinFails);
 }
 
+uint32_t furi_hal_rtc_get_timestamp() {
+    FuriHalRtcDateTime datetime = {0};
+    furi_hal_rtc_get_datetime(&datetime);
+    return furi_hal_rtc_datetime_to_timestamp(&datetime);
+}
+
 uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) {
     uint32_t timestamp = 0;
     uint8_t years = 0;

+ 2 - 0
firmware/targets/furi_hal_include/furi_hal_rtc.h

@@ -93,6 +93,8 @@ void furi_hal_rtc_set_pin_fails(uint32_t value);
 
 uint32_t furi_hal_rtc_get_pin_fails();
 
+uint32_t furi_hal_rtc_get_timestamp();
+
 uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime);
 
 #ifdef __cplusplus