mount bind point support

This commit is contained in:
payonel 2017-11-15 21:24:12 -08:00
parent ebb7dcc0d2
commit 89abb6f30b
6 changed files with 164 additions and 69 deletions

View File

@ -1,15 +1,34 @@
local fs = require("filesystem") local fs = require("filesystem")
local shell = require("shell") local shell = require("shell")
local args, ops = shell.parse(...) local function usage()
local argc = #args io.stderr:write([==[
Usage: mount [OPTIONS] [device path]")
If no args are given, all current mount points are printed.
<Options> Note that multiple options can be used together
-r, --ro Mount the filesystem read only
--bind Create a mount bind point, folder to folder
<Args>
device Specify filesystem device by one of:
a. label
b. address (can be abbreviated)
c. folder path (requires --bind)
path Target folder path to mount to
if ops and (ops.h or ops.help) then See `man mount` for more details
print("see `man mount` for help"); ]==])
os.exit(1) os.exit(1)
end end
if argc == 0 then -- smart parse, follow arg after -o
local args, opts = shell.parse(...)
opts.readonly = opts.r or opts.readonly
if opts.h or opts.help then
usage()
end
local function print_mounts()
-- for each mount -- for each mount
local mounts = {} local mounts = {}
@ -38,29 +57,40 @@ if argc == 0 then
local rw_ro = "(" .. device.rw_ro .. ")" local rw_ro = "(" .. device.rw_ro .. ")"
local fs_label = "\"" .. device.fs_label .. "\"" local fs_label = "\"" .. device.fs_label .. "\""
io.write(string.format("%s on %-10s %s %s\n", io.write(string.format("%-8s on %-10s %s %s\n",
dev_path:sub(1,8), dev_path:sub(1,8),
device.mount_path, device.mount_path,
rw_ro, rw_ro,
fs_label)) fs_label))
end end
end end
elseif argc ~= 2 then end
print("Usage: mount [<label|address> <path>]")
print("Note that the address may be abbreviated.") local function do_mount()
return 1 -- error code -- bind converts a path to a proxy
else local proxy, reason = fs.proxy(args[1], opts)
local proxy, reason = fs.proxy(args[1])
if not proxy then if not proxy then
io.stderr:write(reason,"\n") io.stderr:write("Failed to mount: ", tostring(reason), "\n")
return 1 os.exit(1)
elseif ops.r then
proxy = dofile("/lib/core/ro_wrapper.lua").wrap(proxy)
end end
local result, mount_failure = fs.mount(proxy, shell.resolve(args[2])) local result, mount_failure = fs.mount(proxy, shell.resolve(args[2]))
if not result then if not result then
io.stderr:write(mount_failure, "\n") io.stderr:write(mount_failure, "\n")
return 2 -- error code os.exit(2) -- error code
end end
end end
if #args == 0 then
if next(opts) then
io.stderr:write("Missing argument\n")
usage()
else
print_mounts()
end
elseif #args == 2 then
do_mount()
else
io.stderr:write("wrong number of arguments: ", #args, "\n")
usage()
end

View File

@ -1,4 +1,5 @@
local filesystem = require("filesystem") local filesystem = require("filesystem")
local component = require("component")
function filesystem.makeDirectory(path) function filesystem.makeDirectory(path)
if filesystem.exists(path) then if filesystem.exists(path) then
@ -136,3 +137,101 @@ function filesystem.copy(fromPath, toPath)
return data == nil, reason return data == nil, reason
end end
local function readonly_wrap(proxy)
checkArg(1, proxy, "table")
if proxy.isReadOnly() then
return proxy
end
local function roerr() return nil, "filesystem is readonly" end
return setmetatable({
rename = roerr,
open = function(path, mode)
checkArg(1, path, "string")
checkArg(2, mode, "string")
if mode:match("[wa]") then
return roerr()
end
return proxy.open(path, mode)
end,
isReadOnly = function()
return true
end,
write = roerr,
setLabel = roerr,
makeDirectory = roerr,
remove = roerr,
}, {__index=proxy})
end
local function bind_proxy(path)
local real, reason = filesystem.realPath(path)
if not real then
return nil, reason
end
local real_fs, real_fs_path = filesystem.get(real)
if real == real_fs_path then
return real_fs
end
-- turn /tmp/foo into foo
local rest = real:sub(#real_fs_path + 1)
local function wrap_relative(fp)
return function(path, ...)
return fp(filesystem.concat(rest, path), ...)
end
end
local bind = {
address = real,
isReadOnly = real_fs.isReadOnly,
list = wrap_relative(real_fs.list),
isDirectory = wrap_relative(real_fs.isDirectory),
size = wrap_relative(real_fs.size),
lastModified = wrap_relative(real_fs.lastModified),
exists = wrap_relative(real_fs.exists),
open = wrap_relative(real_fs.open),
read = real_fs.read,
write = real_fs.write,
close = real_fs.close,
getLabel = function() return "" end,
setLabel = function() return nil, "cannot set the label of a bind point" end,
}
setmetatable(bind, { __index = function(...)
print("missing bind method", ...)
end
})
return bind
end
filesystem.internal = {}
function filesystem.internal.proxy(filter, options)
checkArg(1, filter, "string")
checkArg(2, options, "table", "nil")
options = options or {}
local address, proxy, reason
if options.bind then
proxy, reason = bind_proxy(filter)
else
-- no options: filter should be a label or partial address
for c in component.list("filesystem", true) do
if component.invoke(c, "getLabel") == filter then
address = c
break
end
if c:sub(1, filter:len()) == filter then
address = c
break
end
end
if not address then
return nil, "no such file system"
end
proxy, reason = component.proxy(address)
end
if not proxy then
return proxy, reason
end
if options.readonly then
proxy = readonly_wrap(proxy)
end
return proxy
end

View File

@ -1,30 +0,0 @@
local lib = {}
function lib.wrap(proxy)
checkArg(1, proxy, "table")
if proxy.isReadOnly() then
return proxy
end
local function roerr() return nil, "filesystem is readonly" end
return setmetatable({
rename = roerr,
open = function(path, mode)
checkArg(1, path, "string")
checkArg(2, mode, "string")
if mode:match("[wa]") then
return roerr()
end
return proxy.open(path, mode)
end,
isReadOnly = function()
return true
end,
write = roerr,
setLabel = roerr,
makeDirectory = roerr,
remove = roerr,
}, {__index=proxy})
end
return lib

View File

@ -234,23 +234,13 @@ function filesystem.name(path)
return parts[#parts] return parts[#parts]
end end
function filesystem.proxy(filter) function filesystem.proxy(filter, options)
checkArg(1, filter, "string") checkArg(1, filter, "string")
local address if not component.list("filesystem")[filter] then
for c in component.list("filesystem", true) do -- if not, load fs full library, it has a smarter proxy that also supports options
if component.invoke(c, "getLabel") == filter then return filesystem.internal.proxy(filter, options)
address = c
break
end end
if c:sub(1, filter:len()) == filter then return component.proxy(filter) -- it might be a perfect match
address = c
break
end
end
if not address then
return nil, "no such file system"
end
return component.proxy(address)
end end
function filesystem.exists(path) function filesystem.exists(path)

View File

@ -72,9 +72,9 @@ end
function package.delay(lib, file) function package.delay(lib, file)
local mt = { local mt = {
__index = function(tbl, key) __index = function(tbl, key)
dofile(file)
setmetatable(lib, nil) setmetatable(lib, nil)
setmetatable(lib.internal or {}, nil) setmetatable(lib.internal or {}, nil)
dofile(file)
return tbl[key] return tbl[key]
end end
} }

View File

@ -5,9 +5,12 @@ SYNOPSIS
mount mount
mount LABEL PATH mount LABEL PATH
mount ADDRESS PATH mount ADDRESS PATH
mount --bind PATH PATH
OPTIONS OPTIONS
-r mount filesystem readonly -r, --readonly mount filesystem readonly
--bind mount a bind point (folder to folder)
-h, --help print help message
DESCRIPTION DESCRIPTION
All files accessible in OpenOS are arranged in one big tree, starting with the root node, '/'. The files are the leaves of the tree, directories are inner nodes of the tree. Files can be distributed across several devices (file system components, such as hard drives and floppies). The `mount` command is used to attach a file system to this tree. The `umount` command can be used to remove a mounted file system from the tree (note that `rm` works for this, too). All files accessible in OpenOS are arranged in one big tree, starting with the root node, '/'. The files are the leaves of the tree, directories are inner nodes of the tree. Files can be distributed across several devices (file system components, such as hard drives and floppies). The `mount` command is used to attach a file system to this tree. The `umount` command can be used to remove a mounted file system from the tree (note that `rm` works for this, too).
@ -22,5 +25,8 @@ EXAMPLES
mount 56f /var mount 56f /var
Mounts the file system of which the address starts with `56f` at `/var`. Mounts the file system of which the address starts with `56f` at `/var`.
mount -r tmpfs /tmp_ro mount --readonly tmpfs /tmp_ro
Mounts a readonly access path of tmpfs to /tmp_ro Mounts a readonly access path of tmpfs to /tmp_ro
mount --bind /mnt/fa4/home /home
Mounts /mnt/fa5/home to /home