Ported gpgfs to Python 3.5. Whooo!
This commit is contained in:
parent
295a642522
commit
4b22f7014e
85
gpgfs.py
85
gpgfs.py
|
@ -8,12 +8,12 @@ import sys
|
||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
from cStringIO import StringIO
|
from io import BytesIO
|
||||||
import gpgstore
|
import gpgstore
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
magic = 'GPGFS1\n'
|
magic = b'GPGFS1\n'
|
||||||
|
|
||||||
log = logging.getLogger('gpgfs')
|
log = logging.getLogger('gpgfs')
|
||||||
|
|
||||||
|
@ -22,30 +22,31 @@ class Entry:
|
||||||
Filesystem object, either file or directory.
|
Filesystem object, either file or directory.
|
||||||
'''
|
'''
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
for k,v in kwargs.iteritems():
|
for k,v in kwargs.items():
|
||||||
setattr(self, k, v)
|
setattr(self, k, v)
|
||||||
|
|
||||||
def read_index(store, path):
|
def read_index(store, path):
|
||||||
if not store.exists(path):
|
if not store.exists(path):
|
||||||
now = time.time()
|
now = time.time()
|
||||||
root = Entry(children={}, nlink=3, size=0,
|
root = Entry(children={}, nlink=3, size=0,
|
||||||
mode=stat.S_IFDIR | 0755,
|
mode=stat.S_IFDIR | 0o755,
|
||||||
mtime=now, ctime=now)
|
mtime=now, ctime=now)
|
||||||
write_index(store, path, root)
|
write_index(store, path, root)
|
||||||
log.info('created %s', path)
|
log.info('created %s', path)
|
||||||
return root
|
return root
|
||||||
data = store.get(path, format=gpgstore.FMT_GPG)
|
data = store.get(path, format=gpgstore.FMT_GPG)
|
||||||
buf = StringIO(data)
|
buf = BytesIO(data)
|
||||||
if buf.read(len(magic)) != magic:
|
temp = buf.read(len(magic))
|
||||||
raise IOError, 'index parse error: %s' % path
|
if temp != magic:
|
||||||
|
raise IOError('index parse error: %s' % path)
|
||||||
read_atom(buf)
|
read_atom(buf)
|
||||||
root = Entry(**read_dict(buf))
|
root = Entry(**read_dict(buf))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def write_index(store, path, root):
|
def write_index(store, path, root):
|
||||||
buf = StringIO()
|
buf = BytesIO()
|
||||||
buf.write(magic)
|
buf.write(magic)
|
||||||
header = ''
|
header = b''
|
||||||
write_atom(buf, header)
|
write_atom(buf, header)
|
||||||
write_dict(buf, root)
|
write_dict(buf, root)
|
||||||
store.put(buf.getvalue(), path=path, format=gpgstore.FMT_GPG)
|
store.put(buf.getvalue(), path=path, format=gpgstore.FMT_GPG)
|
||||||
|
@ -53,36 +54,36 @@ def write_index(store, path, root):
|
||||||
def write_dict(fd, dct):
|
def write_dict(fd, dct):
|
||||||
# breadth-first
|
# breadth-first
|
||||||
children = []
|
children = []
|
||||||
buf = StringIO()
|
buf = BytesIO()
|
||||||
if not isinstance(dct, dict):
|
if not isinstance(dct, dict):
|
||||||
dct = dct.__dict__
|
dct = dct.__dict__
|
||||||
for key in dct:
|
for key in dct:
|
||||||
write_atom(buf, key.encode('utf8'))
|
write_atom(buf, key.encode('utf-8'))
|
||||||
val = dct[key]
|
val = dct[key]
|
||||||
if isinstance(val, dict):
|
if isinstance(val, dict):
|
||||||
buf.write('D')
|
buf.write(b'D')
|
||||||
children.append(val)
|
children.append(val)
|
||||||
elif isinstance(val, Entry):
|
elif isinstance(val, Entry):
|
||||||
buf.write('E')
|
buf.write(b'E')
|
||||||
children.append(val)
|
children.append(val)
|
||||||
elif isinstance(val, (int, long)):
|
elif isinstance(val, (int)):
|
||||||
if val < 2**32:
|
if val < 2**32:
|
||||||
buf.write('I')
|
buf.write(b'I')
|
||||||
buf.write(struct.pack('<I', val))
|
buf.write(struct.pack('<I', val))
|
||||||
else:
|
else:
|
||||||
buf.write('L')
|
buf.write(b'L')
|
||||||
buf.write(struct.pack('<Q', val))
|
buf.write(struct.pack('<Q', val))
|
||||||
elif isinstance(val, float):
|
elif isinstance(val, float):
|
||||||
buf.write('F')
|
buf.write(b'F')
|
||||||
buf.write(struct.pack('<d', val))
|
buf.write(struct.pack('<d', val))
|
||||||
elif isinstance(val, str):
|
elif isinstance(val, bytes):
|
||||||
buf.write('B')
|
buf.write(b'')
|
||||||
write_atom(buf, val)
|
write_atom(buf, val)
|
||||||
elif isinstance(val, unicode):
|
elif isinstance(val, str):
|
||||||
buf.write('S')
|
buf.write(b'S')
|
||||||
write_atom(buf, val.encode('utf8'))
|
write_atom(buf, val.encode('utf-8'))
|
||||||
else:
|
else:
|
||||||
raise TypeError, type(val)
|
raise TypeError(type(val))
|
||||||
write_atom(fd, buf.getvalue())
|
write_atom(fd, buf.getvalue())
|
||||||
for c in children:
|
for c in children:
|
||||||
write_dict(fd, c)
|
write_dict(fd, c)
|
||||||
|
@ -91,23 +92,23 @@ def read_dict(fd):
|
||||||
dct = {}
|
dct = {}
|
||||||
buf = read_atom(fd)
|
buf = read_atom(fd)
|
||||||
buflen = len(buf)
|
buflen = len(buf)
|
||||||
buf = StringIO(buf)
|
buf = BytesIO(buf)
|
||||||
while buf.tell() < buflen:
|
while buf.tell() < buflen:
|
||||||
key = read_atom(buf).decode('utf8')
|
key = read_atom(buf).decode('utf-8')
|
||||||
tag = buf.read(1)
|
tag = buf.read(1)
|
||||||
if tag == 'D': val = read_dict(fd)
|
if tag == b'D': val = read_dict(fd)
|
||||||
elif tag == 'E': val = Entry(**read_dict(fd))
|
elif tag == b'E': val = Entry(**read_dict(fd))
|
||||||
elif tag == 'I': val = struct.unpack('<I', buf.read(4))[0]
|
elif tag == b'I': val = struct.unpack('<I', buf.read(4))[0]
|
||||||
elif tag == 'L': val = struct.unpack('<Q', buf.read(8))[0]
|
elif tag == b'L': val = struct.unpack('<Q', buf.read(8))[0]
|
||||||
elif tag == 'F': val = struct.unpack('<d', buf.read(8))[0]
|
elif tag == b'F': val = struct.unpack('<d', buf.read(8))[0]
|
||||||
elif tag == 'B': val = read_atom(buf)
|
elif tag == b'': val = read_atom(buf)
|
||||||
elif tag == 'S': val = read_atom(buf).decode('utf8')
|
elif tag == b'S': val = read_atom(buf).decode('utf-8')
|
||||||
else: raise TypeError, tag
|
else: raise TypeError(tag)
|
||||||
dct[key] = val
|
dct[key] = val
|
||||||
return dct
|
return dct
|
||||||
|
|
||||||
def write_atom(fd, atom):
|
def write_atom(fd, atom):
|
||||||
assert isinstance(atom, str)
|
assert isinstance(atom, bytes)
|
||||||
fd.write(struct.pack('<I', len(atom)))
|
fd.write(struct.pack('<I', len(atom)))
|
||||||
fd.write(atom)
|
fd.write(atom)
|
||||||
|
|
||||||
|
@ -126,7 +127,7 @@ class LoggingMixIn:
|
||||||
try:
|
try:
|
||||||
ret = getattr(self, op)(path, *args)
|
ret = getattr(self, op)(path, *args)
|
||||||
return ret
|
return ret
|
||||||
except OSError, e:
|
except OSError as e:
|
||||||
ret = str(e)
|
ret = str(e)
|
||||||
raise
|
raise
|
||||||
except:
|
except:
|
||||||
|
@ -207,16 +208,16 @@ class GpgFs(LoggingMixIn, Operations):
|
||||||
|
|
||||||
def chmod(self, path, mode):
|
def chmod(self, path, mode):
|
||||||
# sanitize mode (clear setuid/gid/sticky bits)
|
# sanitize mode (clear setuid/gid/sticky bits)
|
||||||
mode &= 0777
|
mode &= 0o777
|
||||||
with self.transaction():
|
with self.transaction():
|
||||||
ent = self._find(path)
|
ent = self._find(path)
|
||||||
ent.mode = mode | (ent.mode & 0170000)
|
ent.mode = mode | (ent.mode & 0o170000)
|
||||||
|
|
||||||
def chown(self, path, uid, gid):
|
def chown(self, path, uid, gid):
|
||||||
raise FuseOSError(errno.ENOSYS)
|
raise FuseOSError(errno.ENOSYS)
|
||||||
|
|
||||||
def create(self, path, mode):
|
def create(self, path, mode):
|
||||||
mode &= 0777
|
mode &= 0o777
|
||||||
mode |= stat.S_IFREG
|
mode |= stat.S_IFREG
|
||||||
with self.transaction() as putx:
|
with self.transaction() as putx:
|
||||||
parent, name = self._find(path, parent=True)
|
parent, name = self._find(path, parent=True)
|
||||||
|
@ -237,7 +238,7 @@ class GpgFs(LoggingMixIn, Operations):
|
||||||
log.debug('nothing to flush')
|
log.debug('nothing to flush')
|
||||||
return 0
|
return 0
|
||||||
with self.transaction() as putx:
|
with self.transaction() as putx:
|
||||||
buf = ''.join(self.write_buf)
|
buf = b''.join(self.write_buf)
|
||||||
self.write_buf = [buf]
|
self.write_buf = [buf]
|
||||||
ent = self._find(self.write_path)
|
ent = self._find(self.write_path)
|
||||||
ent.size = len(buf)
|
ent.size = len(buf)
|
||||||
|
@ -265,7 +266,7 @@ class GpgFs(LoggingMixIn, Operations):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def mkdir(self, path, mode):
|
def mkdir(self, path, mode):
|
||||||
mode &= 0777
|
mode &= 0o777
|
||||||
mode |= stat.S_IFDIR
|
mode |= stat.S_IFDIR
|
||||||
with self.transaction():
|
with self.transaction():
|
||||||
parent, name = self._find(path, parent=True)
|
parent, name = self._find(path, parent=True)
|
||||||
|
@ -349,7 +350,7 @@ class GpgFs(LoggingMixIn, Operations):
|
||||||
with self.transaction() as putx:
|
with self.transaction() as putx:
|
||||||
ent = self._find(path)
|
ent = self._find(path)
|
||||||
if length == 0:
|
if length == 0:
|
||||||
buf = ''
|
buf = b''
|
||||||
else:
|
else:
|
||||||
buf = self.store.get(ent.encpath, format=ent.encformat)
|
buf = self.store.get(ent.encpath, format=ent.encformat)
|
||||||
buf = buf[:length]
|
buf = buf[:length]
|
||||||
|
@ -389,7 +390,7 @@ class GpgFs(LoggingMixIn, Operations):
|
||||||
self.write_buf.append(data)
|
self.write_buf.append(data)
|
||||||
self.write_len += len(data)
|
self.write_len += len(data)
|
||||||
else:
|
else:
|
||||||
buf = ''.join(self.write_buf)
|
buf = b''.join(self.write_buf)
|
||||||
buf = buf[:offset] + data + buf[offset + len(data):]
|
buf = buf[:offset] + data + buf[offset + len(data):]
|
||||||
self.write_buf = [buf]
|
self.write_buf = [buf]
|
||||||
self.write_len = len(buf)
|
self.write_len = len(buf)
|
||||||
|
|
14
gpgstore.py
14
gpgstore.py
|
@ -18,22 +18,22 @@ class GpgStore(object):
|
||||||
def put(self, data, path=None, format=FMT_GPG):
|
def put(self, data, path=None, format=FMT_GPG):
|
||||||
assert format == FMT_GPG
|
assert format == FMT_GPG
|
||||||
if not path:
|
if not path:
|
||||||
path = hexlify(os.urandom(20))
|
path = hexlify(os.urandom(20)).decode('utf-8')
|
||||||
path = path[:2] + '/' + path[2:]
|
path = path[:2] + '/' + path[2:]
|
||||||
encdir = self.encroot + '/' + path[:2]
|
encdir = self.encroot + '/' + path[:2]
|
||||||
if not os.path.exists(encdir):
|
if not os.path.exists(encdir):
|
||||||
os.mkdir(encdir, 0755)
|
os.mkdir(encdir, 0o755)
|
||||||
res = self.gpg.encrypt(data, self.keyid, armor=False)
|
res = self.gpg.encrypt(data, self.keyid, armor=False)
|
||||||
if not res.ok:
|
if not res.ok:
|
||||||
log.error("encryption failed (keyid %s), %s: %s",
|
log.error("encryption failed (keyid %s), %s: %s",
|
||||||
self.keyid, res.status, path)
|
self.keyid, res.status, path)
|
||||||
raise OSError(errno.EIO)
|
raise OSError(errno.EIO)
|
||||||
try:
|
try:
|
||||||
with file(self.encroot + '/' + path + '.tmp', 'w') as fd:
|
with open(self.encroot + '/' + path + '.tmp', 'wb') as fd:
|
||||||
fd.write(res.data)
|
fd.write(res.data)
|
||||||
os.rename(self.encroot + '/' + path + '.tmp',
|
os.rename(self.encroot + '/' + path + '.tmp',
|
||||||
self.encroot + '/' + path)
|
self.encroot + '/' + path)
|
||||||
except IOError, err:
|
except IOError as err:
|
||||||
log.error("write failed: %s: %s", path, str(err))
|
log.error("write failed: %s: %s", path, str(err))
|
||||||
raise OSError(err.errno)
|
raise OSError(err.errno)
|
||||||
finally:
|
finally:
|
||||||
|
@ -45,8 +45,8 @@ class GpgStore(object):
|
||||||
def get(self, path, format=FMT_GPG):
|
def get(self, path, format=FMT_GPG):
|
||||||
assert format == FMT_GPG
|
assert format == FMT_GPG
|
||||||
try:
|
try:
|
||||||
data = file(self.encroot + '/' + path).read()
|
data = open(self.encroot + '/' + path, 'rb').read()
|
||||||
except OSError, err:
|
except OSError as err:
|
||||||
log.error("read failed: %s: %s", path, str(err))
|
log.error("read failed: %s: %s", path, str(err))
|
||||||
raise
|
raise
|
||||||
if not data:
|
if not data:
|
||||||
|
@ -56,7 +56,7 @@ class GpgStore(object):
|
||||||
log.error("decryption failed, %s: %s", res.status, path)
|
log.error("decryption failed, %s: %s", res.status, path)
|
||||||
raise OSError(errno.EIO)
|
raise OSError(errno.EIO)
|
||||||
log.debug('decrypted %s' % path)
|
log.debug('decrypted %s' % path)
|
||||||
return data
|
return res.data
|
||||||
|
|
||||||
def delete(self, path):
|
def delete(self, path):
|
||||||
os.remove(self.encroot + '/' + path)
|
os.remove(self.encroot + '/' + path)
|
||||||
|
|
Loading…
Reference in New Issue