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