Compare commits

...

5 Commits

5 changed files with 246 additions and 0 deletions

View File

@ -0,0 +1,17 @@
local fsproxy = require "fsproxy"
local rpc = require "rpc"
local fs = require "filesystem"
local tA = {...}
if #tA < 1 then
print("Usage: exportfs <directory> [directory] ...")
return
end
for k,v in pairs(tA) do
local px = fsproxy.new(v)
for l,m in pairs(px) do
rpc.register("fs_"..v.."_"..l,m)
end
print(v)
end

View File

@ -0,0 +1,60 @@
local computer = require "computer"
local fs = require "filesystem"
local rpc = require "rpc"
local tA = {...}
local rpath, lpath = tA[1], tA[2], tA[3]
if #tA < 2 then
print("Usage: importfs <remote path> <local path>")
return
end
local function parsePath(path)
return path:match("(.+):(.+)")
end
local host, saddr = parsePath(rpath)
local px = rpc.proxy(host,"fs_"..saddr.."_")
local mc = 0
for k,v in pairs(px) do
mc = mc + 1
end
if mc < 1 then
error("no such remote filesystem: "..rpath)
end
local statcache = {}
px.address = rpath
if px.dirstat then -- use single call for file info
function px.list(path)
local t,e = px.dirstat(path)
if not t then return nil,e end
local rt = {}
for k,v in pairs(t) do
rt[#rt+1] = k
statcache[fs.canonical("/"..path.."/"..k)] = {computer.uptime(),v[1],v[2],v[3]}
end
return rt
end
local oid, osize, olm = px.isDirectory, px.size, px.lastModified
local function gce(p,n)
for k,v in pairs(statcache) do
if computer.uptime() > v[1] + 1 then
statcache[k] = nil
end
end
local ci = statcache["/"..fs.canonical(p)]
if ci then
return ci[3]
end
end
function px.isDirectory(path)
return gce(path, 2) or oid(path)
end
function px.size(path)
return gce(path, 3) or osize(path)
end
function px.lastModified(path)
return gce(path, 4) or olm(path)
end
end
fs.mount(px, lpath)

View File

@ -0,0 +1,111 @@
local fs = require "filesystem"
local fsproxy = {}
local fsfunc = {}
local function sanitisePath(path)
local pt = {}
for s in path:gmatch("([^/]+)") do
if s == ".." then
pt[#pt] = nil
elseif s == "." then
else
pt[#pt+1] = s
end
end
return table.concat(pt,"/")
end
function fsproxy.new(path,wp) -- string boolean -- table -- Returns a proxy object for a given path that acts like a filesystem.
local cpath = fs.canonical(path).."/"
if not fs.exists(path) or not fs.isDirectory(path) then
error("invalid directory")
end
local originalProxy = fs.get(path)
local proxy = {}
for k,v in pairs(originalProxy) do
if type(v) ~= "table" then
proxy[k] = v
end
end
local handles = {}
function proxy.isReadOnly()
return originalProxy.isReadOnly()
end
function proxy.getLabel()
return originalProxy.getLabel()
end
function proxy.spaceUsed()
return originalProxy.spaceUsed()
end
function proxy.spaceTotal()
return originalProxy.spaceTotal()
end
function proxy.exists(path)
return fs.exists(cpath..sanitisePath(path))
end
function proxy.isDirectory(path)
return fs.isDirectory(cpath..sanitisePath(path))
end
function proxy.makeDirectory(path)
if wp then return false, "read-only filesystem" end
return fs.makeDirectory(cpath..sanitisePath(path))
end
function proxy.rename(from, to)
if wp then return false, "read-only filesystem" end
return fs.rename(cpath..sanitisePath(from),cpath..sanitisePath(to))
end
function proxy.list(path)
local rt = {}
local iter, err = fs.list(cpath..sanitisePath(path))
if not iter then return nil, err end
for name in iter do
rt[#rt+1] = name
end
return rt
end
function proxy.lastModified(path)
return fs.lastModified(cpath..sanitisePath(path))
end
function proxy.remove(path)
if wp then return false, "read-only filesystem" end
return fs.remove(cpath..sanitisePath(path))
end
function proxy.size(path)
return fs.size(cpath..sanitisePath(path))
end
function proxy.dirstat(path) -- bundle the metadata about files into the listing to reduce round trips
local rt = {}
local dp = cpath..sanitisePath(path).."/"
local iter, err = fs.list(dp)
if not iter then return nil, err end
for name in iter do
rt[name] = {fs.isDirectory(dp..name), fs.size(dp..name), fs.lastModified(dp..name)}
end
return rt
end
function proxy.open(path,mode)
if wp and mode:find("[wa]") then return false, "read-only filesystem" end
local h, e = fs.open(cpath..sanitisePath(path),mode)
if not h then return h, e end
handles[#handles+1] = h
return #handles
end
function proxy.seek(handle, whence, offset)
return handles[handle]:seek(whence,offset)
end
function proxy.write(handle, data)
return handles[handle]:write(data)
end
function proxy.read(handle, count)
return handles[handle]:read(count)
end
function proxy.close(handle)
return handles[handle]:close()
end
return proxy
end
return fsproxy

44
MTFS/README.md Normal file
View File

@ -0,0 +1,44 @@
# MTFS
MTFS is, in effect, a standardised set of names for function endpoints implementing the [API of an OpenComputers filesystem component](https://ocdoc.cil.li/component:filesystem), over Minitel RPC.
## Function names
Names for filesystem "component" functions should follow the form `fs_<identifier>_<function>`, so, for example, `fs_/usr_isDirectory`.
## Functions
Unless otherwise specified, functions should be inherited from, or implemented to emulate, an [OpenComputers filesystem component](https://ocdoc.cil.li/component:filesystem). More specifically:
- spaceUsed
- seek
- makeDirectory
- exists
- isReadOnly
- write
- spaceTotal
- isDirectory
- rename
- list
- lastModified
- getLabel
- setLabel
- remove
- size
- open
- read
- write
- close
### Optional extensions
#### dirstat
An optional extra function, `dirstat`, may be implemented on the server side to allow clients to access directory listings with less RPC calls; it should return data in a Lua table of the following structure:
```
{
[filename: string] = {isDirectory: boolean, size: boolean, lastModified: number},
...
}
```

View File

@ -203,4 +203,18 @@
authors = "Izaya",
repo = "tree/master/"
},
["mtfs"] = {
files = {
["master/MTFS/OpenOS/usr/bin/importfs.lua"] = "/bin",
["master/MTFS/OpenOS/usr/bin/exportfs.lua"] = "/bin",
["master/MTFS/OpenOS/usr/lib/fsproxy.lua"] = "/lib",
},
dependencies = {
["mtrpc"] = ""
},
name = "Minitel Remote Filesystem",
description = "Utilities for accessing filesystems over Minitel RPC",
authors = "Izaya",
repo = "tree/master/"
},
}