2019-11-02 04:37:08 +00:00
|
|
|
local fs = _G.fs
|
|
|
|
|
2016-12-11 19:24:52 +00:00
|
|
|
if fs.native then
|
2018-01-24 22:39:38 +00:00
|
|
|
return
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
2019-06-28 17:50:02 +00:00
|
|
|
local Util = require('opus.util')
|
2017-09-05 06:09:31 +00:00
|
|
|
|
2016-12-11 19:24:52 +00:00
|
|
|
fs.native = Util.shallowCopy(fs)
|
|
|
|
|
|
|
|
local fstypes = { }
|
|
|
|
local nativefs = { }
|
|
|
|
|
|
|
|
for k,fn in pairs(fs) do
|
2018-01-24 22:39:38 +00:00
|
|
|
if type(fn) == 'function' then
|
2019-11-02 04:37:08 +00:00
|
|
|
nativefs[k] = function(_, ...)
|
2018-01-24 22:39:38 +00:00
|
|
|
return fn(...)
|
|
|
|
end
|
|
|
|
end
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
2020-04-27 21:44:09 +00:00
|
|
|
function nativefs.resolve(_, dir)
|
|
|
|
return dir
|
|
|
|
end
|
|
|
|
|
2017-10-08 21:45:01 +00:00
|
|
|
function nativefs.list(node, dir)
|
2018-01-24 22:39:38 +00:00
|
|
|
local files
|
|
|
|
if fs.native.isDir(dir) then
|
|
|
|
files = fs.native.list(dir)
|
|
|
|
end
|
|
|
|
|
|
|
|
local function inList(l, e)
|
|
|
|
for _,v in ipairs(l) do
|
|
|
|
if v == e then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if dir == node.mountPoint and node.nodes then
|
|
|
|
files = files or { }
|
|
|
|
for k in pairs(node.nodes) do
|
|
|
|
if not inList(files, k) then
|
|
|
|
table.insert(files, k)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if not files then
|
|
|
|
error('Not a directory', 2)
|
|
|
|
end
|
|
|
|
|
|
|
|
return files
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
2017-05-12 05:12:58 +00:00
|
|
|
function nativefs.getSize(node, dir, recursive)
|
2018-01-24 22:39:38 +00:00
|
|
|
if recursive and fs.native.isDir(dir) then
|
|
|
|
local function sum(dir)
|
|
|
|
local total = 0
|
|
|
|
local files = fs.native.list(dir)
|
|
|
|
for _,f in ipairs(files) do
|
|
|
|
local fullName = fs.combine(dir, f)
|
|
|
|
if fs.native.isDir(fullName) then
|
|
|
|
total = total + sum(fullName)
|
|
|
|
else
|
|
|
|
total = total + fs.native.getSize(fullName)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return total
|
|
|
|
end
|
|
|
|
return sum(dir)
|
|
|
|
end
|
|
|
|
if node.mountPoint == dir and node.nodes then
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
return fs.native.getSize(dir)
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function nativefs.isDir(node, dir)
|
2018-01-24 22:39:38 +00:00
|
|
|
if node.mountPoint == dir then
|
|
|
|
return not not node.nodes
|
|
|
|
end
|
|
|
|
return fs.native.isDir(dir)
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function nativefs.exists(node, dir)
|
2018-01-24 22:39:38 +00:00
|
|
|
if node.mountPoint == dir then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
return fs.native.exists(dir)
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
2019-04-08 13:30:47 +00:00
|
|
|
function nativefs.getDrive(node, dir)
|
|
|
|
if node.mountPoint == dir then
|
|
|
|
return fs.native.getDrive(dir) or 'virt'
|
|
|
|
end
|
|
|
|
return fs.native.getDrive(dir)
|
|
|
|
end
|
|
|
|
|
2016-12-11 19:24:52 +00:00
|
|
|
function nativefs.delete(node, dir)
|
2018-01-24 22:39:38 +00:00
|
|
|
if node.mountPoint == dir then
|
|
|
|
fs.unmount(dir)
|
2020-05-08 03:34:50 +00:00
|
|
|
-- hack here
|
|
|
|
-- if a file is mounted over an existing directory
|
|
|
|
-- ie. sys/apps/MOUNT.LUA
|
|
|
|
-- then sys and sys/apps are created as temp nodes
|
2020-05-09 04:32:44 +00:00
|
|
|
-- therefore, trying to delete sys was only
|
2020-05-08 03:34:50 +00:00
|
|
|
-- removing the node and not deleting the directory
|
|
|
|
-- Need a better way to backfill nodes in a way
|
|
|
|
-- that preserves the vfs functionality
|
|
|
|
|
2020-05-09 04:32:44 +00:00
|
|
|
-- perhaps a flag that denotes that this
|
2020-05-08 03:34:50 +00:00
|
|
|
-- file/directory is the actual mount
|
|
|
|
|
|
|
|
-- this hack will not fix
|
|
|
|
-- rm packages/common
|
|
|
|
-- where packages is linked from a drive
|
|
|
|
-- and urls are mounted under packages/common
|
|
|
|
-- (as the fstype will be linkfs)
|
|
|
|
if node.fstype == 'nativefs' then
|
|
|
|
fs.native.delete(dir)
|
|
|
|
end
|
2018-01-24 22:39:38 +00:00
|
|
|
else
|
|
|
|
fs.native.delete(dir)
|
|
|
|
end
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
fstypes.nativefs = nativefs
|
|
|
|
fs.nodes = {
|
2018-01-24 22:39:38 +00:00
|
|
|
fs = nativefs,
|
|
|
|
mountPoint = '',
|
|
|
|
fstype = 'nativefs',
|
|
|
|
nodes = { },
|
2016-12-11 19:24:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
local function splitpath(path)
|
2018-01-24 22:39:38 +00:00
|
|
|
local parts = { }
|
|
|
|
for match in string.gmatch(path, "[^/]+") do
|
|
|
|
table.insert(parts, match)
|
|
|
|
end
|
|
|
|
return parts
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
local function getNode(dir)
|
2019-03-13 00:04:28 +00:00
|
|
|
if not dir then error('Invalid directory', 2) end
|
2018-01-24 22:39:38 +00:00
|
|
|
local cd = fs.combine(dir, '')
|
|
|
|
local parts = splitpath(cd)
|
|
|
|
local node = fs.nodes
|
|
|
|
|
|
|
|
for _,d in ipairs(parts) do
|
|
|
|
if node.nodes and node.nodes[d] then
|
|
|
|
node = node.nodes[d]
|
|
|
|
else
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return node
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
2020-04-27 01:39:58 +00:00
|
|
|
fs.getNode = getNode
|
|
|
|
|
2016-12-11 19:24:52 +00:00
|
|
|
local methods = { 'delete', 'getFreeSpace', 'exists', 'isDir', 'getSize',
|
2020-04-27 21:44:09 +00:00
|
|
|
'isReadOnly', 'makeDir', 'getDrive', 'list', 'open', 'attributes' }
|
2016-12-11 19:24:52 +00:00
|
|
|
|
|
|
|
for _,m in pairs(methods) do
|
2018-01-24 22:39:38 +00:00
|
|
|
fs[m] = function(dir, ...)
|
|
|
|
dir = fs.combine(dir or '', '')
|
|
|
|
local node = getNode(dir)
|
|
|
|
return node.fs[m](node, dir, ...)
|
|
|
|
end
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
2020-04-27 21:44:09 +00:00
|
|
|
-- if a link, return the source for this link
|
|
|
|
function fs.resolve(dir)
|
|
|
|
local n = getNode(dir)
|
|
|
|
return n.fs.resolve and n.fs.resolve(n, dir) or dir
|
|
|
|
end
|
|
|
|
|
2016-12-11 19:24:52 +00:00
|
|
|
function fs.complete(partial, dir, includeFiles, includeSlash)
|
2018-01-24 22:39:38 +00:00
|
|
|
dir = fs.combine(dir, '')
|
|
|
|
local node = getNode(dir)
|
|
|
|
if node.fs.complete then
|
|
|
|
return node.fs.complete(node, partial, dir, includeFiles, includeSlash)
|
|
|
|
end
|
|
|
|
return fs.native.complete(partial, dir, includeFiles, includeSlash)
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
2017-08-05 08:17:06 +00:00
|
|
|
function fs.listEx(dir)
|
2018-03-30 17:12:46 +00:00
|
|
|
dir = fs.combine(dir, '')
|
2018-01-24 22:39:38 +00:00
|
|
|
local node = getNode(dir)
|
|
|
|
if node.fs.listEx then
|
|
|
|
return node.fs.listEx(node, dir)
|
|
|
|
end
|
|
|
|
|
|
|
|
local t = { }
|
|
|
|
local files = node.fs.list(node, dir)
|
|
|
|
|
|
|
|
pcall(function()
|
|
|
|
for _,f in ipairs(files) do
|
|
|
|
local fullName = fs.combine(dir, f)
|
|
|
|
local file = {
|
|
|
|
name = f,
|
|
|
|
isDir = fs.isDir(fullName),
|
|
|
|
isReadOnly = fs.isReadOnly(fullName),
|
|
|
|
}
|
|
|
|
if not file.isDir then
|
|
|
|
file.size = fs.getSize(fullName)
|
|
|
|
end
|
|
|
|
table.insert(t, file)
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
return t
|
2017-08-05 08:17:06 +00:00
|
|
|
end
|
|
|
|
|
2016-12-11 19:24:52 +00:00
|
|
|
function fs.copy(s, t)
|
2019-03-13 00:04:28 +00:00
|
|
|
if not s then error('copy: bad argument #1') end
|
|
|
|
if not t then error('copy: bad argument #2') end
|
2018-01-24 22:39:38 +00:00
|
|
|
local sp = getNode(s)
|
|
|
|
local tp = getNode(t)
|
|
|
|
if sp == tp and sp.fs.copy then
|
|
|
|
return sp.fs.copy(sp, s, t)
|
|
|
|
end
|
|
|
|
|
|
|
|
if fs.exists(t) then
|
|
|
|
error('File exists')
|
|
|
|
end
|
|
|
|
|
|
|
|
if fs.isDir(s) then
|
|
|
|
fs.makeDir(t)
|
|
|
|
local list = fs.list(s)
|
|
|
|
for _,f in ipairs(list) do
|
|
|
|
fs.copy(fs.combine(s, f), fs.combine(t, f))
|
|
|
|
end
|
|
|
|
|
|
|
|
else
|
|
|
|
local sf = Util.readFile(s)
|
|
|
|
if not sf then
|
|
|
|
error('No such file')
|
|
|
|
end
|
|
|
|
|
|
|
|
Util.writeFile(t, sf)
|
|
|
|
end
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function fs.find(spec) -- not optimized
|
|
|
|
-- local node = getNode(spec)
|
|
|
|
-- local files = node.fs.find(node, spec)
|
2018-01-24 22:39:38 +00:00
|
|
|
local files = { }
|
|
|
|
-- method from https://github.com/N70/deltaOS/blob/dev/vfs
|
2019-11-08 05:50:11 +00:00
|
|
|
|
|
|
|
-- REVISIT - see globbing in shellex package
|
2018-01-24 22:39:38 +00:00
|
|
|
local function recurse_spec(results, path, spec)
|
|
|
|
local segment = spec:match('([^/]*)'):gsub('/', '')
|
|
|
|
local pattern = '^' .. segment:gsub("[%.%[%]%(%)%%%+%-%?%^%$]","%%%1"):gsub("%z","%%z"):gsub("%*","[^/]-") .. '$'
|
|
|
|
if fs.isDir(path) then
|
|
|
|
for _, file in ipairs(fs.list(path)) do
|
|
|
|
if file:match(pattern) then
|
|
|
|
local f = fs.combine(path, file)
|
|
|
|
if spec == segment then
|
|
|
|
table.insert(results, f)
|
|
|
|
end
|
|
|
|
if fs.isDir(f) then
|
|
|
|
recurse_spec(results, f, spec:sub(#segment + 2))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
recurse_spec(files, '', spec)
|
|
|
|
table.sort(files)
|
|
|
|
|
|
|
|
return files
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function fs.move(s, t)
|
2018-01-24 22:39:38 +00:00
|
|
|
local sp = getNode(s)
|
|
|
|
local tp = getNode(t)
|
|
|
|
if sp == tp and sp.fs.move then
|
|
|
|
return sp.fs.move(sp, s, t)
|
|
|
|
end
|
|
|
|
fs.copy(s, t)
|
|
|
|
fs.delete(s)
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
local function getfstype(fstype)
|
2018-01-24 22:39:38 +00:00
|
|
|
local vfs = fstypes[fstype]
|
|
|
|
if not vfs then
|
2019-06-28 17:50:02 +00:00
|
|
|
vfs = require('opus.fs.' .. fstype)
|
2018-01-24 22:39:38 +00:00
|
|
|
fs.registerType(fstype, vfs)
|
|
|
|
end
|
|
|
|
return vfs
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function fs.mount(path, fstype, ...)
|
2018-01-24 22:39:38 +00:00
|
|
|
local vfs = getfstype(fstype)
|
|
|
|
if not vfs then
|
|
|
|
error('Invalid file system type')
|
|
|
|
end
|
2020-04-27 04:07:18 +00:00
|
|
|
|
2020-04-27 21:44:09 +00:00
|
|
|
-- get the mount point for the path
|
|
|
|
-- ie. if packages is mapped to disk/packages
|
|
|
|
-- and a request to mount /packages/foo
|
|
|
|
-- then use disk/packages/foo as the mountPoint
|
|
|
|
path = fs.resolve(path)
|
2020-04-27 04:07:18 +00:00
|
|
|
|
2018-01-24 22:39:38 +00:00
|
|
|
local node = vfs.mount(path, ...)
|
|
|
|
if node then
|
|
|
|
local parts = splitpath(path)
|
|
|
|
local targetName = table.remove(parts, #parts)
|
|
|
|
|
|
|
|
local tp = fs.nodes
|
|
|
|
for _,d in ipairs(parts) do
|
|
|
|
if not tp.nodes then
|
|
|
|
tp.nodes = { }
|
|
|
|
end
|
|
|
|
if not tp.nodes[d] then
|
|
|
|
tp.nodes[d] = Util.shallowCopy(tp)
|
2020-04-27 04:07:18 +00:00
|
|
|
|
2020-04-27 21:44:09 +00:00
|
|
|
if tp.nodes[d].source then
|
2020-04-27 02:36:50 +00:00
|
|
|
tp.nodes[d].source = fs.combine(tp.nodes[d].source, d)
|
|
|
|
end
|
2020-04-27 04:07:18 +00:00
|
|
|
|
2018-01-24 22:39:38 +00:00
|
|
|
tp.nodes[d].nodes = { }
|
|
|
|
tp.nodes[d].mountPoint = fs.combine(tp.mountPoint, d)
|
|
|
|
end
|
|
|
|
tp = tp.nodes[d]
|
|
|
|
end
|
|
|
|
|
|
|
|
node.fs = vfs
|
|
|
|
node.fstype = fstype
|
|
|
|
if not targetName then
|
|
|
|
node.mountPoint = ''
|
|
|
|
fs.nodes = node
|
|
|
|
else
|
|
|
|
node.mountPoint = fs.combine(tp.mountPoint, targetName)
|
|
|
|
tp.nodes[targetName] = node
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return node
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
2017-09-25 21:00:02 +00:00
|
|
|
function fs.loadTab(path)
|
2018-01-24 22:39:38 +00:00
|
|
|
local mounts = Util.readFile(path)
|
|
|
|
if mounts then
|
|
|
|
for _,l in ipairs(Util.split(mounts)) do
|
2019-11-08 05:50:11 +00:00
|
|
|
l = Util.trim(l)
|
|
|
|
if #l > 0 and l:sub(1, 1) ~= '#' then
|
2018-01-24 22:39:38 +00:00
|
|
|
local s, m = pcall(function()
|
|
|
|
fs.mount(table.unpack(Util.matches(l)))
|
|
|
|
end)
|
|
|
|
if not s then
|
|
|
|
_G.printError('Mount failed')
|
|
|
|
_G.printError(l)
|
|
|
|
_G.printError(m)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-09-25 21:00:02 +00:00
|
|
|
end
|
|
|
|
|
2016-12-11 19:24:52 +00:00
|
|
|
local function getNodeByParts(parts)
|
2018-01-24 22:39:38 +00:00
|
|
|
local node = fs.nodes
|
|
|
|
|
|
|
|
for _,d in ipairs(parts) do
|
|
|
|
if not node.nodes[d] then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
node = node.nodes[d]
|
|
|
|
end
|
|
|
|
return node
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function fs.unmount(path)
|
2018-01-24 22:39:38 +00:00
|
|
|
local parts = splitpath(path)
|
|
|
|
local targetName = table.remove(parts, #parts)
|
2016-12-11 19:24:52 +00:00
|
|
|
|
2018-01-24 22:39:38 +00:00
|
|
|
local node = getNodeByParts(parts)
|
2016-12-11 19:24:52 +00:00
|
|
|
|
2018-01-24 22:39:38 +00:00
|
|
|
if node and node.nodes[targetName] then
|
|
|
|
node.nodes[targetName] = nil
|
|
|
|
end
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
2019-11-02 04:37:08 +00:00
|
|
|
function fs.registerType(name, vfs)
|
|
|
|
fstypes[name] = vfs
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function fs.getTypes()
|
2018-01-24 22:39:38 +00:00
|
|
|
return fstypes
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function fs.restore()
|
2018-01-24 22:39:38 +00:00
|
|
|
local native = fs.native
|
|
|
|
Util.clear(fs)
|
|
|
|
Util.merge(fs, native)
|
2016-12-11 19:24:52 +00:00
|
|
|
end
|