1 /*
2  * Copyright (c) 2018-2023, Arm Limited. All rights reserved.
3  * Copyright (c) 2020, Cypress Semiconductor Corporation. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  */
8 
9 
10 #include <stdbool.h>
11 #include <string.h>
12 
13 #include "config_tfm.h"
14 #include "its_flash_fs.h"
15 #include "its_flash_fs_dblock.h"
16 #include "its_utils.h"
17 
18 /* Filesystem-internal flags, which cannot be passed by the caller */
19 #define ITS_FLASH_FS_INTERNAL_FLAGS_MASK  (UINT32_MAX - ((1U << 24) - 1))
20 /* Flag that indicates the file is to be deleted in the next block update */
21 #define ITS_FLASH_FS_FLAG_DELETE          (1U << 24)
22 
23 static psa_status_t its_flash_fs_delete_idx(struct its_flash_fs_ctx_t *fs_ctx,
24                                             uint32_t del_file_idx);
25 
its_flash_fs_file_write_aligned_data(struct its_flash_fs_ctx_t * fs_ctx,const struct its_block_meta_t * block_meta,const struct its_file_meta_t * file_meta,size_t offset,size_t size,const uint8_t * data)26 static psa_status_t its_flash_fs_file_write_aligned_data(
27                                       struct its_flash_fs_ctx_t *fs_ctx,
28                                       const struct its_block_meta_t *block_meta,
29                                       const struct its_file_meta_t *file_meta,
30                                       size_t offset,
31                                       size_t size,
32                                       const uint8_t *data)
33 {
34 #if (ITS_FLASH_MAX_ALIGNMENT != 1)
35     /* Check that the offset is aligned with the flash program unit */
36     if (!ITS_UTILS_IS_ALIGNED(offset, fs_ctx->cfg->program_unit)) {
37         return PSA_ERROR_INVALID_ARGUMENT;
38     }
39 
40     /* Set the size to be aligned with the flash program unit */
41     size = ITS_UTILS_ALIGN(size, fs_ctx->cfg->program_unit);
42 #endif
43 
44     /* It is not permitted to create gaps in the file */
45     if (offset > file_meta->cur_size) {
46         return PSA_ERROR_INVALID_ARGUMENT;
47     }
48 
49     /* Check that the new data is contained within the file's max size */
50     if (its_utils_check_contained_in(file_meta->max_size, offset, size)
51         != PSA_SUCCESS) {
52         return PSA_ERROR_INVALID_ARGUMENT;
53     }
54 
55     return its_flash_fs_dblock_write_file(fs_ctx, block_meta, file_meta, offset,
56                                           size, data);
57 }
58 
59 /* TODO This is very similar to (static) its_num_active_dblocks() */
its_flash_fs_num_active_dblocks(const struct its_flash_fs_config_t * cfg)60 static uint32_t its_flash_fs_num_active_dblocks(
61                                         const struct its_flash_fs_config_t *cfg)
62 {
63     /* Total number of datablocks is the number of dedicated datablocks plus
64      * logical datablock 0 stored in the metadata block.
65      */
66     if (cfg->num_blocks == 2) {
67         /* Metadata and data are stored in the same physical block, and the
68          * other block is required for power failure safe operation.
69          */
70         /* There are no dedicated data blocks when only two blocks are available
71          */
72         return 1;
73     } else {
74         /* One metadata block and two scratch blocks are reserved. One scratch
75          * block for metadata operations and the other for file data operations.
76          */
77         return cfg->num_blocks - 2;
78     }
79 }
80 
its_flash_fs_all_metadata_size(const struct its_flash_fs_config_t * cfg)81 static size_t its_flash_fs_all_metadata_size(
82                                         const struct its_flash_fs_config_t *cfg)
83 {
84     return sizeof(struct its_metadata_block_header_t)
85            + (its_flash_fs_num_active_dblocks(cfg)
86               * sizeof(struct its_block_meta_t))
87            + (cfg->max_num_files * sizeof(struct its_file_meta_t));
88 }
89 
90 /**
91  * \brief Validates the configuration of the flash filesystem.
92  *
93  * This function checks that the flash block provided is compatible with the
94  * flash_fs described by the cfg parameter.
95  *
96  * \param[in] cfg  Filesystem config
97  *
98  * \return Returns error code as specified in \ref psa_status_t
99  */
its_flash_fs_validate_config(const struct its_flash_fs_config_t * cfg)100 static psa_status_t its_flash_fs_validate_config(
101                                         const struct its_flash_fs_config_t *cfg)
102 {
103     psa_status_t ret = PSA_SUCCESS;
104 
105     /* The minimum number of blocks is 2. In this case, metadata and data are
106      * stored in the same physical block, and the other block is required for
107      * power failure safe operation.
108      * If at least 1 data block is available, 1 data scratch block is required
109      * for power failure safe operation. So, in this case, the minimum number of
110      * blocks is 4 (2 metadata block + 2 data blocks).
111      */
112     if ((cfg->num_blocks < 2) || (cfg->num_blocks == 3)) {
113         ret = PSA_ERROR_INVALID_ARGUMENT;
114     }
115 
116     if (cfg->num_blocks == 2) {
117         /* Metadata and data are stored in the same physical block */
118         if (cfg->max_file_size >
119                         cfg->block_size - its_flash_fs_all_metadata_size(cfg)) {
120             ret = PSA_ERROR_INVALID_ARGUMENT;
121         }
122     }
123 
124     /* It is not required that all files fit in ITS flash area at the same time.
125      * So, it is possible that a create action fails because flash is full.
126      * However, the larger file must have enough space in the ITS flash area to
127      * be created, at least, when the ITS flash area is empty.
128      */
129     if (cfg->max_file_size > cfg->block_size) {
130         ret = PSA_ERROR_INVALID_ARGUMENT;
131     }
132 
133     /* Metadata must fit in a flash block */
134     if (its_flash_fs_all_metadata_size(cfg) > cfg->block_size) {
135         ret = PSA_ERROR_INVALID_ARGUMENT;
136     }
137 
138     return ret;
139 }
140 
its_flash_fs_init_ctx(its_flash_fs_ctx_t * fs_ctx,const struct its_flash_fs_config_t * fs_cfg,const struct its_flash_fs_ops_t * fs_ops)141 psa_status_t its_flash_fs_init_ctx(its_flash_fs_ctx_t *fs_ctx,
142                                    const struct its_flash_fs_config_t *fs_cfg,
143                                    const struct its_flash_fs_ops_t *fs_ops)
144 {
145     psa_status_t err;
146 
147     if (!fs_ctx || !fs_cfg || !fs_ops) {
148         return PSA_ERROR_INVALID_ARGUMENT;
149     }
150 
151     /* Check for valid filesystem configuration */
152     err = its_flash_fs_validate_config(fs_cfg);
153     if (err != PSA_SUCCESS) {
154         return err;
155     }
156 
157     /* Zero the context */
158     memset(fs_ctx, 0, sizeof(*fs_ctx));
159 
160     /* Associate the filesystem config and operations with the context */
161     fs_ctx->cfg = fs_cfg;
162     fs_ctx->ops = fs_ops;
163 
164     return PSA_SUCCESS;
165 }
166 
its_flash_fs_prepare(its_flash_fs_ctx_t * fs_ctx)167 psa_status_t its_flash_fs_prepare(its_flash_fs_ctx_t *fs_ctx)
168 {
169     psa_status_t err;
170     uint32_t idx;
171 
172     /* Initialize metadata block with the valid/active metablock */
173     err = its_flash_fs_mblock_init(fs_ctx);
174     if (err != PSA_SUCCESS) {
175         return err;
176     }
177 
178     /* Check if a file marked for deletion has been left behind by a power
179      * failure. If so, delete it.
180      */
181     err = its_flash_fs_mblock_get_file_idx_flag(fs_ctx,
182                                                 ITS_FLASH_FS_FLAG_DELETE, &idx);
183     if (err == PSA_SUCCESS) {
184         return its_flash_fs_delete_idx(fs_ctx, idx);
185     } else if (err != PSA_ERROR_DOES_NOT_EXIST) {
186         return err;
187     }
188 
189     return PSA_SUCCESS;
190 }
191 
its_flash_fs_wipe_all(struct its_flash_fs_ctx_t * fs_ctx)192 psa_status_t its_flash_fs_wipe_all(struct its_flash_fs_ctx_t *fs_ctx)
193 {
194     /* Clean and initialize the metadata block */
195     return its_flash_fs_mblock_reset_metablock(fs_ctx);
196 }
197 
its_flash_fs_file_get_info(struct its_flash_fs_ctx_t * fs_ctx,const uint8_t * fid,struct its_flash_fs_file_info_t * info)198 psa_status_t its_flash_fs_file_get_info(struct its_flash_fs_ctx_t *fs_ctx,
199                                         const uint8_t *fid,
200                                         struct its_flash_fs_file_info_t *info)
201 {
202     psa_status_t err;
203     uint32_t idx;
204     struct its_file_meta_t tmp_metadata;
205 
206     /* Get the meta data index and meta data */
207     err = its_flash_fs_mblock_get_file_idx_meta(fs_ctx, fid, &idx, &tmp_metadata);
208     if (err != PSA_SUCCESS) {
209         return PSA_ERROR_DOES_NOT_EXIST;
210     }
211     info->size_max = tmp_metadata.max_size;
212     info->size_current = tmp_metadata.cur_size;
213     info->flags = tmp_metadata.flags & ITS_FLASH_FS_USER_FLAGS_MASK;
214 
215 #ifdef ITS_ENCRYPTION
216     memcpy(info->nonce, tmp_metadata.nonce, TFM_ITS_ENC_NONCE_LENGTH);
217     memcpy(info->tag, tmp_metadata.tag, TFM_ITS_AUTH_TAG_LENGTH);
218 #endif
219 
220     return PSA_SUCCESS;
221 }
222 
its_flash_fs_file_write(struct its_flash_fs_ctx_t * fs_ctx,const uint8_t * fid,struct its_flash_fs_file_info_t * finfo,size_t data_size,size_t offset,const uint8_t * data)223 psa_status_t its_flash_fs_file_write(struct its_flash_fs_ctx_t *fs_ctx,
224                                      const uint8_t *fid,
225                                      struct its_flash_fs_file_info_t *finfo,
226                                      size_t data_size,
227                                      size_t offset,
228                                      const uint8_t *data)
229 {
230     struct its_block_meta_t block_meta;
231     struct its_file_meta_t file_meta = {0};
232     uint32_t cur_phys_block;
233     psa_status_t err;
234     uint32_t idx;
235     uint32_t old_idx = ITS_METADATA_INVALID_INDEX;
236     uint32_t new_idx = ITS_METADATA_INVALID_INDEX;
237     bool use_spare;
238 
239     if (finfo == NULL) {
240         return PSA_ERROR_INVALID_ARGUMENT;
241     }
242 
243     /* Do not permit the user to pass filesystem-internal flags */
244     if (finfo->flags & ITS_FLASH_FS_INTERNAL_FLAGS_MASK) {
245         return PSA_ERROR_INVALID_ARGUMENT;
246     }
247 
248 #if (ITS_FLASH_MAX_ALIGNMENT != 1)
249     /* Set the max_size to be aligned with the flash program unit */
250     finfo->size_max = ITS_UTILS_ALIGN(finfo->size_max, fs_ctx->cfg->program_unit);
251 #endif
252 
253     /* Check if the file already exists */
254     err = its_flash_fs_mblock_get_file_idx_meta(fs_ctx, fid, &old_idx, &file_meta);
255     if (err == PSA_SUCCESS) {
256         if (finfo->flags & ITS_FLASH_FS_FLAG_TRUNCATE) {
257             if (file_meta.max_size == finfo->size_max) {
258                 /* Truncate and reuse the existing file, which is already the
259                  * correct size.
260                  */
261                 file_meta.cur_size = 0;
262                 file_meta.flags = finfo->flags;
263                 new_idx = old_idx;
264             } else {
265                 /* Mark the existing file to be deleted in this block update. It
266                  * will be deleted in a second block update, and if there is a
267                  * power failure before that block update completes, then
268                  * deletion will be re-attempted based on this flag.
269                  */
270                 file_meta.flags |= ITS_FLASH_FS_FLAG_DELETE;
271                 err = its_flash_fs_mblock_update_scratch_file_meta(fs_ctx,
272                                                                    old_idx,
273                                                                    &file_meta);
274                 if (err != PSA_SUCCESS) {
275                     return PSA_ERROR_GENERIC_ERROR;
276                 }
277             }
278         } else {
279             /* Write to existing file */
280             new_idx = old_idx;
281         }
282     } else if (err == PSA_ERROR_DOES_NOT_EXIST) {
283         /* The create flag must be supplied to create a new file */
284         if (!(finfo->flags & ITS_FLASH_FS_FLAG_CREATE)) {
285             return PSA_ERROR_DOES_NOT_EXIST;
286         }
287     } else {
288         return err;
289     }
290 
291     /* If the existing file was not reused, then a new one must be reserved */
292     if (new_idx == ITS_METADATA_INVALID_INDEX) {
293         /* Check that the file's maximum size is valid */
294         if (finfo->size_max > fs_ctx->cfg->max_file_size) {
295             return PSA_ERROR_INVALID_ARGUMENT;
296         }
297 
298         /* Only use the spare file if there is an old file to be deleted */
299         use_spare = (old_idx != ITS_METADATA_INVALID_INDEX);
300 
301         /* Try to reserve a new file based on the input parameters */
302         err = its_flash_fs_mblock_reserve_file(fs_ctx, fid, use_spare,
303                                                finfo->size_max, finfo->flags, &new_idx,
304                                                &file_meta, &block_meta);
305         if (err != PSA_SUCCESS) {
306             return err;
307         }
308     } else {
309         /* Read existing block metadata */
310         err = its_flash_fs_mblock_read_block_metadata(fs_ctx, file_meta.lblock,
311                                                       &block_meta);
312         if (err != PSA_SUCCESS) {
313             return PSA_ERROR_GENERIC_ERROR;
314         }
315     }
316 
317     if (data_size != 0) {
318         /* Write the content into scratch data block */
319         err = its_flash_fs_file_write_aligned_data(fs_ctx, &block_meta,
320                                                    &file_meta, offset,
321                                                    data_size, data);
322         if (err != PSA_SUCCESS) {
323             return PSA_ERROR_GENERIC_ERROR;
324         }
325 
326         /* Update the file's current size if required */
327         if (offset + data_size > file_meta.cur_size) {
328             /* Update the file metadata */
329             file_meta.cur_size = offset + data_size;
330         }
331 
332         cur_phys_block = block_meta.phy_id;
333 
334         /* Cur scratch block become the active datablock */
335         block_meta.phy_id =
336             its_flash_fs_mblock_cur_data_scratch_id(fs_ctx, file_meta.lblock);
337 
338         /* Swap the scratch data block */
339         its_flash_fs_mblock_set_data_scratch(fs_ctx, cur_phys_block,
340                                              file_meta.lblock);
341     }
342 
343     /* Update block metadata in scratch metadata block */
344     err = its_flash_fs_mblock_update_scratch_block_meta(fs_ctx,
345                                                         file_meta.lblock,
346                                                         &block_meta);
347     if (err != PSA_SUCCESS) {
348         return PSA_ERROR_GENERIC_ERROR;
349     }
350 
351 #ifdef ITS_ENCRYPTION
352     memcpy(file_meta.nonce, finfo->nonce, sizeof(finfo->nonce));
353     memcpy(file_meta.tag, finfo->tag, sizeof(finfo->tag));
354 #endif
355 
356     /* Write file metadata in the scratch metadata block */
357     err = its_flash_fs_mblock_update_scratch_file_meta(fs_ctx, new_idx,
358                                                        &file_meta);
359     if (err != PSA_SUCCESS) {
360         return PSA_ERROR_GENERIC_ERROR;
361     }
362 
363     /* Copy the file metadata entries from the start to the smaller of the two
364      * indexes.
365      */
366     idx = ITS_UTILS_MIN(new_idx, old_idx);
367     err = its_flash_fs_mblock_cp_file_meta(fs_ctx, 0, idx);
368     if (err != PSA_SUCCESS) {
369         return PSA_ERROR_GENERIC_ERROR;
370     }
371 
372     /* Copy the file metadata entries between the two indexes, if necessary */
373     if (old_idx != ITS_METADATA_INVALID_INDEX && old_idx != new_idx) {
374         err = its_flash_fs_mblock_cp_file_meta(fs_ctx, idx + 1,
375                                                ITS_UTILS_MAX(new_idx, old_idx));
376         if (err != PSA_SUCCESS) {
377             return PSA_ERROR_GENERIC_ERROR;
378         }
379 
380         idx = ITS_UTILS_MAX(new_idx, old_idx);
381     }
382 
383     /* Copy rest of the file metadata entries */
384     err = its_flash_fs_mblock_cp_file_meta(fs_ctx, idx + 1,
385                                            fs_ctx->cfg->max_num_files);
386     if (err != PSA_SUCCESS) {
387         return PSA_ERROR_GENERIC_ERROR;
388     }
389 
390     /* The file data in the logical block 0 is stored in same physical block
391      * where the metadata is stored. A change in the metadata requires a swap of
392      * physical blocks. So, the file data stored in the current metadata block
393      * needs to be copied to the scratch block, if the data of the file
394      * processed is not located in the logical block 0. When file data is
395      * located in the logical block 0, that copy has been done while processing
396      * the file data.
397      */
398     if ((file_meta.lblock != ITS_LOGICAL_DBLOCK0) || (data_size == 0)) {
399         err = its_flash_fs_mblock_migrate_lb0_data_to_scratch(fs_ctx);
400         if (err != PSA_SUCCESS) {
401             return PSA_ERROR_GENERIC_ERROR;
402         }
403     }
404 
405     /* Write metadata header, swap metadata blocks and erase scratch blocks */
406     err = its_flash_fs_mblock_meta_update_finalize(fs_ctx);
407     if (err != PSA_SUCCESS) {
408         return err;
409     }
410 
411     /* Delete the old file in a second block update.
412      * Note: A power failure after this point, but before the deletion has
413      * completed, will leave the old file in the filesystem, so it is always
414      * necessary to check for files to be deleted at initialisation time.
415      */
416     if (old_idx != ITS_METADATA_INVALID_INDEX && old_idx != new_idx) {
417         err = its_flash_fs_delete_idx(fs_ctx, old_idx);
418     }
419 
420     return err;
421 }
422 
its_flash_fs_delete_idx(struct its_flash_fs_ctx_t * fs_ctx,uint32_t del_file_idx)423 static psa_status_t its_flash_fs_delete_idx(struct its_flash_fs_ctx_t *fs_ctx,
424                                             uint32_t del_file_idx)
425 {
426     size_t del_file_data_idx;
427     uint32_t del_file_lblock;
428     size_t del_file_max_size;
429     psa_status_t err;
430     size_t src_offset = fs_ctx->cfg->block_size;
431     size_t nbr_bytes_to_move = 0;
432     uint32_t idx;
433     struct its_file_meta_t file_meta;
434     struct its_block_meta_t block_meta;
435 
436     err = its_flash_fs_mblock_read_file_meta(fs_ctx, del_file_idx, &file_meta);
437     if (err != PSA_SUCCESS) {
438         return err;
439     }
440 
441     if (its_utils_validate_fid(file_meta.id) != PSA_SUCCESS) {
442         return PSA_ERROR_DOES_NOT_EXIST;
443     }
444 
445     /* Save logical block, data_index and max_size to be used later on */
446     del_file_lblock = file_meta.lblock;
447     del_file_data_idx = file_meta.data_idx;
448     del_file_max_size = file_meta.max_size;
449 
450     /* Remove file metadata */
451     file_meta = (struct its_file_meta_t){0};
452 
453     /* Update file metadata in to the scratch block */
454     err = its_flash_fs_mblock_update_scratch_file_meta(fs_ctx, del_file_idx,
455                                                        &file_meta);
456     if (err != PSA_SUCCESS) {
457         return err;
458     }
459 
460     /* Read all file metadata */
461     for (idx = 0; idx < fs_ctx->cfg->max_num_files; idx++) {
462         if (idx == del_file_idx) {
463             /* Skip deleted file */
464             continue;
465         }
466 
467         /* Read file meta for the given file index */
468         err = its_flash_fs_mblock_read_file_meta(fs_ctx, idx, &file_meta);
469         if (err != PSA_SUCCESS) {
470             return err;
471         }
472 
473         /* Check if the file is located in the same logical block and has a
474          * valid FID.
475          */
476         if ((file_meta.lblock == del_file_lblock) &&
477             (its_utils_validate_fid(file_meta.id) == PSA_SUCCESS)) {
478             /* If a file is located after the data to delete, this
479              * needs to be moved.
480              */
481             if (file_meta.data_idx > del_file_data_idx) {
482                 /* Check if this is the position after the deleted
483                  * data. This will be the first file data to move.
484                  */
485                 if (src_offset > file_meta.data_idx) {
486                     src_offset = file_meta.data_idx;
487                 }
488 
489                 /* Set the new file data index location in the
490                  * data block.
491                  */
492                 file_meta.data_idx -= del_file_max_size;
493 
494                 /* Increase number of bytes to move */
495                 nbr_bytes_to_move += file_meta.max_size;
496             }
497         }
498         /* Update file metadata in to the scratch block */
499         err = its_flash_fs_mblock_update_scratch_file_meta(fs_ctx, idx,
500                                                            &file_meta);
501         if (err != PSA_SUCCESS) {
502             return err;
503         }
504     }
505 
506     if (del_file_max_size == 0) {
507         /* If the asset max size is 0, there is no need to compact the data block.
508          * Copy the block metadata and the block data to scratch metadata block.
509          */
510         err = its_flash_fs_mblock_read_block_metadata(fs_ctx, ITS_LOGICAL_DBLOCK0, &block_meta);
511         if (err != PSA_SUCCESS) {
512             return err;
513         }
514         err = its_flash_fs_mblock_update_scratch_block_meta(fs_ctx, ITS_LOGICAL_DBLOCK0,
515                                                             &block_meta);
516     } else {
517         /* Compact data block */
518         err = its_flash_fs_dblock_compact_block(fs_ctx, del_file_lblock,
519                                                 del_file_max_size,
520                                                 src_offset, del_file_data_idx,
521                                                 nbr_bytes_to_move);
522     }
523 
524     if (err != PSA_SUCCESS) {
525         return err;
526     }
527 
528     /* If the max size of file to delete is not 0:
529      * The file data in the logical block 0 is stored in same physical block
530      * where the metadata is stored. A change in the metadata requires a
531      * swap of physical blocks. So, the file data stored in the current
532      * metadata block needs to be copied in the scratch block, if the data
533      * of the file processed is not located in the logical block 0. When an
534      * file data is located in the logical block 0, that copy has been done
535      * while processing the file data.
536      * If the max size of file to delete is 0:
537      * The file metadata and block metadata has been updated into the scratch
538      * metadata block, copy the file data to the scratch block.
539      */
540     if ((del_file_max_size != 0 && del_file_lblock != ITS_LOGICAL_DBLOCK0) ||
541         (del_file_max_size == 0)) {
542         err = its_flash_fs_mblock_migrate_lb0_data_to_scratch(fs_ctx);
543         if (err != PSA_SUCCESS) {
544             return PSA_ERROR_GENERIC_ERROR;
545         }
546     }
547 
548     /* Update the metablock header, swap scratch and active blocks,
549      * erase scratch blocks.
550      */
551     return its_flash_fs_mblock_meta_update_finalize(fs_ctx);
552 }
553 
its_flash_fs_file_delete(struct its_flash_fs_ctx_t * fs_ctx,const uint8_t * fid)554 psa_status_t its_flash_fs_file_delete(struct its_flash_fs_ctx_t *fs_ctx,
555                                       const uint8_t *fid)
556 {
557     psa_status_t err;
558     uint32_t del_file_idx;
559 
560     /* Get the file index. */
561     err = its_flash_fs_mblock_get_file_idx_meta(fs_ctx, fid, &del_file_idx, NULL);
562     if (err != PSA_SUCCESS) {
563         return PSA_ERROR_DOES_NOT_EXIST;
564     }
565 
566     return its_flash_fs_delete_idx(fs_ctx, del_file_idx);
567 }
568 
its_flash_fs_file_read(struct its_flash_fs_ctx_t * fs_ctx,const uint8_t * fid,size_t size,size_t offset,uint8_t * data)569 psa_status_t its_flash_fs_file_read(struct its_flash_fs_ctx_t *fs_ctx,
570                                     const uint8_t *fid,
571                                     size_t size,
572                                     size_t offset,
573                                     uint8_t *data)
574 {
575     psa_status_t err;
576     uint32_t idx;
577     struct its_file_meta_t tmp_metadata;
578 
579     /* Get the file index and meta data */
580     err = its_flash_fs_mblock_get_file_idx_meta(fs_ctx, fid, &idx, &tmp_metadata);
581     if (err != PSA_SUCCESS) {
582         return PSA_ERROR_DOES_NOT_EXIST;
583     }
584 
585     /* Boundary check the incoming request */
586     err = its_utils_check_contained_in(tmp_metadata.cur_size, offset, size);
587     if (err != PSA_SUCCESS) {
588         return err;
589     }
590 
591     /* Read the file from flash */
592     err = its_flash_fs_dblock_read_file(fs_ctx, &tmp_metadata, offset, size,
593                                         data);
594     if (err != PSA_SUCCESS) {
595         return PSA_ERROR_GENERIC_ERROR;
596     }
597 
598     return PSA_SUCCESS;
599 }
600