Advertisement
Guest User

filesystem.lua

a guest
Jul 16th, 2016
113
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.66 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 = 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
  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 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", true) 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 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 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 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 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 = 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. i = i + 1
  355. end
  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 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 function removeVirtual()
  380. local node, rest, vnode, vrest = findNode(filesystem.path(path))
  381. -- vrest represents the remaining path beyond vnode
  382. -- vrest is nil if vnode reaches the full path
  383. -- thus, if vrest is NOT NIL, then we SHOULD NOT remove children nor links
  384. if not vrest then
  385. local name = filesystem.name(path)
  386. if vnode.children[name] then
  387. vnode.children[name] = nil
  388. removeEmptyNodes(vnode)
  389. return true
  390. elseif vnode.links[name] then
  391. vnode.links[name] = nil
  392. removeEmptyNodes(vnode)
  393. return true
  394. end
  395. end
  396. -- return false even if vrest is nil because this means it was a expected
  397. -- to be a real file
  398. return false
  399. end
  400. local function removePhysical()
  401. node, rest = findNode(path)
  402. if node.fs and rest then
  403. return node.fs.remove(rest)
  404. end
  405. return false
  406. end
  407. local success = removeVirtual()
  408. success = removePhysical() or success -- Always run.
  409. if success then return true
  410. else return nil, "no such file or directory"
  411. end
  412. end
  413.  
  414. function filesystem.rename(oldPath, newPath)
  415. if filesystem.isLink(oldPath) then
  416. local node, rest, vnode, vrest = findNode(filesystem.path(oldPath))
  417. local target = vnode.links[filesystem.name(oldPath)]
  418. local result, reason = filesystem.link(target, newPath)
  419. if result then
  420. filesystem.remove(oldPath)
  421. end
  422. return result, reason
  423. else
  424. local oldNode, oldRest = findNode(oldPath)
  425. local newNode, newRest = findNode(newPath)
  426. if oldNode.fs and oldRest and newNode.fs and newRest then
  427. if oldNode.fs.address == newNode.fs.address then
  428. return oldNode.fs.rename(oldRest, newRest)
  429. else
  430. local result, reason = filesystem.copy(oldPath, newPath)
  431. if result then
  432. return filesystem.remove(oldPath)
  433. else
  434. return nil, reason
  435. end
  436. end
  437. end
  438. return nil, "trying to read from or write to virtual directory"
  439. end
  440. end
  441.  
  442. function filesystem.copy(fromPath, toPath)
  443. if filesystem.isDirectory(fromPath) then
  444. return nil, "cannot copy folders"
  445. end
  446. local input, reason = io.open(fromPath, "rb")
  447. if not input then
  448. return nil, reason
  449. end
  450. local output, reason = io.open(toPath, "wb")
  451. if not output then
  452. input:close()
  453. return nil, reason
  454. end
  455. repeat
  456. local buffer, reason = input:read(1024)
  457. if not buffer and reason then
  458. return nil, reason
  459. elseif buffer then
  460. local result, reason = output:write(buffer)
  461. if not result then
  462. input:close()
  463. output:close()
  464. return nil, reason
  465. end
  466. end
  467. until not buffer
  468. input:close()
  469. output:close()
  470. return true
  471. end
  472.  
  473. function fileStream:close()
  474. if self.handle then
  475. self.fs.close(self.handle)
  476. self.handle = nil
  477. end
  478. end
  479.  
  480. function fileStream:read(n)
  481. if not self.handle then
  482. return nil, "file is closed"
  483. end
  484. return self.fs.read(self.handle, n)
  485. end
  486.  
  487. function fileStream:seek(whence, offset)
  488. if not self.handle then
  489. return nil, "file is closed"
  490. end
  491. return self.fs.seek(self.handle, whence, offset)
  492. end
  493.  
  494. function fileStream:write(str)
  495. if not self.handle then
  496. return nil, "file is closed"
  497. end
  498. return self.fs.write(self.handle, str)
  499. end
  500.  
  501. function filesystem.open(path, mode)
  502. checkArg(1, path, "string")
  503. mode = tostring(mode or "r")
  504. checkArg(2, mode, "string")
  505. assert(({r=true, rb=true, w=true, wb=true, a=true, ab=true})[mode],
  506. "bad argument #2 (r[b], w[b] or a[b] expected, got " .. mode .. ")")
  507.  
  508. local node, rest = findNode(path)
  509. if not node.fs or not rest then
  510. return nil, "file not found"
  511. end
  512.  
  513. local handle, reason = node.fs.open(rest, mode)
  514. if not handle then
  515. return nil, reason
  516. end
  517.  
  518. local stream = {fs = node.fs, handle = handle}
  519.  
  520. local metatable = {__index = fileStream,
  521. __metatable = "filestream"}
  522. return setmetatable(stream, metatable)
  523. end
  524.  
  525. -------------------------------------------------------------------------------
  526.  
  527. return filesystem
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement