1 /*
2 * Copyright (C) 2013-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 #include "client_tipc.h"
18
19 #include <assert.h>
20 #include <errno.h>
21 #include <inttypes.h>
22 #include <stddef.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <interface/storage/storage.h>
29 #include <lib/tipc/tipc.h>
30 #include <openssl/mem.h>
31 #include <uapi/err.h>
32
33 #include "client.h"
34 #include "client_session.h"
35 #include "client_session_tipc.h"
36 #include "ipc.h"
37 #include "storage_limits.h"
38 #include "tipc_service.h"
39
40 /* macros to help manage debug output */
41 #define SS_ERR(args...) fprintf(stderr, "ss: " args)
42 #define SS_DBG_IO(args...) \
43 do { \
44 } while (0)
45
46 #if 0
47 /* this can generate alot of spew on debug builds */
48 #define SS_INFO(args...) fprintf(stderr, "ss: " args)
49 #else
50 #define SS_INFO(args...) \
51 do { \
52 } while (0)
53 #endif
54
55 static int client_handle_msg(struct ipc_channel_context* ctx,
56 void* msg,
57 size_t msg_size);
58 static void client_disconnect(struct ipc_channel_context* context);
59 static int send_response(struct storage_tipc_client_session* tipc_session,
60 enum storage_err result,
61 struct storage_msg* msg,
62 void* out,
63 size_t out_size);
64 static int send_result(struct storage_tipc_client_session* tipc_session,
65 struct storage_msg* msg,
66 enum storage_err result);
67
extract_storage_op_flags(uint32_t msg_flags)68 static struct storage_op_flags extract_storage_op_flags(uint32_t msg_flags) {
69 return (struct storage_op_flags){
70 .allow_repaired = msg_flags & STORAGE_MSG_FLAG_FS_REPAIRED_ACK,
71 .complete_transaction =
72 msg_flags & STORAGE_MSG_FLAG_TRANSACT_COMPLETE,
73 .update_checkpoint =
74 msg_flags & STORAGE_MSG_FLAG_TRANSACT_CHECKPOINT,
75 };
76 }
77
storage_tipc_file_delete(struct storage_client_session * session,struct storage_msg * msg,struct storage_file_delete_req * req,size_t req_size)78 static enum storage_err storage_tipc_file_delete(
79 struct storage_client_session* session,
80 struct storage_msg* msg,
81 struct storage_file_delete_req* req,
82 size_t req_size) {
83 if (req_size < sizeof(*req)) {
84 SS_ERR("%s: invalid request size (%zu)\n", __func__, req_size);
85 return STORAGE_ERR_NOT_VALID;
86 }
87
88 if ((req->flags & ~STORAGE_FILE_DELETE_MASK) != 0) {
89 SS_ERR("%s: unexpected flags (0x%" PRIx32 ")\n", __func__, req->flags);
90 return STORAGE_ERR_NOT_VALID;
91 }
92
93 return storage_file_delete(session, req->name, req_size - sizeof(*req),
94 extract_storage_op_flags(msg->flags));
95 }
96
storage_tipc_file_move(struct storage_client_session * session,struct storage_msg * msg,struct storage_file_move_req * req,size_t req_size)97 static enum storage_err storage_tipc_file_move(
98 struct storage_client_session* session,
99 struct storage_msg* msg,
100 struct storage_file_move_req* req,
101 size_t req_size) {
102 if (req_size < sizeof(*req)) {
103 SS_ERR("%s: invalid request size (%zu)\n", __func__, req_size);
104 return STORAGE_ERR_NOT_VALID;
105 }
106
107 if ((req->flags & ~STORAGE_FILE_MOVE_MASK) != 0) {
108 SS_ERR("invalid move flags 0x%" PRIx32 "\n", req->flags);
109 return STORAGE_ERR_NOT_VALID;
110 }
111
112 size_t names_combined_len = req_size - sizeof(*req);
113 size_t src_len = req->old_name_len;
114 if (src_len >= names_combined_len) {
115 SS_ERR("%s: invalid src filename length %zu >= %zu\n", __func__,
116 src_len, names_combined_len);
117 return STORAGE_ERR_NOT_VALID;
118 }
119
120 enum file_create_mode file_create_mode;
121 if (req->flags & STORAGE_FILE_MOVE_CREATE) {
122 file_create_mode = req->flags & STORAGE_FILE_MOVE_CREATE_EXCLUSIVE
123 ? FILE_OPEN_CREATE_EXCLUSIVE
124 : FILE_OPEN_CREATE;
125 } else {
126 file_create_mode = FILE_OPEN_NO_CREATE;
127 }
128
129 return storage_file_move(
130 session, req->handle, req->flags & STORAGE_FILE_MOVE_OPEN_FILE,
131 req->old_new_name, src_len, req->old_new_name + src_len,
132 names_combined_len - src_len, file_create_mode,
133 extract_storage_op_flags(msg->flags));
134 }
135
storage_tipc_file_open(struct storage_tipc_client_session * tipc_session,struct storage_msg * msg,struct storage_file_open_req * req,size_t req_size)136 static enum storage_err storage_tipc_file_open(
137 struct storage_tipc_client_session* tipc_session,
138 struct storage_msg* msg,
139 struct storage_file_open_req* req,
140 size_t req_size) {
141 if (req_size < sizeof(*req)) {
142 SS_ERR("%s: invalid request size (%zu)\n", __func__, req_size);
143 return send_result(tipc_session, msg, STORAGE_ERR_NOT_VALID);
144 }
145
146 if ((req->flags & ~STORAGE_FILE_OPEN_MASK) != 0) {
147 SS_ERR("%s: invalid flags 0x%" PRIx32 "\n", __func__, req->flags);
148 return send_result(tipc_session, msg, STORAGE_ERR_NOT_VALID);
149 }
150
151 enum file_create_mode file_create_mode;
152 if (req->flags & STORAGE_FILE_OPEN_CREATE) {
153 file_create_mode = req->flags & STORAGE_FILE_OPEN_CREATE_EXCLUSIVE
154 ? FILE_OPEN_CREATE_EXCLUSIVE
155 : FILE_OPEN_CREATE;
156 } else {
157 file_create_mode = FILE_OPEN_NO_CREATE;
158 }
159
160 struct storage_file_open_resp resp;
161 enum storage_err result = storage_file_open(
162 &tipc_session->session, req->name, req_size - sizeof(*req),
163 file_create_mode, req->flags & STORAGE_FILE_OPEN_TRUNCATE,
164 extract_storage_op_flags(msg->flags), &resp.handle);
165 if (result != STORAGE_NO_ERROR) {
166 return send_result(tipc_session, msg, result);
167 }
168 return send_response(tipc_session, result, msg, &resp, sizeof(resp));
169 }
170
storage_tipc_file_close(struct storage_client_session * session,struct storage_msg * msg,struct storage_file_close_req * req,size_t req_size)171 static enum storage_err storage_tipc_file_close(
172 struct storage_client_session* session,
173 struct storage_msg* msg,
174 struct storage_file_close_req* req,
175 size_t req_size) {
176 if (req_size != sizeof(*req)) {
177 SS_ERR("%s: invalid request size (%zu)\n", __func__, req_size);
178 return STORAGE_ERR_NOT_VALID;
179 }
180 return storage_file_close(session, req->handle,
181 extract_storage_op_flags(msg->flags));
182 }
183
storage_tipc_file_write(struct storage_client_session * session,struct storage_msg * msg,struct storage_file_write_req * req,size_t req_size)184 static enum storage_err storage_tipc_file_write(
185 struct storage_client_session* session,
186 struct storage_msg* msg,
187 struct storage_file_write_req* req,
188 size_t req_size) {
189 if (req_size <= sizeof(*req)) {
190 SS_ERR("%s: invalid request size (%zu)\n", __func__, req_size);
191 return STORAGE_ERR_NOT_VALID;
192 }
193 return storage_file_write(session, req->handle, req->offset, req->data,
194 req_size - sizeof(*req),
195 extract_storage_op_flags(msg->flags));
196 }
197
storage_tipc_file_read(struct storage_tipc_client_session * tipc_session,struct storage_msg * msg,struct storage_file_read_req * req,size_t req_size)198 static enum storage_err storage_tipc_file_read(
199 struct storage_tipc_client_session* tipc_session,
200 struct storage_msg* msg,
201 struct storage_file_read_req* req,
202 size_t req_size) {
203 if (req_size != sizeof(*req)) {
204 SS_ERR("%s: invalid request size (%zu)\n", __func__, req_size);
205 return send_result(tipc_session, msg, STORAGE_ERR_NOT_VALID);
206 }
207
208 /*
209 * After getting the args out of req, we still need `*msg`, but won't need
210 * the rest of the buffer anymore. We can reuse that space to hold the
211 * response.
212 */
213 uint8_t* resp = (uint8_t*)(msg + 1);
214 size_t resp_len = STORAGE_MAX_BUFFER_SIZE - sizeof(*msg);
215
216 enum storage_err result = storage_file_read(
217 &tipc_session->session, req->handle, req->size, req->offset,
218 extract_storage_op_flags(msg->flags), resp, &resp_len);
219 if (result != STORAGE_NO_ERROR) {
220 return send_result(tipc_session, msg, result);
221 }
222 return send_response(tipc_session, result, msg, resp, resp_len);
223 }
224
225 struct storage_tipc_file_iter_data {
226 char resp_buf[1024];
227 size_t buf_used;
228 };
229
buf_has_space(void * self,size_t max_path_len)230 static bool buf_has_space(void* self, size_t max_path_len) {
231 struct storage_tipc_file_iter_data* this = self;
232 /* One extra byte for flags plus one for the path's nul terminator */
233 size_t max_resp_size = max_path_len + 2;
234 return this->buf_used + max_resp_size <= sizeof(this->resp_buf);
235 }
236
write_to_buf(void * self,enum storage_file_list_flag flags,const char * path,size_t path_len)237 static void write_to_buf(void* self,
238 enum storage_file_list_flag flags,
239 const char* path,
240 size_t path_len) {
241 struct storage_tipc_file_iter_data* this = self;
242 struct storage_file_list_resp* resp =
243 (void*)(this->resp_buf + this->buf_used);
244
245 resp->flags = flags;
246 this->buf_used++;
247
248 if (path) {
249 strncpy(resp->name, path, path_len);
250 resp->name[path_len] = '\0';
251 this->buf_used += path_len + 1;
252 }
253 }
254
storage_tipc_file_list(struct storage_tipc_client_session * tipc_session,struct storage_msg * msg,struct storage_file_list_req * req,size_t req_size)255 static enum storage_err storage_tipc_file_list(
256 struct storage_tipc_client_session* tipc_session,
257 struct storage_msg* msg,
258 struct storage_file_list_req* req,
259 size_t req_size) {
260 if (req_size < sizeof(*req)) {
261 SS_ERR("%s: invalid request size (%zu)\n", __func__, req_size);
262 return send_result(tipc_session, msg, STORAGE_ERR_NOT_VALID);
263 }
264
265 struct storage_tipc_file_iter_data callback_data = {
266 .buf_used = 0,
267 };
268
269 enum storage_err result = storage_file_list(
270 &tipc_session->session, req->max_count,
271 req->flags & STORAGE_FILE_LIST_STATE_MASK, req->name,
272 req_size - sizeof(*req), extract_storage_op_flags(msg->flags),
273 buf_has_space, write_to_buf, &callback_data);
274 if (result != STORAGE_NO_ERROR) {
275 return send_result(tipc_session, msg, result);
276 }
277 return send_response(tipc_session, result, msg, callback_data.resp_buf,
278 callback_data.buf_used);
279 }
280
storage_tipc_file_get_size(struct storage_tipc_client_session * tipc_session,struct storage_msg * msg,struct storage_file_get_size_req * req,size_t req_size)281 static enum storage_err storage_tipc_file_get_size(
282 struct storage_tipc_client_session* tipc_session,
283 struct storage_msg* msg,
284 struct storage_file_get_size_req* req,
285 size_t req_size) {
286 if (req_size != sizeof(*req)) {
287 SS_ERR("%s: invalid request size (%zu)\n", __func__, req_size);
288 return send_result(tipc_session, msg, STORAGE_ERR_NOT_VALID);
289 }
290
291 struct storage_file_get_size_resp resp;
292 enum storage_err result = storage_file_get_size(
293 &tipc_session->session, req->handle,
294 extract_storage_op_flags(msg->flags), &resp.size);
295 if (result != STORAGE_NO_ERROR) {
296 return send_result(tipc_session, msg, result);
297 }
298 return send_response(tipc_session, result, msg, &resp, sizeof(resp));
299 }
300
storage_tipc_file_set_size(struct storage_client_session * session,struct storage_msg * msg,struct storage_file_set_size_req * req,size_t req_size)301 static enum storage_err storage_tipc_file_set_size(
302 struct storage_client_session* session,
303 struct storage_msg* msg,
304 struct storage_file_set_size_req* req,
305 size_t req_size) {
306 if (req_size != sizeof(*req)) {
307 SS_ERR("%s: invalid request size (%zu)\n", __func__, req_size);
308 return STORAGE_ERR_NOT_VALID;
309 }
310 return storage_file_set_size(session, req->handle, req->size,
311 extract_storage_op_flags(msg->flags));
312 }
313
chan_context_to_client_session(struct ipc_channel_context * ctx)314 static struct storage_tipc_client_session* chan_context_to_client_session(
315 struct ipc_channel_context* ctx) {
316 assert(ctx != NULL);
317 struct storage_tipc_client_session* tipc_session;
318
319 tipc_session =
320 containerof(ctx, struct storage_tipc_client_session, chan_ctx);
321 assert(tipc_session->session.magic == STORAGE_CLIENT_SESSION_MAGIC);
322 return tipc_session;
323 }
324
port_context_to_client_port_context(struct ipc_port_context * context)325 static struct client_port_context* port_context_to_client_port_context(
326 struct ipc_port_context* context) {
327 assert(context != NULL);
328
329 return containerof(context, struct client_port_context, client_ctx);
330 }
331
client_channel_ops_init(struct ipc_channel_ops * ops)332 static void client_channel_ops_init(struct ipc_channel_ops* ops) {
333 ops->on_handle_msg = client_handle_msg;
334 ops->on_disconnect = client_disconnect;
335 }
336
client_connect(struct ipc_port_context * parent_ctx,const uuid_t * peer_uuid,handle_t chan_handle)337 static struct ipc_channel_context* client_connect(
338 struct ipc_port_context* parent_ctx,
339 const uuid_t* peer_uuid,
340 handle_t chan_handle) {
341 struct client_port_context* client_port_context;
342 struct storage_tipc_client_session* client_tipc_session;
343
344 client_port_context = port_context_to_client_port_context(parent_ctx);
345
346 client_tipc_session = calloc(1, sizeof(*client_tipc_session));
347 if (client_tipc_session == NULL) {
348 SS_ERR("out of memory allocating client session\n");
349 return NULL;
350 }
351
352 struct storage_client_session* client_session =
353 &client_tipc_session->session;
354 storage_client_session_init(client_session, client_port_context->tr_state,
355 peer_uuid);
356
357 client_channel_ops_init(&client_tipc_session->chan_ctx.ops);
358 return &client_tipc_session->chan_ctx;
359 }
360
client_disconnect(struct ipc_channel_context * context)361 static void client_disconnect(struct ipc_channel_context* context) {
362 struct storage_tipc_client_session* tipc_session =
363 chan_context_to_client_session(context);
364
365 storage_client_session_destroy(&tipc_session->session);
366 OPENSSL_cleanse(tipc_session, sizeof(struct storage_tipc_client_session));
367 free(tipc_session);
368 }
369
send_response(struct storage_tipc_client_session * tipc_session,enum storage_err result,struct storage_msg * msg,void * out,size_t out_size)370 static int send_response(struct storage_tipc_client_session* tipc_session,
371 enum storage_err result,
372 struct storage_msg* msg,
373 void* out,
374 size_t out_size) {
375 size_t resp_buf_count = 1;
376 if (result == STORAGE_NO_ERROR && out != NULL && out_size != 0) {
377 ++resp_buf_count;
378 }
379
380 struct iovec resp_bufs[2];
381
382 msg->cmd |= STORAGE_RESP_BIT;
383 msg->flags = 0;
384 msg->size = sizeof(struct storage_msg) + out_size;
385 msg->result = result;
386
387 resp_bufs[0].iov_base = msg;
388 resp_bufs[0].iov_len = sizeof(struct storage_msg);
389
390 if (resp_buf_count == 2) {
391 resp_bufs[1].iov_base = out;
392 resp_bufs[1].iov_len = out_size;
393 }
394
395 struct ipc_msg resp_ipc_msg = {
396 .iov = resp_bufs,
397 .num_iov = resp_buf_count,
398 };
399
400 return send_msg(tipc_session->chan_ctx.common.handle, &resp_ipc_msg);
401 }
402
send_result(struct storage_tipc_client_session * tipc_session,struct storage_msg * msg,enum storage_err result)403 static int send_result(struct storage_tipc_client_session* tipc_session,
404 struct storage_msg* msg,
405 enum storage_err result) {
406 return send_response(tipc_session, result, msg, NULL, 0);
407 }
408
client_handle_msg(struct ipc_channel_context * ctx,void * msg_buf,size_t msg_size)409 static int client_handle_msg(struct ipc_channel_context* ctx,
410 void* msg_buf,
411 size_t msg_size) {
412 struct storage_tipc_client_session* tipc_session;
413 struct storage_client_session* session;
414 struct storage_msg* msg = msg_buf;
415 size_t payload_len;
416 enum storage_err result;
417 void* payload;
418
419 tipc_session = chan_context_to_client_session(ctx);
420 session = &tipc_session->session;
421
422 if (msg_size < sizeof(struct storage_msg)) {
423 SS_ERR("%s: invalid message of size (%zu)\n", __func__, msg_size);
424 struct storage_msg err_msg = {.cmd = STORAGE_RESP_MSG_ERR};
425 send_result(tipc_session, &err_msg, STORAGE_ERR_NOT_VALID);
426 return ERR_NOT_VALID; /* would force to close connection */
427 }
428
429 payload_len = msg_size - sizeof(struct storage_msg);
430 payload = msg->payload;
431
432 switch (msg->cmd) {
433 case STORAGE_FILE_DELETE:
434 result = storage_tipc_file_delete(session, msg, payload, payload_len);
435 break;
436 case STORAGE_FILE_MOVE:
437 result = storage_tipc_file_move(session, msg, payload, payload_len);
438 break;
439 case STORAGE_FILE_OPEN:
440 return storage_tipc_file_open(tipc_session, msg, payload, payload_len);
441 case STORAGE_FILE_CLOSE:
442 result = storage_tipc_file_close(session, msg, payload, payload_len);
443 break;
444 case STORAGE_FILE_WRITE:
445 result = storage_tipc_file_write(session, msg, payload, payload_len);
446 break;
447 case STORAGE_FILE_READ:
448 return storage_tipc_file_read(tipc_session, msg, payload, payload_len);
449 case STORAGE_FILE_LIST:
450 return storage_tipc_file_list(tipc_session, msg, payload, payload_len);
451 case STORAGE_FILE_GET_SIZE:
452 return storage_tipc_file_get_size(tipc_session, msg, payload,
453 payload_len);
454 case STORAGE_FILE_SET_SIZE:
455 result = storage_tipc_file_set_size(session, msg, payload, payload_len);
456 break;
457 case STORAGE_END_TRANSACTION:
458 result = storage_transaction_end(session,
459 extract_storage_op_flags(msg->flags));
460 break;
461 default:
462 SS_ERR("%s: unsupported command 0x%" PRIx32 "\n", __func__, msg->cmd);
463 result = STORAGE_ERR_UNIMPLEMENTED;
464 break;
465 }
466
467 return send_result(tipc_session, msg, result);
468 }
469
client_create_port(struct tipc_hset * hset,struct ipc_port_context * client_ctx,const char * port_name)470 int client_create_port(struct tipc_hset* hset,
471 struct ipc_port_context* client_ctx,
472 const char* port_name) {
473 int ret;
474 uint32_t flags = IPC_PORT_ALLOW_TA_CONNECT;
475 #if TEST_BUILD
476 flags |= IPC_PORT_ALLOW_NS_CONNECT;
477 #endif
478
479 /* start accepting client connections */
480 client_ctx->ops.on_connect = client_connect;
481 ret = ipc_port_create(hset, client_ctx, port_name, 1,
482 STORAGE_MAX_BUFFER_SIZE, flags);
483 if (ret < 0) {
484 SS_ERR("%s: failure initializing client port (%d)\n", __func__, ret);
485 return ret;
486 }
487 return 0;
488 }
489