3 from django.core.management import setup_environ
4 from pithos import settings
5 setup_environ(settings)
7 from functools import partial
8 from getpass import getuser
9 from errno import EACCES, EBADF, EINVAL, EISDIR, ENOENT, ENOTDIR, ENOTEMPTY
10 from stat import S_IFDIR, S_IFREG
14 from pithos.api.util import hashmap_hash
15 from pithos.backends import backend
16 from pithos.lib.fuse import FUSE, FuseOSError, Operations, LoggingMixIn
22 class BackendProxy(object):
23 """A proxy object that always passes user and account as first args."""
25 def __init__(self, backend, user, account):
26 self.backend = backend
28 self.account = account
30 def __getattr__(self, name):
31 func = getattr(self.backend, name)
32 return partial(func, self.user, self.account)
35 def blocksplit(data, blocksize):
36 """An iterator that splits data into blocks of size `blocksize`."""
39 yield data[:blocksize]
40 data = data[blocksize:]
43 class BackendFS(LoggingMixIn, Operations):
44 def __init__(self, account):
46 self.account = account
47 self.backend = BackendProxy(backend, self.user, self.account)
49 def create(self, path, mode, fi=None):
50 container, sep, object = path[1:].partition('/')
52 raise FuseOSError(EACCES)
55 meta = {'hash': hashmap_hash(hashmap)}
56 self.backend.update_object_hashmap(container, object, 0, hashmap,
60 def getattr(self, path, fh=None):
61 container, sep, object = path[1:].partition('/')
64 containers = self.backend.list_containers()
66 'st_mode': (S_IFDIR | 0755),
70 'st_nlink': 2 + len(containers)}
74 meta = self.backend.get_container_meta(container)
76 raise FuseOSError(ENOENT)
79 'st_mode': (S_IFDIR | 0755),
81 'st_mtime': meta['modified'],
82 'st_atime': meta['modified'],
83 'st_nlink': 2 + meta['count']}
87 meta = self.backend.get_object_meta(container, object)
89 raise FuseOSError(ENOENT)
92 'st_mode': (S_IFREG | 0644),
94 'st_mtime': meta['modified'],
95 'st_atime': meta['modified'],
97 'st_size': meta['bytes']}
99 def mkdir(self, path, mode):
100 container, sep, object = path[1:].partition('/')
102 raise FuseOSError(EACCES)
103 backend.put_container(self.user, self.account, container)
105 def read(self, path, nbyte, offset, fh):
106 container, sep, object = path[1:].partition('/')
108 raise FuseOSError(EBADF)
110 # XXX This implementation is inefficient,
111 # it always reads all the blocks
112 size, hashmap = self.backend.get_object_hashmap(container, object)
115 buf.append(backend.get_block(hash))
116 data = ''.join(buf)[:size]
117 return data[offset:offset + nbyte]
119 def readdir(self, path, fh):
120 container, sep, object = path[1:].partition('/')
123 containers = [c[0] for c in self.backend.list_containers()]
124 return ['.', '..'] + containers
127 objects = [o[0] for o in self.backend.list_objects(container)]
128 return ['.', '..'] + objects
130 def rmdir(self, path):
131 container, sep, object = path[1:].partition('/')
133 raise FuseOSError(ENOTDIR)
136 self.backend.delete_container(container)
138 raise FuseOSError(ENOENT)
140 raise FuseOSError(ENOTEMPTY)
142 def truncate(self, path, length, fh=None):
143 container, sep, object = path[1:].partition('/')
145 raise FuseOSError(EISDIR)
147 size, hashmap = self.backend.get_object_hashmap(container, object)
149 raise FuseOSError(EINVAL) # Extension not supported
151 div, mod = divmod(size, backend.block_size)
152 nblocks = div + 1 if mod else div
153 meta = {'hash': hashmap_hash(hashmap)}
154 self.backend.update_object_hashmap(container, object, size,
155 hashmap[:nblocks], meta, True)
157 def unlink(self, path):
158 container, sep, object = path[1:].partition('/')
160 raise FuseOSError(EACCES)
161 self.backend.delete_object(container, object)
163 def write(self, path, data, offset, fh):
164 container, sep, object = path[1:].partition('/')
166 raise FuseOSError(EBADF)
169 for block in blocksplit(data, backend.block_size):
170 hashmap.append(backend.put_block(block))
171 meta = {'hash': hashmap_hash(hashmap)}
172 self.backend.update_object_hashmap(container, object, len(data),
177 if __name__ == "__main__":
179 print 'usage: %s <mountpoint>' % argv[0]
182 fuse = FUSE(BackendFS(account), argv[1], foreground=True)