diff --git a/manticore/core/workspace.py b/manticore/core/workspace.py index 8b17d00..93196ec 100644 --- a/manticore/core/workspace.py +++ b/manticore/core/workspace.py @@ -53,12 +53,35 @@ class PickleSerializer(StateSerializer): class Store(object): """ - A Store can save arbitrary keys/values (including states) and file streams. Used for generating - output, and state saving and loading. + A `Store` can save arbitrary keys/values (including states) and file streams. + Used for generating output, state saving and state loading. - Implement either save_value/load_value in subclasses, or save_stream/load_stream, or both. + In subclasses: + + * Implement either save_value/load_value, or save_stream/load_stream, or both. + * Define a `store_type` class variable of type str. + * This is used as a prefix for a store descriptor """ + @classmethod + def fromdescriptor(cls, desc): + """ + Create a :class:`~manticore.core.workspace.Store` instance depending on the descriptor. + + Valid descriptors: + * fs: + * redis:: + * mem: + + :param str desc: Store descriptor + :return: Store instance + """ + type_, uri = ('fs', None) if desc is None else desc.split(':', 1) + for subclass in cls.__subclasses__(): + if subclass.store_type == type_: + return subclass(uri) + raise NotImplementedError("Storage type '{0}' not supported.".format(type_)) + def __init__(self, uri, state_serialization_method='pickle'): assert self.__class__ != Store, "The Store class can not be instantiated (create a subclass)" @@ -162,6 +185,8 @@ class FilesystemStore(Store): """ A directory-backed Manticore workspace """ + store_type = 'fs' + def __init__(self, uri=None): """ :param uri: The path to on-disk workspace, or None. @@ -225,11 +250,12 @@ class MemoryStore(Store): NOTE: This is mostly used for experimentation and testing funcionality. Can not be used with multiple workers! """ + store_type = 'mem' #TODO(yan): Once we get a global config store, check it to make sure # we're executing in a single-worker or test environment. - def __init__(self): + def __init__(self, uri=None): self._data = {} super(MemoryStore, self).__init__(None) @@ -249,6 +275,8 @@ class RedisStore(Store): """ A redis-backed Manticore workspace """ + store_type = 'redis' + def __init__(self, uri=None): """ :param uri: A url for redis @@ -288,30 +316,6 @@ class RedisStore(Store): return self._client.keys(glob_str) -def _create_store(desc): - """ - Create a :class:`~manticore.core.workspace.Store` instance depending on the descriptor. - - Valid descriptors: - fs: - redis:: - mem: - - :param str desc: Store descriptor - :return: Store instance - """ - type_, uri = ('fs', None) if desc is None else desc.split(':', 1) - - if type_ == 'fs': - return FilesystemStore(uri) - elif type_ == 'redis': - return RedisStore(uri) - elif type_ == 'mem': - return MemoryStore() - else: - raise NotImplementedError("Storage type '{0}' not supported.".format(type_)) - - # This is copied from Executor to not create a dependency on the naming of the lock field def sync(f): """ Synchronization decorator. """ @@ -330,7 +334,7 @@ class Workspace(object): """ def __init__(self, lock, desc=None): - self._store = _create_store(desc) + self._store = Store.fromdescriptor(desc) self._serializer = PickleSerializer() self._last_id = manager.Value('i', 0) self._lock = lock @@ -401,7 +405,8 @@ class ManticoreOutput(object): :param desc: A descriptor ('type:uri') of where to write output. """ self._named_key_prefix = 'test' - self._store = _create_store(desc) + self._descriptor = desc + self._store = Store.fromdescriptor(desc) self._last_id = 0 self._id_gen = manager.Value('i', self._last_id) self._lock = manager.Condition(manager.RLock()) @@ -410,6 +415,21 @@ class ManticoreOutput(object): def uri(self): return self._store.uri + @property + def descriptor(self): + """ + Return a descriptor that created this workspace. Descriptors are of the + format :, where type signifies the medium. For example, + fs:/tmp/workspace + redis:127.0.0.1:6379 + + :rtype: str + """ + if self._descriptor is None: + self._descriptor = '{}:{}'.format(self._store.store_type, self._store.uri) + + return self._descriptor + @sync def _increment_id(self): self._last_id = self._id_gen.value diff --git a/manticore/manticore.py b/manticore/manticore.py index 696cf47..c1ab964 100644 --- a/manticore/manticore.py +++ b/manticore/manticore.py @@ -708,7 +708,7 @@ class Manticore(object): self._output = ManticoreOutput(ws_path) self._executor = Executor(initial_state, - workspace=ws_path, + workspace=self._output.descriptor, policy=self._policy, context=self.context)