Advertisement
Guest User

filesystem.lua

a guest
Jan 14th, 2015
230
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.01 KB | None | 0 0
  1. local component = require("component")
  2. local unicode = require("unicode")
  3.  
  4. local filesystem, fileStream = {}, {}
  5. local isAutorunEnabled = nil
  6. local mtab = {name="", children={}, links={}}
  7.  
  8. local function segments(path)
  9.   path = path:gsub("\\", "/")
  10.   repeat local n; path, n = path:gsub("//", "/") until n == 0
  11.   local parts = {}
  12.   for part in path:gmatch("[^/]+") do
  13.     table.insert(parts, part)
  14.   end
  15.   local i = 1
  16.   while i <= #parts do
  17.     if parts[i] == "." then
  18.       table.remove(parts, i)
  19.     elseif parts[i] == ".." then
  20.       table.remove(parts, i)
  21.       i = i - 1
  22.       if i > 0 then
  23.         table.remove(parts, i)
  24.       else
  25.         i = 1
  26.       end
  27.     else
  28.       i = i + 1
  29.     end
  30.   end
  31.   return parts
  32. end
  33.  
  34. local function saveConfig()
  35.   local root = filesystem.get("/")
  36.   if root and not root.isReadOnly() then
  37.     filesystem.makeDirectory("/etc")
  38.     local f = io.open("/etc/filesystem.cfg", "w")
  39.     if f then
  40.       f:write("autorun="..tostring(isAutorunEnabled))
  41.       f:close()
  42.     end
  43.   end
  44. end
  45.  
  46. local function findNode(path, create, depth)
  47.   checkArg(1, path, "string")
  48.   depth = depth or 0
  49.   if depth > 100 then
  50.     error("link cycle detected")
  51.   end
  52.   local parts = segments(path)
  53.   local node = mtab
  54.   while #parts > 0 do
  55.     local part = parts[1]
  56.     if not node.children[part] then
  57.       if node.links[part] then
  58.         return findNode(filesystem.concat(node.links[part], table.concat(parts, "/", 2)), create, depth + 1)
  59.       else
  60.         if create then
  61.           node.children[part] = {name=part, parent=node, children={}, links={}}
  62.         else
  63.           local vnode, vrest = node, table.concat(parts, "/")
  64.           local rest = vrest
  65.           while node and not node.fs do
  66.             rest = filesystem.concat(node.name, rest)
  67.             node = node.parent
  68.           end
  69.           return node, rest, vnode, vrest
  70.         end
  71.       end
  72.     end
  73.     node = node.children[part]
  74.     table.remove(parts, 1)
  75.   end
  76.   local vnode, vrest = node, nil
  77.   local rest = nil
  78.   while node and not node.fs do
  79.     rest = rest and filesystem.concat(node.name, rest) or node.name
  80.     node = node.parent
  81.   end
  82.   return node, rest, vnode, vrest
  83. end
  84.  
  85. local function removeEmptyNodes(node)
  86.   while node and node.parent and not node.fs and not next(node.children) and not next(node.links) do
  87.     node.parent.children[node.name] = nil
  88.     node = node.parent
  89.   end
  90. end
  91.  
  92. -------------------------------------------------------------------------------
  93.  
  94. function filesystem.isAutorunEnabled()
  95.   if isAutorunEnabled == nil then
  96.     local env = {}
  97.     local config = loadfile("/etc/filesystem.cfg", nil, env)
  98.     if config then
  99.       pcall(config)
  100.       isAutorunEnabled = not not env.autorun
  101.     else
  102.       isAutorunEnabled = true
  103.     end
  104.     saveConfig()
  105.   end
  106.   return isAutorunEnabled
  107. end
  108.  
  109. function filesystem.setAutorunEnabled(value)
  110.   checkArg(1, value, "boolean")
  111.   isAutorunEnabled = value
  112.   saveConfig()
  113. end
  114.  
  115. function filesystem.segments(path)
  116.   return segments(path)
  117. end
  118.  
  119. function filesystem.canonical(path)
  120.   local result = table.concat(segments(path), "/")
  121.   if unicode.sub(path, 1, 1) == "/" then
  122.     return "/" .. result
  123.   else
  124.     return result
  125.   end
  126. end
  127.  
  128. function filesystem.concat(pathA, pathB, ...)
  129.   checkArg(1, pathA, "string")
  130.   local function concat(n, a, b, ...)
  131.     if not b then
  132.       return a
  133.     end
  134.     checkArg(n, b, "string")
  135.     return concat(n + 1, a .. "/" .. b, ...)
  136.   end
  137.   return filesystem.canonical(concat(2, pathA, pathB, ...))
  138. end
  139.  
  140. function filesystem.get(path)
  141.   local node, rest = findNode(path)
  142.   if node.fs then
  143.     local proxy = component.proxy(node.fs)
  144.     path = ""
  145.     while node and node.parent do
  146.       path = filesystem.concat(node.name, path)
  147.       node = node.parent
  148.     end
  149.     path = filesystem.canonical(path)
  150.     if path ~= "/" then
  151.       path = "/" .. path
  152.     end
  153.     return proxy, path
  154.   end
  155.   return nil, "no such file system"
  156. end
  157.  
  158. function filesystem.isLink(path)
  159.   local node, rest, vnode, vrest = findNode(filesystem.path(path))
  160.   if not vrest and vnode.links[filesystem.name(path)] ~= nil then
  161.     return true, vnode.links[filesystem.name(path)]
  162.   end
  163.   return false
  164. end
  165.  
  166. function filesystem.link(target, linkpath)
  167.   checkArg(1, target, "string")
  168.   checkArg(2, linkpath, "string")
  169.  
  170.   if filesystem.exists(linkpath) then
  171.     return nil, "file already exists"
  172.   end
  173.  
  174.   local node, rest, vnode, vrest = findNode(filesystem.path(linkpath), true)
  175.   vnode.links[filesystem.name(linkpath)] = target
  176.   return true
  177. end
  178.  
  179. function filesystem.mount(fs, path)
  180.   checkArg(1, fs, "string", "table")
  181.   if type(fs) == "string" then
  182.     fs = filesystem.proxy(fs)
  183.   end
  184.   assert(type(fs) == "table", "bad argument #1 (file system proxy or address expected)")
  185.   checkArg(2, path, "string")
  186.  
  187.   if path ~= "/" and filesystem.exists(path) then
  188.     return nil, "file already exists"
  189.   end
  190.  
  191.   local node, rest, vnode, vrest = findNode(path, true)
  192.   if vnode.fs then
  193.     return nil, "another filesystem is already mounted here"
  194.   end
  195.   vnode.fs = fs.address
  196.   return true
  197. end
  198.  
  199. function filesystem.mounts()
  200.   local function path(node)
  201.     local result = "/"
  202.     while node and node.parent do
  203.       for name, child in pairs(node.parent.children) do
  204.         if child == node then
  205.           result = "/" .. name .. result
  206.           break
  207.         end
  208.       end
  209.       node = node.parent
  210.     end
  211.     return result
  212.   end
  213.   local queue = {mtab}
  214.   return function()
  215.     while #queue > 0 do
  216.       local node = table.remove(queue)
  217.       for _, child in pairs(node.children) do
  218.         table.insert(queue, child)
  219.       end
  220.       if node.fs then
  221.         return component.proxy(node.fs) or node.fs, path(node)
  222.       end
  223.     end
  224.   end
  225. end
  226.  
  227. function filesystem.path(path)
  228.   local parts = segments(path)
  229.   local result = table.concat(parts, "/", 1, #parts - 1) .. "/"
  230.   if unicode.sub(path, 1, 1) == "/" and unicode.sub(result, 1, 1) ~= "/" then
  231.     return "/" .. result
  232.   else
  233.     return result
  234.   end
  235. end
  236.  
  237. function filesystem.name(path)
  238.   local parts = segments(path)
  239.   return parts[#parts]
  240. end
  241.  
  242. function filesystem.proxy(filter)
  243.   checkArg(1, filter, "string")
  244.   local address
  245.   for c in component.list("filesystem") do
  246.     if component.invoke(c, "getLabel") == filter then
  247.       address = c
  248.       break
  249.     end
  250.     if c:sub(1, filter:len()) == filter then
  251.       address = c
  252.       break
  253.     end
  254.   end
  255.   if not address then
  256.     return nil, "no such file system"
  257.   end
  258.   return component.proxy(address)
  259. end
  260.  
  261. function filesystem.umount(fsOrPath)
  262.   checkArg(1, fsOrPath, "string", "table")
  263.   if type(fsOrPath) == "string" then
  264.     local node, rest, vnode, vrest = findNode(fsOrPath)
  265.     if not vrest and vnode.fs then
  266.       vnode.fs = nil
  267.       removeEmptyNodes(vnode)
  268.       return true
  269.     end
  270.   end
  271.   local address = type(fsOrPath) == "table" and fsOrPath.address or fsOrPath
  272.   local result = false
  273.   for proxy, path in filesystem.mounts() do
  274.     local addr = type(proxy) == "table" and proxy.address or proxy
  275.     if string.sub(addr, 1, address:len()) == address then
  276.       local node, rest, vnode, vrest = findNode(path)
  277.       vnode.fs = nil
  278.       removeEmptyNodes(vnode)
  279.       result = true
  280.     end
  281.   end
  282.   return result
  283. end
  284.  
  285. function filesystem.exists(path)
  286.   local node, rest, vnode, vrest = findNode(path)
  287.   if not vrest or vnode.links[vrest] then -- virtual directory or symbolic link
  288.     return true
  289.   end
  290.   if node and node.fs then
  291.     return component.proxy(node.fs).exists(rest)
  292.   end
  293.   return false
  294. end
  295.  
  296. function filesystem.size(path)
  297.   local node, rest, vnode, vrest = findNode(path)
  298.   if not vnode.fs and (not vrest or vnode.links[vrest]) then
  299.     return 0 -- virtual directory or symlink
  300.   end
  301.   if node.fs and rest then
  302.     return component.proxy(node.fs).size(rest)
  303.   end
  304.   return 0 -- no such file or directory
  305. end
  306.  
  307. function filesystem.isDirectory(path)
  308.   local node, rest, vnode, vrest = findNode(path)
  309.   if not vnode.fs and not vrest then
  310.     return true -- virtual directory
  311.   end
  312.   if node.fs then
  313.     return not rest or component.proxy(node.fs).isDirectory(rest)
  314.   end
  315.   return false
  316. end
  317.  
  318. function filesystem.lastModified(path)
  319.   local node, rest, vnode, vrest = findNode(path)
  320.   if not vnode.fs and not vrest then
  321.     return 0 -- virtual directory
  322.   end
  323.   if node.fs and rest then
  324.     return component.proxy(node.fs).lastModified(rest)
  325.   end
  326.   return 0 -- no such file or directory
  327. end
  328.  
  329. function filesystem.list(path)
  330.   local node, rest, vnode, vrest = findNode(path)
  331.   if not vnode.fs and vrest and not (node and node.fs) then
  332.     return nil, "no such file or directory"
  333.   end
  334.   local result, reason
  335.   if node and node.fs then
  336.     result, reason = component.proxy(node.fs).list(rest or "")
  337.   end
  338.   result = result or {}
  339.   if not vrest then
  340.     for k in pairs(vnode.children) do
  341.       table.insert(result, k .. "/")
  342.     end
  343.     for k in pairs(vnode.links) do
  344.       table.insert(result, k)
  345.     end
  346.   end
  347.   table.sort(result)
  348.   local i, f = 1, nil
  349.   while i <= #result do
  350.     if result[i] == f then
  351.       table.remove(result, i)
  352.     else
  353.       f = result[i]
  354.     end
  355.     i = i + 1
  356.   end
  357.   local i = 0
  358.   return function()
  359.     i = i + 1
  360.     return result[i]
  361.   end
  362. end
  363.  
  364. function filesystem.makeDirectory(path)
  365.   if filesystem.exists(path) then
  366.     return nil, "file or directory with that name already exists"
  367.   end
  368.   local node, rest = findNode(path)
  369.   if node.fs and rest then
  370.     return component.proxy(node.fs).makeDirectory(rest)
  371.   end
  372.   if node.fs then
  373.     return nil, "virtual directory with that name already exists"
  374.   end
  375.   return nil, "cannot create a directory in a virtual directory"
  376. end
  377.  
  378. function filesystem.remove(path)
  379.   local node, rest, vnode, vrest = findNode(filesystem.path(path))
  380.   local name = filesystem.name(path)
  381.   if vnode.children[name] then
  382.     vnode.children[name] = nil
  383.     removeEmptyNodes(vnode)
  384.     return true
  385.   elseif vnode.links[name] then
  386.     vnode.links[name] = nil
  387.     removeEmptyNodes(vnode)
  388.     return true
  389.   else
  390.     node, rest = findNode(path)
  391.     if node.fs and rest then
  392.       return component.proxy(node.fs).remove(rest)
  393.     end
  394.     return nil, "no such file or directory"
  395.   end
  396. end
  397.  
  398. function filesystem.rename(oldPath, newPath)
  399.   if filesystem.isLink(oldPath) then
  400.     local node, rest, vnode, vrest = findNode(filesystem.path(oldPath))
  401.     local target = vnode.links[filesystem.name(oldPath)]
  402.     local result, reason = filesystem.link(target, newPath)
  403.     if result then
  404.       filesystem.remove(oldPath)
  405.     end
  406.     return result, reason
  407.   else
  408.     local oldNode, oldRest = findNode(oldPath)
  409.     local newNode, newRest = findNode(newPath)
  410.     if oldNode.fs and oldRest and newNode.fs and newRest then
  411.       if oldNode.fs == newNode.fs then
  412.         return component.proxy(oldNode.fs).rename(oldRest, newRest)
  413.       else
  414.         local result, reason = filesystem.copy(oldPath, newPath)
  415.         if result then
  416.           return filesystem.remove(oldPath)
  417.         else
  418.           return nil, reason
  419.         end
  420.       end
  421.     end
  422.     return nil, "trying to read from or write to virtual directory"
  423.   end
  424. end
  425.  
  426. function filesystem.copy(fromPath, toPath)
  427.   if filesystem.isDirectory(fromPath) then
  428.     return nil, "cannot copy folders"
  429.   end
  430.   local input, reason = io.open(fromPath, "rb")
  431.   if not input then
  432.     return nil, reason
  433.   end
  434.   local output, reason = io.open(toPath, "wb")
  435.   if not output then
  436.     input:close()
  437.     return nil, reason
  438.   end
  439.   repeat
  440.     local buffer, reason = input:read(1024)
  441.     if not buffer and reason then
  442.       return nil, reason
  443.     elseif buffer then
  444.       local result, reason = output:write(buffer)
  445.       if not result then
  446.         input:close()
  447.         output:close()
  448.         return nil, reason
  449.       end
  450.     end
  451.   until not buffer
  452.   input:close()
  453.   output:close()
  454.   return true
  455. end
  456.  
  457. function fileStream:close()
  458.   if self.handle then
  459.     component.proxy(self.fs).close(self.handle)
  460.     self.handle = nil
  461.   end
  462. end
  463.  
  464. function fileStream:read(n)
  465.   if not self.handle then
  466.     return nil, "file is closed"
  467.   end
  468.   return component.proxy(self.fs).read(self.handle, n)
  469. end
  470.  
  471. function fileStream:seek(whence, offset)
  472.   if not self.handle then
  473.     return nil, "file is closed"
  474.   end
  475.   return component.proxy(self.fs).seek(self.handle, whence, offset)
  476. end
  477.  
  478. function fileStream:write(str)
  479.   if not self.handle then
  480.     return nil, "file is closed"
  481.   end
  482.   return component.proxy(self.fs).write(self.handle, str)
  483. end
  484.  
  485. function filesystem.open(path, mode)
  486.   checkArg(1, path, "string")
  487.   mode = tostring(mode or "r")
  488.   checkArg(2, mode, "string")
  489.   assert(({r=true, rb=true, w=true, wb=true, a=true, ab=true})[mode],
  490.     "bad argument #2 (r[b], w[b] or a[b] expected, got " .. mode .. ")")
  491.  
  492.   local node, rest = findNode(path)
  493.   if not node.fs or not rest then
  494.     return nil, "file not found"
  495.   end
  496.  
  497.   local handle, reason = component.proxy(node.fs).open(rest, mode)
  498.   if not handle then
  499.     return nil, reason
  500.   end
  501.  
  502.   local stream = {fs = node.fs, handle = handle}
  503.  
  504.   local function cleanup(self)
  505.     if not self.handle then return end
  506.     local proxy = component.proxy(self.fs)
  507.     if proxy then pcall(proxy.close, self.handle) end
  508.   end
  509.   local metatable = {__index = fileStream,
  510.                      __gc = cleanup,
  511.                      __metatable = "filestream"}
  512.   return setmetatable(stream, metatable)
  513. end
  514.  
  515. -------------------------------------------------------------------------------
  516.  
  517. return filesystem
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement