Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix read on fuse host driver #519

Merged
merged 3 commits into from
Jul 21, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 105 additions & 96 deletions run/moros-fuse.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from errno import ENOENT
from fuse import FUSE, FuseOSError, Operations, LoggingMixIn
from stat import S_IFDIR, S_IFREG
from threading import Lock

BLOCK_SIZE = 512
SUPERBLOCK_ADDR = 4096 * BLOCK_SIZE
Expand All @@ -24,6 +25,7 @@ class MorosFuse(LoggingMixIn, Operations):
getxattr = None

def __init__(self, path):
self.rwlock = Lock()
self.block_size = BLOCK_SIZE
self.image = open(path, "r+b")
self.image.seek(SUPERBLOCK_ADDR)
Expand Down Expand Up @@ -125,128 +127,135 @@ def destroy(self, path):
self.image.close()
return

def getattr(self, path, fh=None):
def getattr(self, path, handle=None):
(kind, addr, size, time, name) = self.__scan(path)
if addr == 0:
raise FuseOSError(ENOENT)
mode = S_IFDIR | 0o755 if kind == 0 else S_IFREG | 0o644
return { "st_atime": 0, "st_mtime": time, "st_uid": 0, "st_gid": 0, "st_mode": mode, "st_size": size }

def read(self, path, size, offset, fh):
(kind, next_block_addr, size, time, name) = self.__scan(path)
res = b""
while next_block_addr != 0 and size > 0:
self.image.seek(next_block_addr)
next_block_addr = int.from_bytes(self.image.read(4), "big") * self.block_size
if offset < self.block_size - 4:
buf = self.image.read(max(0, min(self.block_size - 4, size)))
res = b"".join([res, buf[offset:]])
offset = 0
else:
offset -= self.block_size - 4
size -= self.block_size - 4
return res
def read(self, path, size, offset, handle):
with self.rwlock:
(kind, next_block_addr, file_size, time, name) = self.__scan(path)
size = min(size, file_size - offset)
res = bytes()
while next_block_addr != 0 and size > 0:
self.image.seek(next_block_addr)
next_block_addr = int.from_bytes(self.image.read(4), "big") * self.block_size
if offset < self.block_size - 4:
buf = self.image.read(max(0, min(self.block_size - 4, size)))[offset:]
res += buf
size -= min(self.block_size - 4, len(buf))
offset = 0
else:
offset -= self.block_size - 4
return res

def readdir(self, path, fh):
files = [".", ".."]
(_, next_block_addr, _, _, _) = self.__scan(path)
for (kind, addr, size, time, name) in self.__read(next_block_addr):
files.append(name)
return files
def readdir(self, path, handle):
with self.rwlock:
files = [".", ".."]
(_, next_block_addr, _, _, _) = self.__scan(path)
for (kind, addr, size, time, name) in self.__read(next_block_addr):
files.append(name)
return files

def mkdir(self, path, mode):
self.create(path, S_IFDIR | mode)
with self.rwlock:
self.create(path, S_IFDIR | mode)

def create(self, path, mode):
(path, _, name) = path.rpartition("/")
entries = self.readdir(path + "/", 0)
entries.append(name)
pos = self.image.tell()
parent_addr = self.__update_dir_size(path, entries)
with self.rwlock:
(path, _, name) = path.rpartition("/")
entries = self.readdir(path + "/", 0)
entries.append(name)
pos = self.image.tell()
parent_addr = self.__update_dir_size(path, entries)

