2007-03-30 17:52:19 +00:00
|
|
|
import os, re
|
2006-12-01 03:14:23 +00:00
|
|
|
|
|
|
|
from foolscap import Referenceable
|
|
|
|
from twisted.application import service
|
|
|
|
|
2006-12-02 02:17:50 +00:00
|
|
|
from zope.interface import implements
|
2007-03-30 03:19:52 +00:00
|
|
|
from allmydata.interfaces import RIStorageServer, RIBucketWriter
|
|
|
|
from allmydata import interfaces
|
|
|
|
from allmydata.util import bencode, fileutil, idlib
|
|
|
|
from allmydata.util.assertutil import _assert, precondition
|
2006-12-01 03:14:23 +00:00
|
|
|
|
2007-03-30 03:19:52 +00:00
|
|
|
# store/
|
2007-03-30 17:52:19 +00:00
|
|
|
# store/incoming # temp dirs named $VERIFIERID/$SHARENUM that will be moved to store/ on success
|
2007-03-30 03:19:52 +00:00
|
|
|
# store/$VERIFIERID
|
|
|
|
# store/$VERIFIERID/$SHARENUM
|
|
|
|
# store/$VERIFIERID/$SHARENUM/blocksize
|
|
|
|
# store/$VERIFIERID/$SHARENUM/data
|
|
|
|
# store/$VERIFIERID/$SHARENUM/blockhashes
|
|
|
|
# store/$VERIFIERID/$SHARENUM/sharehashtree
|
2006-12-01 03:14:23 +00:00
|
|
|
|
2007-03-30 03:19:52 +00:00
|
|
|
# $SHARENUM matches this regex:
|
2007-03-31 00:07:04 +00:00
|
|
|
NUM_RE=re.compile("[0-9]*")
|
2007-03-30 03:19:52 +00:00
|
|
|
|
|
|
|
class BucketWriter(Referenceable):
|
|
|
|
implements(RIBucketWriter)
|
|
|
|
|
2007-03-30 17:52:19 +00:00
|
|
|
def __init__(self, incominghome, finalhome, blocksize):
|
|
|
|
self.incominghome = incominghome
|
2007-03-30 03:19:52 +00:00
|
|
|
self.finalhome = finalhome
|
|
|
|
self.blocksize = blocksize
|
|
|
|
self.closed = False
|
2007-03-30 23:50:50 +00:00
|
|
|
fileutil.make_dirs(incominghome)
|
|
|
|
fileutil.make_dirs(finalhome)
|
2007-03-30 03:19:52 +00:00
|
|
|
self._write_file('blocksize', str(blocksize))
|
|
|
|
|
|
|
|
def _write_file(self, fname, data):
|
2007-03-30 17:52:19 +00:00
|
|
|
open(os.path.join(self.incominghome, fname), 'wb').write(data)
|
2007-03-30 03:19:52 +00:00
|
|
|
|
|
|
|
def remote_put_block(self, segmentnum, data):
|
|
|
|
precondition(not self.closed)
|
|
|
|
assert len(data) == self.blocksize
|
2007-03-30 17:52:19 +00:00
|
|
|
f = open(os.path.join(self.incominghome, 'data'), 'wb')
|
2007-03-30 03:19:52 +00:00
|
|
|
f.seek(self.blocksize*segmentnum)
|
|
|
|
f.write(data)
|
|
|
|
|
|
|
|
def remote_put_block_hashes(self, blockhashes):
|
|
|
|
precondition(not self.closed)
|
|
|
|
# TODO: verify the length of blockhashes.
|
|
|
|
# TODO: tighten foolscap schema to require exactly 32 bytes.
|
|
|
|
self._write_file('blockhashes', ''.join(blockhashes))
|
|
|
|
|
|
|
|
def remote_put_share_hashes(self, sharehashes):
|
|
|
|
precondition(not self.closed)
|
|
|
|
self._write_file('sharehashree', bencode.bencode(sharehashes))
|
|
|
|
|
2007-03-30 23:50:50 +00:00
|
|
|
def remote_close(self):
|
2007-03-30 03:19:52 +00:00
|
|
|
precondition(not self.closed)
|
|
|
|
# TODO assert or check the completeness and consistency of the data that has been written
|
2007-03-30 17:52:19 +00:00
|
|
|
fileutil.rename(self.incominghome, self.finalhome)
|
2007-03-30 03:19:52 +00:00
|
|
|
self.closed = True
|
|
|
|
|
|
|
|
def str2l(s):
|
|
|
|
""" split string (pulled from storage) into a list of blockids """
|
|
|
|
return [ s[i:i+interfaces.HASH_SIZE] for i in range(0, len(s), interfaces.HASH_SIZE) ]
|
|
|
|
|
|
|
|
class BucketReader(Referenceable):
|
|
|
|
def __init__(self, home):
|
|
|
|
self.home = home
|
|
|
|
self.blocksize = int(self._read_file('blocksize'))
|
|
|
|
|
|
|
|
def _read_file(self, fname):
|
|
|
|
return open(os.path.join(self.home, fname), 'rb').read()
|
|
|
|
|
|
|
|
def remote_get_block(self, blocknum):
|
|
|
|
f = open(os.path.join(self.home, 'data'), 'rb')
|
|
|
|
f.seek(self.blocksize * blocknum)
|
2007-03-30 23:50:50 +00:00
|
|
|
return f.read(self.blocksize) # this might be short for the last block
|
2007-03-30 03:19:52 +00:00
|
|
|
|
|
|
|
def remote_get_block_hashes(self):
|
|
|
|
return str2l(self._read_file('blockhashes'))
|
|
|
|
|
|
|
|
def remote_get_share_hashes(self):
|
|
|
|
return bencode.bdecode(self._read_file('sharehashes'))
|
|
|
|
|
2006-12-01 03:14:23 +00:00
|
|
|
class StorageServer(service.MultiService, Referenceable):
|
2006-12-02 02:17:50 +00:00
|
|
|
implements(RIStorageServer)
|
2006-12-01 03:14:23 +00:00
|
|
|
name = 'storageserver'
|
|
|
|
|
2007-03-30 03:19:52 +00:00
|
|
|
def __init__(self, storedir):
|
|
|
|
fileutil.make_dirs(storedir)
|
|
|
|
self.storedir = storedir
|
2007-03-30 17:52:19 +00:00
|
|
|
self.incomingdir = os.path.join(storedir, 'incoming')
|
|
|
|
self._clean_incomplete()
|
|
|
|
fileutil.make_dirs(self.incomingdir)
|
2007-03-30 03:19:52 +00:00
|
|
|
|
2006-12-01 03:14:23 +00:00
|
|
|
service.MultiService.__init__(self)
|
|
|
|
|
2007-03-30 17:52:19 +00:00
|
|
|
def _clean_incomplete(self):
|
|
|
|
fileutil.rm_dir(self.incomingdir)
|
2007-03-30 03:19:52 +00:00
|
|
|
|
|
|
|
def remote_allocate_buckets(self, verifierid, sharenums, sharesize,
|
|
|
|
blocksize, canary):
|
2007-03-30 17:52:19 +00:00
|
|
|
alreadygot = set()
|
|
|
|
bucketwriters = {} # k: shnum, v: BucketWriter
|
|
|
|
for shnum in sharenums:
|
2007-03-30 23:50:50 +00:00
|
|
|
incominghome = os.path.join(self.incomingdir,
|
|
|
|
idlib.b2a(verifierid) + "%d"%shnum)
|
|
|
|
finalhome = os.path.join(self.storedir, idlib.b2a(verifierid), "%d"%shnum)
|
2007-03-30 17:52:19 +00:00
|
|
|
if os.path.exists(incominghome) or os.path.exists(finalhome):
|
|
|
|
alreadygot.add(shnum)
|
|
|
|
else:
|
|
|
|
bucketwriters[shnum] = BucketWriter(incominghome, finalhome, blocksize)
|
2007-03-30 03:19:52 +00:00
|
|
|
|
2007-03-30 17:52:19 +00:00
|
|
|
return alreadygot, bucketwriters
|
2006-12-01 03:14:23 +00:00
|
|
|
|
2006-12-03 10:01:43 +00:00
|
|
|
def remote_get_buckets(self, verifierid):
|
2007-03-30 03:19:52 +00:00
|
|
|
bucketreaders = {} # k: sharenum, v: BucketReader
|
|
|
|
verifierdir = os.path.join(self.storedir, idlib.b2a(verifierid))
|
|
|
|
for f in os.listdir(verifierdir):
|
2007-03-31 00:07:04 +00:00
|
|
|
_assert(NUM_RE.match(f), f)
|
2007-03-30 03:19:52 +00:00
|
|
|
bucketreaders[int(f)] = BucketReader(os.path.join(verifierdir, f))
|
|
|
|
return bucketreaders
|