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>
This commit is contained in:
		
							parent
							
								
									e3ea5bca76
								
							
						
					
					
						commit
						d68ac50efd
					
				| @ -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, ×tamp); | ||||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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), ×tamp); | ||||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|  | ||||
| @ -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; | ||||
| 
 | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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,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" | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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 @@ | ||||
| Subproject commit 6727eaf287db077dcd28719cd764f5804712223e | ||||
| Subproject commit e5af96e08fea8351898f7b8c6d1e34ce5fd6cdef | ||||
| @ -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" | ||||
|  | ||||
| 
 | 
| @ -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; | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく