MtnMCG

ndisk_verify

Dec 22nd, 2025 (edited)
58
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.62 KB | None | 0 0
  1. -- ndisk_verify.lua
  2. -- Verifies which files in chunklocation.txt actually exist on nodes
  3. -- Reports recoverable vs broken files, optionally cleans up broken entries
  4.  
  5. local modem = peripheral.find("modem")
  6. if not modem then error("No modem found") end
  7. modem.open(5)
  8.  
  9. -- Load NDisk secret for authentication
  10. local NDISK_SECRET = nil
  11. if fs.exists(".ndisk_secret") then
  12. local f = fs.open(".ndisk_secret", "r")
  13. NDISK_SECRET = f.readAll()
  14. f.close()
  15. end
  16.  
  17. local REQUEST_TIMEOUT = 2
  18. local LOG_FILE = "ndisk_verify_results.txt"
  19.  
  20. local function generateRequestId()
  21. return os.getComputerID() .. "_" .. os.epoch("utc") .. "_" .. math.random(10000)
  22. end
  23.  
  24. local function sendRequest(nodeId, request)
  25. request.targetNode = nodeId
  26. request.secret = NDISK_SECRET -- Include auth secret
  27. modem.transmit(5, 5, request)
  28. local timer = os.startTimer(REQUEST_TIMEOUT)
  29. while true do
  30. local event, p1, p2, p3, p4 = os.pullEvent()
  31. if event == "modem_message" and type(p4) == "table" and p4.id == request.id then
  32. return p4.response
  33. elseif event == "timer" and p1 == timer then
  34. return nil
  35. end
  36. end
  37. end
  38.  
  39. local function main()
  40. term.clear()
  41. term.setCursorPos(1, 1)
  42.  
  43. term.setTextColor(colors.cyan)
  44. print("========================================")
  45. print(" NDisk Verification Tool")
  46. print("========================================")
  47. term.setTextColor(colors.white)
  48. print("")
  49.  
  50. if not fs.exists("chunklocation.txt") then
  51. print("ERROR: chunklocation.txt not found!")
  52. return
  53. end
  54.  
  55. -- Parse chunklocation.txt
  56. print("Loading chunk map...")
  57. local chunkMap = {} -- path -> {chunkNum -> {primary, mirror}}
  58. local file = fs.open("chunklocation.txt", "r")
  59. for line in file.readLine do
  60. local parts = {}
  61. for part in line:gmatch("%S+") do
  62. table.insert(parts, part)
  63. end
  64. if #parts >= 3 then
  65. local filename = parts[1]
  66. local chunkMatch = parts[2]:match("chunk(%d+)")
  67. local chunkNum = chunkMatch and tonumber(chunkMatch) or nil
  68. local primaryId = tonumber(parts[3])
  69. local mirrorId = parts[4] and tonumber(parts[4]) or nil
  70.  
  71. if filename and chunkNum and primaryId then
  72. chunkMap[filename] = chunkMap[filename] or {}
  73. chunkMap[filename][chunkNum] = {primary = primaryId, mirror = mirrorId}
  74. end
  75. end
  76. end
  77. file.close()
  78.  
  79. local fileCount = 0
  80. for _ in pairs(chunkMap) do fileCount = fileCount + 1 end
  81. print("Found " .. fileCount .. " files in chunk map")
  82. print("")
  83. print("Verifying each file (this takes a while)...")
  84. print("")
  85.  
  86. -- Verify each file
  87. local working = {}
  88. local broken = {}
  89.  
  90. for path, chunks in pairs(chunkMap) do
  91. local chunkCount = 0
  92. for _ in pairs(chunks) do chunkCount = chunkCount + 1 end
  93.  
  94. local allGood = true
  95. for chunkNum = 1, chunkCount do
  96. local info = chunks[chunkNum]
  97. if not info then
  98. allGood = false
  99. break
  100. end
  101.  
  102. -- Try primary
  103. local response = sendRequest(info.primary, {
  104. id = generateRequestId(),
  105. action = "read",
  106. path = path,
  107. chunk = chunkNum
  108. })
  109.  
  110. if response and response.success then
  111. -- Good!
  112. elseif info.mirror then
  113. -- Try mirror
  114. response = sendRequest(info.mirror, {
  115. id = generateRequestId(),
  116. action = "read",
  117. path = path,
  118. chunk = chunkNum
  119. })
  120. if not (response and response.success) then
  121. allGood = false
  122. break
  123. end
  124. else
  125. allGood = false
  126. break
  127. end
  128. end
  129.  
  130. if allGood then
  131. table.insert(working, path)
  132. else
  133. table.insert(broken, path)
  134. end
  135.  
  136. -- Progress
  137. local total = #working + #broken
  138. if total % 5 == 0 then
  139. term.clearLine()
  140. term.setCursorPos(1, select(2, term.getCursorPos()))
  141. write("Progress: " .. total .. "/" .. fileCount)
  142. end
  143. end
  144.  
  145. -- Write detailed log to file
  146. local logFile = fs.open(LOG_FILE, "w")
  147. logFile.writeLine("=== NDisk Verification Results ===")
  148. logFile.writeLine("")
  149. logFile.writeLine("WORKING FILES (" .. #working .. "):")
  150. for _, path in ipairs(working) do
  151. logFile.writeLine(" + " .. path)
  152. end
  153. logFile.writeLine("")
  154. logFile.writeLine("BROKEN FILES (" .. #broken .. "):")
  155. for _, path in ipairs(broken) do
  156. logFile.writeLine(" X " .. path)
  157. end
  158. logFile.close()
  159.  
  160. -- Show summary on screen
  161. print("")
  162. print("")
  163. print("========================================")
  164. term.setTextColor(colors.lime)
  165. print("WORKING: " .. #working .. " files")
  166. term.setTextColor(colors.red)
  167. print("BROKEN: " .. #broken .. " files")
  168. term.setTextColor(colors.white)
  169. print("========================================")
  170. print("")
  171. print("Full list saved to: " .. LOG_FILE)
  172. print("Run 'edit " .. LOG_FILE .. "' to view details")
  173. print("")
  174.  
  175. if #broken > 0 then
  176. term.setTextColor(colors.yellow)
  177. print("CLEANUP OPTION:")
  178. print("Remove " .. #broken .. " broken entries from chunklocation.txt?")
  179. print("This ONLY removes entries that point to missing data.")
  180. print("A backup will be created first.")
  181. print("")
  182. write("Proceed? [y/N]: ")
  183. term.setTextColor(colors.white)
  184. local input = read()
  185.  
  186. if input:lower() == "y" or input:lower() == "yes" then
  187. -- Backup first
  188. if fs.exists("chunklocation.txt.preverify") then
  189. fs.delete("chunklocation.txt.preverify")
  190. end
  191. fs.copy("chunklocation.txt", "chunklocation.txt.preverify")
  192. print("Backed up to chunklocation.txt.preverify")
  193.  
  194. -- Create broken set for fast lookup
  195. local brokenSet = {}
  196. for _, path in ipairs(broken) do
  197. brokenSet[path] = true
  198. end
  199.  
  200. -- Rewrite chunklocation.txt without broken entries
  201. local oldFile = fs.open("chunklocation.txt.preverify", "r")
  202. local newFile = fs.open("chunklocation.txt", "w")
  203. local removed = 0
  204.  
  205. for line in oldFile.readLine do
  206. local parts = {}
  207. for part in line:gmatch("%S+") do
  208. table.insert(parts, part)
  209. end
  210. local filename = parts[1]
  211. if filename and not brokenSet[filename] then
  212. newFile.writeLine(line)
  213. else
  214. removed = removed + 1
  215. end
  216. end
  217.  
  218. oldFile.close()
  219. newFile.close()
  220.  
  221. term.setTextColor(colors.lime)
  222. print("Removed " .. removed .. " broken chunk entries")
  223. term.setTextColor(colors.white)
  224. else
  225. print("Cleanup skipped.")
  226. end
  227. end
  228.  
  229. print("")
  230. print("Done. Next step: run ndisk_recover_from_disk")
  231. end
  232.  
  233. main()
  234.  
Advertisement
Add Comment
Please, Sign In to add comment