root / tools / backendfs.py @ e3fd7f91
History | View | Annotate | Download (6 kB)
1 | 49350be7 | Giorgos Verigakis | #!/usr/bin/env python
|
---|---|---|---|
2 | 49350be7 | Giorgos Verigakis | |
3 | 49350be7 | Giorgos Verigakis | from django.core.management import setup_environ |
4 | 49350be7 | Giorgos Verigakis | from pithos import settings |
5 | 49350be7 | Giorgos Verigakis | setup_environ(settings) |
6 | 49350be7 | Giorgos Verigakis | |
7 | 49350be7 | Giorgos Verigakis | from functools import partial |
8 | 49350be7 | Giorgos Verigakis | from getpass import getuser |
9 | 49350be7 | Giorgos Verigakis | from errno import EACCES, EBADF, EINVAL, EISDIR, ENOENT, ENOTDIR, ENOTEMPTY |
10 | 49350be7 | Giorgos Verigakis | from stat import S_IFDIR, S_IFREG |
11 | 49350be7 | Giorgos Verigakis | from sys import argv |
12 | 49350be7 | Giorgos Verigakis | from time import time |
13 | 49350be7 | Giorgos Verigakis | |
14 | 49350be7 | Giorgos Verigakis | from pithos.api.util import hashmap_hash |
15 | 49350be7 | Giorgos Verigakis | from pithos.backends import backend |
16 | 49350be7 | Giorgos Verigakis | from pithos.lib.fuse import FUSE, FuseOSError, Operations, LoggingMixIn |
17 | 49350be7 | Giorgos Verigakis | |
18 | 49350be7 | Giorgos Verigakis | |
19 | 49350be7 | Giorgos Verigakis | epoch = int(time())
|
20 | 49350be7 | Giorgos Verigakis | |
21 | 49350be7 | Giorgos Verigakis | |
22 | 49350be7 | Giorgos Verigakis | class BackendProxy(object): |
23 | 49350be7 | Giorgos Verigakis | """A proxy object that always passes user and account as first args."""
|
24 | 49350be7 | Giorgos Verigakis | |
25 | 49350be7 | Giorgos Verigakis | def __init__(self, backend, user, account): |
26 | 49350be7 | Giorgos Verigakis | self.backend = backend
|
27 | 49350be7 | Giorgos Verigakis | self.user = user
|
28 | 49350be7 | Giorgos Verigakis | self.account = account
|
29 | 49350be7 | Giorgos Verigakis | |
30 | 49350be7 | Giorgos Verigakis | def __getattr__(self, name): |
31 | 49350be7 | Giorgos Verigakis | func = getattr(self.backend, name) |
32 | 49350be7 | Giorgos Verigakis | return partial(func, self.user, self.account) |
33 | 49350be7 | Giorgos Verigakis | |
34 | 49350be7 | Giorgos Verigakis | |
35 | 49350be7 | Giorgos Verigakis | def blocksplit(data, blocksize): |
36 | 49350be7 | Giorgos Verigakis | """An iterator that splits data into blocks of size `blocksize`."""
|
37 | 49350be7 | Giorgos Verigakis | |
38 | 49350be7 | Giorgos Verigakis | while data:
|
39 | 49350be7 | Giorgos Verigakis | yield data[:blocksize]
|
40 | 49350be7 | Giorgos Verigakis | data = data[blocksize:] |
41 | 49350be7 | Giorgos Verigakis | |
42 | 49350be7 | Giorgos Verigakis | |
43 | 49350be7 | Giorgos Verigakis | class BackendFS(LoggingMixIn, Operations): |
44 | 49350be7 | Giorgos Verigakis | def __init__(self, account): |
45 | 49350be7 | Giorgos Verigakis | self.user = None |
46 | 49350be7 | Giorgos Verigakis | self.account = account
|
47 | 49350be7 | Giorgos Verigakis | self.backend = BackendProxy(backend, self.user, self.account) |
48 | 49350be7 | Giorgos Verigakis | |
49 | 49350be7 | Giorgos Verigakis | def create(self, path, mode, fi=None): |
50 | 49350be7 | Giorgos Verigakis | container, sep, object = path[1:].partition('/') |
51 | 49350be7 | Giorgos Verigakis | if not object: |
52 | 49350be7 | Giorgos Verigakis | raise FuseOSError(EACCES)
|
53 | 49350be7 | Giorgos Verigakis | |
54 | 49350be7 | Giorgos Verigakis | hashmap = [] |
55 | 49350be7 | Giorgos Verigakis | meta = {'hash': hashmap_hash(hashmap)}
|
56 | 49350be7 | Giorgos Verigakis | self.backend.update_object_hashmap(container, object, 0, hashmap, |
57 | 49350be7 | Giorgos Verigakis | meta, True)
|
58 | 49350be7 | Giorgos Verigakis | return 0 |
59 | 49350be7 | Giorgos Verigakis | |
60 | 49350be7 | Giorgos Verigakis | def getattr(self, path, fh=None): |
61 | 49350be7 | Giorgos Verigakis | container, sep, object = path[1:].partition('/') |
62 | 49350be7 | Giorgos Verigakis | if not container: |
63 | 49350be7 | Giorgos Verigakis | # Root level
|
64 | 49350be7 | Giorgos Verigakis | containers = self.backend.list_containers()
|
65 | 49350be7 | Giorgos Verigakis | return {
|
66 | 49350be7 | Giorgos Verigakis | 'st_mode': (S_IFDIR | 0755), |
67 | 49350be7 | Giorgos Verigakis | 'st_ctime': epoch,
|
68 | 49350be7 | Giorgos Verigakis | 'st_mtime': epoch,
|
69 | 49350be7 | Giorgos Verigakis | 'st_atime': epoch,
|
70 | 49350be7 | Giorgos Verigakis | 'st_nlink': 2 + len(containers)} |
71 | 49350be7 | Giorgos Verigakis | elif not object: |
72 | 49350be7 | Giorgos Verigakis | # Container level
|
73 | 49350be7 | Giorgos Verigakis | try:
|
74 | 49350be7 | Giorgos Verigakis | meta = self.backend.get_container_meta(container)
|
75 | 49350be7 | Giorgos Verigakis | except NameError: |
76 | 49350be7 | Giorgos Verigakis | raise FuseOSError(ENOENT)
|
77 | 49350be7 | Giorgos Verigakis | |
78 | 49350be7 | Giorgos Verigakis | return {
|
79 | 49350be7 | Giorgos Verigakis | 'st_mode': (S_IFDIR | 0755), |
80 | 49350be7 | Giorgos Verigakis | 'st_ctime': epoch,
|
81 | 49350be7 | Giorgos Verigakis | 'st_mtime': meta['modified'], |
82 | 49350be7 | Giorgos Verigakis | 'st_atime': meta['modified'], |
83 | 49350be7 | Giorgos Verigakis | 'st_nlink': 2 + meta['count']} |
84 | 49350be7 | Giorgos Verigakis | else:
|
85 | 49350be7 | Giorgos Verigakis | # Object level
|
86 | 49350be7 | Giorgos Verigakis | try:
|
87 | 49350be7 | Giorgos Verigakis | meta = self.backend.get_object_meta(container, object) |
88 | 49350be7 | Giorgos Verigakis | except NameError: |
89 | 49350be7 | Giorgos Verigakis | raise FuseOSError(ENOENT)
|
90 | 49350be7 | Giorgos Verigakis | |
91 | 49350be7 | Giorgos Verigakis | return {
|
92 | 49350be7 | Giorgos Verigakis | 'st_mode': (S_IFREG | 0644), |
93 | 49350be7 | Giorgos Verigakis | 'st_ctime': epoch,
|
94 | 49350be7 | Giorgos Verigakis | 'st_mtime': meta['modified'], |
95 | 49350be7 | Giorgos Verigakis | 'st_atime': meta['modified'], |
96 | 49350be7 | Giorgos Verigakis | 'st_nlink': 1, |
97 | 49350be7 | Giorgos Verigakis | 'st_size': meta['bytes']} |
98 | 49350be7 | Giorgos Verigakis | |
99 | 49350be7 | Giorgos Verigakis | def mkdir(self, path, mode): |
100 | 49350be7 | Giorgos Verigakis | container, sep, object = path[1:].partition('/') |
101 | 49350be7 | Giorgos Verigakis | if object: |
102 | 49350be7 | Giorgos Verigakis | raise FuseOSError(EACCES)
|
103 | 49350be7 | Giorgos Verigakis | backend.put_container(self.user, self.account, container) |
104 | 49350be7 | Giorgos Verigakis | |
105 | 49350be7 | Giorgos Verigakis | def read(self, path, nbyte, offset, fh): |
106 | 49350be7 | Giorgos Verigakis | container, sep, object = path[1:].partition('/') |
107 | 49350be7 | Giorgos Verigakis | if not object: |
108 | 49350be7 | Giorgos Verigakis | raise FuseOSError(EBADF)
|
109 | 49350be7 | Giorgos Verigakis | |
110 | 49350be7 | Giorgos Verigakis | # XXX This implementation is inefficient,
|
111 | 49350be7 | Giorgos Verigakis | # it always reads all the blocks
|
112 | 49350be7 | Giorgos Verigakis | size, hashmap = self.backend.get_object_hashmap(container, object) |
113 | 49350be7 | Giorgos Verigakis | buf = [] |
114 | 49350be7 | Giorgos Verigakis | for hash in hashmap: |
115 | 49350be7 | Giorgos Verigakis | buf.append(backend.get_block(hash))
|
116 | 49350be7 | Giorgos Verigakis | data = ''.join(buf)[:size]
|
117 | 49350be7 | Giorgos Verigakis | return data[offset:offset + nbyte]
|
118 | 49350be7 | Giorgos Verigakis | |
119 | 49350be7 | Giorgos Verigakis | def readdir(self, path, fh): |
120 | 49350be7 | Giorgos Verigakis | container, sep, object = path[1:].partition('/') |
121 | 49350be7 | Giorgos Verigakis | if not container: |
122 | 49350be7 | Giorgos Verigakis | # Root level
|
123 | 49350be7 | Giorgos Verigakis | containers = [c[0] for c in self.backend.list_containers()] |
124 | 49350be7 | Giorgos Verigakis | return ['.', '..'] + containers |
125 | 49350be7 | Giorgos Verigakis | else:
|
126 | 49350be7 | Giorgos Verigakis | # Container level
|
127 | 49350be7 | Giorgos Verigakis | objects = [o[0] for o in self.backend.list_objects(container)] |
128 | 49350be7 | Giorgos Verigakis | return ['.', '..'] + objects |
129 | 49350be7 | Giorgos Verigakis | |
130 | 49350be7 | Giorgos Verigakis | def rmdir(self, path): |
131 | 49350be7 | Giorgos Verigakis | container, sep, object = path[1:].partition('/') |
132 | 49350be7 | Giorgos Verigakis | if object: |
133 | 49350be7 | Giorgos Verigakis | raise FuseOSError(ENOTDIR)
|
134 | 49350be7 | Giorgos Verigakis | |
135 | 49350be7 | Giorgos Verigakis | try:
|
136 | 49350be7 | Giorgos Verigakis | self.backend.delete_container(container)
|
137 | 49350be7 | Giorgos Verigakis | except NameError: |
138 | 49350be7 | Giorgos Verigakis | raise FuseOSError(ENOENT)
|
139 | 49350be7 | Giorgos Verigakis | except IndexError: |
140 | 49350be7 | Giorgos Verigakis | raise FuseOSError(ENOTEMPTY)
|
141 | 49350be7 | Giorgos Verigakis | |
142 | 49350be7 | Giorgos Verigakis | def truncate(self, path, length, fh=None): |
143 | 49350be7 | Giorgos Verigakis | container, sep, object = path[1:].partition('/') |
144 | 49350be7 | Giorgos Verigakis | if object: |
145 | 49350be7 | Giorgos Verigakis | raise FuseOSError(EISDIR)
|
146 | 49350be7 | Giorgos Verigakis | |
147 | 49350be7 | Giorgos Verigakis | size, hashmap = self.backend.get_object_hashmap(container, object) |
148 | 49350be7 | Giorgos Verigakis | if length > size:
|
149 | 49350be7 | Giorgos Verigakis | raise FuseOSError(EINVAL) # Extension not supported |
150 | 49350be7 | Giorgos Verigakis | |
151 | 49350be7 | Giorgos Verigakis | div, mod = divmod(size, backend.block_size)
|
152 | 49350be7 | Giorgos Verigakis | nblocks = div + 1 if mod else div |
153 | 49350be7 | Giorgos Verigakis | meta = {'hash': hashmap_hash(hashmap)}
|
154 | 49350be7 | Giorgos Verigakis | self.backend.update_object_hashmap(container, object, size, |
155 | 49350be7 | Giorgos Verigakis | hashmap[:nblocks], meta, True)
|
156 | 49350be7 | Giorgos Verigakis | |
157 | 49350be7 | Giorgos Verigakis | def unlink(self, path): |
158 | 49350be7 | Giorgos Verigakis | container, sep, object = path[1:].partition('/') |
159 | 49350be7 | Giorgos Verigakis | if not object: |
160 | 49350be7 | Giorgos Verigakis | raise FuseOSError(EACCES)
|
161 | 49350be7 | Giorgos Verigakis | self.backend.delete_object(container, object) |
162 | 49350be7 | Giorgos Verigakis | |
163 | 49350be7 | Giorgos Verigakis | def write(self, path, data, offset, fh): |
164 | 49350be7 | Giorgos Verigakis | container, sep, object = path[1:].partition('/') |
165 | 49350be7 | Giorgos Verigakis | if not object: |
166 | 49350be7 | Giorgos Verigakis | raise FuseOSError(EBADF)
|
167 | 49350be7 | Giorgos Verigakis | |
168 | 49350be7 | Giorgos Verigakis | hashmap = [] |
169 | 49350be7 | Giorgos Verigakis | for block in blocksplit(data, backend.block_size): |
170 | 49350be7 | Giorgos Verigakis | hashmap.append(backend.put_block(block)) |
171 | 49350be7 | Giorgos Verigakis | meta = {'hash': hashmap_hash(hashmap)}
|
172 | 49350be7 | Giorgos Verigakis | self.backend.update_object_hashmap(container, object, len(data), |
173 | 49350be7 | Giorgos Verigakis | hashmap, meta, True)
|
174 | 49350be7 | Giorgos Verigakis | return len(data) |
175 | 49350be7 | Giorgos Verigakis | |
176 | 49350be7 | Giorgos Verigakis | |
177 | 49350be7 | Giorgos Verigakis | if __name__ == "__main__": |
178 | 49350be7 | Giorgos Verigakis | if len(argv) != 2: |
179 | 49350be7 | Giorgos Verigakis | print 'usage: %s <mountpoint>' % argv[0] |
180 | 49350be7 | Giorgos Verigakis | exit(1) |
181 | 49350be7 | Giorgos Verigakis | account = getuser() |
182 | 49350be7 | Giorgos Verigakis | fuse = FUSE(BackendFS(account), argv[1], foreground=True) |