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