trusty: storage: implement storage proxy daemon

Change-Id: I80cdf61e5ced00dd32a3e35eb81969d25b624df9
diff --git a/trusty/storage/proxy/Android.mk b/trusty/storage/proxy/Android.mk
new file mode 100644
index 0000000..9fc73d3
--- /dev/null
+++ b/trusty/storage/proxy/Android.mk
@@ -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)
+
diff --git a/trusty/storage/proxy/ipc.c b/trusty/storage/proxy/ipc.c
new file mode 100644
index 0000000..b4748e2
--- /dev/null
+++ b/trusty/storage/proxy/ipc.c
@@ -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;
+}
+
+
diff --git a/trusty/storage/proxy/ipc.h b/trusty/storage/proxy/ipc.h
new file mode 100644
index 0000000..2e366bb
--- /dev/null
+++ b/trusty/storage/proxy/ipc.h
@@ -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);
diff --git a/trusty/storage/proxy/log.h b/trusty/storage/proxy/log.h
new file mode 100644
index 0000000..471cb50
--- /dev/null
+++ b/trusty/storage/proxy/log.h
@@ -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>
+
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
new file mode 100644
index 0000000..d645ac0
--- /dev/null
+++ b/trusty/storage/proxy/proxy.c
@@ -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;
+}
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
new file mode 100644
index 0000000..9130458
--- /dev/null
+++ b/trusty/storage/proxy/rpmb.c
@@ -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;
+}
+
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
new file mode 100644
index 0000000..85cff44
--- /dev/null
+++ b/trusty/storage/proxy/rpmb.h
@@ -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);
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
new file mode 100644
index 0000000..c61e89d
--- /dev/null
+++ b/trusty/storage/proxy/storage.c
@@ -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;
+}
+
diff --git a/trusty/storage/proxy/storage.h b/trusty/storage/proxy/storage.h
new file mode 100644
index 0000000..5a670d4
--- /dev/null
+++ b/trusty/storage/proxy/storage.h
@@ -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);
+
diff --git a/trusty/trusty-storage.mk b/trusty/trusty-storage.mk
new file mode 100644
index 0000000..3f26316
--- /dev/null
+++ b/trusty/trusty-storage.mk
@@ -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 \