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