Advertisement
Tatantyler

VirtualFSLayer

Jul 25th, 2013
580
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 27.88 KB | None | 0 0
  1. -- VirtualFSLayer
  2.  
  3. -- Modes of operation:
  4. -- -1:  No FS loaded
  5. --  0:  RAM Filesystem (no flush)
  6. --  1:  Unencrypted Filesystem
  7. --  2:  Encrypted Filesystem
  8. --  3:  Networked Filesystem
  9.  
  10. local canUseEncryptedFS = true
  11. local canUseNFS = true
  12. local canUsePasswordedNFS = true
  13.  
  14. if (not base64) and (not os.loadAPI("base64")) then
  15.     if http then
  16.         local h = http.get("http://pastebin.com/raw.php?i=pp3kpb19")
  17.         if h then
  18.             local data = h.readAll()
  19.             h.close()
  20.             local f = fs.open("base64", "w")
  21.             f.write(data)
  22.             f.close()
  23.             if not os.loadAPI("base64") then
  24.                 error("Could not load base64 API!")
  25.             end
  26.         else
  27.             error("Could not load base64 API!")
  28.         end
  29.     else
  30.         error("Could not load base64 API!")
  31.     end
  32. end
  33.  
  34. -- We can't really protect against adversaries modifying the libraries themselves. Oh well.
  35. if (not AES) and (not os.loadAPI("AES")) then
  36.     canUseEncryptedFS = false
  37.     print("Encrypted FS disabled; could not load AES library.")
  38. end
  39.  
  40. if (not SHA1) and (not os.loadAPI("SHA1")) then
  41.     canUseNFS = false
  42.     print("NetFS disabled; could not load SHA1 library.")
  43. end
  44.  
  45. if (not SHA2) and (not os.loadAPI("SHA2")) then
  46.     canUsePasswordedNFS = false
  47.     print("Passworded NetFS disabled; could not load SHA2 library.")
  48. end
  49.  
  50. local sides = rs.getSides()
  51. local modem = false
  52. for i=1, 6 do
  53.     if peripheral.isPresent(sides[i]) and (peripheral.getType(sides[i]) == "modem") then
  54.         modem = peripheral.wrap(sides[i])
  55.         break
  56.     end
  57. end
  58.  
  59. if not modem then
  60.     canUseNFS = false
  61.     print("NetFS disabled; could not find modem.")
  62. end
  63.  
  64. local fileCache = {
  65.     files = {},
  66.     dirs = { [""] = {} },
  67. } -- list of files contained in the mounted filesystem
  68. local locks = {} -- list of file locks
  69. local modTimestamps = {} -- File mod timestamps
  70.  
  71. local mountPath = "" -- Folder that the encrypted file is "mounted" to
  72. local loadedFile = "" -- path to the file storing the files
  73. local key = {}
  74. local mode = -1
  75.  
  76. local oldFSOpen = fs.open
  77. local oldFSDelete = fs.delete
  78. local oldFSMakeDir = fs.makeDir
  79. local oldFSList = fs.list
  80. local oldFSExists = fs.exists
  81. local oldFSIsDir = fs.isDir
  82. local oldFSGetDrive = fs.getDrive
  83. local oldFSMove = fs.move
  84. local oldFSCopy = fs.copy
  85.  
  86. local NFSTimeout = 60
  87. local NFSIdent = "NetFS"
  88. local NFSChannel = 0xDA7A
  89. local NFSCredential = {}
  90. local NFSChecksums = {}
  91. local NFSServer = -1
  92.  
  93. if modem then
  94.     modem.open(NFSChannel)
  95. end
  96.  
  97. function flush(verbose)
  98.     if loadedFile ~= "" then
  99.         local fCacheCopy = { -- file cache copy, for writing to disk.
  100.             files = {},
  101.             dirs = fileCache.dirs,
  102.         }
  103.         for file, contents in pairs(fileCache.files) do
  104.             local rawData = {string.byte(contents, 1, #contents)}
  105.             local out = ""
  106.             local isBinary = false
  107.             for i=1, #contents do
  108.                 local byte = string.byte(contents, i, i)
  109.                 if (byte < 0x20) or (byte > 0x7E) then
  110.                     out = "BINARY:"
  111.                     isBinary = true
  112.                     break
  113.                 end
  114.                 out = out..string.sub(contents, i, i)
  115.             end
  116.             if isBinary then
  117.                 out = out..base64.encode(rawData)
  118.             end
  119.             fCacheCopy.files[file] = out
  120.         end
  121.         if mode == 2 then
  122.             if verbose then
  123.                 print("1. Preprocessing..")
  124.             end
  125.             local iv = {}
  126.             for i=1, 16 do
  127.                 iv[i] = math.random(0, 255)
  128.             end
  129.             --local ivStr = base64.encode(iv)
  130.             local pText = {}
  131.             local pText_str = textutils.serialize(fCacheCopy)
  132.             local lastPause = os.clock()
  133.             for i=1, #pText_str do
  134.                 pText[i] = string.byte(pText_str, i, i)
  135.                 if (os.clock() - lastPause) >= 2.90 then -- Only pause when necessary
  136.                     os.queueEvent("")
  137.                     os.pullEvent("")
  138.                     lastPause = os.clock()
  139.                 end
  140.             end
  141.             if verbose then
  142.                 print("2. Encrypting..")
  143.             end
  144.             local cText = AES.encrypt_bytestream(pText, key, iv)
  145.             --local cText_str = base64.encode(cText)
  146.             local file = fs.open(loadedFile, "wb")
  147.             --file.write(ivStr..":"..cText_str)
  148.             for i=1, 16 do
  149.                 file.write(iv[i])
  150.             end
  151.             os.queueEvent("")
  152.             os.pullEvent("")
  153.             local lastPause = os.clock()
  154.             if verbose then
  155.                 print("3. Writing..")
  156.             end
  157.             local _, y = term.getCursorPos()
  158.             for i=1, #cText do
  159.                 if verbose then
  160.                     term.setCursorPos(1, y)
  161.                     term.clearLine()
  162.                     write(math.floor((i/#cText)*100).."% done.")
  163.                 end
  164.                 file.write(cText[i])
  165.                 if (os.clock() - lastPause) >= 2.90 then
  166.                     os.queueEvent("")
  167.                     os.pullEvent("")
  168.                     lastPause = os.clock()
  169.                 end
  170.             end
  171.             file.close()
  172.         elseif mode == 1 then
  173.             local file = fs.open(loadedFile, "w")
  174.             local data = textutils.serialize(fCacheCopy)
  175.             file.write(data)
  176.             file.close()
  177.         elseif mode == 3 then
  178.             -- NFS sync method:
  179.             -- Step1 / C->S: Request to connect
  180.             -- Step2 / S->C: Send challenge
  181.             -- Step3 / C->S: Send response
  182.             -- Step4 / S->C: Get SHA1 hash of all files on local store,
  183.             -- Client: Check which versions differ,
  184.             -- Step5 / C->S: transmit newer versions if necessary and list of files to get
  185.             -- Client: Write their versions of files to cache if necessary.
  186.             -- Step6 / S->C: transmit newer versions if necessary.
  187.             local function compareTimestamps(ts1, ts2)
  188.                 if ts1[1] == ts2[1] then
  189.                     return (ts1[2] > ts2[2])
  190.                 else
  191.                     if ts1[1] > ts2[1] then
  192.                         return true
  193.                     elseif ts1[1] < ts2[1] then
  194.                         return false
  195.                     end
  196.                 end
  197.             end
  198.             if verbose then
  199.                 print("Calculating file checksums...")
  200.             end
  201.             NFSChecksums = {}
  202.             local nChecksums = 0
  203.             local cksumItr = 0
  204.             for i,v in pairs(fCacheCopy.files) do
  205.                 nChecksums = nChecksums+1
  206.             end
  207.             for i,v in pairs(fCacheCopy.files) do
  208.                 cksumItr = cksumItr+1
  209.                 NFSChecksums[i] = SHA1.digest2str(SHA1.digestStr(v))
  210.                 if verbose then
  211.                     print("File "..cksumItr.."/"..nChecksums..": "..i)
  212.                 end
  213.                 os.sleep(0)
  214.             end
  215.             --print("Done calculating file checksums.")
  216.             local state = 1
  217.             if verbose then print("NFS Server: "..NFSServer) end
  218.             --print("Sending step 1...")
  219.             modem.transmit(NFSChannel, NFSChannel, textutils.serialize({NFSIdent, os.computerID(), NFSServer, "Step1"}))
  220.             --print("Sent step 1.")
  221.             local timer = os.startTimer(NFSTimeout)
  222.             local challenge = false
  223.             local serverHashes = {}
  224.             while true do
  225.                 local event, side, sCh, rCh, msg = os.pullEvent()
  226.                 if event == "timer" then
  227.                     if side == timer then
  228.                         error("VirtualFSLayer-Sync: Server timed out.", 2)
  229.                     end
  230.                 elseif event == "modem_message" then
  231.                     msg = textutils.unserialize(msg)
  232.                     if type(msg) == "table" then
  233.                         if (msg[1] == NFSIdent) and (msg[3] == os.computerID()) and (msg[2] == NFSServer) then
  234.                             if state == 1 then
  235.                                 if msg[4] == "Step2" then
  236.                                     if verbose then
  237.                                         print("Got step 2 response.")
  238.                                     end
  239.                                     state = 2
  240.                                     challenge = msg[4]
  241.                                     local response = {}
  242.                                     for i=1, #NFSCredential do
  243.                                         response[i] = NFSCredential[i]
  244.                                     end
  245.                                     for i=1, #challenge do
  246.                                         table.insert(response, challenge[i])
  247.                                     end
  248.                                     response = SHA2.digest(response)
  249.                                     --print("Sending step 3...")
  250.                                     modem.transmit(NFSChannel, NFSChannel, textutils.serialize({NFSIdent, os.computerID(), NFSServer, "Step3", response}))
  251.                                     --print("Sent step 3.")
  252.                                 elseif msg[4] == "Step4" then
  253.                                     if verbose then print("Skipping to step 4.") end
  254.                                     state = 4
  255.                                     serverHashes = msg[5]
  256.                                     local serverTimestamps = msg[6]
  257.                                     local filesToSend = {}
  258.                                     local filesToGet = {}
  259.                                     for i,v in pairs(NFSChecksums) do
  260.                                         if serverHashes[i] ~= v then
  261.                                             if serverHashes[i] == nil then
  262.                                                 if verbose then print("Our version of "..i.." is a new file.") end
  263.                                                 filesToSend[i] = fileCache.files[i]
  264.                                             else
  265.                                                 if modTimestamps[i] then
  266.                                                     if verbose then print("Server timestamp is "..serverTimestamps[i][1].."/"..serverTimestamps[i][2]) end
  267.                                                     if verbose then print("Our timestamp is "..modTimestamps[i][1].."/"..modTimestamps[i][2]) end
  268.                                                     if compareTimestamps(modTimestamps[i], serverTimestamps[i]) then -- Our version is newer
  269.                                                         if verbose then print("Our version of "..i.." is newer.") end
  270.                                                         filesToSend[i] = fCacheCopy.files[i]
  271.                                                     else
  272.                                                         if verbose then print("Server version of "..i.." is newer.") end
  273.                                                         filesToGet[i] = true -- Their version is newer
  274.                                                     end
  275.                                                 else -- assume their version is newer
  276.                                                     if verbose then print("Server version of "..i.." is apparently a new file.") end
  277.                                                     filesToGet[i] = true
  278.                                                 end
  279.                                             end
  280.                                         end
  281.                                     end
  282.                                     for i,v in pairs(serverHashes) do
  283.                                         local file = i
  284.                                         file = string.gsub(file, "\\", "/")
  285.                                         if string.sub(file, 1, 1) == "/" then file = string.sub(file, 2) end
  286.                                         if string.sub(file, #file, #file) == "/" then file = string.sub(file, 1, #file-1) end
  287.                                         if (not NFSChecksums[i]) or (not fileCache.files[file]) then
  288.                                             filesToGet[i] = true
  289.                                         end
  290.                                     end
  291.                                     --print("Sending step 5...")
  292.                                     modem.transmit(NFSChannel, NFSChannel, textutils.serialize({NFSIdent, os.computerID(), NFSServer, "Step5", filesToSend, filesToGet}))
  293.                                     --print("Sent step 5.")
  294.                                 end
  295.                             elseif state == 2 then
  296.                                 if msg[4] == "Step4" then
  297.                                     if verbose then print("Got step 4 response.") end
  298.                                     state = 4
  299.                                     serverHashes = msg[5]
  300.                                     local serverTimestamps = msg[6]
  301.                                     local filesToSend = {}
  302.                                     local filesToGet = {}
  303.                                     for i,v in pairs(NFSChecksums) do
  304.                                         if serverHashes[i] ~= v then
  305.                                             if serverHashes[i] == nil then
  306.                                                 if verbose then print("Our version of "..i.." is a new file.") end
  307.                                                 filesToSend[i] = fileCache.files[i]
  308.                                             else
  309.                                                 if modTimestamps[i] then
  310.                                                     if verbose then print("Server timestamp is "..serverTimestamps[i][1].."/"..serverTimestamps[i][2]) end
  311.                                                     if verbose then print("Our timestamp is "..modTimestamps[i][1].."/"..modTimestamps[i][2]) end
  312.                                                     if compareTimestamps(modTimestamps[i], serverTimestamps[i]) then -- Our version is newer
  313.                                                         if verbose then print("Our version of "..i.." is newer.") end
  314.                                                         filesToSend[i] = fCacheCopy.files[i]
  315.                                                     else
  316.                                                         if verbose then print("Server version of "..i.." is newer.") end
  317.                                                         filesToGet[i] = true -- Their version is newer
  318.                                                     end
  319.                                                 else -- assume their version is newer
  320.                                                     if verbose then print("Server version of "..i.." is apparently a new file.") end
  321.                                                     filesToGet[i] = true
  322.                                                 end
  323.                                             end
  324.                                         end
  325.                                     end
  326.                                     for i,v in pairs(serverHashes) do
  327.                                         local file = i
  328.                                         file = string.gsub(file, "\\", "/")
  329.                                         if string.sub(file, 1, 1) == "/" then file = string.sub(file, 2) end
  330.                                         if string.sub(file, #file, #file) == "/" then file = string.sub(file, 1, #file-1) end
  331.                                         if (not NFSChecksums[i]) or (not fileCache.files[file]) then
  332.                                             filesToGet[i] = true
  333.                                         end
  334.                                     end
  335.                                     --print("Sending step 5...")
  336.                                     modem.transmit(NFSChannel, NFSChannel, textutils.serialize({NFSIdent, os.computerID(), NFSServer, "Step5", filesToSend, filesToGet}))
  337.                                     --print("Sent step 5.")
  338.                                 elseif msg[4] == "AuthFailure" then
  339.                                     error("NFS-Sync: Authentication failure", 2)
  340.                                 end
  341.                             elseif state == 4 then
  342.                                 if msg[4] == "Step6" then
  343.                                     if verbose then print("Got step 6 response.") end
  344.                                     local fSendData = msg[5]
  345.                                     for i,v in pairs(fSendData) do
  346.                                         local file = i
  347.                                         file = string.gsub(file, "\\", "/")
  348.                                         if string.sub(file, 1, 1) == "/" then file = string.sub(file, 2) end
  349.                                         if string.sub(file, #file, #file) == "/" then file = string.sub(file, 1, #file-1) end
  350.                                         if string.sub(v, 1, 7) == "BINARY:" then
  351.                                             local data = base64.decode(string.sub(v, 8))
  352.                                             fileCache.files[file] = ""
  353.                                             for i2=1, #data do
  354.                                                 fileCache.files[file] = fileCache.files[file]..string.char(data[i2])
  355.                                             end
  356.                                         else
  357.                                             fileCache.files[file] = v
  358.                                         end
  359.                                     end
  360.                                     return
  361.                                 end
  362.                             end
  363.                             timer = os.startTimer(NFSTimeout)
  364.                         end
  365.                     end
  366.                 end
  367.             end
  368.         end
  369.     else
  370.         error("VirtualFSLayer-Flush: No file has been loaded.", 2)
  371.     end
  372. end
  373.  
  374. function unmount()
  375.     if mode >= 1 then
  376.         flush()
  377.     end
  378.     mode = -1
  379.     key = {}
  380.     mountPath = ""
  381.     locks = {}
  382.     fileCache = { dirs = { [""] = {} }, files = {} }
  383. end
  384.  
  385. function mount(mtPath, file, k)
  386.     if file then
  387.         file = string.gsub(file, "\\", "/")
  388.         if string.sub(file, 1, 1) == "/" then file = string.sub(file, 2) end
  389.         if string.sub(file, #file, #file) == "/" then file = string.sub(file, 1, #file-1) end
  390.     end
  391.    
  392.     mtPath = string.gsub(mtPath, "\\", "/")
  393.     if string.sub(mtPath, 1, 1) == "/" then mtPath = string.sub(mtPath, 2) end
  394.     if string.sub(mtPath, #mtPath, #mtPath) == "/" then mtPath = string.sub(mtPath, 1, #mtPath-1) end
  395.    
  396.     if mtPath == "" then
  397.         error("VirtualFSLayer-Mount: Cannot mount directly to root folder!", 2)
  398.     end
  399.    
  400.     if oldFSExists(mtPath) then
  401.         error("VirtualFSLayer-Mount: Mount path already exists!", 2)
  402.     end
  403.    
  404.     if loadedFile ~= "" then
  405.         unmount() -- Unload the current file before loading a new one
  406.     end
  407.    
  408.     if canUseNFS and (string.sub(file, 1, 2) == "c:") then
  409.         print("Mounting NFS...")
  410.         NFSCredential = {}
  411.         if type(k) == "string" then
  412.             for i=1, #k do
  413.                 NFSCredential[i] = string.byte(k, i, i)
  414.             end
  415.         elseif type(k) == "table" then
  416.             for i=1, #k do
  417.                 NFSCredential[i] = k[i]
  418.             end
  419.         elseif type(k) ~= "nil" then
  420.             error("NFS-Mount: Invalid password type.", 2)
  421.         end
  422.         if (#NFSCredential > 0) and (not canUsePasswordedNFS) then
  423.             error("NFS-Mount: Cannot use passworded NFS!", 2)
  424.         end
  425.         NFSServer = tonumber(string.sub(file, 3))
  426.         if not NFSServer then
  427.             error("NFS-Mount: Invalid server.", 2)
  428.         end
  429.         fileCache = {
  430.             files = {},
  431.             dirs = { [""] = {} },
  432.         }
  433.         loadedFile = file
  434.         mountPath = mtPath
  435.         mode = 3
  436.         print("Performing inital sync...")
  437.         flush() -- Sync with server
  438.         return
  439.     end
  440.    
  441.     if canUseEncryptedFS and k then
  442.         if type(k) == "string" then
  443.             if SHA2 then
  444.                 _, k = SHA2.digestStr(k)
  445.                 k = SHA2.hashToBytes(k)
  446.             else
  447.                 local t = {}
  448.                 for i=1, #k do
  449.                     t[i] = string.byte(k, i, i)
  450.                 end
  451.                 k = t
  452.             end
  453.         elseif type(k) ~= "table" then
  454.             error("VirtualFSLayer-Mount: Needs a string or table as a key!", 2)
  455.         end
  456.     end
  457.     key = {}
  458.     if canUseEncryptedFS and k then
  459.         for i=1, #k do
  460.             if (type(k[i]) == "number") and ((k[i] >= 0) and (k[i] <= 255)) then
  461.                 table.insert(key, k[i])
  462.             end
  463.         end
  464.         if #key < 16 then
  465.             error("VirtualFSLayer-Mount: Not enough keying data!", 2)
  466.         end
  467.     end
  468.    
  469.     if canUseEncryptedFS and k and file then
  470.         mode = 2
  471.     elseif file then
  472.         mode = 1
  473.     else
  474.         mode = 0
  475.     end
  476.    
  477.     if file and fs.exists(file) then
  478.         --print(fData)
  479.         -- File format: [iv (16 bytes / 24 when base64 encoded)]:[encrypted data]
  480.         if canUseEncryptedFS and k then
  481.             local iv = {}
  482.             local encData = {}
  483.             local f = fs.open(file, "rb")
  484.             -- Load the IV:
  485.             for i=1, 16 do
  486.                 local byte = f.read()
  487.                 if byte then
  488.                     table.insert(iv, byte)
  489.                 else
  490.                     f.close()
  491.                     error("VirtualFSLayer-Mount: Invalid file loaded!", 2)
  492.                 end
  493.             end
  494.             -- Pause for a bit, and reset the "watchdog" timer:
  495.             os.queueEvent("")
  496.             os.pullEvent("")
  497.             local lastPause = os.clock()
  498.             -- Read the the encrypted data:
  499.             while true do
  500.                 local byte = f.read()
  501.                 if byte then
  502.                     table.insert(encData, byte)
  503.                 else
  504.                     break
  505.                 end
  506.                 if (os.clock() - lastPause) >= 2.90 then
  507.                     os.queueEvent("")
  508.                     os.pullEvent("")
  509.                     lastPause = os.clock()
  510.                 end
  511.             end
  512.             f.close()
  513.             print("Loaded data!")
  514.             --local iv = base64.decode(string.sub(fData, 1, 24))
  515.             --local encData = base64.decode(string.sub(fData, 26))
  516.             local decData = AES.decrypt_bytestream(encData, key, iv)
  517.             local decFileCache = textutils.unserialize(string.char(unpack(decData)))
  518.             if type(decFileCache) ~= "table" then
  519.                 error("VirtualFSLayer-Mount: Could not decrypt file!", 2)
  520.             end
  521.             for file, contents in pairs(decFileCache) do
  522.                 if string.sub(contents, 1, 7) == "BINARY:" then
  523.                     decFileCache.files[file] = string.char(unpack(base64.decode(string.sub(contents, 8))))
  524.                 end
  525.             end
  526.             fileCache = decFileCache
  527.         else
  528.             local f = fs.open(file, "r")
  529.             local fData = f.readAll()
  530.             f.close()
  531.             local lFileCache = textutils.unserialize(fData)
  532.             if type(lFileCache) ~= "table" then
  533.                 error("VirtualFSLayer-Mount: Invalid file loaded!", 2)
  534.             end
  535.             for file, contents in pairs(lFileCache.files) do
  536.                 if string.sub(contents, 1, 7) == "BINARY:" then
  537.                     lFileCache.files[file] = string.char(unpack(base64.decode(string.sub(contents, 8))))
  538.                 end
  539.             end
  540.             fileCache = lFileCache
  541.         end
  542.     end
  543.     loadedFile = file or ""
  544.     mountPath = mtPath
  545. end
  546.  
  547. function unload() -- Remove the hooks we've installed into the FS API. It's up to the caller to unload the API itself.
  548.     if mode ~= -1 then
  549.         unmount()
  550.     end
  551.     fs.open = oldFSOpen
  552.     fs.delete = oldFSDelete
  553.     fs.makeDir = oldFSMakeDir
  554.     fs.list = oldFSList
  555.     fs.exists = oldFSExists
  556.     fs.isDir = oldFSIsDir
  557.     fs.getDrive = oldFSGetDrive
  558.     fs.move = oldFSMove
  559.     fs.copy = oldFSCopy
  560. end
  561.  
  562. function makeNFS(server, k) -- mode change to 3
  563.     if canUseNFS then
  564.         key = {}
  565.         print("Mounting NFS...")
  566.         NFSCredential = {}
  567.         if type(k) == "string" then
  568.             for i=1, #k do
  569.                 NFSCredential[i] = string.byte(k, i, i)
  570.             end
  571.         elseif type(k) == "table" then
  572.             for i=1, #k do
  573.                 NFSCredential[i] = k[i]
  574.             end
  575.         elseif type(k) ~= "nil" then
  576.             error("NFS-Mount: Invalid password type.", 2)
  577.         end
  578.         if (#NFSCredential > 0) and (not canUsePasswordedNFS) then
  579.             error("NFS-Mount: Cannot use passworded NFS!", 2)
  580.         end
  581.         NFSServer = server
  582.         if not NFSServer then
  583.             error("NFS-Mount: Invalid server.", 2)
  584.         end
  585.         loadedFile = "c:"..server
  586.         mode = 3
  587.         print("Performing inital sync...")
  588.         flush() -- Sync with server
  589.     end
  590. end
  591.  
  592. function makeEncrypted(k) -- mode change to 2
  593.     if not canUseEncryptedFS then
  594.         error("VirtualFSLayer-makeEncrypted: Cannot use encrypted filesystem!", 2)
  595.     end
  596.     if type(k) == "string" then
  597.         if SHA2 then
  598.             _, k = SHA2.digestStr(k)
  599.             k = SHA2.hashToBytes(k)
  600.         else
  601.             local t = {}
  602.             for i=1, #k do
  603.                 t[i] = string.byte(k, i, i)
  604.             end
  605.             k = t
  606.         end
  607.     elseif type(k) ~= "table" then
  608.         error("VirtualFSLayer-makeEncrypted: Needs a string or table as a key!", 2)
  609.     end
  610.     key = {}
  611.     for i=1, #k do
  612.         if (type(k[i]) == "number") and ((k[i] >= 0) and (k[i] <= 255)) then
  613.             table.insert(key, k[i])
  614.         end
  615.     end
  616.     if #key < 16 then
  617.         error("VirtualFSLayer-makeEncrypted: Not enough keying data!", 2)
  618.     end
  619.     mode = 2
  620. end
  621.  
  622. function makeUnencrypted() -- mode change to 1
  623.     mode = 1
  624.     key = {}
  625. end
  626.  
  627. function makeMemdisk() -- mode change to 0
  628.     mode = 0
  629.     key = {}
  630.     loadedFile = ""
  631. end
  632.  
  633. function getMountPath()
  634.     return mountPath
  635. end
  636.  
  637. function getLoadedFile()
  638.     return loadedFile
  639. end
  640.  
  641. function setLoadedFile(file)
  642.     loadedFile = file
  643. end
  644.  
  645. function getMode()
  646.     return mode
  647. end
  648.  
  649. local function updateDirTables() -- Update fileCache.dirs
  650.     for i,v in pairs(fileCache.dirs) do
  651.         fileCache.dirs[i] = {}
  652.     end
  653.     for i,v in pairs(fileCache.files) do
  654.         if i == fs.getName(i) then -- Bit of a hack, to make files directly inside the mount path work
  655.             table.insert(fileCache.dirs[""], i)
  656.         else
  657.             --print(i)
  658.             local fileBasePath = string.sub(i, 1, (#i - #fs.getName(i))-1)
  659.             --print(fileBasePath)
  660.             if type(fileCache.dirs[fileBasePath]) == "table" then
  661.                 table.insert(fileCache.dirs[fileBasePath], fs.getName(i))
  662.             end
  663.         end
  664.     end
  665.     for i,v in pairs(fileCache.dirs) do
  666.         if i == fs.getName(i) then
  667.             table.insert(fileCache.dirs[""], i)
  668.         else
  669.             --print(i)
  670.             local fileBasePath = string.sub(i, 1, (#i - #fs.getName(i))-1)
  671.             --print(fileBasePath)
  672.             if (type(fileCache.dirs[fileBasePath]) == "table") and (fileBasePath ~= i) then
  673.                 table.insert(fileCache.dirs[fileBasePath], fs.getName(i))
  674.             end
  675.         end
  676.     end
  677. end
  678.  
  679. fs.open = function(file, mode)
  680.     -- force paths to a "standard" format:
  681.     file = string.gsub(file, "\\", "/")
  682.     if string.sub(file, 1, 1) == "/" then file = string.sub(file, 2) end
  683.     if string.sub(file, #file, #file) == "/" then file = string.sub(file, 1, #file-1) end
  684.     local isInMountedPath = false
  685.     if mode ~= -1 then -- we don't have anything loaded yet, skip the special handling:
  686.         isInMountedPath = ((string.sub(file, 1, #mountPath) == mountPath) and (mountPath ~= ""))
  687.         if isInMountedPath then
  688.             local mountSubpath = string.sub(file, #mountPath+2)
  689.             if not locks[mountSubpath] then
  690.                 local fileObject = { vfs = true }
  691.                 fileObject.internal = fileCache.files[mountSubpath] or ""
  692.                 fileObject.ptr = 1
  693.                 fileObject.fileHandleIdent = math.random(0, 0xFFFF)
  694.                 locks[mountSubpath] = fileObject.fileHandleIdent
  695.                 fileObject.close = function()
  696.                     if locks[mountSubpath] == fileObject.fileHandleIdent then -- Keep us from writing to the cache twice
  697.                         if fileCache.files[mountSubpath] ~= fileObject.internal then
  698.                             modTimestamps[mountSubpath] = {os.day(), os.time()}
  699.                         end
  700.                         fileCache.files[mountSubpath] = fileObject.internal
  701.                         locks[mountSubpath] = nil
  702.                     end
  703.                 end
  704.                 if string.sub(mode, 1, 1) == "r" then
  705.                     if string.sub(mode, 2, 2) == "b" then
  706.                         fileObject.read = function()
  707.                             fileObject.ptr = fileObject.ptr+1
  708.                             return string.byte(fileObject.internal, fileObject.ptr-1, fileObject.ptr-1)
  709.                         end
  710.                     else
  711.                         fileObject.readAll = function()
  712.                             local ret = string.sub(fileObject.ptr, #fileObject.internal)
  713.                             fileObject.ptr = #fileObject.internal+1
  714.                             if ret == "" then
  715.                                 ret = nil
  716.                             end
  717.                             return ret
  718.                         end
  719.                         fileObject.readLine = function()
  720.                             local stPtr = fileObject.ptr
  721.                             for i=fileObject.ptr, #fileObject.internal do
  722.                                 if (string.sub(fileObject.internal, i, i) == "\n") or (string.sub(fileObject.internal, i, i) == "") then
  723.                                     fileObject.ptr = i+1
  724.                                     return string.sub(fileObject.internal, stPtr, i-1)
  725.                                 end
  726.                             end
  727.                         end
  728.                     end
  729.                 elseif (string.sub(mode, 1, 1) == "w") or (string.sub(mode, 1, 1) == "a") then
  730.                     if string.sub(mode, 1, 1) == "w" then
  731.                         fileObject.internal = ""
  732.                     end
  733.                     if string.sub(mode, 2, 2) == "b" then
  734.                         fileObject.write = function(b)
  735.                             fileObject.internal = fileObject.internal..string.char(b)
  736.                         end
  737.                     else
  738.                         fileObject.writeLine = function(t)
  739.                             fileObject.internal = fileObject.internal..t..'\n'
  740.                         end
  741.                         fileObject.write = function(t)
  742.                             fileObject.internal = fileObject.internal..t
  743.                         end
  744.                     end
  745.                 end
  746.                 return fileObject
  747.             end
  748.         end
  749.     end
  750.     return oldFSOpen(file, mode)
  751. end
  752.  
  753. fs.exists = function(path)
  754.     path = string.gsub(path, "\\", "/")
  755.     if string.sub(path, 1, 1) == "/" then path = string.sub(path, 2) end
  756.     if string.sub(path, #path, #path) == "/" then path = string.sub(path, 1, #path-1) end
  757.     if ((string.sub(path, 1, #mountPath) == mountPath) and (mountPath ~= "")) then
  758.         return ((fileCache.files[string.sub(path, #mountPath+2)] ~= nil) or (fileCache.dirs[string.sub(path, #mountPath+2)] ~= nil))
  759.     else
  760.         return oldFSExists(path)
  761.     end
  762. end
  763.  
  764. fs.isDir = function(path)
  765.     path = string.gsub(path, "\\", "/")
  766.     if string.sub(path, 1, 1) == "/" then path = string.sub(path, 2) end
  767.     if string.sub(path, #path, #path) == "/" then path = string.sub(path, 1, #path-1) end
  768.     if ((string.sub(path, 1, #mountPath) == mountPath) and (mountPath ~= "")) then
  769.         return (fs.exists(path) and (fileCache.dirs[string.sub(path, #mountPath+2)] ~= nil))
  770.     else
  771.         return oldFSIsDir(path)
  772.     end
  773. end
  774.  
  775. fs.makeDir = function(dir)
  776.     dir = string.gsub(dir, "\\", "/")
  777.     if string.sub(dir, 1, 1) == "/" then dir = string.sub(dir, 2) end
  778.     if string.sub(dir, #dir, #dir) == "/" then dir = string.sub(dir, 1, #dir-1) end -- Strip first and last characters if they're slashes: '/a/b/c/' becomes 'a/b/c'
  779.     if ((string.sub(dir, 1, #mountPath) == mountPath) and (mountPath ~= "")) then
  780.         if not fileCache.dirs[string.sub(dir, #mountPath+2)] then
  781.             fileCache.dirs[string.sub(dir, #mountPath+2)] = {}
  782.         end
  783.     else
  784.         oldFSMakeDir(dir)
  785.     end
  786. end
  787.  
  788. fs.delete = function(path)
  789.     path = string.gsub(path, "\\", "/")
  790.     if string.sub(path, 1, 1) == "/" then path = string.sub(path, 2) end
  791.     if string.sub(path, #path, #path) == "/" then path = string.sub(path, 1, #path-1) end
  792.     if ((string.sub(path, 1, #mountPath) == mountPath) and (mountPath ~= "")) then
  793.         if fs.isDir(path) then
  794.             fileCache.dirs[string.sub(path, #mountPath+2)] = nil
  795.         else
  796.             fileCache.files[string.sub(path, #mountPath+2)] = nil
  797.         end
  798.     else
  799.         oldFSDelete(path)
  800.     end
  801. end
  802.  
  803. fs.getDrive = function(path)
  804.     path = string.gsub(path, "\\", "/")
  805.     if string.sub(path, 1, 1) == "/" then path = string.sub(path, 2) end
  806.     if string.sub(path, #path, #path) == "/" then path = string.sub(path, 1, #path-1) end
  807.     if ((string.sub(path, 1, #mountPath) == mountPath) and (mountPath ~= "")) then
  808.         return "virtual filesystem"
  809.     else
  810.         return oldFSGetDrive(path)
  811.     end
  812. end
  813.  
  814. fs.list = function(dir)
  815.     dir = string.gsub(dir, "\\", "/")
  816.     if string.sub(dir, 1, 1) == "/" then dir = string.sub(dir, 2) end
  817.     if string.sub(dir, #dir, #dir) == "/" then dir = string.sub(dir, 1, #dir-1) end
  818.     if ((string.sub(dir, 1, #mountPath) == mountPath) and (mountPath ~= "")) then
  819.         if fileCache.dirs[string.sub(dir, #mountPath+2)] then
  820.             updateDirTables()
  821.             local list = {}
  822.             for i=1, #fileCache.dirs[string.sub(dir, #mountPath+2)] do
  823.                 list[i] = fileCache.dirs[string.sub(dir, #mountPath+2)][i]
  824.             end
  825.             return list
  826.         end
  827.     else
  828.         local list = oldFSList(dir)
  829.         if ((dir ~= "") and (dir.."/"..fs.getName(mountPath) == mountPath)) or ((dir == "") and (fs.getName(mountPath) == mountPath)) then
  830.             table.insert(list, fs.getName(mountPath))
  831.         end
  832.         return list
  833.     end
  834.     return {}
  835. end
  836.  
  837. fs.copy = function(oldPath, newPath)
  838.     if not (fs.exists(oldPath) and (not fs.exists(newPath))) then
  839.         return
  840.     end
  841.     if mode ~= -1 then
  842.         if (string.sub(oldPath, 1, #mountPath) == mountPath) and (string.sub(newPath, 1, #mountPath) == mountPath) then -- copying from the cache to itself
  843.             fileCache.files[string.sub(newPath, #mountPath+2)] = fileCache.files[string.sub(oldPath, #mountPath+2)]
  844.         elseif (string.sub(oldPath, 1, #mountPath) == mountPath) and (not (string.sub(newPath, 1, #mountPath) == mountPath)) then -- copying from the cache to disk
  845.             local file = fs.open(newPath, "wb")
  846.             local lastPause = os.clock()
  847.             for i=1, #fileCache.files[string.sub(oldPath, #mountPath+2)] do
  848.                 file.write(string.byte(fileCache.files[string.sub(oldPath, #mountPath+2)], i, i))
  849.                 if (os.clock() - lastPause) >= 2.90 then
  850.                     os.queueEvent("")
  851.                     os.pullEvent("")
  852.                     lastPause = os.clock()
  853.                 end
  854.             end
  855.             file.close()
  856.         elseif (not (string.sub(oldPath, 1, #mountPath) == mountPath)) and (string.sub(newPath, 1, #mountPath) == mountPath) then -- copying from disk to the cache
  857.             local file = fs.open(oldPath, "rb")
  858.             fileCache.files[string.sub(newPath, #mountPath+2)] = ""
  859.             local lastPause = os.clock()
  860.             while true do
  861.                 local byte = file.read()
  862.                 if not byte then
  863.                     file.close()
  864.                     break
  865.                 end
  866.                 fileCache.files[string.sub(newPath, #mountPath+2)] = fileCache.files[string.sub(newPath, #mountPath+2)]..string.char(byte)
  867.                 if (os.clock() - lastPause) >= 2.90 then
  868.                     os.queueEvent("")
  869.                     os.pullEvent("")
  870.                     lastPause = os.clock()
  871.                 end
  872.             end
  873.         else -- copying from disk to disk
  874.             oldFSCopy(oldPath, newPath)
  875.         end
  876.     else -- again, disk->disk copy
  877.         oldFSCopy(oldPath, newPath)
  878.     end
  879. end
  880.  
  881. fs.move = function(oldPath, newPath)
  882.     if not (fs.exists(oldPath) and (not fs.exists(newPath))) then
  883.         return
  884.     end
  885.     if (((string.sub(oldPath, 1, #mountPath) == mountPath) or (string.sub(newPath, 1, #mountPath) == mountPath)) and (mountPath ~= "")) then -- moving to or from the cache
  886.         fs.copy(oldPath, newPath)
  887.         fs.delete(oldPath)
  888.     else
  889.         oldFSMove(oldPath, newPath)
  890.     end
  891. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement