1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "trusty_storage_client"
18 
19 #include <errno.h>
20 #include <stdarg.h>
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <string.h>
24 #include <sys/uio.h>
25 
26 #include <log/log.h>
27 #include <trusty/tipc.h>
28 #include <trusty/lib/storage.h>
29 
30 #define MAX_CHUNK_SIZE 4040
31 
make_file_handle(storage_session_t s,uint32_t fid)32 static inline file_handle_t make_file_handle(storage_session_t s, uint32_t fid)
33 {
34     return ((uint64_t)s << 32) | fid;
35 }
36 
_to_session(file_handle_t fh)37 static inline storage_session_t _to_session(file_handle_t fh)
38 {
39     return (storage_session_t)(fh >> 32);
40 }
41 
_to_handle(file_handle_t fh)42 static inline uint32_t _to_handle(file_handle_t fh)
43 {
44     return (uint32_t) fh;
45 }
46 
_to_msg_flags(uint32_t opflags)47 static inline uint32_t _to_msg_flags(uint32_t opflags)
48 {
49     uint32_t msg_flags = 0;
50 
51     if (opflags & STORAGE_OP_COMPLETE)
52         msg_flags |= STORAGE_MSG_FLAG_TRANSACT_COMPLETE;
53 
54     return msg_flags;
55 }
56 
check_response(struct storage_msg * msg,ssize_t res)57 static ssize_t check_response(struct storage_msg *msg, ssize_t res)
58 {
59     if (res < 0)
60         return res;
61 
62     if ((size_t)res < sizeof(*msg)) {
63         ALOGE("invalid msg length (%zd < %zd)\n", res, sizeof(*msg));
64         return -EIO;
65     }
66 
67     ALOGV("cmd 0x%x: server returned %u\n", msg->cmd, msg->result);
68 
69     switch(msg->result) {
70         case STORAGE_NO_ERROR:
71             return res - sizeof(*msg);
72 
73         case STORAGE_ERR_NOT_FOUND:
74             return -ENOENT;
75 
76         case STORAGE_ERR_EXIST:
77             return -EEXIST;
78 
79         case STORAGE_ERR_NOT_VALID:
80             return -EINVAL;
81 
82         case STORAGE_ERR_UNIMPLEMENTED:
83             ALOGE("cmd 0x%x: is unhandles command\n", msg->cmd);
84             return -EINVAL;
85 
86         case STORAGE_ERR_ACCESS:
87              return -EACCES;
88 
89         case STORAGE_ERR_TRANSACT:
90              return -EBUSY;
91 
92         case STORAGE_ERR_GENERIC:
93             ALOGE("cmd 0x%x: internal server error\n", msg->cmd);
94             return -EIO;
95 
96         default:
97             ALOGE("cmd 0x%x: unhandled server response %u\n",
98                    msg->cmd, msg->result);
99     }
100 
101     return -EIO;
102 }
103 
send_reqv(storage_session_t session,const struct iovec * tx_iovs,uint tx_iovcnt,const struct iovec * rx_iovs,uint rx_iovcnt)104 static ssize_t send_reqv(storage_session_t session,
105                          const struct iovec *tx_iovs, uint tx_iovcnt,
106                          const struct iovec *rx_iovs, uint rx_iovcnt)
107 {
108     ssize_t rc;
109 
110     rc = writev(session, tx_iovs, tx_iovcnt);
111     if (rc < 0) {
112         rc = -errno;
113         ALOGE("failed to send request: %s\n", strerror(errno));
114         return rc;
115     }
116 
117     rc = readv(session, rx_iovs, rx_iovcnt);
118     if (rc < 0) {
119         rc = -errno;
120         ALOGE("failed to recv response: %s\n", strerror(errno));
121         return rc;
122     }
123 
124     return rc;
125 }
126 
storage_open_session(const char * device,storage_session_t * session_p,const char * port)127 int storage_open_session(const char *device, storage_session_t *session_p,
128                          const char *port)
129 {
130     int rc = tipc_connect(device, port);
131     if (rc < 0)
132         return rc;
133     *session_p = (storage_session_t) rc;
134     return 0;
135 }
136 
storage_close_session(storage_session_t session)137 void storage_close_session(storage_session_t session)
138 {
139     tipc_close(session);
140 }
141 
142 
storage_open_file(storage_session_t session,file_handle_t * handle_p,const char * name,uint32_t flags,uint32_t opflags)143 int storage_open_file(storage_session_t session, file_handle_t *handle_p, const char *name,
144                       uint32_t flags, uint32_t opflags)
145 {
146     struct storage_msg msg = { .cmd = STORAGE_FILE_OPEN, .flags = _to_msg_flags(opflags)};
147     struct storage_file_open_req req = { .flags = flags };
148     struct iovec tx[3] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}, {(void *)name, strlen(name)}};
149     struct storage_file_open_resp rsp = { 0 };
150     struct iovec rx[2] = {{&msg, sizeof(msg)}, {&rsp, sizeof(rsp)}};
151 
152     ssize_t rc = send_reqv(session, tx, 3, rx, 2);
153     rc = check_response(&msg, rc);
154     if (rc < 0)
155         return rc;
156 
157     if ((size_t)rc != sizeof(rsp)) {
158         ALOGE("%s: invalid response length (%zd != %zd)\n", __func__, rc, sizeof(rsp));
159         return -EIO;
160     }
161 
162     *handle_p = make_file_handle(session, rsp.handle);
163     return 0;
164 }
165 
storage_close_file(file_handle_t fh)166 void storage_close_file(file_handle_t fh)
167 {
168     struct storage_msg msg = { .cmd = STORAGE_FILE_CLOSE };
169     struct storage_file_close_req req = { .handle = _to_handle(fh)};
170     struct iovec tx[2] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}};
171     struct iovec rx[1] = {{&msg, sizeof(msg)}};
172 
173     ssize_t rc = send_reqv(_to_session(fh), tx, 2, rx, 1);
174     rc = check_response(&msg, rc);
175     if (rc < 0) {
176         ALOGE("close file failed (%d)\n", (int)rc);
177     }
178 }
179 
storage_delete_file(storage_session_t session,const char * name,uint32_t opflags)180 int storage_delete_file(storage_session_t session, const char *name, uint32_t opflags)
181 {
182     struct storage_msg msg = { .cmd = STORAGE_FILE_DELETE, .flags = _to_msg_flags(opflags)};
183     struct storage_file_delete_req req = { .flags = 0, };
184     struct iovec tx[3] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}, {(void *)name, strlen(name)}};
185     struct iovec rx[1] = {{&msg, sizeof(msg)}};
186 
187     ssize_t rc = send_reqv(session, tx, 3, rx, 1);
188     return check_response(&msg, rc);
189 }
190 
_read_chunk(file_handle_t fh,storage_off_t off,void * buf,size_t size)191 static int _read_chunk(file_handle_t fh, storage_off_t off, void *buf, size_t size)
192 {
193     struct storage_msg msg = { .cmd = STORAGE_FILE_READ };
194     struct storage_file_read_req req = { .handle = _to_handle(fh), .size = size, .offset = off };
195     struct iovec tx[2] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}};
196     struct iovec rx[2] = {{&msg, sizeof(msg)}, {buf, size}};
197 
198     ssize_t rc = send_reqv(_to_session(fh), tx, 2, rx, 2);
199     return check_response(&msg, rc);
200 }
201 
storage_read(file_handle_t fh,storage_off_t off,void * buf,size_t size)202 ssize_t storage_read(file_handle_t fh, storage_off_t off, void *buf, size_t size)
203 {
204     int rc;
205     size_t bytes_read = 0;
206     size_t chunk = MAX_CHUNK_SIZE;
207     uint8_t *ptr = buf;
208 
209     while (size) {
210         if (chunk > size)
211             chunk = size;
212         rc = _read_chunk(fh, off, ptr, chunk);
213         if (rc < 0)
214             return rc;
215         if (rc == 0)
216             break;
217         off += rc;
218         ptr += rc;
219         bytes_read += rc;
220         size -= rc;
221     }
222     return bytes_read;
223 }
224 
_write_req(file_handle_t fh,storage_off_t off,const void * buf,size_t size,uint32_t msg_flags)225 static int _write_req(file_handle_t fh, storage_off_t off,
226                       const void *buf, size_t size, uint32_t msg_flags)
227 {
228     struct storage_msg msg = { .cmd = STORAGE_FILE_WRITE, .flags = msg_flags, };
229     struct storage_file_write_req req = { .handle = _to_handle(fh), .offset = off, };
230     struct iovec tx[3] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}, {(void *)buf, size}};
231     struct iovec rx[1] = {{&msg, sizeof(msg)}};
232 
233     ssize_t rc = send_reqv(_to_session(fh), tx, 3, rx, 1);
234     rc = check_response(&msg, rc);
235     return rc < 0 ? rc : size;
236 }
237 
storage_write(file_handle_t fh,storage_off_t off,const void * buf,size_t size,uint32_t opflags)238 ssize_t storage_write(file_handle_t fh, storage_off_t off,
239                       const void *buf, size_t size, uint32_t opflags)
240 {
241     int rc;
242     size_t bytes_written = 0;
243     size_t chunk = MAX_CHUNK_SIZE;
244     const uint8_t *ptr = buf;
245     uint32_t msg_flags = _to_msg_flags(opflags & ~STORAGE_OP_COMPLETE);
246 
247     while (size) {
248         if (chunk >= size) {
249             /* last chunk in sequence */
250             chunk = size;
251             msg_flags = _to_msg_flags(opflags);
252         }
253         rc = _write_req(fh, off, ptr, chunk, msg_flags);
254         if (rc < 0)
255             return rc;
256         if ((size_t)rc != chunk) {
257             ALOGE("got partial write (%d)\n", (int)rc);
258             return -EIO;
259         }
260         off += chunk;
261         ptr += chunk;
262         bytes_written += chunk;
263         size -= chunk;
264     }
265     return bytes_written;
266 }
267 
storage_set_file_size(file_handle_t fh,storage_off_t file_size,uint32_t opflags)268 int storage_set_file_size(file_handle_t fh, storage_off_t file_size, uint32_t opflags)
269 {
270     struct storage_msg msg = { .cmd = STORAGE_FILE_SET_SIZE, .flags = _to_msg_flags(opflags)};
271     struct storage_file_set_size_req req = { .handle = _to_handle(fh), .size = file_size, };
272     struct iovec tx[2] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}};
273     struct iovec rx[1] = {{&msg, sizeof(msg)}};
274 
275     ssize_t rc = send_reqv(_to_session(fh), tx, 2, rx, 1);
276     return check_response(&msg, rc);
277 }
278 
storage_get_file_size(file_handle_t fh,storage_off_t * size_p)279 int storage_get_file_size(file_handle_t fh, storage_off_t *size_p)
280 {
281     struct storage_msg msg = { .cmd = STORAGE_FILE_GET_SIZE };
282     struct storage_file_get_size_req  req = { .handle = _to_handle(fh), };
283     struct iovec tx[2] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}};
284     struct storage_file_get_size_resp rsp;
285     struct iovec rx[2] = {{&msg, sizeof(msg)}, {&rsp, sizeof(rsp)}};
286 
287     ssize_t rc = send_reqv(_to_session(fh), tx, 2, rx, 2);
288     rc = check_response(&msg, rc);
289     if (rc < 0)
290         return rc;
291 
292     if ((size_t)rc != sizeof(rsp)) {
293         ALOGE("%s: invalid response length (%zd != %zd)\n", __func__, rc, sizeof(rsp));
294         return -EIO;
295     }
296 
297     *size_p = rsp.size;
298     return 0;
299 }
300 
storage_end_transaction(storage_session_t session,bool complete)301 int storage_end_transaction(storage_session_t session, bool complete)
302 {
303     struct storage_msg msg = {
304         .cmd = STORAGE_END_TRANSACTION,
305         .flags = complete ? STORAGE_MSG_FLAG_TRANSACT_COMPLETE : 0,
306     };
307     struct iovec iov = {&msg, sizeof(msg)};
308 
309     ssize_t rc = send_reqv(session, &iov, 1, &iov, 1);
310     return check_response(&msg, rc);
311 }
312