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