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 shell = require("shell")
local args, ops = shell.parse(...)
local argc = #args
local function usage()
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
print("see `man mount` for help");
See `man mount` for more details
]==])
os.exit(1)
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
local mounts = {}
@ -38,29 +57,40 @@ if argc == 0 then
local rw_ro = "(" .. device.rw_ro .. ")"
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),
device.mount_path,
rw_ro,
fs_label))
end
end
elseif argc ~= 2 then
print("Usage: mount [<label|address> <path>]")
print("Note that the address may be abbreviated.")
return 1 -- error code
else
local proxy, reason = fs.proxy(args[1])
end
local function do_mount()
-- bind converts a path to a proxy
local proxy, reason = fs.proxy(args[1], opts)
if not proxy then
io.stderr:write(reason,"\n")
return 1
elseif ops.r then
proxy = dofile("/lib/core/ro_wrapper.lua").wrap(proxy)
io.stderr:write("Failed to mount: ", tostring(reason), "\n")
os.exit(1)
end
local result, mount_failure = fs.mount(proxy, shell.resolve(args[2]))
if not result then
io.stderr:write(mount_failure, "\n")
return 2 -- error code
os.exit(2) -- error code
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 component = require("component")
function filesystem.makeDirectory(path)
if filesystem.exists(path) then
@ -136,3 +137,101 @@ function filesystem.copy(fromPath, toPath)
return data == nil, reason
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]
end
function filesystem.proxy(filter)
function filesystem.proxy(filter, options)
checkArg(1, filter, "string")
local address
for c in component.list("filesystem", true) do
if component.invoke(c, "getLabel") == filter then
address = c
break
if not component.list("filesystem")[filter] then
-- if not, load fs full library, it has a smarter proxy that also supports options
return filesystem.internal.proxy(filter, options)
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
return component.proxy(address)
return component.proxy(filter) -- it might be a perfect match
end
function filesystem.exists(path)

View File

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

View File

@ -5,9 +5,12 @@ SYNOPSIS
mount
mount LABEL PATH
mount ADDRESS PATH
mount --bind PATH PATH
OPTIONS
-r mount filesystem readonly
-r, --readonly mount filesystem readonly
--bind mount a bind point (folder to folder)
-h, --help print help message
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).
@ -22,5 +25,8 @@ EXAMPLES
mount 56f /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
mount --bind /mnt/fa4/home /home
Mounts /mnt/fa5/home to /home