trusty: storage: implement storage proxy daemon

Change-Id: I80cdf61e5ced00dd32a3e35eb81969d25b624df9
This commit is contained in:
Michael Ryleev 2016-03-08 15:16:11 -08:00
parent 117b1307d9
commit f59560df2c
10 changed files with 1285 additions and 0 deletions

View file

@ -0,0 +1,39 @@
#
# Copyright (C) 2016 The Android Open-Source Project
#
# 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.
#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := storageproxyd
LOCAL_SRC_FILES := \
ipc.c \
rpmb.c \
storage.c \
proxy.c
LOCAL_CLFAGS = -Wall -Werror
LOCAL_SHARED_LIBRARIES := \
liblog \
LOCAL_STATIC_LIBRARIES := \
libtrustystorageinterface \
libtrusty
include $(BUILD_EXECUTABLE)

114
trusty/storage/proxy/ipc.c Normal file
View file

@ -0,0 +1,114 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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 <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include <trusty/tipc.h>
#include "ipc.h"
#include "log.h"
#define MAX_RECONNECT_RETRY_COUNT 5
#define TRUSTY_RECONNECT_TIMEOUT_SEC 5
static int tipc_fd = -1;
int ipc_connect(const char *device, const char *port)
{
int rc;
uint retry_cnt = 0;
assert(tipc_fd == -1);
while(true) {
rc = tipc_connect(device, port);
if (rc >= 0)
break;
ALOGE("failed (%d) to connect to storage server\n", rc);
if (++retry_cnt > MAX_RECONNECT_RETRY_COUNT) {
ALOGE("max number of reconnect retries (%d) has been reached\n",
retry_cnt);
return -1;
}
sleep(TRUSTY_RECONNECT_TIMEOUT_SEC);
}
tipc_fd = rc;
return 0;
}
void ipc_disconnect(void)
{
assert(tipc_fd >= 0);
tipc_close(tipc_fd);
tipc_fd = -1;
}
ssize_t ipc_get_msg(struct storage_msg *msg, void *req_buf, size_t req_buf_len)
{
ssize_t rc;
struct iovec iovs[2] = {{msg, sizeof(*msg)}, {req_buf, req_buf_len}};
assert(tipc_fd >= 0);
rc = readv(tipc_fd, iovs, 2);
if (rc < 0) {
ALOGE("failed to read request: %s\n", strerror(errno));
return rc;
}
/* check for minimum size */
if ((size_t)rc < sizeof(*msg)) {
ALOGE("message is too short (%zu bytes received)\n", rc);
return -1;
}
/* check for message completeness */
if (msg->size != (uint32_t)rc) {
ALOGE("inconsistent message size [cmd=%d] (%u != %u)\n",
msg->cmd, msg->size, (uint32_t)rc);
return -1;
}
return rc - sizeof(*msg);
}
int ipc_respond(struct storage_msg *msg, void *out, size_t out_size)
{
ssize_t rc;
struct iovec iovs[2] = {{msg, sizeof(*msg)}, {out, out_size}};
assert(tipc_fd >= 0);
msg->cmd |= STORAGE_RESP_BIT;
rc = writev(tipc_fd, iovs, out ? 2 : 1);
if (rc < 0) {
ALOGE("error sending response 0x%x: %s\n",
msg->cmd, strerror(errno));
return -1;
}
return 0;
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.
*/
#pragma once
#include <stdint.h>
#include <trusty/interface/storage.h>
int ipc_connect(const char *device, const char *service_name);
void ipc_disconnect(void);
ssize_t ipc_get_msg(struct storage_msg *msg, void *req_buf, size_t req_buf_len);
int ipc_respond(struct storage_msg *msg, void *out, size_t out_size);

View file

@ -0,0 +1,19 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.
*/
#define LOG_TAG "storageproxyd"
#include <cutils/log.h>

View file

@ -0,0 +1,264 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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 <errno.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <private/android_filesystem_config.h>
#include "ipc.h"
#include "log.h"
#include "rpmb.h"
#include "storage.h"
#define REQ_BUFFER_SIZE 4096
static uint8_t req_buffer[REQ_BUFFER_SIZE + 1];
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 *_sopts = "hp:d:r:";
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'},
{0, 0, 0, 0}
};
static void show_usage_and_exit(int code)
{
ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev>\n");
exit(code);
}
static int drop_privs(void)
{
struct __user_cap_header_struct capheader;
struct __user_cap_data_struct capdata[2];
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
return -1;
}
/*
* ensure we're running as the system user
*/
if (setgid(AID_SYSTEM) != 0) {
return -1;
}
if (setuid(AID_SYSTEM) != 0) {
return -1;
}
/*
* drop all capabilities except SYS_RAWIO
*/
memset(&capheader, 0, sizeof(capheader));
memset(&capdata, 0, sizeof(capdata));
capheader.version = _LINUX_CAPABILITY_VERSION_3;
capheader.pid = 0;
capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted = CAP_TO_MASK(CAP_SYS_RAWIO);
capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective = CAP_TO_MASK(CAP_SYS_RAWIO);
if (capset(&capheader, &capdata[0]) < 0) {
return -1;
}
/* no-execute for user, no access for group and other */
umask(S_IXUSR | S_IRWXG | S_IRWXO);
return 0;
}
static int handle_req(struct storage_msg *msg, const void *req, size_t req_len)
{
int rc;
if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) &&
(msg->cmd != STORAGE_RPMB_SEND)) {
/*
* handling post commit messages on non rpmb commands are not
* implemented as there is no use case for this yet.
*/
ALOGE("cmd 0x%x: post commit option is not implemented\n", msg->cmd);
msg->result = STORAGE_ERR_UNIMPLEMENTED;
return ipc_respond(msg, NULL, 0);
}
if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT) {
rc = storage_sync_checkpoint();
if (rc < 0) {
msg->result = STORAGE_ERR_GENERIC;
return ipc_respond(msg, NULL, 0);
}
}
switch (msg->cmd) {
case STORAGE_FILE_DELETE:
rc = storage_file_delete(msg, req, req_len);
break;
case STORAGE_FILE_OPEN:
rc = storage_file_open(msg, req, req_len);
break;
case STORAGE_FILE_CLOSE:
rc = storage_file_close(msg, req, req_len);
break;
case STORAGE_FILE_WRITE:
rc = storage_file_write(msg, req, req_len);
break;
case STORAGE_FILE_READ:
rc = storage_file_read(msg, req, req_len);
break;
case STORAGE_FILE_GET_SIZE:
rc = storage_file_get_size(msg, req, req_len);
break;
case STORAGE_FILE_SET_SIZE:
rc = storage_file_set_size(msg, req, req_len);
break;
case STORAGE_RPMB_SEND:
rc = rpmb_send(msg, req, req_len);
break;
default:
ALOGE("unhandled command 0x%x\n", msg->cmd);
msg->result = STORAGE_ERR_UNIMPLEMENTED;
rc = 1;
}
if (rc > 0) {
/* still need to send response */
rc = ipc_respond(msg, NULL, 0);
}
return rc;
}
static int proxy_loop(void)
{
ssize_t rc;
struct storage_msg msg;
/* enter main message handling loop */
while (true) {
/* get incoming message */
rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE);
if (rc < 0)
return rc;
/* handle request */
req_buffer[rc] = 0; /* force zero termination */
rc = handle_req(&msg, req_buffer, rc);
if (rc)
return rc;
}
return 0;
}
static void parse_args(int argc, char *argv[])
{
int opt;
int oidx = 0;
while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
switch (opt) {
case 'd':
trusty_devname = strdup(optarg);
break;
case 'p':
ss_data_root = strdup(optarg);
break;
case 'r':
rpmb_devname = strdup(optarg);
break;
default:
ALOGE("unrecognized option (%c):\n", opt);
show_usage_and_exit(EXIT_FAILURE);
}
}
if (ss_data_root == NULL ||
trusty_devname == NULL ||
rpmb_devname == NULL) {
ALOGE("missing required argument(s)\n");
show_usage_and_exit(EXIT_FAILURE);
}
ALOGI("starting storageproxyd\n");
ALOGI("storage data root: %s\n", ss_data_root);
ALOGI("trusty dev: %s\n", trusty_devname);
ALOGI("rpmb dev: %s\n", rpmb_devname);
}
int main(int argc, char *argv[])
{
int rc;
uint retry_cnt;
/* drop privileges */
if (drop_privs() < 0)
return EXIT_FAILURE;
/* parse arguments */
parse_args(argc, argv);
/* initialize secure storage directory */
rc = storage_init(ss_data_root);
if (rc < 0)
return EXIT_FAILURE;
/* open rpmb device */
rc = rpmb_open(rpmb_devname);
if (rc < 0)
return EXIT_FAILURE;
/* connect to Trusty secure storage server */
rc = ipc_connect(trusty_devname, ss_srv_name);
if (rc < 0)
return EXIT_FAILURE;
/* enter main loop */
rc = proxy_loop();
ALOGE("exiting proxy loop with status (%d)\n", rc);
ipc_disconnect();
rpmb_close();
return (rc < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
}

210
trusty/storage/proxy/rpmb.c Normal file
View file

@ -0,0 +1,210 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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 <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/major.h>
#include <linux/mmc/ioctl.h>
#include "ipc.h"
#include "log.h"
#include "rpmb.h"
#include "storage.h"
#define MMC_READ_MULTIPLE_BLOCK 18
#define MMC_WRITE_MULTIPLE_BLOCK 25
#define MMC_RELIABLE_WRITE_FLAG (1 << 31)
#define MMC_RSP_PRESENT (1 << 0)
#define MMC_RSP_CRC (1 << 2)
#define MMC_RSP_OPCODE (1 << 4)
#define MMC_CMD_ADTC (1 << 5)
#define MMC_RSP_SPI_S1 (1 << 7)
#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1)
#define MMC_WRITE_FLAG_R 0
#define MMC_WRITE_FLAG_W 1
#define MMC_WRITE_FLAG_RELW (MMC_WRITE_FLAG_W | MMC_RELIABLE_WRITE_FLAG)
#define MMC_BLOCK_SIZE 512
static int rpmb_fd = -1;
static uint8_t read_buf[4096];
#ifdef RPMB_DEBUG
static void print_buf(const char *prefix, const uint8_t *buf, size_t size)
{
size_t i;
printf("%s @%p [%zu]", prefix, buf, size);
for (i = 0; i < size; i++) {
if (i && i % 32 == 0)
printf("\n%*s", (int) strlen(prefix), "");
printf(" %02x", buf[i]);
}
printf("\n");
fflush(stdout);
}
#endif
int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len)
{
int rc;
struct {
struct mmc_ioc_multi_cmd multi;
struct mmc_ioc_cmd cmd_buf[3];
} mmc = {};
struct mmc_ioc_cmd *cmd = mmc.multi.cmds;
const struct storage_rpmb_send_req *req = r;
if (req_len < sizeof(*req)) {
ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n",
req_len, sizeof(*req));
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
size_t expected_len =
sizeof(*req) + req->reliable_write_size + req->write_size;
if (req_len != expected_len) {
ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n",
req_len, expected_len);
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
const uint8_t *write_buf = req->payload;
if (req->reliable_write_size) {
if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
cmd->write_flag = MMC_WRITE_FLAG_RELW;
cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
cmd->blksz = MMC_BLOCK_SIZE;
cmd->blocks = req->reliable_write_size / MMC_BLOCK_SIZE;
mmc_ioc_cmd_set_data((*cmd), write_buf);
#ifdef RPMB_DEBUG
ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
print_buf("request: ", write_buf, req->reliable_write_size);
#endif
write_buf += req->reliable_write_size;
mmc.multi.num_of_cmds++;
cmd++;
}
if (req->write_size) {
if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
ALOGW("invalid write size %u\n", req->write_size);
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
cmd->write_flag = MMC_WRITE_FLAG_W;
cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
cmd->blksz = MMC_BLOCK_SIZE;
cmd->blocks = req->write_size / MMC_BLOCK_SIZE;
mmc_ioc_cmd_set_data((*cmd), write_buf);
#ifdef RPMB_DEBUG
ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
print_buf("request: ", write_buf, req->write_size);
#endif
write_buf += req->write_size;
mmc.multi.num_of_cmds++;
cmd++;
}
if (req->read_size) {
if (req->read_size % MMC_BLOCK_SIZE != 0 ||
req->read_size > sizeof(read_buf)) {
ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
cmd->write_flag = MMC_WRITE_FLAG_R;
cmd->opcode = MMC_READ_MULTIPLE_BLOCK;
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
cmd->blksz = MMC_BLOCK_SIZE;
cmd->blocks = req->read_size / MMC_BLOCK_SIZE;
mmc_ioc_cmd_set_data((*cmd), read_buf);
#ifdef RPMB_DEBUG
ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
#endif
mmc.multi.num_of_cmds++;
cmd++;
}
rc = ioctl(rpmb_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
if (rc < 0) {
ALOGE("%s: mmc ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
msg->result = STORAGE_ERR_GENERIC;
goto err_response;
}
#ifdef RPMB_DEBUG
if (req->read_size)
print_buf("response: ", read_buf, req->read_size);
#endif
if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
/*
* Nothing todo for post msg commit request as MMC_IOC_MULTI_CMD
* is fully synchronous in this implementation.
*/
}
msg->result = STORAGE_NO_ERROR;
return ipc_respond(msg, read_buf, req->read_size);
err_response:
return ipc_respond(msg, NULL, 0);
}
int rpmb_open(const char *rpmb_devname)
{
int rc;
rc = open(rpmb_devname, O_RDWR, 0);
if (rc < 0) {
ALOGE("unable (%d) to open rpmb device '%s': %s\n",
errno, rpmb_devname, strerror(errno));
return rc;
}
rpmb_fd = rc;
return 0;
}
void rpmb_close(void)
{
close(rpmb_fd);
rpmb_fd = -1;
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.
*/
#pragma once
#include <stdint.h>
#include <trusty/interface/storage.h>
int rpmb_open(const char *rpmb_devname);
int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len);
void rpmb_close(void);

View file

@ -0,0 +1,529 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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 <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include "log.h"
#include "ipc.h"
#include "storage.h"
#define FD_TBL_SIZE 64
#define MAX_READ_SIZE 4096
enum sync_state {
SS_UNUSED = -1,
SS_CLEAN = 0,
SS_DIRTY = 1,
};
static int ssdir_fd = -1;
static const char *ssdir_name;
static enum sync_state fs_state;
static enum sync_state dir_state;
static enum sync_state fd_state[FD_TBL_SIZE];
static struct {
struct storage_file_read_resp hdr;
uint8_t data[MAX_READ_SIZE];
} read_rsp;
static uint32_t insert_fd(int open_flags, int fd)
{
uint32_t handle = fd;
if (open_flags & O_CREAT) {
dir_state = SS_DIRTY;
}
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 */
}
} else {
ALOGW("%s: untracked fd %u\n", __func__, fd);
if (open_flags & (O_TRUNC | O_CREAT)) {
fs_state = SS_DIRTY;
}
}
return handle;
}
static int lookup_fd(uint32_t handle, bool dirty)
{
if (dirty) {
if (handle < FD_TBL_SIZE) {
fd_state[handle] = SS_DIRTY;
} else {
fs_state = SS_DIRTY;
}
}
return handle;
}
static int remove_fd(uint32_t handle)
{
if (handle < FD_TBL_SIZE) {
fd_state[handle] = SS_UNUSED; /* set to uninstalled */
}
return handle;
}
static enum storage_err translate_errno(int error)
{
enum storage_err result;
switch (error) {
case 0:
result = STORAGE_NO_ERROR;
break;
case EBADF:
case EINVAL:
case ENOTDIR:
case EISDIR:
case ENAMETOOLONG:
result = STORAGE_ERR_NOT_VALID;
break;
case ENOENT:
result = STORAGE_ERR_NOT_FOUND;
break;
case EEXIST:
result = STORAGE_ERR_EXIST;
break;
case EPERM:
case EACCES:
result = STORAGE_ERR_ACCESS;
break;
default:
result = STORAGE_ERR_GENERIC;
break;
}
return result;
}
static ssize_t write_with_retry(int fd, const void *buf_, size_t size, off_t offset)
{
ssize_t rc;
const uint8_t *buf = buf_;
while (size > 0) {
rc = TEMP_FAILURE_RETRY(pwrite(fd, buf, size, offset));
if (rc < 0)
return rc;
size -= rc;
buf += rc;
offset += rc;
}
return 0;
}
static ssize_t read_with_retry(int fd, void *buf_, size_t size, off_t offset)
{
ssize_t rc;
size_t rcnt = 0;
uint8_t *buf = buf_;
while (size > 0) {
rc = TEMP_FAILURE_RETRY(pread(fd, buf, size, offset));
if (rc < 0)
return rc;
if (rc == 0)
break;
size -= rc;
buf += rc;
offset += rc;
rcnt += rc;
}
return rcnt;
}
int storage_file_delete(struct storage_msg *msg,
const void *r, size_t req_len)
{
char *path = NULL;
const struct storage_file_delete_req *req = r;
if (req_len < sizeof(*req)) {
ALOGE("%s: invalid request length (%zd < %zd)\n",
__func__, req_len, sizeof(*req));
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
size_t fname_len = strlen(req->name);
if (fname_len != req_len - sizeof(*req)) {
ALOGE("%s: invalid filename length (%zd != %zd)\n",
__func__, fname_len, req_len - sizeof(*req));
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
int rc = asprintf(&path, "%s/%s", ssdir_name, req->name);
if (rc < 0) {
ALOGE("%s: asprintf failed\n", __func__);
msg->result = STORAGE_ERR_GENERIC;
goto err_response;
}
dir_state = SS_DIRTY;
rc = unlink(path);
if (rc < 0) {
rc = errno;
if (errno == ENOENT) {
ALOGV("%s: error (%d) unlinking file '%s'\n",
__func__, rc, path);
} else {
ALOGE("%s: error (%d) unlinking file '%s'\n",
__func__, rc, path);
}
msg->result = translate_errno(rc);
goto err_response;
}
ALOGV("%s: \"%s\"\n", __func__, path);
msg->result = STORAGE_NO_ERROR;
err_response:
if (path)
free(path);
return ipc_respond(msg, NULL, 0);
}
int storage_file_open(struct storage_msg *msg,
const void *r, size_t req_len)
{
char *path = NULL;
const struct storage_file_open_req *req = r;
struct storage_file_open_resp resp = {0};
if (req_len < sizeof(*req)) {
ALOGE("%s: invalid request length (%zd < %zd)\n",
__func__, req_len, sizeof(*req));
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
size_t fname_len = strlen(req->name);
if (fname_len != req_len - sizeof(*req)) {
ALOGE("%s: invalid filename length (%zd != %zd)\n",
__func__, fname_len, req_len - sizeof(*req));
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
int rc = asprintf(&path, "%s/%s", ssdir_name, req->name);
if (rc < 0) {
ALOGE("%s: asprintf failed\n", __func__);
msg->result = STORAGE_ERR_GENERIC;
goto err_response;
}
int open_flags = O_RDWR;
if (req->flags & STORAGE_FILE_OPEN_TRUNCATE)
open_flags |= O_TRUNC;
if (req->flags & STORAGE_FILE_OPEN_CREATE) {
/* open or create */
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));
} 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));
}
}
} else {
/* open an existing file */
rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
}
if (rc < 0) {
rc = errno;
if (errno == EEXIST || errno == ENOENT) {
ALOGV("%s: failed to open file \"%s\": %s\n",
__func__, path, strerror(errno));
} else {
ALOGE("%s: failed to open file \"%s\": %s\n",
__func__, path, strerror(errno));
}
msg->result = translate_errno(rc);
goto err_response;
}
free(path);
/* at this point rc contains storage file fd */
msg->result = STORAGE_NO_ERROR;
resp.handle = insert_fd(open_flags, rc);
ALOGV("%s: \"%s\": fd = %u: handle = %d\n",
__func__, path, rc, resp.handle);
return ipc_respond(msg, &resp, sizeof(resp));
err_response:
if (path)
free(path);
return ipc_respond(msg, NULL, 0);
}
int storage_file_close(struct storage_msg *msg,
const void *r, size_t req_len)
{
const struct storage_file_close_req *req = r;
if (req_len != sizeof(*req)) {
ALOGE("%s: invalid request length (%zd != %zd)\n",
__func__, req_len, sizeof(*req));
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
int fd = remove_fd(req->handle);
ALOGV("%s: handle = %u: fd = %u\n", __func__, req->handle, fd);
int rc = fsync(fd);
if (rc < 0) {
rc = errno;
ALOGE("%s: fsync failed for fd=%u: %s\n",
__func__, fd, strerror(errno));
msg->result = translate_errno(rc);
goto err_response;
}
rc = close(fd);
if (rc < 0) {
rc = errno;
ALOGE("%s: close failed for fd=%u: %s\n",
__func__, fd, strerror(errno));
msg->result = translate_errno(rc);
goto err_response;
}
msg->result = STORAGE_NO_ERROR;
err_response:
return ipc_respond(msg, NULL, 0);
}
int storage_file_write(struct storage_msg *msg,
const void *r, size_t req_len)
{
int rc;
const struct storage_file_write_req *req = r;
if (req_len < sizeof(*req)) {
ALOGE("%s: invalid request length (%zd < %zd)\n",
__func__, req_len, sizeof(*req));
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
int fd = lookup_fd(req->handle, true);
if (write_with_retry(fd, &req->data[0], req_len - sizeof(*req),
req->offset) < 0) {
rc = errno;
ALOGW("%s: error writing file (fd=%d): %s\n",
__func__, fd, strerror(errno));
msg->result = translate_errno(rc);
goto err_response;
}
msg->result = STORAGE_NO_ERROR;
err_response:
return ipc_respond(msg, NULL, 0);
}
int storage_file_read(struct storage_msg *msg,
const void *r, size_t req_len)
{
int rc;
const struct storage_file_read_req *req = r;
if (req_len != sizeof(*req)) {
ALOGE("%s: invalid request length (%zd != %zd)\n",
__func__, req_len, sizeof(*req));
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
if (req->size > MAX_READ_SIZE) {
ALOGW("%s: request is too large (%zd > %zd) - refusing\n",
__func__, req->size, MAX_READ_SIZE);
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
int fd = lookup_fd(req->handle, false);
ssize_t read_res = read_with_retry(fd, read_rsp.hdr.data, req->size,
(off_t)req->offset);
if (read_res < 0) {
rc = errno;
ALOGW("%s: error reading file (fd=%d): %s\n",
__func__, fd, strerror(errno));
msg->result = translate_errno(rc);
goto err_response;
}
msg->result = STORAGE_NO_ERROR;
return ipc_respond(msg, &read_rsp, read_res + sizeof(read_rsp.hdr));
err_response:
return ipc_respond(msg, NULL, 0);
}
int storage_file_get_size(struct storage_msg *msg,
const void *r, size_t req_len)
{
const struct storage_file_get_size_req *req = r;
struct storage_file_get_size_resp resp = {0};
if (req_len != sizeof(*req)) {
ALOGE("%s: invalid request length (%zd != %zd)\n",
__func__, req_len, sizeof(*req));
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
struct stat stat;
int fd = lookup_fd(req->handle, false);
int rc = fstat(fd, &stat);
if (rc < 0) {
rc = errno;
ALOGE("%s: error stat'ing file (fd=%d): %s\n",
__func__, fd, strerror(errno));
msg->result = translate_errno(rc);
goto err_response;
}
resp.size = stat.st_size;
msg->result = STORAGE_NO_ERROR;
return ipc_respond(msg, &resp, sizeof(resp));
err_response:
return ipc_respond(msg, NULL, 0);
}
int storage_file_set_size(struct storage_msg *msg,
const void *r, size_t req_len)
{
const struct storage_file_set_size_req *req = r;
if (req_len != sizeof(*req)) {
ALOGE("%s: invalid request length (%zd != %zd)\n",
__func__, req_len, sizeof(*req));
msg->result = STORAGE_ERR_NOT_VALID;
goto err_response;
}
int fd = lookup_fd(req->handle, true);
int rc = TEMP_FAILURE_RETRY(ftruncate(fd, req->size));
if (rc < 0) {
rc = errno;
ALOGE("%s: error truncating file (fd=%d): %s\n",
__func__, fd, strerror(errno));
msg->result = translate_errno(rc);
goto err_response;
}
msg->result = STORAGE_NO_ERROR;
err_response:
return ipc_respond(msg, NULL, 0);
}
int storage_init(const char *dirname)
{
fs_state = SS_CLEAN;
dir_state = SS_CLEAN;
for (uint i = 0; i < FD_TBL_SIZE; i++) {
fd_state[i] = SS_UNUSED; /* uninstalled */
}
ssdir_fd = open(dirname, O_RDONLY);
if (ssdir_fd < 0) {
ALOGE("failed to open ss root dir \"%s\": %s\n",
dirname, strerror(errno));
return -1;
}
ssdir_name = dirname;
return 0;
}
int storage_sync_checkpoint(void)
{
int rc;
/* 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 */
}
}
/* check if we need to sync the directory */
if (dir_state == SS_DIRTY) {
if (fs_state == SS_CLEAN) {
rc = fsync(ssdir_fd);
if (rc < 0) {
ALOGE("fsync for ssdir failed: %s\n", strerror(errno));
return rc;
}
}
dir_state = SS_CLEAN; /* set to clean */
}
/* check if we need to sync the whole fs */
if (fs_state == SS_DIRTY) {
rc = syscall(SYS_syncfs, ssdir_fd);
if (rc < 0) {
ALOGE("syncfs failed: %s\n", strerror(errno));
return rc;
}
fs_state = SS_CLEAN;
}
return 0;
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.
*/
#pragma once
#include <stdint.h>
#include <trusty/interface/storage.h>
int storage_file_delete(struct storage_msg *msg,
const void *req, size_t req_len);
int storage_file_open(struct storage_msg *msg,
const void *req, size_t req_len);
int storage_file_close(struct storage_msg *msg,
const void *req, size_t req_len);
int storage_file_write(struct storage_msg *msg,
const void *req, size_t req_len);
int storage_file_read(struct storage_msg *msg,
const void *req, size_t req_len);
int storage_file_get_size(struct storage_msg *msg,
const void *req, size_t req_len);
int storage_file_set_size(struct storage_msg *msg,
const void *req, size_t req_len);
int storage_init(const char *dirname);
int storage_sync_checkpoint(void);

18
trusty/trusty-storage.mk Normal file
View file

@ -0,0 +1,18 @@
#
# Copyright (C) 2015 The Android Open-Source Project
#
# 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.
#
PRODUCT_PACKAGES += \
storageproxyd \