diff --git a/src/_zkapauthorizer/_storage_client.py b/src/_zkapauthorizer/_storage_client.py index eafc1196f95d5a826cc846f079c452b9c91bfd43..8a9acdf15b9dedec2db6226c2932c5f80275f484 100644 --- a/src/_zkapauthorizer/_storage_client.py +++ b/src/_zkapauthorizer/_storage_client.py @@ -25,7 +25,10 @@ import attr from zope.interface import ( implementer, ) - +from twisted.internet.defer import ( + inlineCallbacks, + returnValue, +) from allmydata.interfaces import ( IStorageServer, ) @@ -38,6 +41,7 @@ from .storage_common import ( renew_lease_message, slot_testv_and_readv_and_writev_message, has_writes, + get_implied_data_length, ) @implementer(IStorageServer) @@ -164,6 +168,7 @@ class ZKAPAuthorizerStorageClient(object): reason, ) + @inlineCallbacks def slot_testv_and_readv_and_writev( self, storage_index, @@ -172,17 +177,41 @@ class ZKAPAuthorizerStorageClient(object): r_vector, ): if has_writes(tw_vectors): - passes = self._get_encoded_passes(slot_testv_and_readv_and_writev_message(storage_index), 1) + current_size = yield self._rref.callRemote( + "slot_share_sizes", + storage_index, + set(tw_vectors), + ) + if current_size is None: + current_pass_count = 0 + else: + current_pass_count = required_passes(BYTES_PER_PASS, {0}, current_size) + new_size = sum( + ( + get_implied_data_length(data_vector, length) + for (_, data_vector, length) + in tw_vectors.values() + ), + 0, + ) + new_pass_count = required_passes(BYTES_PER_PASS, {0}, new_size) + pass_count_increase = new_pass_count - current_pass_count + passes = self._get_encoded_passes( + slot_testv_and_readv_and_writev_message(storage_index), + pass_count_increase, + ) else: passes = [] - return self._rref.callRemote( - "slot_testv_and_readv_and_writev", - passes, - storage_index, - secrets, - tw_vectors, - r_vector, - ) + returnValue(( + yield self._rref.callRemote( + "slot_testv_and_readv_and_writev", + passes, + storage_index, + secrets, + tw_vectors, + r_vector, + ) + )) def slot_readv( self, diff --git a/src/_zkapauthorizer/_storage_server.py b/src/_zkapauthorizer/_storage_server.py index 061d6136125bc20877a4624e97d806e58440e86c..d57fb9c2d9cadb862d68dfa9b07de72416cc8aab 100644 --- a/src/_zkapauthorizer/_storage_server.py +++ b/src/_zkapauthorizer/_storage_server.py @@ -24,6 +24,10 @@ from __future__ import ( absolute_import, ) +from errno import ( + ENOENT, +) + from functools import ( partial, ) @@ -234,6 +238,14 @@ class ZKAPAuthorizerStorageServer(Referenceable): """ return self._original.remote_advise_corrupt_share(*a, **kw) + def remote_slot_share_sizes(self, storage_index, sharenums): + try: + return get_slot_share_size(self._original, storage_index, sharenums) + except OSError as e: + if e.errno == ENOENT: + return None + raise + def remote_slot_testv_and_readv_and_writev( self, passes, diff --git a/src/_zkapauthorizer/foolscap.py b/src/_zkapauthorizer/foolscap.py index 948aff38eb681754af6e24e57d5252ce34254196..088dbccd0f825265cf94c2788561562a7d11e49e 100644 --- a/src/_zkapauthorizer/foolscap.py +++ b/src/_zkapauthorizer/foolscap.py @@ -6,7 +6,9 @@ from foolscap.constraint import ( ByteStringConstraint, ) from foolscap.api import ( + SetOf, ListOf, + ChoiceOf, ) from foolscap.remoteinterface import ( RemoteMethodSchema, @@ -14,7 +16,10 @@ from foolscap.remoteinterface import ( ) from allmydata.interfaces import ( + MAX_BUCKETS, + StorageIndex, RIStorageServer, + Offset, ) # The Foolscap convention seems to be to try to constrain inputs to valid @@ -110,6 +115,16 @@ class RIPrivacyPassAuthorizedStorageServer(RemoteInterface): get_buckets = RIStorageServer["get_buckets"] + def slot_share_sizes( + storage_index=StorageIndex, + sharenums=SetOf(int, maxLength=MAX_BUCKETS), + ): + """ + Get the size of the given shares in the given storage index. If there are + no shares, ``None``. + """ + return ChoiceOf(None, Offset) + slot_readv = RIStorageServer["slot_readv"] slot_testv_and_readv_and_writev = add_passes(