Адаптировал к новому API llfuse
parent
7753267d07
commit
1a69027a4b
|
@ -4,6 +4,8 @@
|
|||
Приложение работает на python3.
|
||||
Зависит от модуля `llfuse`.
|
||||
|
||||
В одной базе данных может храниться неограниченное количество изолированных дург от друга файловых систем.
|
||||
|
||||
База данных и пользователь с полным доступом к ней (если их ещё нет) должны быть созданы на сервере:
|
||||
```
|
||||
su - postrges
|
||||
|
@ -21,12 +23,13 @@ chmod +x /usr/local/bin/udavfs3
|
|||
|
||||
`udavfs3 "host=srv_hostname dbname=fs_db user=fs_user password=fs_user_password" /mount/point/path [-o mount_options]`
|
||||
|
||||
mount_options имеют смысл только для вновь созданной базы данных.
|
||||
Если мы монтируем уже инициализированную файловую систему, mount_options игнорируются.
|
||||
mount_options (кроме fsname) имеют смысл только для несуществующей в базе ФС. fsname - обязателен, т.к. выбирает
|
||||
конкретную файловую систему из базы.
|
||||
Если мы монтируем уже созданную файловую систему, mount_options игнорируются.
|
||||
|
||||
Список опций mount_options:
|
||||
|
||||
* fsid - уникальная (в рамках конкретного сервера) строка без пробелов. Из данной строки сформируется sha1-хэш, который и будет
|
||||
* fsname - уникальная (в рамках конкретного сервера) строка без пробелов. Из данной строки сформируется sha1-хэш, который и будет
|
||||
уникальным идентификатором ФС для операционной системы.
|
||||
* blocksize - размер блока ФС в байтах. Обычно используется 4096 (4 КБ).
|
||||
* fssize - размер файловой системы. Можно использовать окончание еденицы измерения (k,m,g,t). Минимальный размер - 4 МБ.
|
||||
|
|
121
udavfs3
121
udavfs3
|
@ -66,9 +66,9 @@ class Operations(llfuse.Operations):
|
|||
uid INT NOT NULL,
|
||||
gid INT NOT NULL,
|
||||
mode INT NOT NULL,
|
||||
mtime FLOAT NOT NULL,
|
||||
atime FLOAT NOT NULL,
|
||||
ctime FLOAT NOT NULL,
|
||||
mtime BIGINT NOT NULL,
|
||||
atime BIGINT NOT NULL,
|
||||
ctime BIGINT NOT NULL,
|
||||
target BYTEA,
|
||||
size BIGINT NOT NULL DEFAULT 0,
|
||||
rdev INT NOT NULL DEFAULT 0,
|
||||
|
@ -111,8 +111,8 @@ class Operations(llfuse.Operations):
|
|||
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
|
||||
(self.fsid, llfuse.ROOT_INODE, stat.S_IFDIR | stat.S_IRUSR | stat.S_IWUSR
|
||||
| stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH
|
||||
| stat.S_IXOTH, os.getuid(), os.getgid(), time(),
|
||||
time(), time()))
|
||||
| stat.S_IXOTH, os.getuid(), os.getgid(), int(time() * 1e9),
|
||||
int(time() * 1e9), int(time() * 1e9)))
|
||||
self.cursor.execute("SELECT setval('inodes_inode_id_seq', %s);", (llfuse.ROOT_INODE + 1,))
|
||||
self.cursor.execute("INSERT INTO contents (fsid, name, parent_inode, inode_id) VALUES (%s,%s,%s,%s)",
|
||||
(self.fsid, b'..', llfuse.ROOT_INODE, llfuse.ROOT_INODE))
|
||||
|
@ -129,7 +129,7 @@ class Operations(llfuse.Operations):
|
|||
|
||||
return row
|
||||
|
||||
def lookup(self, inode_p, name):
|
||||
def lookup(self, inode_p, name, ctx):
|
||||
if name == b'.':
|
||||
inode = inode_p
|
||||
elif name == b'..':
|
||||
|
@ -142,9 +142,9 @@ class Operations(llfuse.Operations):
|
|||
except NoSuchRowError:
|
||||
raise(llfuse.FUSEError(errno.ENOENT))
|
||||
|
||||
return self.getattr(inode)
|
||||
return self.getattr(inode, ctx)
|
||||
|
||||
def getattr(self, inode):
|
||||
def getattr(self, inode, ctx = None):
|
||||
cur = self.db.cursor(cursor_factory=psycopg2.extras.DictCursor)
|
||||
cur.execute('SELECT * FROM inodes WHERE inode_id=%s AND fsid=%s', (inode, self.fsid))
|
||||
row = cur.fetchone()
|
||||
|
@ -164,16 +164,16 @@ class Operations(llfuse.Operations):
|
|||
entry.st_blksize = self.blocksize
|
||||
entry.st_blocks = self.get_row("SELECT COUNT(1) FROM body WHERE inode_id=%s AND fsid=%s",
|
||||
(inode, self.fsid))[0]
|
||||
entry.st_atime = row['atime']
|
||||
entry.st_mtime = row['mtime']
|
||||
entry.st_ctime = row['ctime']
|
||||
entry.st_atime_ns = row['atime']
|
||||
entry.st_mtime_ns = row['mtime']
|
||||
entry.st_ctime_ns = row['ctime']
|
||||
|
||||
return entry
|
||||
|
||||
def readlink(self, inode):
|
||||
def readlink(self, inode, ctx):
|
||||
return bytes(self.get_row('SELECT target FROM inodes WHERE inode_id=%s AND fsid=%s', (inode, self.fsid))[0])
|
||||
|
||||
def opendir(self, inode):
|
||||
def opendir(self, inode, ctx):
|
||||
return inode
|
||||
|
||||
def readdir(self, inode, off):
|
||||
|
@ -187,16 +187,16 @@ class Operations(llfuse.Operations):
|
|||
for row in cursor2.fetchall():
|
||||
yield (bytes(row['name']), self.getattr(row['inode_id']), row['rowid'])
|
||||
|
||||
def unlink(self, inode_p, name):
|
||||
entry = self.lookup(inode_p, name)
|
||||
def unlink(self, inode_p, name, ctx):
|
||||
entry = self.lookup(inode_p, name, ctx)
|
||||
|
||||
if stat.S_ISDIR(entry.st_mode):
|
||||
raise llfuse.FUSEError(errno.EISDIR)
|
||||
|
||||
self._remove(inode_p, name, entry)
|
||||
|
||||
def rmdir(self, inode_p, name):
|
||||
entry = self.lookup(inode_p, name)
|
||||
def rmdir(self, inode_p, name, ctx):
|
||||
entry = self.lookup(inode_p, name, ctx)
|
||||
|
||||
if not stat.S_ISDIR(entry.st_mode):
|
||||
raise llfuse.FUSEError(errno.ENOTDIR)
|
||||
|
@ -221,11 +221,11 @@ class Operations(llfuse.Operations):
|
|||
stat.S_IROTH | stat.S_IWOTH | stat.S_IXOTH)
|
||||
return self._create(inode_p, name, mode, ctx, target=target)
|
||||
|
||||
def rename(self, inode_p_old, name_old, inode_p_new, name_new):
|
||||
entry_old = self.lookup(inode_p_old, name_old)
|
||||
def rename(self, inode_p_old, name_old, inode_p_new, name_new, ctx):
|
||||
entry_old = self.lookup(inode_p_old, name_old, ctx)
|
||||
|
||||
try:
|
||||
entry_new = self.lookup(inode_p_new, name_new)
|
||||
entry_new = self.lookup(inode_p_new, name_new, ctx)
|
||||
except llfuse.FUSEError as exc:
|
||||
if exc.errno != errno.ENOENT:
|
||||
raise
|
||||
|
@ -257,21 +257,21 @@ class Operations(llfuse.Operations):
|
|||
self.cursor.execute("DELETE FROM inodes WHERE inode_id=%s AND fsid=%s", (entry_new.st_ino, self.fsid))
|
||||
|
||||
|
||||
def link(self, inode, new_inode_p, new_name):
|
||||
entry_p = self.getattr(new_inode_p)
|
||||
def link(self, inode, new_inode_p, new_name, ctx):
|
||||
entry_p = self.getattr(new_inode_p, ctx)
|
||||
if entry_p.st_nlink == 0:
|
||||
raise FUSEError(errno.EINVAL)
|
||||
|
||||
self.cursor.execute("INSERT INTO contents (name, inode_id, parent_inode, fsid) VALUES(%s,%s,%s,%s)",
|
||||
(new_name, inode, new_inode_p, self.fsid))
|
||||
|
||||
return self.getattr(inode)
|
||||
return self.getattr(inode, ctx)
|
||||
def del_block(self, inode, num):
|
||||
self.cursor.execute("DELETE FROM body WHERE inode_id=%s AND block_no=%s AND fsid=%s", (inode, num, self.fsid))
|
||||
|
||||
def setattr(self, inode, attr):
|
||||
def setattr(self, inode, attr, fields, fh, ctx):
|
||||
|
||||
if attr.st_size is not None:
|
||||
if fields.update_size:
|
||||
size = self.get_row('SELECT size FROM inodes WHERE inode_id=%s AND fsid=%s', (inode, self.fsid))[0]
|
||||
if size is None:
|
||||
size = 0
|
||||
|
@ -314,15 +314,15 @@ class Operations(llfuse.Operations):
|
|||
else:
|
||||
self.cursor.execute("UPDATE inodes SET size=%s WHERE inode_id=%s AND fsid=%s",
|
||||
(attr.st_size, inode, self.fsid))
|
||||
if attr.st_mode is not None:
|
||||
if fields.update_mode:
|
||||
self.cursor.execute('UPDATE inodes SET mode=%s WHERE inode_id=%s AND fsid=%s',
|
||||
(attr.st_mode, inode, self.fsid))
|
||||
|
||||
if attr.st_uid is not None:
|
||||
if fields.update_uid:
|
||||
self.cursor.execute('UPDATE inodes SET uid=%s WHERE inode_id=%s AND fsid=%s',
|
||||
(attr.st_uid, inode, self.fsid))
|
||||
|
||||
if attr.st_gid is not None:
|
||||
if fields.update_gid:
|
||||
self.cursor.execute('UPDATE inodes SET gid=%s WHERE inode_id=%s AND fsid=%s',
|
||||
(attr.st_gid, inode, self.fsid))
|
||||
|
||||
|
@ -330,19 +330,19 @@ class Operations(llfuse.Operations):
|
|||
self.cursor.execute('UPDATE inodes SET rdev=%s WHERE inode_id=%s AND fsid=%s',
|
||||
(attr.st_rdev, inode, self.fsid))
|
||||
|
||||
if attr.st_atime is not None:
|
||||
if fields.update_atime:
|
||||
self.cursor.execute('UPDATE inodes SET atime=%s WHERE inode_id=%s AND fsid=%s',
|
||||
(attr.st_atime, inode, self.fsid))
|
||||
(attr.st_atime_ns, inode, self.fsid))
|
||||
|
||||
if attr.st_mtime is not None:
|
||||
if fields.update_mtime:
|
||||
self.cursor.execute('UPDATE inodes SET mtime=%s WHERE inode_id=%s AND fsid=%s',
|
||||
(attr.st_mtime, inode, self.fsid))
|
||||
(attr.st_mtime_ns, inode, self.fsid))
|
||||
|
||||
if attr.st_ctime is not None:
|
||||
if attr.st_ctime_ns is not None:
|
||||
self.cursor.execute('UPDATE inodes SET ctime=%s WHERE inode_id=%s AND fsid=%s',
|
||||
(attr.st_ctime, inode, self.fsid))
|
||||
(attr.st_ctime_ns, inode, self.fsid))
|
||||
|
||||
return self.getattr(inode)
|
||||
return self.getattr(inode, ctx)
|
||||
|
||||
def mknod(self, inode_p, name, mode, rdev, ctx):
|
||||
return self._create(inode_p, name, mode, ctx, rdev=rdev)
|
||||
|
@ -350,7 +350,7 @@ class Operations(llfuse.Operations):
|
|||
def mkdir(self, inode_p, name, mode, ctx):
|
||||
return self._create(inode_p, name, mode, ctx)
|
||||
|
||||
def statfs(self):
|
||||
def statfs(self, ctx):
|
||||
stat_ = llfuse.StatvfsData()
|
||||
|
||||
stat_.f_bsize = self.blocksize
|
||||
|
@ -368,11 +368,11 @@ class Operations(llfuse.Operations):
|
|||
|
||||
return stat_
|
||||
|
||||
def open(self, inode, flags):
|
||||
def open(self, inode, flags, ctx):
|
||||
self.inode_open_count[inode] += 1
|
||||
return inode
|
||||
|
||||
def access(self, inode, mode, ctx):
|
||||
def access(self, inode, mode, ctx=None):
|
||||
if mode != os.F_OK and not self.__access(inode, mode, ctx):
|
||||
return False
|
||||
return True
|
||||
|
@ -396,18 +396,18 @@ class Operations(llfuse.Operations):
|
|||
return (entry.st_ino, entry)
|
||||
|
||||
def _create(self, inode_p, name, mode, ctx, rdev=0, target=None):
|
||||
if self.getattr(inode_p).st_nlink == 0:
|
||||
if self.getattr(inode_p, ctx).st_nlink == 0:
|
||||
raise FUSEError(errno.EINVAL)
|
||||
|
||||
self.cursor.execute('INSERT INTO inodes (uid, gid, mode, mtime, atime, '
|
||||
'ctime, target, rdev, fsid) VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING inode_id;',
|
||||
(ctx.uid, ctx.gid, mode, time(), time(), time(), target, rdev, self.fsid))
|
||||
(ctx.uid, ctx.gid, mode, int(time() * 1e9), int(time() * 1e9), int(time() * 1e9), target, rdev, self.fsid))
|
||||
|
||||
inode = self.cursor.fetchone()[0]
|
||||
self.cursor.execute("INSERT INTO contents(name, inode_id, parent_inode, fsid) VALUES(%s,%s,%s,%s);",
|
||||
(name, inode, inode_p, self.fsid))
|
||||
|
||||
return self.getattr(inode)
|
||||
return self.getattr(inode, ctx)
|
||||
|
||||
def block_info(self, offset, length):
|
||||
info = {}
|
||||
|
@ -502,38 +502,40 @@ class NoSuchRowError(Exception):
|
|||
|
||||
def usage():
|
||||
raise SystemExit('''\
|
||||
Usage: %s "host=dbhost dbname=database user=dbuser password=dbpass" <mountpoint> [-o mount_options]
|
||||
Usage: %s "host=dbhost dbname=database user=dbuser password=dbpass" <mountpoint> -o mount_options
|
||||
UdavFS mount_option:
|
||||
fsid=<uniq_string>
|
||||
blocksize=<bytes>
|
||||
fssize=<size>[k|m|g|t]
|
||||
[locksize=<bytes>
|
||||
fssize=<size>[k|m|g|t] ]
|
||||
|
||||
Option 'user_allow_other' MUST be set in /etc/fuse.conf''' % sys.argv[0])
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if len(sys.argv) < 3 or len(sys.argv) > 3 and len(sys.argv) < 5 or len(sys.argv) > 5:
|
||||
if len(sys.argv) != 5:
|
||||
usage()
|
||||
if sys.argv[3] != '-o' and len(sys.argv) != 5:
|
||||
if sys.argv[3] != '-o':
|
||||
usage()
|
||||
conn_str = sys.argv[1]
|
||||
conn_str = sys.argv[1] + ' sslmode=\'require\''
|
||||
options = {x.split('=')[0].strip(): len(x.split('=')) > 1 and x.split('=')[1].strip() or True for x in sys.argv[4].split(',') }
|
||||
|
||||
db = psycopg2.connect(conn_str)
|
||||
db.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
cursor = db.cursor()
|
||||
|
||||
try:
|
||||
cursor.execute('SELECT fsid, bs, size FROM fsinfo WHERE fsid=%s', (fsid,))
|
||||
fsid, blocksize, fssize = cursor.fetchone()
|
||||
except:
|
||||
if 'fsid' not in options.keys():
|
||||
print('fsid MUST be in mountoptions\n')
|
||||
usage()
|
||||
fsname = options['fsid']
|
||||
del options['fsid']
|
||||
fsid = sha1(bytes(fsname.strip(), 'UTF-8')).hexdigest()
|
||||
if 'fsname' not in options.keys():
|
||||
print('fsname MUST be in mountoptions\n')
|
||||
usage()
|
||||
fsname = options['fsname']
|
||||
del options['fsname']
|
||||
fsid = sha1(bytes(fsname.strip(), 'UTF-8')).hexdigest()
|
||||
|
||||
try:
|
||||
cursor.execute('SELECT bs, size FROM fsinfo WHERE fsid=%s', (fsid,))
|
||||
blocksize, fssize = cursor.fetchone()
|
||||
if options['fssize']: del options['fssize']
|
||||
if options['blocksize']: del options['blocksize']
|
||||
except:
|
||||
if 'blocksize' not in options.keys():
|
||||
print('blocksize mountoption MUST be specified\n')
|
||||
usage()
|
||||
|
@ -593,12 +595,11 @@ if __name__ == '__main__':
|
|||
|
||||
|
||||
operations = Operations(conn_str, fsid, blocksize, fssize)
|
||||
llfuse.init(operations, mountpoint,
|
||||
m_params)
|
||||
llfuse.init(operations, mountpoint, m_params)
|
||||
|
||||
|
||||
try:
|
||||
llfuse.main(single=False)
|
||||
llfuse.main(workers=None)
|
||||
except:
|
||||
llfuse.close(unmount=False)
|
||||
raise
|
||||
|
|
Loading…
Reference in New Issue