hard drives. writable frikkin hardrives. next up: limiting their capacity...

This commit is contained in:
Florian Nücke 2013-10-04 01:13:55 +02:00
parent 0927f076ce
commit e85afe8518
10 changed files with 149 additions and 69 deletions

View File

@ -1,5 +1,7 @@
driver.fs = {} driver.fs = {}
-------------------------------------------------------------------------------
local mtab = {children={}} local mtab = {children={}}
local function segments(path) local function segments(path)
@ -31,30 +33,20 @@ end
local function findNode(path, create) local function findNode(path, create)
local parts = segments(path) local parts = segments(path)
local node = mtab local node = mtab
for _, part in ipairs(parts) do for i = 1, #parts do
if not node.children[part] then if not node.children[parts[i]] then
if create then if create then
node.children[part] = {children={}} node.children[parts[i]] = {children={}, parent=node}
else else
return nil return node, table.concat(parts, "/", i)
end end
end end
node = node.children[part] node = node.children[parts[i]]
end end
return node return node
end end
local function findFs(path) -------------------------------------------------------------------------------
local parts = segments(path)
local node = mtab
for i = 1, #parts do
if not node.children[parts[i]] then
return node.fs, table.concat(parts, "/", i), node
end
node = node.children[parts[i]]
end
return node.fs, "", node
end
function driver.fs.mount(fs, path) function driver.fs.mount(fs, path)
assert(type(fs) == "string", assert(type(fs) == "string",
@ -71,18 +63,29 @@ end
function driver.fs.umount(fsOrPath) function driver.fs.umount(fsOrPath)
assert(type(fsOrPath) == "string", assert(type(fsOrPath) == "string",
"bad argument #1 (string expected, got " .. type(fsOrPath) .. ")") "bad argument #1 (string expected, got " .. type(fsOrPath) .. ")")
if type(fsOrPath) == "string" then local function removeEmptyNodes(node)
local node = findNode(fsOrPath) while node and node.parent and not node.fs and not next(node.children) do
if node and node.fs then for k, c in pairs(node.parent.children) do
node.fs = nil if c == node then
return true node.parent.children[k] = nil
break
end
end
node = node.parent
end end
end
local node, rest = findNode(fsOrPath)
if not rest and node.fs then
node.fs = nil
removeEmptyNodes(node)
return true
else else
local queue = {mtab} local queue = {mtab}
repeat repeat
local node = table.remove(queue) local node = table.remove(queue)
if node.fs == fsOrPath then if node.fs == fsOrPath then
node.fs = nil node.fs = nil
removeEmptyNodes(node)
return true return true
end end
for _, child in ipairs(node.children) do for _, child in ipairs(node.children) do
@ -92,30 +95,63 @@ function driver.fs.umount(fsOrPath)
end end
end end
function driver.fs.listdir(path) -------------------------------------------------------------------------------
local fs, subpath, node = findFs(path)
local result = {} function driver.fs.exists(path)
for k, _ in pairs(node.children) do local node, rest = findNode(path)
table.insert(result, k .. "/") if not rest then -- virtual directory
return true
end end
if fs then if node.fs then
local fsresult = {sendToNode(fs, "fs.list", subpath)} return sendToNode(node.fs, "fs.exists", rest)
for _, k in ipairs(fsresult) do end
table.insert(result, k) end
function driver.fs.size(path)
local node, rest = findNode(path)
if node.fs and rest then
return sendToNode(node.fs, "fs.size", rest)
end
return 0 -- no such file or directory or virtual directory
end
function driver.fs.listdir(path)
local node, rest = findNode(path)
local result
if node.fs then
result = {sendToNode(node.fs, "fs.list", rest or "")}
else
result = {}
end
if not rest then
for k, _ in pairs(node.children) do
table.insert(result, k .. "/")
end end
end end
table.sort(result) table.sort(result)
return table.unpack(result) return table.unpack(result)
end end
-------------------------------------------------------------------------------
function driver.fs.remove(path) function driver.fs.remove(path)
local node, rest = findNode(path)
if node.fs and rest then
return sendToNode(node.fs, "fs.remove", rest)
end
end end
function driver.fs.rename(oldPath, newPath) function driver.fs.rename(oldPath, newPath)
--[[ TODO moving between file systems will require actual data copying...
local node, rest = findNode(path)
local newNode, newRest = findNode(newPath)
if node.fs and rest and newNode and newRest then
return sendToNode(node.fs, "fs.rename", rest)
end
]]
end end
function driver.fs.tmpname() -------------------------------------------------------------------------------
end
local file = {} local file = {}
@ -306,35 +342,37 @@ function file.write(f, ...)
end end
buffer = buffer .. arg buffer = buffer .. arg
]] ]]
sendToNode(f.fs, "fs.write", arg) sendToNode(f.fs, "fs.write", f.handle, arg)
end end
return f return f
end end
-------------------------------------------------------------------------------
function driver.fs.open(path, mode) function driver.fs.open(path, mode)
assert(type(path) == "string", assert(type(path) == "string",
"bad argument #1 (string expected, got " .. type(path) .. ")") "bad argument #1 (string expected, got " .. type(path) .. ")")
mode = mode or "r" mode = mode or "r"
assert(({r=true, rb=true, w=true, wb=true, a=true, ab=true})[mode], assert(({r=true, rb=true, w=true, wb=true, a=true, ab=true})[mode],
"bad argument #2 (r[b], w[b] or a[b] expected, got " .. tostring(mode) .. ")") "bad argument #2 (r[b], w[b] or a[b] expected, got " .. tostring(mode) .. ")")
local fs, subpath = findFs(path) local node, rest = findNode(path)
if not fs then if not node.fs or not rest then -- files can only be in file systems
return nil, "file not found" return nil, "file not found"
end end
local handle, reason = sendToNode(fs, "fs.open", subpath, mode) local handle, reason = sendToNode(node.fs, "fs.open", rest or "", mode)
if not handle then if not handle then
return nil, reason return nil, reason
end end
return setmetatable({ return setmetatable({
fs = fs, fs = node.fs,
handle = handle, handle = handle,
bsize = 8 * 1024, bsize = 8 * 1024,
bmode = "no" bmode = "full"
}, { }, {
__index = file, __index = file,
__gc = function(f) __gc = function(f)
-- File.close does a syscall, which yields, and that's not possible in -- file.close does a syscall, which yields, and that's not possible in
-- the __gc metamethod. So we start a timer to do the yield. -- the __gc metamethod. So we start a timer to do the yield/cleanup.
event.timer(0, function() event.timer(0, function()
file.close(f) file.close(f)
end) end)
@ -353,19 +391,7 @@ function driver.fs.type(f)
end end
end end
-- Aliases for vanilla Lua. -------------------------------------------------------------------------------
os.remove = driver.fs.remove
os.rename = driver.fs.rename
os.tmpname = driver.fs.tmpname
io = {}
-- TODO io.flush = function() end
-- TODO io.lines = function(filename) end
io.open = driver.fs.open
-- TODO io.popen = function(prog, mode) end
io.read = driver.fs.read
-- TODO io.tmpfile = function() end
io.type = driver.fs.type
function loadfile(file, env) function loadfile(file, env)
local f, reason = driver.fs.open(file) local f, reason = driver.fs.open(file)

View File

@ -152,8 +152,8 @@ end
local function main(args) local function main(args)
local function init() local function init()
sandbox.driver.fs.mount(os.romAddress(), "/rom") sandbox.driver.fs.mount(os.romAddress(), "/boot")
local result, reason = sandbox.loadfile("/rom/init.lua") local result, reason = sandbox.loadfile("/boot/init.lua")
if not result then if not result then
error(reason) error(reason)
end end

View File

@ -0,0 +1,31 @@
-- Aliases for vanilla Lua.
os.remove = driver.fs.remove
os.rename = driver.fs.rename
-- TODO os.tmpname = function() end
io = {}
-- TODO io.flush = function() end
-- TODO io.lines = function(filename) end
io.open = driver.fs.open
-- TODO io.popen = function(prog, mode) end
io.read = driver.fs.read
-- TODO io.tmpfile = function() end
io.type = driver.fs.type
-------------------------------------------------------------------------------
event.listen("component_added", function(_, address)
if component.type(address) == "filesystem" then
local name = address:sub(1, 3)
repeat
name = address:sub(1, name:len() + 1)
until not driver.fs.exists("/dev/" .. name)
driver.fs.mount(address, "/dev/" .. name)
end
end)
event.listen("component_removed", function(_, address)
if component.type(address) == "filesystem" then
driver.fs.umount(address)
end
end)

View File

@ -1,7 +1,10 @@
dofile("/rom/api/event.lua") dofile("/boot/api/event.lua")
dofile("/rom/api/component.lua") dofile("/boot/api/component.lua")
dofile("/rom/api/term.lua") dofile("/boot/api/filesystem.lua")
dofile("/rom/sh.lua") dofile("/boot/api/term.lua")
dofile("/boot/sh.lua")
driver.fs.umount("/boot")
event.fire(...) event.fire(...)
while true do while true do

View File

@ -6,22 +6,24 @@ object Items {
var multi: item.Delegator = null var multi: item.Delegator = null
var gpu: item.GraphicsCard = null var gpu: item.GraphicsCard = null
var hdd: item.Hdd = null
var rs: item.RedstoneCard = null var rs: item.RedstoneCard = null
var ram32k: item.Memory = null var ram32k: item.Memory = null
var ram64k: item.Memory = null var ram64k: item.Memory = null
var ram128k: item.Memory = null var ram128k: item.Memory = null
var hdd: item.Hdd = null
def init() { def init() {
multi = new item.Delegator(Config.itemId) multi = new item.Delegator(Config.itemId)
gpu = new item.GraphicsCard(multi) gpu = new item.GraphicsCard(multi)
hdd = new item.Hdd(multi)
rs = new item.RedstoneCard(multi) rs = new item.RedstoneCard(multi)
ram32k = new item.Memory(multi, 32) ram32k = new item.Memory(multi, 32)
ram64k = new item.Memory(multi, 64) ram64k = new item.Memory(multi, 64)
ram128k = new item.Memory(multi, 128) ram128k = new item.Memory(multi, 128)
hdd = new item.Hdd(multi, 2 * 1024 * 1024)
} }
} }

View File

@ -100,7 +100,7 @@ class Delegator(id: Int) extends Block(id, Material.iron) {
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def breakBlock(world: World, x: Int, y: Int, z: Int, blockId: Int, metadata: Int) = { override def breakBlock(world: World, x: Int, y: Int, z: Int, blockId: Int, metadata: Int) = {
subBlock(world, x, y, z) match { subBlock(metadata) match {
case None => // Invalid but avoid match error. case None => // Invalid but avoid match error.
case Some(subBlock) => { case Some(subBlock) => {
world.getBlockTileEntity(x, y, z) match { world.getBlockTileEntity(x, y, z) match {

View File

@ -1,5 +1,5 @@
package li.cil.oc.common.item package li.cil.oc.common.item
class Hdd(val parent: Delegator) extends Delegate { class Hdd(val parent: Delegator, val capacity: Int) extends Delegate {
def unlocalizedName = "HardDiskDrive" def unlocalizedName = "HardDiskDrive"
} }

View File

@ -46,6 +46,7 @@ trait ComponentInventory extends IInventory with Node {
} }
} }
} }
computer.recomputeMemory()
} }
override def save(nbt: NBTTagCompound) = { override def save(nbt: NBTTagCompound) = {

View File

@ -39,7 +39,7 @@ class FileSystem(val fileSystem: api.FileSystem) extends Node {
None None
case Array(path: Array[Byte]) if message.name == "fs.exists" => case Array(path: Array[Byte]) if message.name == "fs.exists" =>
Some(Array(fileSystem.exists(clean(path)).asInstanceOf[Any])) Some(Array(fileSystem.exists(clean(path)).asInstanceOf[Any]))
case Array(path: Array[Byte]) if message.name == "fs.exists" => case Array(path: Array[Byte]) if message.name == "fs.size" =>
Some(Array(fileSystem.size(clean(path)).asInstanceOf[Any])) Some(Array(fileSystem.size(clean(path)).asInstanceOf[Any]))
case Array(path: Array[Byte]) if message.name == "fs.isDirectory" => case Array(path: Array[Byte]) if message.name == "fs.isDirectory" =>
Some(Array(fileSystem.isDirectory(clean(path)).asInstanceOf[Any])) Some(Array(fileSystem.isDirectory(clean(path)).asInstanceOf[Any]))

View File

@ -1,16 +1,33 @@
package li.cil.oc.server.driver package li.cil.oc.server.driver
import li.cil.oc
import li.cil.oc.api.driver.{Item, Slot}
import li.cil.oc.{Config, Items} import li.cil.oc.{Config, Items}
import li.cil.oc.api.driver
import li.cil.oc.api.driver.Slot
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
object FileSystem extends driver.Item { object FileSystem extends Item {
override def api = Option(getClass.getResourceAsStream(Config.driverPath + "fs.lua")) override def api = Option(getClass.getResourceAsStream(Config.driverPath + "fs.lua"))
override def worksWith(item: ItemStack) = WorksWith(Items.hdd)(item) override def worksWith(item: ItemStack) = WorksWith(Items.hdd)(item)
override def slot(item: ItemStack) = Slot.HDD override def slot(item: ItemStack) = Slot.HDD
override def node(item: ItemStack) = None override def node(item: ItemStack) = {
// We have a bit of a chicken-egg problem here, because we want to use the
// node's address as the folder name... so we generate the address here,
// if necessary. No one will know, right? Right!?
val tag = nbt(item)
val address =
if (tag.hasKey("address"))
tag.getString("address")
else
java.util.UUID.randomUUID().toString
oc.api.FileSystem.fromSaveDir(address).flatMap(oc.api.FileSystem.asNode) match {
case None => None
case Some(node) =>
node.address = Some(address)
node.load(tag)
Some(node)
}
}
} }