mirror of
https://github.com/google/pebble.git
synced 2025-05-05 09:21:40 -04:00
194 lines
6.4 KiB
C
194 lines
6.4 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "persist.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "kernel/pbl_malloc.h"
|
|
#include "os/mutex.h"
|
|
#include "process_management/app_install_manager.h"
|
|
#include "services/normal/filesystem/app_file.h"
|
|
#include "services/normal/filesystem/pfs.h"
|
|
#include "services/normal/legacy/persist_map.h"
|
|
#include "services/normal/settings/settings_file.h"
|
|
#include "system/logging.h"
|
|
#include "system/passert.h"
|
|
#include "util/list.h"
|
|
#include "util/units.h"
|
|
|
|
#define PERSIST_STORAGE_MAX_SPACE KiBYTES(6)
|
|
|
|
typedef struct PersistStore {
|
|
ListNode list_node;
|
|
Uuid uuid;
|
|
SettingsFile file;
|
|
bool file_open;
|
|
uint8_t usage_count; //!< How many clients are using this store
|
|
} PersistStore;
|
|
|
|
// Each open client has a PersistStore structure linked into this list. If both
|
|
// a worker and foreground app of the same UUID are running, then they share the
|
|
// same store.
|
|
static ListNode *s_client_stores;
|
|
static PebbleMutex *s_mutex;
|
|
|
|
|
|
static bool prv_uuid_list_filter(ListNode* node, void* data) {
|
|
const Uuid *uuid = data;
|
|
PersistStore* store = (PersistStore*)node;
|
|
return uuid_equal(&store->uuid, uuid);
|
|
}
|
|
|
|
static PersistStore * prv_find_open_store(const Uuid *uuid) {
|
|
return (PersistStore *)list_find(s_client_stores, prv_uuid_list_filter,
|
|
(void *)uuid);
|
|
}
|
|
|
|
static ALWAYS_INLINE void prv_lock(void) {
|
|
mutex_lock_with_lr(s_mutex, (uint32_t)__builtin_return_address(0));
|
|
}
|
|
|
|
static inline void prv_unlock(void) {
|
|
mutex_unlock(s_mutex);
|
|
}
|
|
|
|
#define PERSIST_FILE_NAME_MAX_LENGTH sizeof("ps000001")
|
|
|
|
static status_t prv_get_file_name(char *name, size_t buf_len, const Uuid *uuid) {
|
|
// Firmware 2.x persist files are named "p%06d", the added "s" in the file
|
|
// name prefix indicates that it is in SettingsFile format.
|
|
int pid = persist_map_auto_id(uuid);
|
|
if (FAILED(pid)) {
|
|
// Attempting to debug persist map failure
|
|
PBL_LOG(LOG_LEVEL_WARNING, "Failed to get pid! %d", pid);
|
|
persist_map_dump();
|
|
return pid;
|
|
}
|
|
return snprintf(name, buf_len, "ps%06d", pid);
|
|
}
|
|
|
|
status_t persist_service_delete_file(const Uuid *uuid) {
|
|
char name[PERSIST_FILE_NAME_MAX_LENGTH];
|
|
|
|
status_t status = prv_get_file_name(name, sizeof(name), uuid);
|
|
if (FAILED(status)) {
|
|
return status;
|
|
}
|
|
return pfs_remove(name);
|
|
}
|
|
|
|
static bool prv_bad_persist_file_filter(const char *filename) {
|
|
return is_app_file_name(filename) &&
|
|
strcmp(filename + APP_FILE_NAME_PREFIX_LENGTH, "persist") == 0;
|
|
}
|
|
|
|
// Designed to be called once during reset
|
|
void persist_service_init(void) {
|
|
persist_map_init();
|
|
s_mutex = mutex_create();
|
|
|
|
// Find and delete any AppInstallId-indexed persist files. Due to PBL-16663
|
|
// (affecting FW 3.0-dp5 thru -dp7), the AppInstallId in the file name may not
|
|
// correspond to the app that the persist file originally belonged to. Since
|
|
// we can't be sure that the persist files correspond to the current
|
|
// AppInstallId, the safest thing to do is to simply blow them away.
|
|
// TODO: remove this code before FW 3.0-golden.
|
|
PFSFileListEntry *bad_file_list = pfs_create_file_list(
|
|
prv_bad_persist_file_filter);
|
|
PFSFileListEntry *iter = bad_file_list;
|
|
while (iter) {
|
|
pfs_remove(iter->name);
|
|
iter = (PFSFileListEntry *)iter->list_node.next;
|
|
}
|
|
pfs_delete_file_list(bad_file_list);
|
|
}
|
|
|
|
// Return a pointer to the store for the given UUID. Each task that uses persist
|
|
// must call persist_service_client_open() to create/open the store during its
|
|
// startup and persist_service_client_close() during its shutdown.
|
|
//
|
|
// The SettingsFile is opened/created lazily. A persist file will not be
|
|
// created for an app unless it calls a persist function.
|
|
//
|
|
// The persist service mutex is locked when this function is called. It will
|
|
// only be unlocked after a call to persist_service_unlock(). While the global
|
|
// persist service mutex is currently used, the API is designed such that a
|
|
// per-file mutex could be used without altering the callers.
|
|
SettingsFile * persist_service_lock_and_get_store(const Uuid *uuid) {
|
|
prv_lock();
|
|
PersistStore *store = prv_find_open_store(uuid);
|
|
PBL_ASSERTN(store);
|
|
if (!store->file_open) {
|
|
char filename[PERSIST_FILE_NAME_MAX_LENGTH];
|
|
PBL_ASSERTN(PASSED(prv_get_file_name(filename, sizeof(filename), uuid)));
|
|
PBL_ASSERTN(PASSED(settings_file_open(&store->file, filename, PERSIST_STORAGE_MAX_SPACE)));
|
|
store->file_open = true;
|
|
}
|
|
return &store->file;
|
|
}
|
|
|
|
void persist_service_unlock_store(SettingsFile *store) {
|
|
prv_unlock();
|
|
}
|
|
|
|
// Create a store for a client of the given UUID it doesn't already exist. If it
|
|
// exists already (another client with the same UUID is running), then just
|
|
// increment its usage count. This is called by the process startup code
|
|
// (app_state_init() or worker_state_init()).
|
|
void persist_service_client_open(const Uuid *uuid) {
|
|
prv_lock();
|
|
{
|
|
PersistStore *store = prv_find_open_store(uuid);
|
|
if (store) {
|
|
store->usage_count++;
|
|
} else {
|
|
store = kernel_malloc_check(sizeof(*store));
|
|
*store = (PersistStore) {
|
|
.uuid = *uuid,
|
|
.usage_count = 1,
|
|
.file_open = false,
|
|
};
|
|
s_client_stores = list_insert_before(s_client_stores, &store->list_node);
|
|
}
|
|
}
|
|
prv_unlock();
|
|
}
|
|
|
|
// Release the store for the given UUID. Called by ProcessManager to clean up
|
|
// after a task exists. If there are no other processes using the same store, it
|
|
// will be freed
|
|
void persist_service_client_close(const Uuid *uuid) {
|
|
prv_lock();
|
|
{
|
|
PersistStore *store = prv_find_open_store(uuid);
|
|
PBL_ASSERTN(store &&
|
|
list_contains(s_client_stores, &store->list_node) &&
|
|
store->usage_count >= 1);
|
|
|
|
if (--store->usage_count == 0) {
|
|
if (store->file_open) {
|
|
settings_file_close(&store->file);
|
|
}
|
|
list_remove(&store->list_node,
|
|
&s_client_stores /* &head */, NULL /* &tail */);
|
|
kernel_free(store);
|
|
}
|
|
}
|
|
prv_unlock();
|
|
}
|