diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/mount.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/mount.lua index de5eb75b7..12b64d2df 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/mount.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/mount.lua @@ -1,18 +1,37 @@ 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. + Note that multiple options can be used together + -r, --ro Mount the filesystem read only + --bind Create a mount bind point, folder to folder + + 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 = {} - + for proxy,path in fs.mounts() do local device = {} @@ -25,7 +44,7 @@ if argc == 0 then local dev_mounts = mounts[device.dev_path] table.insert(dev_mounts, device) end - + local smounts = {} for key,value in pairs(mounts) do smounts[#smounts+1] = {key, value} @@ -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 [ ]") - 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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_filesystem.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_filesystem.lua index 99328e9eb..bfa44f849 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_filesystem.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_filesystem.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/ro_wrapper.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/ro_wrapper.lua deleted file mode 100644 index c9f2f3d12..000000000 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/ro_wrapper.lua +++ /dev/null @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua index a8e91d315..b35cc598a 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua @@ -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 - end - if c:sub(1, filter:len()) == filter then - address = c - break - end + 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 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) diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua index 394247935..3f356ebff 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua @@ -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 } diff --git a/src/main/resources/assets/opencomputers/loot/openos/usr/man/mount b/src/main/resources/assets/opencomputers/loot/openos/usr/man/mount index 491f15777..1236216d1 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/usr/man/mount +++ b/src/main/resources/assets/opencomputers/loot/openos/usr/man/mount @@ -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 - Mounts a readonly access path of tmpfs to /tmp_ro \ No newline at end of file + 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