Author: Simon Hughes
This document is the High Level Design for the first version of the Configuration Store (CFSTORE). The configuration store is a secure, associative key-value (KV) store C-Language Hardware Abstraction Layer. CFSTORE's main function is storing and managing (key, value) pairs in persistent storage media. It implements a subset of the requirements as listed in the supported requirements section.
CFSTORE provides the secure and persistent storage for:
i.e. CFSTORE is a general purpose registry for storing code and data objects.
These services are presented to clients with:
The terminology used throughout this document is defined in the following: CFSTORE Terminology for definition of terms used in CFSTORE documents.
The scope of this document is the High Level Design (HLD) of the Configuration Store (CFSTORE) component which includes the following features:
The overview section provides an introduction to this document including an executive summary, an outline of sections and a list of outstanding issues.
The motivation section discusses the rationale for the CFSTORE design including considerations of security, complexity, KV storage, KV ownership, access control, error handling, and KV operations including creating, reading, writing and finding.
The use cases section describes the important CFSTORE client use cases, in particular the FOTA use case.
The software architecture section describes the entities in the CFSTORE software stack including the CFSTORE clients (e.g. FOTA), CFSTORE, the flash-abstraction layer, the CMSIS-Driver Flash driver layer, uvisor and the hardware/software interface.
The CFSTORE API) section describes the application programming interface to the CFSTORE component. This includes sequence diagrams describing the client-CFSTORE API call sequences to use the interface for common operations e.g. CFSTORE initialization/de-initialization, creating a key and finding KVs that match a given search string.
The miscellaneous issues section discusses a number of API design considerations including how to increase/decrease the size of a pre-existing KV value blob, operating with severely limited SRAM, and changing a key-values ACL permissions.
The error handling section discusses procedures for error recovery when a CFSTORE API function returns an error.
The known limitations section describes the limitations of the current design.
There are no outstanding issues with this document.
A persistent store is a building block of many embedded systems, forming the central repository where configuration for many modules can be managed. The store hides the complexity of implementing a similar system from each module and encourages rich configuration interfaces for the developer.
A consideration of the wide variety of data types requiring persistent storage provides the motivation and rationale for a number of CFSTORE features. For example, CFSTORE provides persistent storage for:
i.e. CFSTORE is a general purpose registry for storing code and data objects.
These services are presented to clients in the following way:
For security reasons the uVisor security model allows applications (general purpose code) only restricted access to NV (flash) storage. This ensures modifications to OS or application code are tightly controlled to ensure:
The design concept behind the secure key-value store is one of simplicity to ensure a low attack surface. The design reflects the smallest possible common denominator for storing data and code blocks in a mutually-distrustful operating system.
Complex access restrictions are implemented in additional uvisor boxes as CFSTORE clients i.e. "on top of" the secure CFSTORE key-value storage. An example of a complex access restriction is given as follows:
The following key can be only updated from Monday to Thursday by a remote server matching the following valid public key.
The additional secure uvisor box is used to wrap values with box-specific ACLs and marks these keys as accessible only to the owner box. Restricting access in this way guarantees domain-specific key-access restrictions can be reliably enforced by each security context.
The same is true for supporting custom or complex data types beyond the simple octet blob supported by CFSTORE. For sophisticated configuration storage systems arbitrary types can be supported by wrapping values with a type identifier. This ensures that every programming language can be adequately and safely supported by the key-value storage.
The CFSTORE KV storage has the following characteristics:
The following illustrates valid name examples:
'com.arm.mbed.wifi.accesspoint{5}.essid' = 'AccessNG' 'com.arm.mbed.wifi.accesspoint{home}.essid' = 'HomeSweetHome' 'com.arm.mbed.your-registry-module-name.your-value' = 'XYZ' 'com.arm.mbed.hello-world.animal{dog}{foot}{3}' = 'dirty'
Key ownership is tied to the name of the key.
Consider the following example:
Uvisor box security_prefix_name's are not allowed to overlap.
Access control lists (ACL) enforce access for the following security groups:
The permissions for these two groups are:
The resulting matrix can be represented by a 6-bit binary field:
Note the following:
Whenever a key is read, the CFSTORE is scanned for active keys with suitable access permissions key-by-key. Wild card searches are explicitly supported. The reserved character asterisk ('*') is used to indicate a wild card search to the API. Wild card operations are only supported for finding keys, not for accessing keys. The wild card operator can occur once at any point in the search string.
The following shows examples of valid key_name query strings to the Find() method:
'com.arm.mbed.wifi.accesspoint*.essid' 'com.arm.mbed.your-registry-module-name.*' 'com.arm.mbed.hello-world.animal{dog}{foot}{*}' 'com.arm.mbed.hello-world.animal{dog}{foot}*' 'com.arm.mbed.hello-world.animal{dog*3}'
Note that whenever a search returns a key candidate, the search can be resumed to return further matches.
Keys must be explicitly created using Create() with the following parameters:
Note the following:
The design addresses the requirements of the following use cases:
Network configuration varies by network type. As a result, presenting a common interface for network configuration is difficult. Using a pointer to a configuration blob simplifies the Network API and concentrates complexity and knowledge about the network implementation in the network driver and the configuration mechanism in the application.
Networks need hierarchical configuration. A flat model starts to fail when multiple interfaces with similar parameters are used. Most networks need non-volatile, runtime configuration, but Wi-Fi demonstrates this need the best: configuring a Wi-Fi network on a device requires, at minimum, selecting a SSID and entering a password. These must persist past power cycles.
Network configuration also needs to support overrides. When configuring a network device, it should be possible to recover old configuration until new configuration is committed to non-volatile storage. A network device should ship with sensible default configuration (e.g. DHCP), but this should be overridden when necessary.
Network configuration requires many kinds of value types: integer (Channel number), string (SSID), binary blob (hashed password). There is an argument for floating point (transmit power), but this can be done via integer and fixed-point.
Storing credentials requires secure access to the storage. As a result, it must be possible to apply permissions to parts or the whole of the tree.
It is conceptually possible to reduce the number of code versions by using more configuration. For example, clock configuration can be done using the config mechanism. If this is the case, then the permanent config store must be accessible early in the boot process, when clocks are configured.
It may be necessary to provide a list of modules that explicitly require an init function to be called. To make this possible, those functions could be listed, in order in the configuration store. This has some advantages over conventional approaches, such as linker sections, in that it provides a much more readable way to inspect the modules that are being initialized.
In the future, a resource manager may be an integral part of mbed OS. In this instance, the resource manager needs in-depth information about how peripherals are connected to clocks and power domains. CFSTORE should contain this information.
If the system interface API were aware of the CFSTORE, then it would be straight-forward to encode defaults via config. This would allow the interface API to extract application-dependent, but still sensible defaults from the config store when they are omitted. This could be combined with Resource Management to automatically correct for clock scaling of buses or the core.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Configuration Store Client | | e.g. FOTA | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Configuration Store | | uvisor | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | Flash Abstraction Layer | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | Flash Driver Layer | | | | e.g. CMSIS-Driver | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ SW ----------------------------------------------------------------------- HW +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NV Storage Media e.g. Flash | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Configuration Store Software Architecture
The above figure shows the following entities:
The main API methods for creating, reading and writing CFSTORE KV are as follows:
Note that the above methods show similarities with a file system interface, but CFSTORE is not intended to be a file system e.g. CFSTORE does not implement volume management or directory structures required for a file system.
Additionally, the API supports also includes the following support methods:
ARM_CFSTORE_DRIVER
dispatch methods.The CFSTORE is aligned with the CMSIS-Driver Model pattern as follows:
ARM_CFSTORE_DRIVER
structure with dispatch functions for the API interface methods.This document refers to invocation of the ARM_CFSTORE_DRIVER
dispatch methods using a notional pointer 'drv' to the ARM_CFSTORE_DRIVER
object instance. Thus drv->Initialize() refers to the invocation of the CFSTORE API Initialize() method.
See the CFSTORE low level Design for the detailed specification for function prototypes. See the CMSIS-Driver Documentation for more information.
ARM_CFSTORE_HANDLE
hkey)In common with a file interface, CFSTORE API functions return an opaque file handle for accessing a particular KV. In general terms:
\image html cfstrore_hld_seqdiag_getversion_getcapabilities_sync.png "Call Sequence Diagram for GetVersion(), GetCapabilities()"
The above diagram shows the client-CFSTORE call sequence demonstrating how the client discovers API and CMSIS-Driver versions supported by the API.
ARM_DRIVER_VERSION
structure. GetVersion() is a synchronous function.ARM_CFSTORE_CAPABILITIES
structure which reports whether the CFSTORE implementation is either:
In synchronous mode ARM_CFSTORE_DRIVER::Dispatch_Method_Xxx()
will return:
RETURN_CODE >= ARM_DRIVER_OK
implies CFSTORE Dispatch_Method_Xxx() completed successfully.RETURN_CODE < ARM_DRIVER_OK
implies CFSTORE Dispatch_Method_Xxx() did not complete successfully. The value of the return code supplies further details of the failure cause.In asynchronous mode ARM_CFSTORE_DRIVER::Dispatch_Method_Xxx()
will return:
RETURN_CODE = ARM_DRIVER_OK
(==0) implies CFSTORE Dispatch_Method_Xxx() completion is pending. Dispatch_Method_Xxx completion status will be indicated via an asynchronous call to the ARM_CFSTORE_CALLBACK
registered with ARM_CFSTORE_DRIVER::(*Initialize)()
.RETURN_CODE
> 0 => CFSTORE Dispatch_Method_Xxx() completely synchronously and successfully. The RETURN_CODE
has specific meaning for the Dispatch_Method_Xxx() e.g. for the Read() method the RETURN_CODE
is the number of bytes read.RETURN_CODE
< 0 implies CFSTORE Dispatch_Method_Xxx() completed unsuccessfully. The return code supplies further details of the cause of the failure.The client registered asynchronous callback method ARM_CFSTORE_CALLBACK
is registered by the client using:
ARM_CFSTORE_DRIVER::(*Initialize)(ARM_CFSTORE_CALLBACK callback, void* client_context)
The registered callback has the following prototype:
typedef void (*ARM_CFSTORE_CALLBACK)(int32_t status, ARM_CFSTORE_OPCODE cmd_code, void *client_context, ARM_CFSTORE_HANDLE handle);
Before an asynchronous notification is received, a client can check on the status of the call by calling ARM_CFSTORE_DRIVER::(*GetStatus)()
.
See the CFSTORE low level Design for the detailed specification for function prototypes.
\image html cfstrore_hld_seqdiag_init_uninit_sync.png "Call Sequence Diagram for Initialize()/Uninitialize() (Sync, Success)"
The above diagram shows the client-CFSTORE call sequence for Initializing/Uninitializing the CFSTORE for a synchronous CFSTORE implementation (drv->GetCapabilites() has returned an ARM_CFSTORE_CAPABILITIES
structure with the synchronous flag set).
See the CFSTORE low level Design for the detailed specification for function prototypes.
\image html cfstrore_hld_seqdiag_init_uninit_async.png "Call Sequence Diagram for Initialize()/Uninitialize() (Async, Success)"
The above diagram shows the client-CFSTORE call sequence for Initializing/Uninitializing the CFSTORE for an asynchronous CFSTORE implementation (drv->GetCapabilites() has returned an ARM_CFSTORE_CAPABILITIES
structure with the asynchronous flag set).
Cfstore_Client_callback()
which will be invoked by CFSTORE for asynchronous notification of command completion events.Cfstore_Client_callback()
call.Cfstore_Client_callback(OPCODE=INITIALISE, status, client_context)
to notify the client of the completion status. The previously registered client_context
is supplied as an argument.Cfstore_Client_callback(OPCODE=UNINITIALISE, status, client_context)
to notify the client of the completion status. The previously registered client_context
is supplied as an argument. CFSTORE will not invoke the callback method again.Note this example is the pathological case where all CFSTORE methods return OK i.e. the transactions are pending. In reality, some calls will be completed synchronous and successfully, indicated by returning a value > 0.
See the CFSTORE low level Design for the detailed specification for function prototypes.
\image html cfstrore_hld_seqdiag_create_sync.png "Call Sequence Diagram for Create() Key (Sync, Success)"
The above diagram shows the client-CFSTORE call sequence for creating a KV for a synchronous CFSTORE implementation (drv->GetCapabilites() has returned an ARM_CFSTORE_CAPABILITIES
structure with the synchronous flag set).
See the CFSTORE low level Design for the detailed specification for function prototypes.
\image html cfstrore_hld_seqdiag_create_async.png "Call Sequence Diagram for Create() Key (Async, Success)"
The above diagram shows the client-CFSTORE call sequence for creating a KV for an asynchronous CFSTORE implementation (drv->GetCapabilites() has returned an ARM_CFSTORE_CAPABILITIES
structure with the asynchronous flag set).
Cfstore_Client_callback(OPCODE=CREATE, status, client_context, key_handle)
to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, key_handle is an opaque handle to the newly created open key.Cfstore_Client_callback(OPCODE=WRITE, status, client_context, key_handle)
to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status > 0, the value of status indicates the number of bytes successfully written.Cfstore_Client_callback(OPCODE=CLOSE, status, client_context, key_handle)
to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, the key has successfully been closed and key_handle is NULL. The previously used key_handle should no longer be used.Cfstore_Client_callback(OPCODE=FLUSH, status, client_context, NULL)
to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, the flush operation was completed successfully.Note this example is the pathological case where all CFSTORE methods return OK i.e. the transactions are pending. In reality, some calls will be completed synchronous and successfully, indicated by returning a value > 0.
See the CFSTORE low level Design for the detailed specification for function prototypes.
\image html cfstrore_hld_seqdiag_open_read_sync.png "Call Sequence Diagram for Open()/Read() Key (Sync, Success)"
The above diagram shows the client-CFSTORE call sequence for opening and reading a pre-existing key in the CFSTORE for a synchronous CFSTORE implementation (drv->GetCapabilites() has returned an ARM_CFSTORE_CAPABILITIES
structure with the synchronous flag set).
See the CFSTORE low level Design for the detailed specification for function prototypes.
\image html cfstrore_hld_seqdiag_open_read_async.png "Call Sequence Diagram for Open()/Read() Key (Async, Success)"
The above diagram shows the client-CFSTORE call sequence for opening and reading a pre-existing key in the CFSTORE for an asynchronous CFSTORE implementation (drv->GetCapabilites() has returned an ARM_CFSTORE_CAPABILITIES
structure with the asynchronous flag set).
Cfstore_Client_callback(OPCODE=OPEN, status, client_context, key_handle)
to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, key_handle is a valid opaque handle to the newly opened KV.Cfstore_Client_callback(OPCODE=RSEEK, status, client_context, key_handle)
to notify the client of the completion status. The previously registered client_context is supplied as an argument.Cfstore_Client_callback(OPCODE=READ, status, client_context, key_handle)
to notify the client of the completion status. The previously registered client_context is supplied as an argument.Cfstore_Client_callback(OPCODE=CLOSE, status, client_context, key_handle)
to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, the close operation completed successfully, key_handle is null and the previously stored key_handle value is no longer valid.Note this example is the pathological case where all CFSTORE methods return OK i.e. the transactions are pending. In reality, some calls will be completed synchronous and successfully, indicated by returning a value > 0.
See the CFSTORE low level Design for the detailed specification for function prototypes.
\image html cfstrore_hld_seqdiag_open_write_sync.png "Call Sequence Diagram for Open()/Write() Key (Sync, Success)"
The above diagram shows the client-CFSTORE call sequence for opening and writing a pre-existing key in the CFSTORE for a synchronous CFSTORE implementation (drv->GetCapabilites() has returned an ARM_CFSTORE_CAPABILITIES
structure with the synchronous flag set).
See the CFSTORE low level Design for the detailed specification for function prototypes.
\image html cfstrore_hld_seqdiag_open_write_async.png "Call Sequence Diagram for Open()/Write() Key (Async, Success)"
The above diagram shows the client-CFSTORE call sequence for opening and writing a pre-existing key in the CFSTORE for an asynchronous CFSTORE implementation (drv->GetCapabilites() has returned an ARM_CFSTORE_CAPABILITIES
structure with the asynchronous flag set).
Cfstore_Client_callback(OPCODE=OPEN, status, client_context, key_handle)
to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, key_handle is a valid handle to the open KV.Cfstore_Client_callback(OPCODE=WRITE, status, client_context, key_handle)
to notify the client of the completion status. The previously registered client_context is supplied as an argument.Cfstore_Client_callback(OPCODE=CLOSE, status, client_context, key_handle)
to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, the close operation was successfully, key_handle is NULL and if the key_handle value was previously stored it is no longer valid.Cfstore_Client_callback(OPCODE=FLUSH, status, client_context, NULL)
to notify the client of the completion status. The previously registered client_context is supplied as an argument.Note this example is the pathological case where all CFSTORE methods return OK i.e. the transactions are pending. In reality, some calls will be completed synchronous and successfully, indicated by returning a value > 0.
See the CFSTORE low level Design for the detailed specification for function prototypes.
\image html cfstore_hld_seqdiag_find_full_part_sync.png "Call Sequence Diagram for Find() Key (Sync, Full and Part Walk Success)"
The above diagram shows the client-CFSTORE call sequence for finding pre-existing keys in the CFSTORE for a synchronous CFSTORE implementation. (drv->GetCapabilites() has returned an ARM_CFSTORE_CAPABILITIES
structure with the synchronous flag set). The example shows a complete walk of all the find results.
For the top alternative "Use Case for Full Walk of All Matching Results":
For the bottom alternative "Use Case for Partial Walk of All Matching Results":
See the CFSTORE low level Design for the detailed specification for function prototypes.
\image html cfstore_hld_seqdiag_find_full_part_async.png "Call Sequence Diagram for Find() Key (Async, Full and Part Walk Success)"
The above diagram shows the client-CFSTORE call sequence for finding pre-existing keys in the CFSTORE for an asynchronous CFSTORE implementation. (drv->GetCapabilites() has returned an ARM_CFSTORE_CAPABILITIES
structure with the synchronous flag set). The example shows a complete walk of all the find results.
Cfstore_Client_callback(OPCODE=FIND, status, client_context, next)
to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, next is an open key handle to a KV matching the key_name_search_string.Cfstore_Client_callback(OPCODE=FIND, status, client_context, next)
to notify the client of the completion status. The previously registered client_context is supplied as an argument.For the top alternative "Use Case for Full Walk of All Matching Results":
For the bottom alternative "Use Case for Partial Walk of All Matching Results":
Cfstore_Client_callback(OPCODE=CLOSE, status, client_context, key_handle)
to notify the client of the completion status. The previously registered client_context is supplied as an argument.Note this example is the pathological case where all CFSTORE methods return OK i.e. the transactions are pending. In reality, some calls will be completed synchronous and successfully, indicated by returning a value > 0.
See the CFSTORE low level Design for the detailed specification for function prototypes.
\image html cfstrore_hld_seqdiag_power_control_sync.png "API Call Sequence Diagram for PowerControl() (Sync, Success)"
The above diagram shows the client-CFSTORE call sequence for setting the CFSTORE PowerControl() setting. The call is synchronous.
The following provides general notes on the handling of errors:
The CFSTORE has 2 modes of operations:
The mode is determined by inspecting the results of the GetCapabilites() API call.
All CFSTORE API calls (apart from specific exclusions listed below) return an int32_t return code designated RETURN_CODE
.
RETURN_CODE
< 0 always indicates an error.RETURN_CODE
>= 0 always indicates success.
RETURN_CODE_ASYNC
is supplied to the client registered callback handler (if such a callback handler has been registered).RETURN_CODE
or RETURN_CODE_ASYNC
when >=0. For example RETURN_CODE
or RETURN_CODE_ASYNC
for a successful Read() call may be interpretted as the number of octets read. Consult the documentation for specific API calls for further details.In ASYNC mode:
RETURN_CODE_ASYNC
.RETURN_CODE
. For example, if RETURN_CODE
=100 for a successful Read() call with a supplied buffer length of 100 bytes, then the client infers the call completed synchronously.CFSTORE API calls that do not return int32_t return values (i.e. exclusions to the foregoing) are as follows:
CFSTORE clients must check all RETURN_CODE
values for errors and act accordingly if an error is detected.
Some API calls may return values < 0 as a part of the their normal operations. For example, when iterating over a list of Find() results matching a wildcard, Find() may return RETURN_CODE
< 0 to indicate no more matches are found
If a RETURN_CODE
error indicates a system failure then the CFSTORE client should implement the following recovery procedure:
RETURN_CODE_UNINIT
. If RETURN_CODE_UNINIT
< 0, abort any further action. All client maintained state variables (e.g. hkeys) are then invalid.RETURN_CODE_REINIT
. If RETURN_CODE_REINIT
< 0, abort any further action.Note the following:
This design intends that all available NV storage made available to CFSTORE be readable/writable by CFSTORE clients. However, in the case that available NV storage (holding CFSTORE data) is much larger than available SRAM then it is not possible for all KV pairs to be resident in SRAM simultaneously. A developer may elect to use only a restricted portion of a large backing store for a simplified implmentation.
In order to increase the size of the pre-existing KV={key_name, value1, len1} to {key_name, value2, len2} where len2 != len1 then the client should call Create() on a pre-existing key, supplying NULL for the key descriptor argument and the new length for the value_len argument.
The procedure can be realised in the following way (in the synchronous case) to double the size of a pre-existing KV value blob to hold a duplicate of the data:
ARM_CFSTORE_HANDLE hkey ARM_CFSTORE_FMODE flags = 0; const char *key_name = "mykeyname"; uint32_t len = 0; uint32_t value_len = 0; void* data = NULL; ARM_CFSTORE_KEYDESC kdesc; // Open a pre-existing KV to find the length and read the value data drv->Open(key_name, flags, &hkey); // store key_name and value drv->GetValueLen(hkey, &value_len); data = malloc(value_len); len = value_len; drv->Read(hkey, data, &len) // Read() returns bytes read in len. Assume that Read() call has read all the data drv->Close(hkey) // Call Create() with kdesc=NULL to grow the value length to increase // the blob size. drv->Create(key_name, 2 * value_len, NULL, &hkey); // store the data. This first Write() writes bytes 0 to value_len-1 in the // value blob (sequential-access). len = value_len; drv->Write(hkey, data, &len) // Write() returns bytes written in len. Assume write has written value_len bytes // write supports sequential access. The second Write() writes bytes // value_len to 2*value_len -1 in the value blob. len = value_len; drv->Write(hkey, data, &len) // Write() returns bytes written in len. Assume write has written value_len bytes drv->Close(hkey) // de-init free(data);
Consider the case where a client needs to write a value blob whose size exceeds the available SRAM. When writing a data buffer of size N bytes, CFSTORE may require N bytes of SRAM plus some additional overhead for setting up the storage transaction. In the case that N exceeds the available SRAM remaining, the client can split the writing of the value into M writes, where N/M is smaller than available memory.
In the case the the Write() call fails with return code ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY
then the client may retry the write transaction with a smaller length (less bytes).
Further, a client of the Write() function should always check the returned value of the len argment as this records the actual number of bytes written. CFSTORE may be able to write a number of bytes less that the total number in the data buffer supplied by the client. The unwritten bytes should be supplied to a second call to Write().
In order to change the Access Control List permissions of the pre-existing KV={key_name, value1, len1, kdesc1} to {key_name, value1, len1, kdesc2} where kdesc1 != kdesc2 then the client should use the following procedure:
The procedure can be realised as illustrated in the following example code:
/* brief function to changes the permissions on KV pair * * param key_name * pre-existing KV key name for which the permissions will be * changed. * param kdesc * key descriptor describing the new properties including * permissions. */ void myclient_change_kv_perms(const char *key_name, ARM_CFSTORE_KEYDESC kdesc) { ARM_CFSTORE_HANDLE hkey ARM_CFSTORE_FMODE flags = 0; uint32_t value_len = 0; void* data = NULL; // Get KV data from store drv->Open(key_name, flags, &hkey); // store key_name and value drv->GetValueLen(hkey, &value_len); data = malloc(value_len); drv->Read(hkey, data, value_len) drv->Delete(hkey) drv->Close(hkey) // Re-create new KV with same name and new permissions drv->Create(key_name, value_len, &kdesc, &hkey); // store key_name and value drv->Write(hkey, data, value_len) drv->Close(hkey) // de-init free(data); }
The Create() method uses a key descriptor to request storage properties for the KV pair. For example, the descriptor may include the following attributes:
Associative store APIs often include a GetKeyDesc() convenience method permitting clients to retrieve the KV descriptor after creation. This is not included in the CFSTORE API for the following reasons:
Associative store APIs often include a Cancel() method for terminating in-flight asynchronous transactions. This is not supported in the current API to simplify the implementation. All asynchronous transactions will have associated guard timers to guarantee the termination of errored transactions.
This document was made possible through the contributions of the following people: