Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compile-time APP_DATA_PATH() and APP_ASSETS_PATH() #3435

Draft
wants to merge 23 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions applications/debug/unit_tests/tests/storage/storage_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ MU_TEST_SUITE(storage_rename) {
furi_record_close(RECORD_STORAGE);
}

#define APPSDATA_APP_PATH(path) APPS_DATA_PATH "/" path
#define APPSDATA_APP_PATH(path) STORAGE_APPS_DATA_STEM "/" path

static const char* storage_test_apps[] = {
"-_twilight_-",
Expand Down Expand Up @@ -470,7 +470,7 @@ MU_TEST(test_storage_data_path) {
storage_file_free(file);

// check that appsdata folder exists
mu_check(storage_dir_exists(storage, APPS_DATA_PATH));
mu_check(storage_dir_exists(storage, STORAGE_APPS_DATA_STEM));

// check that cli folder exists
mu_check(storage_dir_exists(storage, APPSDATA_APP_PATH("cli")));
Expand Down
10 changes: 6 additions & 4 deletions applications/examples/example_apps_assets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ Source code for this example can be found [here](https://github.com/flipperdevic

The **Apps Assets** folder is a folder where external applications unpack their assets.

The path to the current application folder is related to the `appid` of the app. The `appid` is used to identify the app in the app store and is stored in the `application.fam` file.
The path to the current application assets folder is related to the `appid` of the app. The `appid` is used to identify the app in the app store and is stored in the `application.fam` file.
The Apps Assets folder is located only on the external storage, the SD card.

For example, if the `appid` of the app is `snake_game`, the path to the Apps Assets folder will be `/ext/apps_assets/snake_game`. But using raw paths is not recommended, because the path to the Apps Assets folder can change in the future. Use the `/assets` alias instead.
For example, if the `appid` of the app is `snake_game`, the path to the Apps Assets folder will be `/ext/apps_assets/snake_game`. But using raw paths is not recommended, because the path to the Apps Assets folder can change in the future. Use the `APP_ASSETS_PATH()` and/or `STORAGE_APP_ASSETS_PATH_PREFIX` macros instead.

## How to get the path to the Apps Assets folder?

You can use `/assets` alias to get the path to the current application data folder. For example, if you want to open a file `database.txt` in the Apps Assets folder, you can use the next path: `/data/database.txt`. But this way is not recommended, because even the `/assets` alias can change in the future.
You can use `/ext/apps_assets/appid` directly (replacing `appid`) to access your application assets folder. For example, if you want to open a file `database.txt` in the Apps Assets folder, you can use this path: `/ext/apps_assets/appid/database.txt`. But this way is not recommended, because the path can change in the future.

We recommend to use the `APP_ASSETS_PATH` macro to get the path to the Apps Assets folder. For example, if you want to open a file `database.txt` in the Apps Assets folder, you can use the next path: `APP_ASSETS_PATH("database.txt")`.
We recommend to use the `APP_ASSETS_PATH()` macro to get the path to the Apps Assets folder. For example, if you want to open a file `database.txt` in the Apps Assets folder, you can use this path: `APP_ASSETS_PATH("database.txt")`.

If you want to construct paths at run-time, you can use the `STORAGE_APP_ASSETS_PATH_PREFIX` macro, which evaluates to `/ext/apps_assets/appid` at compile-time.

## What is the difference between the Apps Assets folder and the Apps Data folder?

Expand Down
10 changes: 7 additions & 3 deletions applications/examples/example_apps_data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ The **Apps Data** folder is a folder used to store data for external apps that a
The path to the current application folder is related to the `appid` of the app. The `appid` is used to identify the app in the app store and is stored in the `application.fam` file.
The Apps Data folder is located only on the external storage, the SD card.

For example, if the `appid` of the app is `snake_game`, the path to the Apps Data folder will be `/ext/apps_data/snake_game`. But using raw paths is not recommended, because the path to the Apps Data folder can change in the future. Use the `/data` alias instead.
For example, if the `appid` of the app is `snake_game`, the path to the Apps Data folder will be `/ext/apps_data/snake_game`. But using raw paths is not recommended, because the path to the Apps Data folder can change in the future. Use the `APP_DATA_PATH()` and/or `STORAGE_APP_DATA_PATH_PREFIX` macros instead.

Accessing a path like this will also ensure the directory exists (just the root `/ext/apps_data/snake_game` for example, not subdirectories), so you won't need to create it.

## How to get the path to the Apps Data folder?

You can use `/data` alias to get the path to the current application data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `/data/config.txt`. But this way is not recommended, because even the `/data` alias can change in the future.
You can use `/ext/apps_data/appid` directly (replacing `appid`) to access your application data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use this path: `/ext/apps_data/appid/config.txt`. But this way is not recommended, because the path can change in the future.

We recommend to use the `APP_DATA_PATH()` macro to get the path to the Apps Data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use this path: `APP_DATA_PATH("config.txt")`.

We recommend to use the `APP_DATA_PATH` macro to get the path to the Apps Data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `APP_DATA_PATH("config.txt")`.
If you want to construct paths at run-time, you can use the `STORAGE_APP_DATA_PATH_PREFIX` macro, which evaluates to `/ext/apps_data/appid` at compile-time.

## What is the difference between the Apps Assets folder and the Apps Data folder?

Expand Down
8 changes: 6 additions & 2 deletions applications/services/storage/storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ extern "C" {
#define STORAGE_INT_PATH_PREFIX "/int"
#define STORAGE_EXT_PATH_PREFIX "/ext"
#define STORAGE_ANY_PATH_PREFIX "/any"
#define STORAGE_APP_DATA_PATH_PREFIX "/data"
#define STORAGE_APP_ASSETS_PATH_PREFIX "/assets"

#define INT_PATH(path) STORAGE_INT_PATH_PREFIX "/" path
#define EXT_PATH(path) STORAGE_EXT_PATH_PREFIX "/" path
#define ANY_PATH(path) STORAGE_ANY_PATH_PREFIX "/" path

#define STORAGE_APPS_DATA_STEM EXT_PATH("apps_data")
#define STORAGE_APPS_ASSETS_STEM EXT_PATH("apps_assets")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved from storage_i.h "APPS_DATA_PATH" and "APPS_ASSETS_PATH" to here to avoid duplicated definition with "apps_data" explicitly in string. not sure if its fine to be exposed to public api, if it gives wrong idea


#define STORAGE_APP_DATA_PATH_PREFIX STORAGE_APPS_DATA_STEM "/" FAP_APPID
#define STORAGE_APP_ASSETS_PATH_PREFIX STORAGE_APPS_ASSETS_STEM "/" FAP_APPID
#define APP_DATA_PATH(path) STORAGE_APP_DATA_PATH_PREFIX "/" path
#define APP_ASSETS_PATH(path) STORAGE_APP_ASSETS_PATH_PREFIX "/" path

Expand Down
9 changes: 0 additions & 9 deletions applications/services/storage/storage_external_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ static bool storage_file_open_internal(
.path = path,
.access_mode = access_mode,
.open_mode = open_mode,
.thread_id = furi_thread_get_current_id(),
}};

file->type = FileTypeOpenFile;
Expand Down Expand Up @@ -327,7 +326,6 @@ static bool storage_dir_open_internal(File* file, const char* path) {
.dopen = {
.file = file,
.path = path,
.thread_id = furi_thread_get_current_id(),
}};

file->type = FileTypeOpenDir;
Expand Down Expand Up @@ -436,7 +434,6 @@ FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t*
.ctimestamp = {
.path = path,
.timestamp = timestamp,
.thread_id = furi_thread_get_current_id(),
}};

S_API_MESSAGE(StorageCommandCommonTimestamp);
Expand All @@ -452,7 +449,6 @@ FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* filei
.cstat = {
.path = path,
.fileinfo = fileinfo,
.thread_id = furi_thread_get_current_id(),
}};

S_API_MESSAGE(StorageCommandCommonStat);
Expand All @@ -467,7 +463,6 @@ FS_Error storage_common_remove(Storage* storage, const char* path) {
SAData data = {
.path = {
.path = path,
.thread_id = furi_thread_get_current_id(),
}};

S_API_MESSAGE(StorageCommandCommonRemove);
Expand Down Expand Up @@ -748,7 +743,6 @@ FS_Error storage_common_mkdir(Storage* storage, const char* path) {
SAData data = {
.path = {
.path = path,
.thread_id = furi_thread_get_current_id(),
}};

S_API_MESSAGE(StorageCommandCommonMkDir);
Expand All @@ -770,7 +764,6 @@ FS_Error storage_common_fs_info(
.fs_path = fs_path,
.total_space = total_space,
.free_space = free_space,
.thread_id = furi_thread_get_current_id(),
}};

S_API_MESSAGE(StorageCommandCommonFSInfo);
Expand All @@ -786,7 +779,6 @@ void storage_common_resolve_path_and_ensure_app_directory(Storage* storage, Furi
SAData data = {
.cresolvepath = {
.path = path,
.thread_id = furi_thread_get_current_id(),
}};

S_API_MESSAGE(StorageCommandCommonResolvePath);
Expand Down Expand Up @@ -830,7 +822,6 @@ bool storage_common_equivalent_path(
.path1 = path1,
.path2 = path2,
.truncate = truncate,
.thread_id = furi_thread_get_current_id(),
}};

S_API_MESSAGE(StorageCommandCommonEquivalentPath);
Expand Down
3 changes: 0 additions & 3 deletions applications/services/storage/storage_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ extern "C" {

#define STORAGE_COUNT (ST_INT + 1)

#define APPS_DATA_PATH EXT_PATH("apps_data")
#define APPS_ASSETS_PATH EXT_PATH("apps_assets")

typedef struct {
ViewPort* view_port;
bool enabled;
Expand Down
8 changes: 0 additions & 8 deletions applications/services/storage/storage_message.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ typedef struct {
const char* path;
FS_AccessMode access_mode;
FS_OpenMode open_mode;
FuriThreadId thread_id;
} SADataFOpen;

typedef struct {
Expand All @@ -35,7 +34,6 @@ typedef struct {
typedef struct {
File* file;
const char* path;
FuriThreadId thread_id;
} SADataDOpen;

typedef struct {
Expand All @@ -48,32 +46,27 @@ typedef struct {
typedef struct {
const char* path;
uint32_t* timestamp;
FuriThreadId thread_id;
} SADataCTimestamp;

typedef struct {
const char* path;
FileInfo* fileinfo;
FuriThreadId thread_id;
} SADataCStat;

typedef struct {
const char* fs_path;
uint64_t* total_space;
uint64_t* free_space;
FuriThreadId thread_id;
} SADataCFSInfo;

typedef struct {
FuriString* path;
FuriThreadId thread_id;
} SADataCResolvePath;

typedef struct {
const char* path1;
const char* path2;
bool truncate;
FuriThreadId thread_id;
} SADataCEquivPath;

typedef struct {
Expand All @@ -82,7 +75,6 @@ typedef struct {

typedef struct {
const char* path;
FuriThreadId thread_id;
} SADataPath;

typedef struct {
Expand Down
72 changes: 26 additions & 46 deletions applications/services/storage/storage_processing.c
Original file line number Diff line number Diff line change
Expand Up @@ -520,45 +520,26 @@ static FS_Error storage_process_sd_status(Storage* app) {

/******************** Aliases processing *******************/

void storage_process_alias(
Storage* app,
FuriString* path,
FuriThreadId thread_id,
bool create_folders) {
if(furi_string_start_with(path, STORAGE_APP_DATA_PATH_PREFIX)) {
FuriString* apps_data_path_with_appsid = furi_string_alloc_set(APPS_DATA_PATH "/");
furi_string_cat(apps_data_path_with_appsid, furi_thread_get_appid(thread_id));

// "/data" -> "/ext/apps_data/appsid"
furi_string_replace_at(
path,
0,
strlen(STORAGE_APP_DATA_PATH_PREFIX),
furi_string_get_cstr(apps_data_path_with_appsid));
void storage_process_alias(Storage* app, FuriString* path, bool create_folders) {
if(furi_string_start_with(path, STORAGE_APPS_DATA_STEM "/")) {
FuriString* apps_data_appid = furi_string_alloc_set(path);
// Only create "/ext/apps_data/appid", not any subdir
// "/ext/apps_data/appid/whatever" -> "/ext/apps_data/appid"
size_t slash =
furi_string_search_char(apps_data_appid, '/', strlen(STORAGE_APPS_DATA_STEM "/"));
if(slash != FURI_STRING_FAILURE) {
furi_string_left(apps_data_appid, slash);
}

// Create app data folder if not exists
if(create_folders &&
storage_process_common_stat(app, apps_data_path_with_appsid, NULL) != FSE_OK) {
furi_string_set(apps_data_path_with_appsid, APPS_DATA_PATH);
storage_process_common_mkdir(app, apps_data_path_with_appsid);
furi_string_cat(apps_data_path_with_appsid, "/");
furi_string_cat(apps_data_path_with_appsid, furi_thread_get_appid(thread_id));
storage_process_common_mkdir(app, apps_data_path_with_appsid);
if(create_folders && storage_process_common_stat(app, apps_data_appid, NULL) != FSE_OK) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like its still the error i mentioned above, setting up the CI unit tests, not running them. code that causes the issue is not in flipper code, but python helper is failing when creating a folder that already exists. i had tried in 887cd4b to make it ignore an "already exists" error but seems like this fix had some syntax issue.

i will fix this, but maybe could consider making it so checking for dir exist in "/ext/apps_data/*" will say that dir already exists?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maintaining compatibility and existing behaviour from API is quite important - looking forward to hearing @DrZlo13's opinion on these changes.

FuriString* apps_data = furi_string_alloc_set(STORAGE_APPS_DATA_STEM);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're checking existence of one path, but creating a different one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it first makes /ext/apps_data here, then 2 lines below it makes /ext/apps_data/appid. it is just ensuring that apps_data exists, previous implementation did this too

storage_process_common_mkdir(app, apps_data);
furi_string_free(apps_data);
storage_process_common_mkdir(app, apps_data_appid);
}

furi_string_free(apps_data_path_with_appsid);
} else if(furi_string_start_with(path, STORAGE_APP_ASSETS_PATH_PREFIX)) {
FuriString* apps_assets_path_with_appsid = furi_string_alloc_set(APPS_ASSETS_PATH "/");
furi_string_cat(apps_assets_path_with_appsid, furi_thread_get_appid(thread_id));

// "/assets" -> "/ext/apps_assets/appsid"
furi_string_replace_at(
path,
0,
strlen(STORAGE_APP_ASSETS_PATH_PREFIX),
furi_string_get_cstr(apps_assets_path_with_appsid));

furi_string_free(apps_assets_path_with_appsid);
furi_string_free(apps_data_appid);
}
}

Expand All @@ -571,7 +552,7 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
// File operations
case StorageCommandFileOpen:
path = furi_string_alloc_set(message->data->fopen.path);
storage_process_alias(app, path, message->data->fopen.thread_id, true);
storage_process_alias(app, path, true);
message->return_data->bool_value = storage_process_file_open(
app,
message->data->fopen.file,
Expand Down Expand Up @@ -627,7 +608,7 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
// Dir operations
case StorageCommandDirOpen:
path = furi_string_alloc_set(message->data->dopen.path);
storage_process_alias(app, path, message->data->dopen.thread_id, true);
storage_process_alias(app, path, true);
message->return_data->bool_value =
storage_process_dir_open(app, message->data->dopen.file, path);
break;
Expand All @@ -651,44 +632,43 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
// Common operations
case StorageCommandCommonTimestamp:
path = furi_string_alloc_set(message->data->ctimestamp.path);
storage_process_alias(app, path, message->data->ctimestamp.thread_id, false);
storage_process_alias(app, path, false);
message->return_data->error_value =
storage_process_common_timestamp(app, path, message->data->ctimestamp.timestamp);
break;
case StorageCommandCommonStat:
path = furi_string_alloc_set(message->data->cstat.path);
storage_process_alias(app, path, message->data->cstat.thread_id, false);
storage_process_alias(app, path, false);
message->return_data->error_value =
storage_process_common_stat(app, path, message->data->cstat.fileinfo);
break;
case StorageCommandCommonRemove:
path = furi_string_alloc_set(message->data->path.path);
storage_process_alias(app, path, message->data->path.thread_id, false);
storage_process_alias(app, path, false);
message->return_data->error_value = storage_process_common_remove(app, path);
break;
case StorageCommandCommonMkDir:
path = furi_string_alloc_set(message->data->path.path);
storage_process_alias(app, path, message->data->path.thread_id, true);
storage_process_alias(app, path, true);
message->return_data->error_value = storage_process_common_mkdir(app, path);
break;
case StorageCommandCommonFSInfo:
path = furi_string_alloc_set(message->data->cfsinfo.fs_path);
storage_process_alias(app, path, message->data->cfsinfo.thread_id, false);
storage_process_alias(app, path, false);
message->return_data->error_value = storage_process_common_fs_info(
app, path, message->data->cfsinfo.total_space, message->data->cfsinfo.free_space);
break;
case StorageCommandCommonResolvePath:
storage_process_alias(
app, message->data->cresolvepath.path, message->data->cresolvepath.thread_id, true);
storage_process_alias(app, message->data->cresolvepath.path, true);
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);
storage_process_alias(app, path1, false);
storage_process_alias(app, path2, false);
if(message->data->cequivpath.truncate) {
furi_string_left(path2, furi_string_size(path1));
}
Expand Down
2 changes: 2 additions & 0 deletions applications/system/js_app/js_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ static void js_app_free(JsApp* app) {
int32_t js_app(void* arg) {
JsApp* app = js_app_alloc();

#define FAP_APPID "js_app"
FuriString* script_path = furi_string_alloc_set(APP_ASSETS_PATH());
#undef FAP_APPID
Willy-JL marked this conversation as resolved.
Show resolved Hide resolved
do {
if(arg != NULL && strlen(arg) > 0) {
furi_string_set(script_path, (const char*)arg);
Expand Down
Loading
Loading