Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers FileSystemStore.cpp Source File

FileSystemStore.cpp

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2018 ARM Limited
00003  *
00004  * SPDX-License-Identifier: Apache-2.0
00005  *
00006  * Licensed under the Apache License, Version 2.0 (the "License");
00007  * you may not use this file except in compliance with the License.
00008  * You may obtain a copy of the License at
00009  *
00010  *     http://www.apache.org/licenses/LICENSE-2.0
00011  *
00012  * Unless required by applicable law or agreed to in writing, software
00013  * distributed under the License is distributed on an "AS IS" BASIS,
00014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015  * See the License for the specific language governing permissions and
00016  * limitations under the License.
00017  */
00018 
00019 #include "FileSystemStore.h"
00020 #include "features/storage/kvstore/conf/kv_config.h"
00021 #include "features/storage/filesystem/Dir.h"
00022 #include "features/storage/filesystem/File.h"
00023 #include "features/storage/blockdevice/BlockDevice.h"
00024 #include "mbed_error.h"
00025 #include <string.h>
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 
00029 #include "mbed_trace.h"
00030 #define TRACE_GROUP "FSST"
00031 
00032 #define FSST_REVISION 1
00033 #define FSST_MAGIC 0x46535354 // "FSST" hex 'magic' signature
00034 
00035 #define FSST_DEFAULT_FOLDER_PATH "kvstore" //default FileSystemStore folder path on fs
00036 
00037 // Only write once flag is supported, other two are kept in storage but ignored
00038 static const uint32_t supported_flags = mbed::KVStore::WRITE_ONCE_FLAG | mbed::KVStore::REQUIRE_CONFIDENTIALITY_FLAG |
00039                                         mbed::KVStore::REQUIRE_REPLAY_PROTECTION_FLAG;
00040 
00041 using namespace mbed;
00042 
00043 namespace {
00044 
00045 // incremental set handle
00046 typedef struct {
00047     char *key;
00048     uint32_t create_flags;
00049     size_t data_size;
00050     File *file_handle;
00051 } inc_set_handle_t;
00052 
00053 // iterator handle
00054 typedef struct {
00055     void *dir_handle;
00056     char *prefix;
00057 } key_iterator_handle_t;
00058 
00059 } // anonymous namespace
00060 
00061 // Local Functions
00062 static char *string_ndup(const char *src, size_t size);
00063 
00064 
00065 // Class Functions
00066 FileSystemStore::FileSystemStore(FileSystem *fs) : _fs(fs),
00067     _is_initialized(false), _cfg_fs_path(NULL), _cfg_fs_path_size(0),
00068     _full_path_key(NULL), _cur_inc_data_size(0), _cur_inc_set_handle(NULL)
00069 {
00070 
00071 }
00072 
00073 int FileSystemStore::init()
00074 {
00075     int status = MBED_SUCCESS;
00076 
00077     _mutex.lock();
00078     const char *temp_path = get_filesystemstore_folder_path();
00079     if (temp_path == NULL) {
00080         _cfg_fs_path_size = strlen(FSST_DEFAULT_FOLDER_PATH);
00081         _cfg_fs_path = string_ndup(FSST_DEFAULT_FOLDER_PATH, _cfg_fs_path_size);
00082     } else {
00083         _cfg_fs_path_size = strlen(temp_path);
00084         _cfg_fs_path = string_ndup(temp_path, _cfg_fs_path_size);
00085     }
00086 
00087     _full_path_key = new char[_cfg_fs_path_size + KVStore::MAX_KEY_SIZE + 1];
00088     memset(_full_path_key, 0, (_cfg_fs_path_size + KVStore::MAX_KEY_SIZE + 1));
00089     strncpy(_full_path_key, _cfg_fs_path, _cfg_fs_path_size);
00090     _full_path_key[_cfg_fs_path_size] = '/';
00091     _cur_inc_data_size = 0;
00092     _cur_inc_set_handle = NULL;
00093     Dir kv_dir;
00094 
00095     if (kv_dir.open(_fs, _cfg_fs_path) != 0) {
00096         tr_info("KV Dir: %s, doesnt exist - creating new.. ", _cfg_fs_path); //TBD verify ERRNO NOEXIST
00097         if (_fs->mkdir(_cfg_fs_path,/* which flags ? */0777) != 0) {
00098             tr_error("KV Dir: %s, mkdir failed.. ", _cfg_fs_path); //TBD verify ERRNO NOEXIST
00099             status = MBED_ERROR_FAILED_OPERATION;
00100             goto exit_point;
00101         }
00102     } else {
00103         tr_info("KV Dir: %s, exists(verified) - now closing it", _cfg_fs_path);
00104         if (kv_dir.close() != 0) {
00105             tr_error("KV Dir: %s, dir_close failed", _cfg_fs_path); //TBD verify ERRNO NOEXIST
00106         }
00107     }
00108 
00109     _is_initialized = true;
00110 exit_point:
00111 
00112     _mutex.unlock();
00113 
00114     return status;
00115 
00116 }
00117 
00118 int FileSystemStore::deinit()
00119 {
00120     _mutex.lock();
00121     _is_initialized = false;
00122     delete[] _cfg_fs_path;
00123     delete[] _full_path_key;
00124     _mutex.unlock();
00125     return MBED_SUCCESS;
00126 
00127 }
00128 
00129 int FileSystemStore::reset()
00130 {
00131     int status = MBED_SUCCESS;
00132     Dir kv_dir;
00133     struct dirent dir_ent;
00134 
00135     _mutex.lock();
00136     if (false == _is_initialized) {
00137         status = MBED_ERROR_NOT_READY;
00138         goto exit_point;
00139     }
00140 
00141     kv_dir.open(_fs, _cfg_fs_path);
00142 
00143     while (kv_dir.read(&dir_ent) != 0) {
00144         if (dir_ent.d_type != DT_REG) {
00145             continue;
00146         }
00147         // Build File's full path name and delete it (even if write-onced)
00148         _build_full_path_key(dir_ent.d_name);
00149         _fs->remove(_full_path_key);
00150     }
00151 
00152     kv_dir.close();
00153 
00154 exit_point:
00155     _mutex.unlock();
00156     return status;
00157 }
00158 
00159 int FileSystemStore::set(const char *key, const void *buffer, size_t size, uint32_t create_flags)
00160 {
00161     int status = MBED_SUCCESS;
00162     set_handle_t handle;
00163 
00164     if (false == _is_initialized) {
00165         status = MBED_ERROR_NOT_READY;
00166         goto exit_point;
00167     }
00168 
00169     if ((!is_valid_key(key)) || ((buffer == NULL) && (size > 0))) {
00170         status = MBED_ERROR_INVALID_ARGUMENT;
00171         goto exit_point;
00172     }
00173 
00174     status = set_start(&handle, key, size, create_flags);
00175     if (status != MBED_SUCCESS) {
00176         tr_error("FSST Set set_start Failed: %d", status);
00177         goto exit_point;
00178     }
00179 
00180     status = set_add_data(handle, buffer, size);
00181     if (status != MBED_SUCCESS) {
00182         tr_error("FSST Set set_add_data Failed: %d", status);
00183         set_finalize(handle);
00184         goto exit_point;
00185     }
00186 
00187     status = set_finalize(handle);
00188     if (status != MBED_SUCCESS) {
00189         tr_error("FSST Set set_finalize Failed: %d", status);
00190         goto exit_point;
00191     }
00192 
00193 exit_point:
00194 
00195     return status;
00196 }
00197 
00198 int FileSystemStore::get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size, size_t offset)
00199 {
00200     int status = MBED_SUCCESS;
00201 
00202     File kv_file;
00203     size_t kv_file_size = 0;
00204     size_t value_actual_size = 0;
00205 
00206     _mutex.lock();
00207 
00208     if (false == _is_initialized) {
00209         status = MBED_ERROR_NOT_READY;
00210         goto exit_point;
00211     }
00212 
00213     key_metadata_t key_metadata;
00214 
00215     if ((status = _verify_key_file(key, &key_metadata, &kv_file)) != MBED_SUCCESS) {
00216         tr_debug("File Verification failed, status: %d", status);
00217         goto exit_point;
00218     }
00219 
00220     kv_file_size = kv_file.size() - key_metadata.metadata_size;
00221     // Actual size is the minimum of buffer_size and remainder of data in file (file's data size - offset)
00222     value_actual_size = buffer_size;
00223     if (offset > kv_file_size) {
00224         status = MBED_ERROR_INVALID_SIZE;
00225         goto exit_point;
00226     } else if ((kv_file_size - offset) < buffer_size) {
00227         value_actual_size = kv_file_size - offset;
00228     }
00229 
00230     if ((buffer == NULL) && (value_actual_size > 0)) {
00231         status = MBED_ERROR_INVALID_DATA_DETECTED;
00232         goto exit_point;
00233     }
00234 
00235     if (actual_size != NULL) {
00236         *actual_size = value_actual_size;
00237     }
00238 
00239     kv_file.seek(key_metadata.metadata_size + offset, SEEK_SET);
00240     // Read remainder of data
00241     kv_file.read(buffer, value_actual_size);
00242 
00243 exit_point:
00244     if ((status == MBED_SUCCESS) ||
00245             (status == MBED_ERROR_INVALID_DATA_DETECTED)) {
00246         kv_file.close();
00247     }
00248     _mutex.unlock();
00249 
00250     return status;
00251 }
00252 
00253 int FileSystemStore::get_info(const char *key, info_t *info)
00254 {
00255     int status = MBED_SUCCESS;
00256     File kv_file;
00257 
00258     _mutex.lock();
00259 
00260     if (false == _is_initialized) {
00261         status = MBED_ERROR_NOT_READY;
00262         goto exit_point;
00263     }
00264 
00265     key_metadata_t key_metadata;
00266 
00267     if ((status = _verify_key_file(key, &key_metadata, &kv_file)) != MBED_SUCCESS) {
00268         tr_debug("File Verification failed, status: %d", status);
00269         goto exit_point;
00270     }
00271 
00272     if (info != NULL) {
00273         info->size = kv_file.size() - key_metadata.metadata_size;
00274         info->flags = key_metadata.user_flags;
00275     }
00276 
00277 exit_point:
00278     if ((status == MBED_SUCCESS) ||
00279             (status == MBED_ERROR_INVALID_DATA_DETECTED)) {
00280         kv_file.close();
00281     }
00282     _mutex.unlock();
00283 
00284     return status;
00285 }
00286 
00287 int FileSystemStore::remove(const char *key)
00288 {
00289     File kv_file;
00290     key_metadata_t key_metadata;
00291 
00292     _mutex.lock();
00293 
00294     int status = MBED_SUCCESS;
00295 
00296     if (false == _is_initialized) {
00297         status = MBED_ERROR_NOT_READY;
00298         goto exit_point;
00299     }
00300 
00301     /* If File Exists and is Valid, then check its Write Once Flag to verify its disabled before removing */
00302     /* If File exists and is not valid, or is Valid and not Write-Onced then remove it */
00303     if ((status = _verify_key_file(key, &key_metadata, &kv_file)) == MBED_SUCCESS) {
00304         if (key_metadata.user_flags & KVStore::WRITE_ONCE_FLAG) {
00305             kv_file.close();
00306             tr_error("File: %s, Exists but write protected", _full_path_key);
00307             status = MBED_ERROR_WRITE_PROTECTED;
00308             goto exit_point;
00309         }
00310     } else if ((status == MBED_ERROR_ITEM_NOT_FOUND) ||
00311                (status == MBED_ERROR_INVALID_ARGUMENT)) {
00312         goto exit_point;
00313     }
00314     kv_file.close();
00315 
00316     if (0 != _fs->remove(_full_path_key)) {
00317         status =  MBED_ERROR_FAILED_OPERATION;
00318     }
00319 
00320 exit_point:
00321     _mutex.unlock();
00322     return status;
00323 }
00324 
00325 // Incremental set API
00326 int FileSystemStore::set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags)
00327 {
00328     int status = MBED_SUCCESS;
00329     inc_set_handle_t *set_handle = NULL;
00330     File *kv_file;
00331     key_metadata_t key_metadata;
00332     int key_len = 0;
00333 
00334     if (create_flags & ~supported_flags) {
00335         return MBED_ERROR_INVALID_ARGUMENT;
00336     }
00337 
00338     // Only a single key file can be incrementaly editted at a time
00339     _mutex.lock();
00340 
00341     kv_file = new File;
00342 
00343     if (handle == NULL) {
00344         status = MBED_ERROR_INVALID_ARGUMENT;
00345         goto exit_point;
00346     }
00347 
00348     /* If File Exists and is Valid, then check its Write Once Flag to verify its disabled before setting */
00349     /* If File exists and is not valid, or is Valid and not Write-Onced then erase it */
00350     status = _verify_key_file(key, &key_metadata, kv_file);
00351 
00352     if (status == MBED_ERROR_INVALID_ARGUMENT) {
00353         tr_error("File Verification failed, status: %d", status);
00354         goto exit_point;
00355     }
00356 
00357     if (status == MBED_SUCCESS) {
00358         if (key_metadata.user_flags & KVStore::WRITE_ONCE_FLAG) {
00359             kv_file->close();
00360             status = MBED_ERROR_WRITE_PROTECTED;
00361             goto exit_point;
00362         }
00363     }
00364 
00365     /* For Success (not write_once) and for corrupted data close file before recreating it as a new file */
00366     if (status != MBED_ERROR_ITEM_NOT_FOUND) {
00367         kv_file->close();
00368     }
00369 
00370     if ((status = kv_file->open(_fs, _full_path_key, O_WRONLY | O_CREAT | O_TRUNC)) != MBED_SUCCESS) {
00371         tr_info("set_start failed to open: %s, for writing, err: %d", _full_path_key, status);
00372         status = MBED_ERROR_FAILED_OPERATION ;
00373         goto exit_point;
00374     }
00375     _cur_inc_data_size = 0;
00376 
00377     set_handle = new inc_set_handle_t;
00378     set_handle->create_flags = create_flags;
00379     set_handle->data_size = final_data_size;
00380     set_handle->file_handle = kv_file;
00381     key_len = strlen(key);
00382     set_handle->key = string_ndup(key, key_len);
00383     *handle = (set_handle_t)set_handle;
00384     _cur_inc_set_handle = *handle;
00385 
00386     key_metadata.magic = FSST_MAGIC;
00387     key_metadata.metadata_size = sizeof(key_metadata_t);
00388     key_metadata.revision = FSST_REVISION;
00389     key_metadata.user_flags = create_flags;
00390     kv_file->write(&key_metadata, sizeof(key_metadata_t));
00391 
00392 exit_point:
00393     if (status != MBED_SUCCESS) {
00394         delete kv_file;
00395         _mutex.unlock();
00396     }
00397     return status;
00398 }
00399 
00400 int FileSystemStore::set_add_data(set_handle_t handle, const void *value_data, size_t data_size)
00401 {
00402     int status = MBED_SUCCESS;
00403     size_t added_data = 0;
00404     inc_set_handle_t *set_handle = (inc_set_handle_t *)handle;
00405     File *kv_file;
00406 
00407     if (((value_data == NULL) && (data_size > 0)) || (handle == NULL) || (handle != _cur_inc_set_handle)) {
00408         status = MBED_ERROR_INVALID_ARGUMENT;
00409         goto exit_point;
00410     }
00411 
00412     // Single key incrementally edited, can be edited from multiple threads - lock to protect
00413     _inc_data_add_mutex.lock();
00414     if ((_cur_inc_data_size + data_size) > set_handle->data_size) {
00415         tr_warning("Added Data(%d) will exceed set_start final size(%d) - not adding data to file: %s",
00416                    _cur_inc_data_size + data_size, set_handle->data_size, _full_path_key);
00417         status = MBED_ERROR_INVALID_SIZE;
00418         goto exit_point;
00419     }
00420 
00421     kv_file = set_handle->file_handle;
00422 
00423     added_data = kv_file->write(value_data, data_size);
00424     if (added_data != data_size) {
00425         status = MBED_ERROR_FAILED_OPERATION ;
00426     }
00427     _cur_inc_data_size += added_data;
00428 
00429 exit_point:
00430     if (status != MBED_ERROR_INVALID_ARGUMENT) {
00431         _inc_data_add_mutex.unlock();
00432     }
00433 
00434     return status;
00435 }
00436 
00437 int FileSystemStore::set_finalize(set_handle_t handle)
00438 {
00439     int status = MBED_SUCCESS;
00440     inc_set_handle_t *set_handle = NULL;
00441 
00442     if ((handle == NULL) || (handle != _cur_inc_set_handle)) {
00443         status =  MBED_ERROR_INVALID_ARGUMENT;
00444         goto exit_point;
00445     }
00446 
00447     set_handle = (inc_set_handle_t *)handle;
00448 
00449     if (set_handle->key == NULL) {
00450         status = MBED_ERROR_INVALID_DATA_DETECTED;
00451     } else {
00452         if (_cur_inc_data_size != set_handle->data_size) {
00453             tr_error("Accumulated Data (%d) size doesn't match set_start final size (%d) - file: %s", _cur_inc_data_size,
00454                      set_handle->data_size, _full_path_key);
00455             status = MBED_ERROR_INVALID_SIZE;
00456             _fs->remove(_full_path_key);
00457         }
00458         delete[] set_handle->key;
00459     }
00460 
00461     set_handle->file_handle->close();
00462     delete set_handle->file_handle;
00463     delete set_handle;
00464     _cur_inc_data_size = 0;
00465     _cur_inc_set_handle = NULL;
00466 
00467 exit_point:
00468     if (status != MBED_ERROR_INVALID_ARGUMENT) {
00469         _mutex.unlock();
00470     }
00471 
00472     return status;
00473 }
00474 
00475 int FileSystemStore::iterator_open(iterator_t *it, const char *prefix)
00476 {
00477     int status = MBED_SUCCESS;
00478     Dir *kv_dir = NULL;
00479     key_iterator_handle_t *key_it = NULL;
00480 
00481     if (it == NULL) {
00482         return MBED_ERROR_INVALID_ARGUMENT;
00483     }
00484 
00485     _mutex.lock();
00486     if (false == _is_initialized) {
00487         status = MBED_ERROR_NOT_READY;
00488         goto exit_point;
00489     }
00490     key_it = new key_iterator_handle_t;
00491     key_it->dir_handle = NULL;
00492     key_it->prefix = NULL;
00493     if (prefix != NULL) {
00494         key_it->prefix = string_ndup(prefix, KVStore::MAX_KEY_SIZE);
00495     }
00496 
00497     kv_dir = new Dir;
00498     if (kv_dir->open(_fs, _cfg_fs_path) != 0) {
00499         tr_error("KV Dir: %s, doesnt exist", _cfg_fs_path); //TBD verify ERRNO NOEXIST
00500         delete kv_dir;
00501         if (key_it->prefix != NULL) {
00502             delete[] key_it->prefix;
00503         }
00504         delete key_it;
00505         status = MBED_ERROR_ITEM_NOT_FOUND;
00506         goto exit_point;
00507     }
00508 
00509     key_it->dir_handle = kv_dir;
00510 
00511     *it = (iterator_t)key_it;
00512 
00513 exit_point:
00514     _mutex.unlock();
00515 
00516     return status;
00517 }
00518 
00519 int FileSystemStore::iterator_next(iterator_t it, char *key, size_t key_size)
00520 {
00521     Dir *kv_dir;
00522     struct dirent kv_dir_ent;
00523     int status = MBED_ERROR_ITEM_NOT_FOUND;
00524     key_iterator_handle_t *key_it;
00525     size_t key_name_size = KVStore::MAX_KEY_SIZE;
00526     if (key_size < key_name_size) {
00527         key_name_size = key_size;
00528     }
00529 
00530     _mutex.lock();
00531     if (false == _is_initialized) {
00532         status = MBED_ERROR_NOT_READY;
00533         goto exit_point;
00534     }
00535 
00536     key_it = (key_iterator_handle_t *)it;
00537 
00538     if ((key_it->prefix != NULL) && (key_name_size < strlen(key_it->prefix))) {
00539         status = MBED_ERROR_INVALID_SIZE;
00540         goto exit_point;
00541     }
00542 
00543     kv_dir = (Dir *)key_it->dir_handle;
00544 
00545     while (kv_dir->read(&kv_dir_ent) != 0) {
00546         if (kv_dir_ent.d_type != DT_REG) {
00547             continue;
00548         }
00549 
00550         if ((key_it->prefix == NULL) ||
00551                 (strncmp(kv_dir_ent.d_name, key_it->prefix, strlen(key_it->prefix)) == 0)) {
00552             if (key_name_size < strlen(kv_dir_ent.d_name)) {
00553                 status = MBED_ERROR_INVALID_SIZE;
00554                 break;
00555             }
00556             strncpy(key, kv_dir_ent.d_name, key_name_size);
00557             key[key_name_size - 1] = '\0';
00558             status = MBED_SUCCESS;
00559             break;
00560         }
00561     }
00562 
00563 exit_point:
00564     _mutex.unlock();
00565     return status;
00566 }
00567 
00568 int FileSystemStore::iterator_close(iterator_t it)
00569 {
00570     int status = MBED_SUCCESS;
00571     key_iterator_handle_t *key_it = (key_iterator_handle_t *)it;
00572 
00573     _mutex.lock();
00574     if (key_it == NULL) {
00575         status = MBED_ERROR_INVALID_ARGUMENT;
00576         goto exit_point;
00577     }
00578 
00579     if (key_it->prefix != NULL) {
00580         delete[] key_it->prefix;
00581     }
00582 
00583     if (key_it->dir_handle != NULL) {
00584         ((Dir *)(key_it->dir_handle))->close();
00585         delete ((Dir *)(key_it->dir_handle));
00586     }
00587     delete key_it;
00588 
00589 exit_point:
00590     _mutex.unlock();
00591     return status;
00592 }
00593 
00594 int FileSystemStore::_verify_key_file(const char *key, key_metadata_t *key_metadata, File *kv_file)
00595 {
00596     int status = MBED_SUCCESS;
00597 
00598     if (!is_valid_key(key)) {
00599         status = MBED_ERROR_INVALID_ARGUMENT;
00600         goto exit_point;
00601     }
00602 
00603     _build_full_path_key(key);
00604 
00605     if (0 != kv_file->open(_fs, _full_path_key, O_RDONLY)) {
00606         status = MBED_ERROR_ITEM_NOT_FOUND;
00607         goto exit_point;
00608     }
00609 
00610     //Read Metadata
00611     kv_file->read(key_metadata, sizeof(key_metadata_t));
00612 
00613     if ((key_metadata->magic != FSST_MAGIC) ||
00614             (key_metadata->revision > FSST_REVISION)) {
00615         status = MBED_ERROR_INVALID_DATA_DETECTED;
00616         goto exit_point;
00617     }
00618 
00619 exit_point:
00620     return status;
00621 }
00622 
00623 int FileSystemStore::_build_full_path_key(const char *key_src)
00624 {
00625     strncpy(&_full_path_key[_cfg_fs_path_size + 1/* for path's \ */], key_src, KVStore::MAX_KEY_SIZE);
00626     _full_path_key[(_cfg_fs_path_size + KVStore::MAX_KEY_SIZE)] = '\0';
00627     return 0;
00628 }
00629 
00630 // Local Functions
00631 static char *string_ndup(const char *src, size_t size)
00632 {
00633     char *string_copy = new char[size + 1];
00634     strncpy(string_copy, src, size);
00635     string_copy[size] = '\0';
00636     return string_copy;
00637 }
00638 
00639 
00640