1 /*
2 * Copyright (C) 2023 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 #include "client.h"
17
18 #include <inttypes.h>
19 #include <stddef.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22
23 #include <interface/storage/storage.h>
24 #include <lib/system_state/system_state.h>
25 #include <lk/list.h>
26
27 #include "client_session.h"
28 #include "file.h"
29 #include "session.h"
30 #include "storage_limits.h"
31
32 // macros to help manage debug output
33 #define SS_ERR(args...) fprintf(stderr, "ss: " args)
34
35 #if 0
36 // this can generate a lot of spew on debug builds
37 #define SS_INFO(args...) fprintf(stderr, "ss: " args)
38 #else
39 #define SS_INFO(args...) \
40 do { \
41 } while (0)
42 #endif
43
44 /**
45 * checkpoint_update_allowed - Is checkpoint modification currently allowed?
46 * @transaction: Transaction object.
47 *
48 * Check if the given transaction is allowed to create a new or update the
49 * existing checkpoint for its file system. Checkpoint updates may only be
50 * requested by client while the device is in provisioning mode.
51 *
52 * Returns %true if a transaction may update the current checkpoint state,
53 * %false otherwise.
54 */
checkpoint_update_allowed(struct transaction * tr)55 static bool checkpoint_update_allowed(struct transaction* tr) {
56 return system_state_provisioning_allowed();
57 }
58
59 /*
60 * Legal secure storage directory and file names contain only
61 * characters from the following set: [a-z][A-Z][0-9][.-_]
62 *
63 * It is not null terminated.
64 */
is_valid_name(const char * name,size_t name_len)65 static int is_valid_name(const char* name, size_t name_len) {
66 size_t i;
67
68 if (!name_len)
69 return 0;
70
71 for (i = 0; i < name_len; i++) {
72 if ((name[i] >= 'a') && (name[i] <= 'z'))
73 continue;
74 if ((name[i] >= 'A') && (name[i] <= 'Z'))
75 continue;
76 if ((name[i] >= '0') && (name[i] <= '9'))
77 continue;
78 if ((name[i] == '.') || (name[i] == '-') || (name[i] == '_'))
79 continue;
80
81 // not a legal character so reject this name
82 return 0;
83 }
84
85 return 1;
86 }
87
get_path(char * path_out,size_t path_out_size,const uuid_t * uuid,const char * file_name,size_t file_name_len)88 static int get_path(char* path_out,
89 size_t path_out_size,
90 const uuid_t* uuid,
91 const char* file_name,
92 size_t file_name_len) {
93 unsigned int rc;
94
95 rc = snprintf(path_out, path_out_size,
96 "%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x/",
97 uuid->time_low, uuid->time_mid, uuid->time_hi_and_version,
98 uuid->clock_seq_and_node[0], uuid->clock_seq_and_node[1],
99 uuid->clock_seq_and_node[2], uuid->clock_seq_and_node[3],
100 uuid->clock_seq_and_node[4], uuid->clock_seq_and_node[5],
101 uuid->clock_seq_and_node[6], uuid->clock_seq_and_node[7]);
102
103 if (rc + file_name_len >= path_out_size) {
104 return STORAGE_ERR_NOT_VALID;
105 }
106
107 memcpy(path_out + rc, file_name, file_name_len);
108 path_out[rc + file_name_len] = '\0';
109
110 return STORAGE_NO_ERROR;
111 }
112
file_op_result_to_storage_err(enum file_op_result result)113 static enum storage_err file_op_result_to_storage_err(
114 enum file_op_result result) {
115 switch (result) {
116 case FILE_OP_SUCCESS:
117 return STORAGE_NO_ERROR;
118 case FILE_OP_ERR_FAILED:
119 // TODO: Consider returning STORAGE_ERR_TRANSACT consistently
120 return STORAGE_ERR_GENERIC;
121 case FILE_OP_ERR_EXIST:
122 return STORAGE_ERR_EXIST;
123 case FILE_OP_ERR_ALREADY_OPEN:
124 return STORAGE_ERR_NOT_ALLOWED;
125 case FILE_OP_ERR_NOT_FOUND:
126 return STORAGE_ERR_NOT_FOUND;
127 case FILE_OP_ERR_FS_REPAIRED:
128 return STORAGE_ERR_FS_REPAIRED;
129 }
130
131 SS_ERR("%s: Unknown file_op_result: %d\n", __func__, result);
132 return STORAGE_ERR_GENERIC;
133 }
134
session_set_files_count(struct storage_client_session * session,size_t files_count)135 static enum storage_err session_set_files_count(
136 struct storage_client_session* session,
137 size_t files_count) {
138 struct storage_file_handle** files;
139
140 if (files_count > STORAGE_MAX_OPEN_FILES) {
141 SS_ERR("%s: too many open files\n", __func__);
142 return STORAGE_ERR_NOT_VALID;
143 }
144
145 if (!files_count) {
146 free(session->files);
147 session->files = NULL;
148 } else {
149 files = realloc(session->files, sizeof(files[0]) * files_count);
150 if (!files) {
151 SS_ERR("%s: out of memory\n", __func__);
152 return STORAGE_ERR_GENERIC;
153 }
154 if (files_count > session->files_count)
155 memset(files + session->files_count, 0,
156 sizeof(files[0]) * (files_count - session->files_count));
157
158 session->files = files;
159 }
160 session->files_count = files_count;
161
162 SS_INFO("%s: new file table size, 0x%zx\n", __func__, files_count);
163
164 return STORAGE_NO_ERROR;
165 }
166
session_shrink_files(struct storage_client_session * session)167 static void session_shrink_files(struct storage_client_session* session) {
168 uint32_t handle;
169
170 handle = session->files_count;
171 while (handle > 0 && !session->files[handle - 1])
172 handle--;
173
174 if (handle < session->files_count)
175 session_set_files_count(session, handle);
176 }
177
session_close_all_files(struct storage_client_session * session)178 static void session_close_all_files(struct storage_client_session* session) {
179 uint32_t f_handle;
180 struct storage_file_handle* file;
181
182 for (f_handle = 0; f_handle < session->files_count; f_handle++) {
183 file = session->files[f_handle];
184 if (file) {
185 file_close(file);
186 free(file);
187 }
188 }
189 if (session->files) {
190 free(session->files);
191 }
192 session->files_count = 0;
193 }
194
create_file_handle(struct storage_client_session * session,uint32_t * handlep,struct storage_file_handle ** file_p)195 static enum storage_err create_file_handle(
196 struct storage_client_session* session,
197 uint32_t* handlep,
198 struct storage_file_handle** file_p) {
199 enum storage_err result;
200 uint32_t handle;
201 struct storage_file_handle* file;
202
203 for (handle = 0; handle < session->files_count; handle++)
204 if (!session->files[handle])
205 break;
206
207 if (handle >= session->files_count) {
208 result = session_set_files_count(session, handle + 1);
209 if (result != STORAGE_NO_ERROR)
210 return result;
211 }
212
213 file = calloc(1, sizeof(*file));
214 if (!file) {
215 SS_ERR("%s: out of memory\n", __func__);
216 return STORAGE_ERR_GENERIC;
217 }
218
219 session->files[handle] = file;
220
221 SS_INFO("%s: created file handle 0x%" PRIx32 "\n", __func__, handle);
222 *handlep = handle;
223 *file_p = file;
224 return STORAGE_NO_ERROR;
225 }
226
free_file_handle(struct storage_client_session * session,uint32_t handle)227 static void free_file_handle(struct storage_client_session* session,
228 uint32_t handle) {
229 if (handle >= session->files_count) {
230 SS_ERR("%s: invalid handle, 0x%" PRIx32 "\n", __func__, handle);
231 return;
232 }
233 if (session->files[handle] == NULL) {
234 SS_ERR("%s: closed handle, 0x%" PRIx32 "\n", __func__, handle);
235 return;
236 }
237 free(session->files[handle]);
238 session->files[handle] = NULL;
239
240 SS_INFO("%s: deleted file handle 0x%" PRIx32 "\n", __func__, handle);
241
242 session_shrink_files(session);
243 }
244
get_file_handle(struct storage_client_session * session,uint32_t handle)245 static struct storage_file_handle* get_file_handle(
246 struct storage_client_session* session,
247 uint32_t handle) {
248 struct storage_file_handle* file;
249 if (handle >= session->files_count) {
250 SS_ERR("%s: invalid handle, 0x%" PRIx32 "\n", __func__, handle);
251 return NULL;
252 }
253 file = session->files[handle];
254 if (!file) {
255 SS_ERR("%s: closed handle, 0x%" PRIx32 "\n", __func__, handle);
256 return NULL;
257 }
258 return file;
259 }
260
assert_checkpoint_flag_valid(struct storage_client_session * session,struct storage_op_flags flags,const char * func_name)261 static enum storage_err assert_checkpoint_flag_valid(
262 struct storage_client_session* session,
263 struct storage_op_flags flags,
264 const char* func_name) {
265 if (flags.update_checkpoint) {
266 if (!(flags.complete_transaction)) {
267 SS_ERR("%s: STORAGE_MSG_FLAG_TRANSACT_CHECKPOINT cannot "
268 "be used without STORAGE_MSG_FLAG_TRANSACT_COMPLETE\n",
269 func_name);
270 return STORAGE_ERR_NOT_VALID;
271 }
272
273 if (!checkpoint_update_allowed(&session->tr)) {
274 SS_ERR("%s: Checkpoint requested but not currently allowed.\n",
275 func_name);
276 return STORAGE_ERR_NOT_ALLOWED;
277 }
278 }
279 return STORAGE_NO_ERROR;
280 }
281
ensure_active_transaction(struct storage_client_session * session,struct storage_op_flags flags)282 static enum storage_err ensure_active_transaction(
283 struct storage_client_session* session,
284 struct storage_op_flags flags) {
285 if (session->tr.failed) {
286 if (flags.complete_transaction) {
287 /* last command in current transaction:
288 * reset failed state and return error */
289 session->tr.failed = false;
290 }
291 return STORAGE_ERR_TRANSACT;
292 }
293
294 if (!transaction_is_active(&session->tr)) {
295 /* previous transaction complete */
296 transaction_activate(&session->tr);
297 }
298 return STORAGE_NO_ERROR;
299 }
300
storage_client_session_init(struct storage_client_session * session,struct fs * fs,const uuid_t * peer)301 void storage_client_session_init(struct storage_client_session* session,
302 struct fs* fs,
303 const uuid_t* peer) {
304 session->magic = STORAGE_CLIENT_SESSION_MAGIC;
305 session->files = NULL;
306 session->files_count = 0;
307
308 transaction_init(&session->tr, fs, false);
309
310 /* cache identity information */
311 memcpy(&session->uuid, peer, sizeof(*peer));
312 }
313
storage_client_session_destroy(struct storage_client_session * session)314 void storage_client_session_destroy(struct storage_client_session* session) {
315 if (list_in_list(&session->tr.allocated.node) && !session->tr.failed) {
316 /* discard partial transaction */
317 transaction_fail(&session->tr);
318 }
319 session_close_all_files(session);
320 transaction_free(&session->tr);
321 }
322
323 /* abort transaction and clear sticky transaction error */
storage_transaction_end(struct storage_client_session * session,struct storage_op_flags flags)324 enum storage_err storage_transaction_end(struct storage_client_session* session,
325 struct storage_op_flags flags) {
326 enum storage_err result =
327 assert_checkpoint_flag_valid(session, flags, __func__);
328 if (result != STORAGE_NO_ERROR) {
329 return result;
330 }
331
332 if (flags.complete_transaction) {
333 /* Allow checkpoint creation without an active transaction */
334 if (flags.update_checkpoint && !transaction_is_active(&session->tr)) {
335 transaction_activate(&session->tr);
336 }
337 /* try to complete current transaction */
338 if (transaction_is_active(&session->tr)) {
339 transaction_complete_etc(&session->tr, flags.update_checkpoint);
340 }
341 if (session->tr.failed) {
342 SS_ERR("%s: failed to complete transaction\n", __func__);
343 /* clear transaction failed state */
344 session->tr.failed = false;
345 return STORAGE_ERR_TRANSACT;
346 }
347 return STORAGE_NO_ERROR;
348 }
349
350 /* discard current transaction */
351 if (transaction_is_active(&session->tr)) {
352 transaction_fail(&session->tr);
353 }
354 /* clear transaction failed state */
355 session->tr.failed = false;
356 return STORAGE_NO_ERROR;
357 }
358
storage_file_delete(struct storage_client_session * session,const char * fname,size_t fname_len,struct storage_op_flags flags)359 enum storage_err storage_file_delete(struct storage_client_session* session,
360 const char* fname,
361 size_t fname_len,
362 struct storage_op_flags flags) {
363 enum file_op_result delete_res;
364 char path_buf[FS_PATH_MAX];
365
366 enum storage_err result =
367 assert_checkpoint_flag_valid(session, flags, __func__);
368 if (result != STORAGE_NO_ERROR) {
369 return result;
370 }
371 result = ensure_active_transaction(session, flags);
372 if (result != STORAGE_NO_ERROR) {
373 return result;
374 }
375
376 /* make sure filename is legal */
377 if (!is_valid_name(fname, fname_len)) {
378 SS_ERR("%s: invalid filename\n", __func__);
379 return STORAGE_ERR_NOT_VALID;
380 }
381
382 result = get_path(path_buf, sizeof(path_buf), &session->uuid, fname,
383 fname_len);
384 if (result != STORAGE_NO_ERROR) {
385 return result;
386 }
387
388 SS_INFO("%s: path %s\n", __func__, path_buf);
389
390 delete_res = file_delete(&session->tr, path_buf, flags.allow_repaired);
391
392 if (delete_res != FILE_OP_SUCCESS) {
393 return file_op_result_to_storage_err(delete_res);
394 }
395
396 if (flags.complete_transaction) {
397 transaction_complete_etc(&session->tr, flags.update_checkpoint);
398 if (session->tr.failed) {
399 SS_ERR("%s: transaction commit failed\n", __func__);
400 return STORAGE_ERR_GENERIC;
401 }
402 }
403
404 return STORAGE_NO_ERROR;
405 }
406
407 /**
408 * storage_file_check_name - Check if file handle matches path
409 * @tr: Transaction object.
410 * @file: File handle object.
411 * @path: Path to check.
412 *
413 * Return: %true if @file matches @path, %false otherwise.
414 */
storage_file_check_name(struct transaction * tr,const struct storage_file_handle * file,const char * path)415 static bool storage_file_check_name(struct transaction* tr,
416 const struct storage_file_handle* file,
417 const char* path) {
418 bool ret;
419 const struct file_info* file_info;
420 struct obj_ref ref = OBJ_REF_INITIAL_VALUE(ref);
421
422 file_info = file_get_info(tr, &file->block_mac, &ref);
423 if (!file_info) {
424 printf("can't read file entry at %" PRIu64 "\n",
425 block_mac_to_block(tr, &file->block_mac));
426 return false;
427 }
428 assert(file_info);
429 ret = strcmp(file_info->path, path) == 0;
430 file_info_put(file_info, &ref);
431
432 return ret;
433 }
434
storage_file_move(struct storage_client_session * session,uint32_t handle,bool src_already_opened,const char * src_name,size_t src_name_len,const char * dst_name,size_t dst_name_len,enum file_create_mode dst_file_create_mode,struct storage_op_flags flags)435 enum storage_err storage_file_move(struct storage_client_session* session,
436 uint32_t handle,
437 bool src_already_opened,
438 const char* src_name,
439 size_t src_name_len,
440 const char* dst_name,
441 size_t dst_name_len,
442 enum file_create_mode dst_file_create_mode,
443 struct storage_op_flags flags) {
444 enum file_op_result open_result;
445 enum file_op_result move_result;
446 struct storage_file_handle* file = NULL;
447 char path_buf[FS_PATH_MAX];
448 struct storage_file_handle tmp_file;
449
450 enum storage_err result =
451 assert_checkpoint_flag_valid(session, flags, __func__);
452 if (result != STORAGE_NO_ERROR) {
453 return result;
454 }
455 result = ensure_active_transaction(session, flags);
456 if (result != STORAGE_NO_ERROR) {
457 return result;
458 }
459
460 /* make sure filenames are legal */
461 if (src_name && !is_valid_name(src_name, src_name_len)) {
462 SS_ERR("%s: invalid src filename\n", __func__);
463 return STORAGE_ERR_NOT_VALID;
464 }
465 if (!is_valid_name(dst_name, dst_name_len)) {
466 SS_ERR("%s: invalid dst filename\n", __func__);
467 return STORAGE_ERR_NOT_VALID;
468 }
469
470 if (!src_name && !src_already_opened) {
471 SS_ERR("%s: src needs to be opened, but no src name provided\n",
472 __func__);
473 }
474
475 if (src_already_opened) {
476 file = get_file_handle(session, handle);
477 if (!file)
478 return STORAGE_ERR_NOT_VALID;
479 }
480
481 if (src_name) {
482 result = get_path(path_buf, sizeof(path_buf), &session->uuid, src_name,
483 src_name_len);
484 if (result != STORAGE_NO_ERROR) {
485 return result;
486 }
487 }
488
489 SS_INFO("%s: src path %s\n", __func__, path_buf);
490
491 if (file) {
492 if (src_name &&
493 !storage_file_check_name(&session->tr, file, path_buf)) {
494 return STORAGE_ERR_NOT_VALID;
495 }
496 } else {
497 open_result = file_open(&session->tr, path_buf, &tmp_file,
498 FILE_OPEN_NO_CREATE, flags.allow_repaired);
499 if (open_result != FILE_OP_SUCCESS) {
500 return file_op_result_to_storage_err(open_result);
501 }
502 file = &tmp_file;
503 }
504
505 result = get_path(path_buf, sizeof(path_buf), &session->uuid, dst_name,
506 dst_name_len);
507 if (result != STORAGE_NO_ERROR) {
508 if (file == &tmp_file) {
509 file_close(&tmp_file);
510 }
511 return result;
512 }
513 SS_INFO("%s: dst path %s\n", __func__, path_buf);
514
515 move_result = file_move(&session->tr, file, path_buf, dst_file_create_mode,
516 flags.allow_repaired);
517 if (file == &tmp_file) {
518 file_close(&tmp_file);
519 }
520
521 if (move_result != FILE_OP_SUCCESS) {
522 return file_op_result_to_storage_err(move_result);
523 }
524
525 if (flags.complete_transaction) {
526 transaction_complete_etc(&session->tr, flags.update_checkpoint);
527 if (session->tr.failed) {
528 SS_ERR("%s: transaction commit failed\n", __func__);
529 return STORAGE_ERR_GENERIC;
530 }
531 }
532
533 return STORAGE_NO_ERROR;
534 }
535
storage_file_open(struct storage_client_session * session,const char * fname,size_t fname_len,enum file_create_mode file_create_mode,bool truncate,struct storage_op_flags flags,uint32_t * handle)536 enum storage_err storage_file_open(struct storage_client_session* session,
537 const char* fname,
538 size_t fname_len,
539 enum file_create_mode file_create_mode,
540 bool truncate,
541 struct storage_op_flags flags,
542 uint32_t* handle) {
543 enum file_op_result open_result;
544 struct storage_file_handle* file = NULL;
545 uint32_t f_handle;
546 char path_buf[FS_PATH_MAX];
547
548 enum storage_err result =
549 assert_checkpoint_flag_valid(session, flags, __func__);
550 if (result != STORAGE_NO_ERROR) {
551 return result;
552 }
553 result = ensure_active_transaction(session, flags);
554 if (result != STORAGE_NO_ERROR) {
555 return result;
556 }
557
558 /* make sure filename is legal */
559 if (!is_valid_name(fname, fname_len)) {
560 SS_ERR("%s: invalid filename\n", __func__);
561 return STORAGE_ERR_NOT_VALID;
562 }
563
564 result = get_path(path_buf, sizeof(path_buf), &session->uuid, fname,
565 fname_len);
566 if (result != STORAGE_NO_ERROR) {
567 return result;
568 }
569
570 SS_INFO("%s: path %s (create_mode: %d, truncate: %d)\n", __func__, path_buf,
571 file_create_mode, truncate);
572
573 SS_INFO("%s: call create_file_handle\n", __func__);
574 /* alloc file info struct */
575 result = create_file_handle(session, &f_handle, &file);
576 if (result != STORAGE_NO_ERROR) {
577 return result;
578 }
579
580 open_result = file_open(&session->tr, path_buf, file, file_create_mode,
581 flags.allow_repaired);
582 if (open_result != FILE_OP_SUCCESS) {
583 result = file_op_result_to_storage_err(open_result);
584 goto err_open_file;
585 }
586
587 if (truncate && file->size) {
588 file_set_size(&session->tr, file, 0);
589 }
590
591 if (session->tr.failed) {
592 SS_ERR("%s: transaction failed\n", __func__);
593 result = STORAGE_ERR_GENERIC;
594 goto err_transaction_failed;
595 }
596
597 if (flags.complete_transaction) {
598 transaction_complete_etc(&session->tr, flags.update_checkpoint);
599 if (session->tr.failed) {
600 SS_ERR("%s: transaction commit failed\n", __func__);
601 result = STORAGE_ERR_GENERIC;
602 goto err_transaction_failed;
603 }
604 }
605
606 *handle = f_handle;
607 return STORAGE_NO_ERROR;
608
609 err_transaction_failed:
610 file_close(file);
611 err_open_file:
612 free_file_handle(session, f_handle);
613 return result;
614 }
615
storage_file_close(struct storage_client_session * session,uint32_t handle,struct storage_op_flags flags)616 enum storage_err storage_file_close(struct storage_client_session* session,
617 uint32_t handle,
618 struct storage_op_flags flags) {
619 struct storage_file_handle* file;
620
621 enum storage_err result =
622 assert_checkpoint_flag_valid(session, flags, __func__);
623 if (result != STORAGE_NO_ERROR) {
624 return result;
625 }
626 result = ensure_active_transaction(session, flags);
627 if (result != STORAGE_NO_ERROR) {
628 return result;
629 }
630
631 file = get_file_handle(session, handle);
632 if (!file) {
633 return STORAGE_ERR_NOT_VALID;
634 }
635
636 file_close(file);
637 free_file_handle(session, handle);
638
639 if (flags.complete_transaction) {
640 transaction_complete_etc(&session->tr, flags.update_checkpoint);
641 if (session->tr.failed) {
642 SS_ERR("%s: transaction commit failed\n", __func__);
643 return STORAGE_ERR_GENERIC;
644 }
645 }
646
647 return STORAGE_NO_ERROR;
648 }
649
storage_file_read(struct storage_client_session * session,uint32_t handle,uint32_t size,uint64_t offset,struct storage_op_flags flags,uint8_t * resp,size_t * resp_len)650 enum storage_err storage_file_read(struct storage_client_session* session,
651 uint32_t handle,
652 uint32_t size,
653 uint64_t offset,
654 struct storage_op_flags flags,
655 uint8_t* resp,
656 size_t* resp_len) {
657 void* bufp = resp;
658 size_t buflen;
659 size_t bytes_left, len;
660 struct storage_file_handle* file;
661 size_t block_size = get_file_block_size(session->tr.fs);
662 data_block_t block_num;
663 const uint8_t* block_data;
664 struct obj_ref block_data_ref = OBJ_REF_INITIAL_VALUE(block_data_ref);
665 size_t block_offset;
666
667 enum storage_err result =
668 assert_checkpoint_flag_valid(session, flags, __func__);
669 if (result != STORAGE_NO_ERROR) {
670 return result;
671 }
672 result = ensure_active_transaction(session, flags);
673 if (result != STORAGE_NO_ERROR) {
674 return result;
675 }
676
677 file = get_file_handle(session, handle);
678 if (!file) {
679 SS_ERR("%s: invalid file handle (%" PRIx32 ")\n", __func__, handle);
680 return STORAGE_ERR_NOT_VALID;
681 }
682
683 buflen = size;
684 if (buflen > *resp_len) {
685 SS_ERR("can't read more than %zu bytes, requested %zu\n", *resp_len,
686 buflen);
687 return STORAGE_ERR_NOT_VALID;
688 }
689
690 if (offset > file->size) {
691 SS_ERR("can't read past end of file (%" PRIu64 " > %" PRIu64 ")\n",
692 offset, file->size);
693 return STORAGE_ERR_NOT_VALID;
694 }
695
696 /* calc number of bytes to read */
697 if ((offset + buflen) > file->size) {
698 bytes_left = (size_t)(file->size - offset);
699 } else {
700 bytes_left = buflen;
701 }
702 buflen = bytes_left; /* save to return it to caller */
703
704 SS_INFO("%s: start 0x%" PRIx64 " cnt %zu\n", __func__, offset, bytes_left);
705
706 while (bytes_left) {
707 block_num = offset / block_size;
708 block_data =
709 file_get_block(&session->tr, file, block_num, &block_data_ref);
710 block_offset = offset % block_size;
711 len = (block_offset + bytes_left > block_size)
712 ? block_size - block_offset
713 : bytes_left;
714 if (!block_data) {
715 if (session->tr.failed) {
716 SS_ERR("error reading block %" PRIu64 "\n", block_num);
717 return STORAGE_ERR_GENERIC;
718 }
719 memset(bufp, 0, len);
720 } else {
721 memcpy(bufp, block_data + block_offset, len);
722 file_block_put(block_data, &block_data_ref);
723 }
724
725 bytes_left -= len;
726 offset += len;
727 bufp += len;
728 }
729
730 *resp_len = buflen;
731 return STORAGE_NO_ERROR;
732 }
733
storage_create_gap(struct storage_client_session * session,struct storage_file_handle * file)734 static enum storage_err storage_create_gap(
735 struct storage_client_session* session,
736 struct storage_file_handle* file) {
737 size_t block_size = get_file_block_size(session->tr.fs);
738 data_block_t block_num;
739 size_t block_offset;
740 uint8_t* block_data;
741 struct obj_ref block_data_ref = OBJ_REF_INITIAL_VALUE(block_data_ref);
742
743 block_num = file->size / block_size;
744 block_offset = file->size % block_size;
745
746 if (block_offset) {
747 /*
748 * The file does not currently end on a block boundary.
749 * We don't clear data in partial blocks when truncating
750 * a file, so the last block could contain data that
751 * should not be readable. We unconditionally clear the
752 * exposed data when creating gaps in the file, as we
753 * don't know if that data is already clear.
754 */
755 block_data = file_get_block_write(&session->tr, file, block_num, true,
756 &block_data_ref);
757 if (!block_data) {
758 SS_ERR("error getting block %" PRIu64 "\n", block_num);
759 return STORAGE_ERR_GENERIC;
760 }
761
762 memset(block_data + block_offset, 0, block_size - block_offset);
763 file_block_put_dirty(&session->tr, file, block_num, block_data,
764 &block_data_ref);
765
766 SS_INFO("%s: clear block at old size 0x%" PRIx64
767 ", block_offset 0x%zx\n",
768 __func__, file->size, block_offset);
769 }
770 return STORAGE_NO_ERROR;
771 }
772
storage_file_write(struct storage_client_session * session,uint32_t handle,uint64_t offset,const uint8_t * data,size_t data_len,struct storage_op_flags flags)773 enum storage_err storage_file_write(struct storage_client_session* session,
774 uint32_t handle,
775 uint64_t offset,
776 const uint8_t* data,
777 size_t data_len,
778 struct storage_op_flags flags) {
779 size_t len;
780 struct storage_file_handle* file;
781 size_t block_size = get_file_block_size(session->tr.fs);
782 data_block_t block_num;
783 uint8_t* block_data;
784 struct obj_ref block_data_ref = OBJ_REF_INITIAL_VALUE(block_data_ref);
785 size_t block_offset;
786
787 enum storage_err result =
788 assert_checkpoint_flag_valid(session, flags, __func__);
789 if (result != STORAGE_NO_ERROR) {
790 return result;
791 }
792 result = ensure_active_transaction(session, flags);
793 if (result != STORAGE_NO_ERROR) {
794 return result;
795 }
796
797 file = get_file_handle(session, handle);
798 if (!file) {
799 SS_ERR("%s: invalid file handle (%" PRIx32 ")\n", __func__, handle);
800 return STORAGE_ERR_NOT_VALID;
801 }
802
803 if (offset > file->size) {
804 result = storage_create_gap(session, file);
805 if (result != STORAGE_NO_ERROR) {
806 goto err_write;
807 }
808 }
809
810 /* transfer data one ss block at a time */
811 while (data_len) {
812 block_num = offset / block_size;
813 block_offset = offset % block_size;
814 len = (block_offset + data_len > block_size) ? block_size - block_offset
815 : data_len;
816
817 block_data = file_get_block_write(&session->tr, file, block_num,
818 len != block_size, &block_data_ref);
819 if (!block_data) {
820 SS_ERR("error getting block %" PRIu64 "\n", block_num);
821 result = STORAGE_ERR_GENERIC;
822 goto err_write;
823 }
824
825 memcpy(block_data + block_offset, data, len);
826 file_block_put_dirty(&session->tr, file, block_num, block_data,
827 &block_data_ref);
828
829 #if TLOG_LVL >= TLOG_LVL_DEBUG
830 SS_INFO("%s: data %p offset 0x%" PRIx64 " len 0x%zx\n", __func__, data,
831 offset, len);
832 #endif
833 offset += len;
834 data += len;
835 data_len -= len;
836 }
837
838 if (offset > file->size) {
839 file_set_size(&session->tr, file, offset);
840 }
841
842 if (session->tr.failed) {
843 SS_ERR("%s: transaction failed\n", __func__);
844 return STORAGE_ERR_GENERIC;
845 }
846
847 if (flags.complete_transaction) {
848 transaction_complete_etc(&session->tr, flags.update_checkpoint);
849 if (session->tr.failed) {
850 SS_ERR("%s: transaction commit failed\n", __func__);
851 return STORAGE_ERR_GENERIC;
852 }
853 }
854
855 return STORAGE_NO_ERROR;
856
857 err_write:
858 if (!session->tr.failed) {
859 transaction_fail(&session->tr);
860 }
861 err_transaction_complete:
862 return result;
863 }
864
865 struct storage_file_list_state {
866 struct file_iterate_state iter;
867 char prefix[34];
868 size_t prefix_len;
869 uint8_t max_count;
870 uint8_t count;
871 bool (*can_record_path)(void* callback_data, size_t max_path_len);
872 void (*record_path)(void* callback_data,
873 enum storage_file_list_flag flags,
874 const char* path,
875 size_t path_len);
876 void* callback_data;
877 };
878
storage_file_list_buf_full(struct storage_file_list_state * miter)879 static bool storage_file_list_buf_full(struct storage_file_list_state* miter) {
880 if (miter->max_count && miter->count >= miter->max_count) {
881 return true;
882 }
883
884 return !miter->can_record_path(miter->callback_data,
885 FS_PATH_MAX - miter->prefix_len);
886 }
887
storage_file_list_add(struct storage_file_list_state * miter,enum storage_file_list_flag flags,const char * path)888 static void storage_file_list_add(struct storage_file_list_state* miter,
889 enum storage_file_list_flag flags,
890 const char* path) {
891 assert(!storage_file_list_buf_full(miter));
892 size_t path_len = path ? strlen(path) : 0;
893 assert(path_len <= FS_PATH_MAX - miter->prefix_len);
894 miter->record_path(miter->callback_data, flags, path, path_len);
895 miter->count++;
896 }
897
storage_file_list_iter(struct file_iterate_state * iter,struct transaction * tr,const struct block_mac * block_mac,bool added,bool removed)898 static bool storage_file_list_iter(struct file_iterate_state* iter,
899 struct transaction* tr,
900 const struct block_mac* block_mac,
901 bool added,
902 bool removed) {
903 struct storage_file_list_state* miter =
904 containerof(iter, struct storage_file_list_state, iter);
905 const struct file_info* file_info;
906 struct obj_ref ref = OBJ_REF_INITIAL_VALUE(ref);
907
908 file_info = file_get_info(tr, block_mac, &ref);
909 if (!file_info) {
910 printf("can't read file entry at %" PRIu64 "\n",
911 block_mac_to_block(tr, block_mac));
912 return true;
913 }
914
915 if (strncmp(file_info->path, miter->prefix, miter->prefix_len) == 0) {
916 storage_file_list_add(miter,
917 added ? STORAGE_FILE_LIST_ADDED
918 : removed ? STORAGE_FILE_LIST_REMOVED
919 : STORAGE_FILE_LIST_COMMITTED,
920 file_info->path + miter->prefix_len);
921 }
922
923 file_info_put(file_info, &ref);
924
925 return storage_file_list_buf_full(miter);
926 }
927
storage_file_list(struct storage_client_session * session,uint8_t max_count,enum storage_file_list_flag last_state,const char * last_fname,size_t last_fname_len,struct storage_op_flags flags,bool (* can_record_path)(void * callback_data,size_t max_path_len),void (* record_path)(void * callback_data,enum storage_file_list_flag flags,const char * path,size_t path_len),void * callback_data)928 enum storage_err storage_file_list(
929 struct storage_client_session* session,
930 uint8_t max_count,
931 enum storage_file_list_flag last_state,
932 const char* last_fname,
933 size_t last_fname_len,
934 struct storage_op_flags flags,
935 bool (*can_record_path)(void* callback_data, size_t max_path_len),
936 void (*record_path)(void* callback_data,
937 enum storage_file_list_flag flags,
938 const char* path,
939 size_t path_len),
940 void* callback_data) {
941 enum file_op_result iterate_res;
942 const char* last_name;
943 char path_buf[FS_PATH_MAX];
944
945 struct storage_file_list_state state = {
946 .iter.file = storage_file_list_iter,
947 .max_count = max_count,
948 .record_path = record_path,
949 .can_record_path = can_record_path,
950 .callback_data = callback_data,
951 };
952
953 enum storage_err result =
954 assert_checkpoint_flag_valid(session, flags, __func__);
955 if (result != STORAGE_NO_ERROR) {
956 return result;
957 }
958 result = ensure_active_transaction(session, flags);
959 if (result != STORAGE_NO_ERROR) {
960 return result;
961 }
962
963 result =
964 get_path(state.prefix, sizeof(state.prefix), &session->uuid, "", 0);
965 if (result != STORAGE_NO_ERROR) {
966 SS_ERR("%s: internal error, get_path failed\n", __func__);
967 return STORAGE_ERR_GENERIC;
968 }
969 state.prefix_len = strlen(state.prefix);
970
971 if (last_state == STORAGE_FILE_LIST_END) {
972 SS_ERR("%s: invalid request state (%" PRIx8 ")\n", __func__,
973 last_state);
974 return STORAGE_ERR_NOT_VALID;
975 }
976
977 if (last_state == STORAGE_FILE_LIST_START) {
978 last_name = NULL;
979 } else {
980 /* make sure filename is legal */
981 if (!is_valid_name(last_fname, last_fname_len)) {
982 SS_ERR("%s: invalid filename\n", __func__);
983 return STORAGE_ERR_NOT_VALID;
984 }
985
986 result = get_path(path_buf, sizeof(path_buf), &session->uuid,
987 last_fname, last_fname_len);
988 if (result != STORAGE_NO_ERROR) {
989 return result;
990 }
991
992 last_name = path_buf;
993 }
994
995 if (last_state != STORAGE_FILE_LIST_ADDED) {
996 iterate_res = file_iterate(&session->tr, last_name, false, &state.iter,
997 flags.allow_repaired);
998 last_name = NULL;
999 } else {
1000 iterate_res = FILE_OP_SUCCESS;
1001 }
1002 if (iterate_res == FILE_OP_SUCCESS && !storage_file_list_buf_full(&state)) {
1003 iterate_res = file_iterate(&session->tr, last_name, true, &state.iter,
1004 flags.allow_repaired);
1005 }
1006 if (iterate_res != FILE_OP_SUCCESS) {
1007 SS_ERR("%s: file_iterate failed\n", __func__);
1008 return file_op_result_to_storage_err(iterate_res);
1009 }
1010
1011 if (!storage_file_list_buf_full(&state)) {
1012 storage_file_list_add(&state, STORAGE_FILE_LIST_END, NULL);
1013 }
1014
1015 return STORAGE_NO_ERROR;
1016 }
1017
storage_file_get_size(struct storage_client_session * session,uint32_t handle,struct storage_op_flags flags,uint64_t * size)1018 enum storage_err storage_file_get_size(struct storage_client_session* session,
1019 uint32_t handle,
1020 struct storage_op_flags flags,
1021 uint64_t* size) {
1022 bool valid;
1023 struct storage_file_handle* file;
1024
1025 enum storage_err result =
1026 assert_checkpoint_flag_valid(session, flags, __func__);
1027 if (result != STORAGE_NO_ERROR) {
1028 return result;
1029 }
1030 result = ensure_active_transaction(session, flags);
1031 if (result != STORAGE_NO_ERROR) {
1032 return result;
1033 }
1034
1035 file = get_file_handle(session, handle);
1036 if (!file) {
1037 SS_ERR("%s: invalid file handle (%" PRIx32 ")\n", __func__, handle);
1038 return STORAGE_ERR_NOT_VALID;
1039 }
1040
1041 valid = file_get_size(&session->tr, file, size);
1042 if (!valid) {
1043 return STORAGE_ERR_NOT_VALID;
1044 }
1045
1046 return STORAGE_NO_ERROR;
1047 }
1048
storage_file_set_size(struct storage_client_session * session,uint32_t handle,uint64_t new_size,struct storage_op_flags flags)1049 enum storage_err storage_file_set_size(struct storage_client_session* session,
1050 uint32_t handle,
1051 uint64_t new_size,
1052 struct storage_op_flags flags) {
1053 struct storage_file_handle* file;
1054
1055 enum storage_err result =
1056 assert_checkpoint_flag_valid(session, flags, __func__);
1057 if (result != STORAGE_NO_ERROR) {
1058 return result;
1059 }
1060 result = ensure_active_transaction(session, flags);
1061 if (result != STORAGE_NO_ERROR) {
1062 return result;
1063 }
1064
1065 file = get_file_handle(session, handle);
1066 if (!file) {
1067 SS_ERR("%s: invalid file handle (%" PRIx32 ")\n", __func__, handle);
1068 return STORAGE_ERR_NOT_VALID;
1069 }
1070
1071 SS_INFO("%s: new size 0x%" PRIx64 ", old size 0x%" PRIx64 "\n", __func__,
1072 new_size, file->size);
1073
1074 /* for now we only support shrinking the file */
1075 if (new_size > file->size) {
1076 result = storage_create_gap(session, file);
1077 if (result != STORAGE_NO_ERROR) {
1078 return result;
1079 }
1080 storage_create_gap(session, file);
1081 }
1082
1083 /* check for nop */
1084 if (new_size != file->size) {
1085 /* update size */
1086 file_set_size(&session->tr, file, new_size);
1087 }
1088
1089 /* try to commit */
1090 if (flags.complete_transaction) {
1091 transaction_complete_etc(&session->tr, flags.update_checkpoint);
1092 }
1093
1094 if (session->tr.failed) {
1095 SS_ERR("%s: transaction failed\n", __func__);
1096 return STORAGE_ERR_GENERIC;
1097 }
1098
1099 return STORAGE_NO_ERROR;
1100 }
1101