Merge "storageproxyd: Add arguments for storage mapping and max file" into main

This commit is contained in:
Donnie Pollitz 2024-04-22 18:20:44 +00:00 committed by Gerrit Code Review
commit 63ef65c138
3 changed files with 340 additions and 31 deletions

View file

@ -41,9 +41,13 @@ static const char* ss_data_root;
static const char* trusty_devname;
static const char* rpmb_devname;
static const char* ss_srv_name = STORAGE_DISK_PROXY_PORT;
static const char* max_file_size_from;
static enum dev_type dev_type = MMC_RPMB;
/* List head for storage mapping, elements added at init, and never removed */
static struct storage_mapping_node* storage_mapping_head;
static enum dev_type parse_dev_type(const char* dev_type_name) {
if (!strcmp(dev_type_name, "mmc")) {
return MMC_RPMB;
@ -58,17 +62,61 @@ static enum dev_type parse_dev_type(const char* dev_type_name) {
}
}
static const char* _sopts = "hp:d:r:t:";
static int parse_and_append_file_mapping(const char* file_mapping) {
if (file_mapping == NULL) {
ALOGE("Provided file mapping is null\n");
return -1;
}
char* file_mapping_dup = strdup(file_mapping);
if (file_mapping_dup == NULL) {
ALOGE("Couldn't duplicate string: %s\n", file_mapping);
return -1;
}
const char* file_name = strtok(file_mapping_dup, ":");
if (file_name == NULL) {
ALOGE("No file name found\n");
return -1;
}
const char* backing_storage = strtok(NULL, ":");
if (backing_storage == NULL) {
ALOGE("No backing storage found\n");
return -1;
}
struct storage_mapping_node* new_node = malloc(sizeof(struct storage_mapping_node));
if (new_node == NULL) {
ALOGE("Couldn't allocate additional storage_mapping_node\n");
return -1;
}
*new_node = (struct storage_mapping_node){.file_name = file_name,
.backing_storage = backing_storage,
.next = storage_mapping_head,
.fd = -1};
storage_mapping_head = new_node;
return 0;
}
static const char* _sopts = "hp:d:r:t:m:f:";
static const struct option _lopts[] = {{"help", no_argument, NULL, 'h'},
{"trusty_dev", required_argument, NULL, 'd'},
{"data_path", required_argument, NULL, 'p'},
{"rpmb_dev", required_argument, NULL, 'r'},
{"dev_type", required_argument, NULL, 't'},
{"max_file_size_from", required_argument, NULL, 'm'},
{"file_storage_mapping", required_argument, NULL, 'f'},
{0, 0, 0, 0}};
static void show_usage_and_exit(int code) {
ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>\n");
ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type> [-m "
"<file>] [-f <file>:<mapping>]\n");
ALOGE("Available dev types: mmc, virt\n");
ALOGE("-f = Maps secure storage files like `0` and `persist/0`\n"
"to block devices. Storageproxyd will handle creating the\n"
"appropriate symlinks in the root datapath.\n");
ALOGE("-m = Specifies the max size constraint for file backed storages.\n"
"The constraint is chosen by giving a file, this allows for passing a\n"
"block device for which a max file size can be queried. File based\n"
"storages will be constrained to that size as well.\n");
exit(code);
}
@ -187,6 +235,7 @@ static int proxy_loop(void) {
static void parse_args(int argc, char* argv[]) {
int opt;
int oidx = 0;
int rc = 0;
while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
switch (opt) {
@ -210,6 +259,18 @@ static void parse_args(int argc, char* argv[]) {
}
break;
case 'f':
rc = parse_and_append_file_mapping(optarg);
if (rc < 0) {
ALOGE("Failed to parse file mapping: %s\n", optarg);
show_usage_and_exit(EXIT_FAILURE);
}
break;
case 'm':
max_file_size_from = strdup(optarg);
break;
default:
ALOGE("unrecognized option (%c):\n", opt);
show_usage_and_exit(EXIT_FAILURE);
@ -225,6 +286,12 @@ static void parse_args(int argc, char* argv[]) {
ALOGI("storage data root: %s\n", ss_data_root);
ALOGI("trusty dev: %s\n", trusty_devname);
ALOGI("rpmb dev: %s\n", rpmb_devname);
ALOGI("File Mappings: \n");
const struct storage_mapping_node* curr = storage_mapping_head;
for (; curr != NULL; curr = curr->next) {
ALOGI("\t%s -> %s\n", curr->file_name, curr->backing_storage);
}
ALOGI("max file size from: %s\n", max_file_size_from ? max_file_size_from : "(unset)");
}
int main(int argc, char* argv[]) {
@ -252,7 +319,7 @@ int main(int argc, char* argv[]) {
ABinderProcess_startThreadPool();
/* initialize secure storage directory */
rc = storage_init(ss_data_root);
rc = storage_init(ss_data_root, storage_mapping_head, max_file_size_from);
if (rc < 0) return EXIT_FAILURE;
/* open rpmb device */

View file

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <cutils/properties.h>
#include <errno.h>
#include <fcntl.h>
@ -39,16 +40,20 @@
#define ALTERNATE_DATA_DIR "alternate/"
/* Maximum file size for filesystem backed storage (i.e. not block dev backed storage) */
#define MAX_FILE_SIZE (0x10000000000)
static size_t max_file_size = 0x10000000000;
enum sync_state {
SS_UNUSED = -1,
SS_CLEAN = 0,
SS_DIRTY = 1,
SS_CLEAN = 0,
SS_DIRTY = 1,
SS_CLEAN_NEED_SYMLINK = 2,
};
static const char *ssdir_name;
/* List head for storage mapping, elements added at init, and never removed */
static struct storage_mapping_node* storage_mapping_head;
/*
* Property set to 1 after we have opened a file under ssdir_name. The backing
* files for both TD and TDP are currently located under /data/vendor/ss and can
@ -75,24 +80,103 @@ static struct {
uint8_t data[MAX_READ_SIZE];
} read_rsp;
static uint32_t insert_fd(int open_flags, int fd)
{
static uint32_t insert_fd(int open_flags, int fd, struct storage_mapping_node* node) {
uint32_t handle = fd;
if (handle < FD_TBL_SIZE) {
fd_state[fd] = SS_CLEAN; /* fd clean */
if (open_flags & O_TRUNC) {
fd_state[fd] = SS_DIRTY; /* set fd dirty */
}
fd_state[fd] = SS_CLEAN; /* fd clean */
if (open_flags & O_TRUNC) {
assert(node == NULL);
fd_state[fd] = SS_DIRTY; /* set fd dirty */
}
if (node != NULL) {
fd_state[fd] = SS_CLEAN_NEED_SYMLINK;
}
} else {
ALOGW("%s: untracked fd %u\n", __func__, fd);
if (open_flags & (O_TRUNC | O_CREAT)) {
fs_state = SS_DIRTY;
}
}
if (node != NULL) {
node->fd = fd;
}
return handle;
}
static void clear_fd_symlink_status(uint32_t handle, struct storage_mapping_node* entry) {
/* Always clear FD, in case fd is not in FD_TBL */
entry->fd = -1;
if (handle >= FD_TBL_SIZE) {
ALOGE("%s: untracked fd=%u\n", __func__, handle);
return;
}
if (fd_state[handle] == SS_CLEAN_NEED_SYMLINK) {
fd_state[handle] = SS_CLEAN;
}
}
static struct storage_mapping_node* get_pending_symlink_mapping(uint32_t handle) {
/* Fast lookup failure, is it in FD TBL */
if (handle < FD_TBL_SIZE && fd_state[handle] != SS_CLEAN_NEED_SYMLINK) {
return NULL;
}
/* Go find our mapping */
struct storage_mapping_node* curr = storage_mapping_head;
for (; curr != NULL; curr = curr->next) {
if (curr->fd == handle) {
return curr;
}
}
/* Safety check: state inconsistent if we get here with handle inside table range */
assert(handle >= FD_TBL_SIZE);
return NULL;
};
static int possibly_symlink_and_clear_mapping(uint32_t handle) {
struct storage_mapping_node* entry = get_pending_symlink_mapping(handle);
if (entry == NULL) {
/* No mappings pending */
return 0;
}
/* Create full path */
char* path = NULL;
int rc = asprintf(&path, "%s/%s", ssdir_name, entry->file_name);
if (rc < 0) {
ALOGE("%s: asprintf failed\n", __func__);
return -1;
}
/* Try and setup the symlinking */
ALOGI("Creating symlink %s->%s\n", path, entry->backing_storage);
rc = symlink(entry->backing_storage, path);
if (rc < 0) {
ALOGE("%s: error symlinking %s->%s (%s)\n", __func__, path, entry->backing_storage,
strerror(errno));
free(path);
return rc;
}
free(path);
clear_fd_symlink_status(handle, entry);
return rc;
}
static bool is_pending_symlink(uint32_t handle) {
struct storage_mapping_node* entry = get_pending_symlink_mapping(handle);
return entry != NULL;
}
static int lookup_fd(uint32_t handle, bool dirty)
{
if (dirty) {
@ -107,6 +191,12 @@ static int lookup_fd(uint32_t handle, bool dirty)
static int remove_fd(uint32_t handle)
{
/* Cleanup fd in symlink mapping if it exists */
struct storage_mapping_node* entry = get_pending_symlink_mapping(handle);
if (entry != NULL) {
entry->fd = -1;
}
if (handle < FD_TBL_SIZE) {
fd_state[handle] = SS_UNUSED; /* set to uninstalled */
}
@ -247,11 +337,73 @@ static void sync_parent(const char* path, struct watcher* watcher) {
watch_progress(watcher, "done syncing parent");
}
static struct storage_mapping_node* get_storage_mapping_entry(const char* source) {
struct storage_mapping_node* curr = storage_mapping_head;
for (; curr != NULL; curr = curr->next) {
if (!strcmp(source, curr->file_name)) {
ALOGI("Found backing file %s for %s\n", curr->backing_storage, source);
return curr;
}
}
return NULL;
}
static bool is_backing_storage_mapped(const char* source) {
const struct storage_mapping_node* curr = storage_mapping_head;
for (; curr != NULL; curr = curr->next) {
if (!strcmp(source, curr->backing_storage)) {
ALOGI("Backed storage mapping exists for %s\n", curr->backing_storage);
return true;
}
}
return false;
}
/* Attempts to open a backed file, if mapped, without creating the symlink. Symlink will be created
* later on the first write. This allows us to continue reporting zero read sizes until the first
* write. */
static int open_possibly_mapped_file(const char* short_path, const char* full_path, int open_flags,
struct storage_mapping_node** entry) {
/* See if mapping exists, report upstream if there is no mapping. */
struct storage_mapping_node* mapping_entry = get_storage_mapping_entry(short_path);
if (mapping_entry == NULL) {
return TEMP_FAILURE_RETRY(open(full_path, open_flags, S_IRUSR | S_IWUSR));
}
/* Check for existence of root path, we don't allow mappings during early boot */
struct stat buf = {0};
if (stat(ssdir_name, &buf) != 0) {
ALOGW("Root path not accessible yet, refuse to open mappings for now.\n");
return -1;
}
/* We don't support exclusive opening of mapped files */
if (open_flags & O_EXCL) {
ALOGE("Requesting exclusive open on backed storage isn't supported: %s\n", full_path);
return -1;
}
/* Try and open mapping file */
open_flags &= ~(O_CREAT | O_EXCL);
ALOGI("%s Attempting to open mapped file: %s\n", __func__, mapping_entry->backing_storage);
int fd =
TEMP_FAILURE_RETRY(open(mapping_entry->backing_storage, open_flags, S_IRUSR | S_IWUSR));
if (fd < 0) {
ALOGE("%s Failed to open mapping file: %s\n", __func__, mapping_entry->backing_storage);
return -1;
}
/* Let caller know which entry we used for opening */
*entry = mapping_entry;
return fd;
}
int storage_file_open(struct storage_msg* msg, const void* r, size_t req_len,
struct watcher* watcher) {
char* path = NULL;
const struct storage_file_open_req *req = r;
struct storage_file_open_resp resp = {0};
struct storage_mapping_node* mapping_entry = NULL;
if (req_len < sizeof(*req)) {
ALOGE("%s: invalid request length (%zd < %zd)\n",
@ -321,14 +473,18 @@ int storage_file_open(struct storage_msg* msg, const void* r, size_t req_len,
if (req->flags & STORAGE_FILE_OPEN_CREATE_EXCLUSIVE) {
/* create exclusive */
open_flags |= O_CREAT | O_EXCL;
rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
/* Look for and attempt opening a mapping, else just do normal open. */
rc = open_possibly_mapped_file(req->name, path, open_flags, &mapping_entry);
} else {
/* try open first */
rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
if (rc == -1 && errno == ENOENT) {
/* then try open with O_CREATE */
open_flags |= O_CREAT;
rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
/* Look for and attempt opening a mapping, else just do normal open. */
rc = open_possibly_mapped_file(req->name, path, open_flags, &mapping_entry);
}
}
@ -356,7 +512,7 @@ int storage_file_open(struct storage_msg* msg, const void* r, size_t req_len,
/* at this point rc contains storage file fd */
msg->result = STORAGE_NO_ERROR;
resp.handle = insert_fd(open_flags, rc);
resp.handle = insert_fd(open_flags, rc, mapping_entry);
ALOGV("%s: \"%s\": fd = %u: handle = %d\n",
__func__, path, rc, resp.handle);
@ -433,6 +589,14 @@ int storage_file_write(struct storage_msg* msg, const void* r, size_t req_len,
goto err_response;
}
/* Handle any delayed symlinking for this handle if any */
rc = possibly_symlink_and_clear_mapping(req->handle);
if (rc < 0) {
ALOGE("Failed to symlink storage\n");
msg->result = STORAGE_ERR_GENERIC;
goto err_response;
}
int fd = lookup_fd(req->handle, true);
watch_progress(watcher, "writing");
if (write_with_retry(fd, &req->data[0], req_len - sizeof(*req),
@ -479,6 +643,14 @@ int storage_file_read(struct storage_msg* msg, const void* r, size_t req_len,
goto err_response;
}
/* If this handle has a delayed symlink we should report 0 size reads until first write occurs
*/
if (is_pending_symlink(req->handle)) {
ALOGI("Pending symlink: Forcing read result 0.\n");
msg->result = STORAGE_NO_ERROR;
return ipc_respond(msg, &read_rsp, sizeof(read_rsp.hdr));
}
int fd = lookup_fd(req->handle, false);
watch_progress(watcher, "reading");
ssize_t read_res = read_with_retry(fd, read_rsp.hdr.data, req->size,
@ -592,7 +764,7 @@ int storage_file_get_max_size(struct storage_msg* msg, const void* r, size_t req
goto err_response;
}
} else {
max_size = MAX_FILE_SIZE;
max_size = max_file_size;
}
resp.max_size = max_size;
@ -603,17 +775,78 @@ err_response:
return ipc_respond(msg, NULL, 0);
}
int storage_init(const char *dirname)
{
int determine_max_file_size(const char* max_file_size_from) {
/* Use default if none passed in */
if (max_file_size_from == NULL) {
ALOGI("No max file source given, continuing to use default: 0x%lx\n", max_file_size);
return 0;
}
/* Check that max_file_size_from is part of our mapping list. */
if (!is_backing_storage_mapped(max_file_size_from)) {
ALOGE("%s: file doesn't match mapped storages (filename=%s)\n", __func__,
max_file_size_from);
return -1;
}
ALOGI("Using %s to determine max file size.\n", max_file_size_from);
/* Error if max file size source not found, possible misconfig. */
struct stat buf = {0};
int rc = stat(max_file_size_from, &buf);
if (rc < 0) {
ALOGE("%s: error stat'ing file (filename=%s): %s\n", __func__, max_file_size_from,
strerror(errno));
return -1;
}
/* Currently only support block device as max file size source */
if ((buf.st_mode & S_IFMT) != S_IFBLK) {
ALOGE("Unsupported max file size source type: %d\n", buf.st_mode);
return -1;
}
ALOGI("%s is a block device, determining block device size\n", max_file_size_from);
uint64_t max_size = 0;
int fd = TEMP_FAILURE_RETRY(open(max_file_size_from, O_RDONLY | O_NONBLOCK));
if (fd < 0) {
ALOGE("%s: failed to open backing file %s for ioctl: %s\n", __func__, max_file_size_from,
strerror(errno));
return -1;
}
rc = ioctl(fd, BLKGETSIZE64, &max_size);
if (rc < 0) {
ALOGE("%s: error calling ioctl on file (fd=%d): %s\n", __func__, fd, strerror(errno));
close(fd);
return -1;
}
close(fd);
max_file_size = max_size;
ALOGI("Using 0x%lx as max file size\n", max_file_size);
return 0;
}
int storage_init(const char* dirname, struct storage_mapping_node* mappings,
const char* max_file_size_from) {
/* If there is an active DSU image, use the alternate fs mode. */
alternate_mode = is_gsi_running();
fs_state = SS_CLEAN;
for (uint i = 0; i < FD_TBL_SIZE; i++) {
fd_state[i] = SS_UNUSED; /* uninstalled */
fd_state[i] = SS_UNUSED; /* uninstalled */
}
ssdir_name = dirname;
storage_mapping_head = mappings;
/* Set the max file size based on incoming configuration */
int rc = determine_max_file_size(max_file_size_from);
if (rc < 0) {
return rc;
}
return 0;
}
@ -623,17 +856,17 @@ int storage_sync_checkpoint(struct watcher* watcher) {
watch_progress(watcher, "sync fd table");
/* sync fd table and reset it to clean state first */
for (uint fd = 0; fd < FD_TBL_SIZE; fd++) {
if (fd_state[fd] == SS_DIRTY) {
if (fs_state == SS_CLEAN) {
/* need to sync individual fd */
rc = fsync(fd);
if (rc < 0) {
ALOGE("fsync for fd=%d failed: %s\n", fd, strerror(errno));
return rc;
}
}
fd_state[fd] = SS_CLEAN; /* set to clean */
}
if (fd_state[fd] == SS_DIRTY) {
if (fs_state == SS_CLEAN) {
/* need to sync individual fd */
rc = fsync(fd);
if (rc < 0) {
ALOGE("fsync for fd=%d failed: %s\n", fd, strerror(errno));
return rc;
}
}
fd_state[fd] = SS_CLEAN; /* set to clean */
}
}
/* check if we need to sync all filesystems */

View file

@ -21,6 +21,14 @@
/* Defined in watchdog.h */
struct watcher;
/* Is used for managing alternate backing storage, generally will be a block device. */
struct storage_mapping_node {
struct storage_mapping_node* next;
const char* file_name;
const char* backing_storage;
int fd;
};
int storage_file_delete(struct storage_msg* msg, const void* req, size_t req_len,
struct watcher* watcher);
@ -45,6 +53,7 @@ int storage_file_set_size(struct storage_msg* msg, const void* req, size_t req_l
int storage_file_get_max_size(struct storage_msg* msg, const void* req, size_t req_len,
struct watcher* watcher);
int storage_init(const char* dirname);
int storage_init(const char* dirname, struct storage_mapping_node* head,
const char* max_file_size_from);
int storage_sync_checkpoint(struct watcher* watcher);