# Allocate space for the new dir entry if needed
blocks = 0
addr = parent_addr
next_addr = parent_addr
while next_addr != 0:
addr = next_addr
blocks += 1
self.image.seek(addr)
next_addr = int.from_bytes(self.image.read(4), "big") * self.block_size
free_size = (self.block_size - 4) - (pos - addr)
if free_size < ENTRY_SIZE + len(name):
pos = self.image.tell() - 4
# Allocate space for the new dir entry if needed
blocks = 0
addr = parent_addr
next_addr = parent_addr
while next_addr != 0:
addr = next_addr
blocks += 1
self.image.seek(addr)
next_addr = int.from_bytes(self.image.read(4), "big") * self.block_size
free_size = (self.block_size - 4) - (pos - addr)
if free_size < ENTRY_SIZE + len(name):
pos = self.image.tell() - 4
addr = self.__next_free_addr()
self.__alloc(addr)
self.image.seek(pos)
self.image.write((addr // self.block_size).to_bytes(4, "big"))
pos = addr + 4

# Allocate space for the new file
kind = int((mode & S_IFDIR) != S_IFDIR)
size = 0
addr = self.__next_free_addr()
self.__alloc(addr)

# Add dir entry
self.image.seek(pos)
self.image.write(kind.to_bytes(1, "big"))
self.image.write((addr // self.block_size).to_bytes(4, "big"))
pos = addr + 4
self.image.write(size.to_bytes(4, "big"))
self.image.write(int(time()).to_bytes(8, "big"))
self.image.write(len(name).to_bytes(1, "big"))
self.image.write(name.encode("utf-8"))
return 0

# Allocate space for the new file
kind = int((mode & S_IFDIR) != S_IFDIR)
size = 0
addr = self.__next_free_addr()
self.__alloc(addr)
def write(self, path, data, offset, handle):
with self.rwlock:
(_, addr, size, _, name) = self.__scan(path)
n = self.block_size - 4 # Space available for data in blocks
j = size % n # Start of space available in last block

# Add dir entry
self.image.seek(pos)
self.image.write(kind.to_bytes(1, "big"))
self.image.write((addr // self.block_size).to_bytes(4, "big"))
self.image.write(size.to_bytes(4, "big"))
self.image.write(int(time()).to_bytes(8, "big"))
self.image.write(len(name).to_bytes(1, "big"))
self.image.write(name.encode("utf-8"))
return 0

def write(self, path, data, offset, fh):
(_, addr, size, _, name) = self.__scan(path)
n = self.block_size - 4 # Space available for data in blocks
j = size % n # Start of space available in last block
# Update file size
self.image.seek(-(4 + 8 + 1 + len(name)), 1)
size = max(size, offset + len(data))
self.image.write(size.to_bytes(4, "big"))

# Update file size
self.image.seek(-(4 + 8 + 1 + len(name)), 1)
size = max(size, offset + len(data))
self.image.write(size.to_bytes(4, "big"))
for i in range(0, offset, n):
self.image.seek(addr)
next_addr = int.from_bytes(self.image.read(4), "big") * self.block_size
if i + n >= offset:
self.image.seek(addr + 4 + j)
self.image.write(data[0:(n - j)])
if next_addr == 0:
next_addr = self.__next_free_addr()
self.__alloc(next_addr)
self.image.seek(addr)
self.image.write((next_addr // self.block_size).to_bytes(4, "big"))
addr = next_addr

for i in range(0, offset, n):
self.image.seek(addr)
next_addr = int.from_bytes(self.image.read(4), "big") * self.block_size
if i + n >= offset:
self.image.seek(addr + 4 + j)
self.image.write(data[0:(n - j)])
if next_addr == 0:
next_addr = self.__next_free_addr()
self.__alloc(next_addr)
for i in range(n - j if j > 0 else 0, len(data), n):
next_addr = 0
if i + n < len(data): # TODO: check for off by one error
next_addr = self.__next_free_addr()
self.__alloc(next_addr)
self.image.seek(addr)
self.image.write((next_addr // self.block_size).to_bytes(4, "big"))
addr = next_addr
self.image.write(data[i:min(i + n, len(data))])
addr = next_addr

for i in range(n - j if j > 0 else 0, len(data), n):
next_addr = 0
if i + n < len(data): # TODO: check for off by one error
next_addr = self.__next_free_addr()
self.__alloc(next_addr)
self.image.seek(addr)
self.image.write((next_addr // self.block_size).to_bytes(4, "big"))
self.image.write(data[i:min(i + n, len(data))])
addr = next_addr

return len(data)
return len(data)

def unlink(self, path):
# Unlink and free blocs
(_, addr, size, _, name) = self.__scan(path)
self.image.seek(-(4 + 4 + 8 + 1 + len(name)), 1)
self.image.write((0).to_bytes(4, "big"))
while addr != 0:
self.__free(addr)
self.image.seek(addr)
addr = int.from_bytes(self.image.read(4), "big") * self.block_size
with self.rwlock:
# Unlink and free blocs
(_, addr, size, _, name) = self.__scan(path)
self.image.seek(-(4 + 4 + 8 + 1 + len(name)), 1)
self.image.write((0).to_bytes(4, "big"))
while addr != 0:
self.__free(addr)
self.image.seek(addr)
addr = int.from_bytes(self.image.read(4), "big") * self.block_size

# Update parent dir size
(path, _, _) = path.rpartition("/")
entries = self.readdir(path + "/", 0)
self.__update_dir_size(path, entries)
# Update parent dir size
(path, _, _) = path.rpartition("/")
entries = self.readdir(path + "/", 0)
self.__update_dir_size(path, entries)

if __name__ == '__main__':
import argparse
Expand Down