Skip to content

Instantly share code, notes, and snippets.

@jaredallard
Created June 5, 2015 23:30
Show Gist options
  • Save jaredallard/51fc66786c1ebf618a52 to your computer and use it in GitHub Desktop.
Save jaredallard/51fc66786c1ebf618a52 to your computer and use it in GitHub Desktop.
Almost done cext implementation
--[[
A lightweight ext-like FS implemented in pure lua.
Specs: <insert link here>
@author Jared Allard <jaredallard@outlook.com>
@license MIT
@notice uses JSDoc like insource documention.
]]
-- intial declearation of the FS table.
local cext = {
["version"] = 001,
["date"] = 20150530,
["inodeAllocations"] = 500, -- amount of lines to save for inodes untill dynamic.
["print"] = function(msg)
return nil -- no output
end,
["fs"] = nil, -- assurance.
}
-- temporary.
-- split a string
function string.split(s, delimiter)
result = {};
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
end
return result;
end
--[[
Create a filesystem.
@return {boolean} - success or failure
]]
cext.createFS = function(this, location, size)
-- this.checkArgs(this, size)
-- superblockHeader.
local superblockHeader = this.version..",0,"..size..",1"
local inodeAllocation = ""
-- generate whitespace.
this.print("generating lines of whitespace.")
for i = 1,this.inodeAllocations do
inodeAllocation = inodeAllocation.."\n"
end
local contents = this.inodeAllocations+1
-- remove a stale filesystem.
if fs.exists(location) then
this.print("removing stale fs")
fs.delete(location)
end
-- open a io file handle and inject the superblockHeader & inodeAllocation ws.
this.print("writing fs")
local iohandle = io.open(location, "w")
iohandle:write(superblockHeader..","..tostring(contents)..inodeAllocation)
iohandle:close()
this.print("done")
-- return true since we probably had no issues.
return true
end
--[[
Open the file system (essentially load it into memory)
@return {boolean} - success or fail.
]]
cext.open = function(this, filesystem)
-- give the fs object an initial start.
this.fs = {
["superblock"] = nil,
["data"] = nil,
["path"] = nil,
}
-- load the raw filesystem.
local iohandle = fs.open(filesystem, "r")
this.fs.superblock = iohandle.readLine()
this.fs.data = iohandle.readAll() -- read the data raw. This includes inodes.
iohandle.close()
-- look at the superblock for the next available inode line.
local superblockSplit = string.split(this.fs.superblock, ",")
-- assign it to the table.
this.fs.superblock = {}
this.fs.superblock.version = superblockSplit[1]
this.fs.superblock.inodes = superblockSplit[2]
this.fs.superblock.size = superblockSplit[3]
this.fs.superblock.nextInode = superblockSplit[4]
this.fs.superblock.nextContents = superblockSplit[5]
-- probably no errors, so set the path.
this.fs.path = filesystem
this.print('opened cext filesystem version: '..this.fs.superblock.version)
-- return true as we probably had no issues.
return true
end
--[[
Close the filesystem. (write to disk)
@return {boolean} - success or fail
]]
cext.close = function(this)
local superblock = this.fs.superblock.version..","..this.fs.superblock.inodes..
","..this.fs.superblock.size..","..this.fs.superblock.nextInode..","..
this.fs.superblock.nextContents
local data = this.fs.data
-- create the final object of the fs.
local filesystem = superblock..data
local fshandle = fs.open(this.fs.path, "w")
fshandle.write(filesystem)
fshandle.close()
this.print("cext: this.fs.data_table = "..tostring(this.fs.data_table))
return true
end
--[[
Write to the filesystem
@return {boolean} - success or fail, if fail then error message
on second param return.
]]
cext.write = function(this, filename, data)
if this.fs == nil then
error("fs isn't loaded. load with this:open(fs)")
end
local fileLocation = fs.getDir(filename)
local fileName = fs.getName(filename)
-- generate the object.
this.print("generating inode for '"..tostring(filename).."'")
local inodeObject = fileLocation..","..fileName
-- write the filecontents.
local nl = string.split(data, "\n")
local min = this.fs.superblock.nextContents
local max = min
for i, v in pairs(nl) do
max = max+1
local n = this.fs.superblock.nextContents+(i-1)
if i == #nl then
this.print("cext: write: i is nl len. call asm.")
this:writeToLine(n, v, true)
else
this:writeToLine(n, v)
end
end
inodeObject = inodeObject..","..min.."-"..max
-- print it out for now.
this.print(inodeObject)
this.print("fs.superblock.nextInode = "..tostring(this.fs.superblock.nextInode))
-- write the new inode to the actual line.
this:writeToLine(this.fs.superblock.nextInode, inodeObject, true)
-- incrememnt the amount of inodes by 1.
this.fs.superblock.nextInode = math.floor(this.fs.superblock.nextInode+1)
-- increment the amount of content lines
this.fs.superblock.nextContents = max
return true
end
--[[
Write to a line in the filesystem cache.
@return {boolean} - success or fail.
]]
cext.writeToLine = function(this, line, data, asm)
-- check if we've already been called. We should sync on asm = true
if this.fs.data_table == nil then
this.fs.data_table = string.split(this.fs.data, "\n")
end
-- write to the line, this is a very fragile operation.
this.fs.data_table[line] = data
if asm == true then
this.print("cext: told to assemble the table into a string")
-- convert the table into a string.
local d = ""
for i,v in pairs(this.fs.data_table) do
d = d..v.."\n"
end
-- set the string as the new data object.
this.fs.data = tostring(d)
-- invalidate the table.
this.fs.data_table = nil
end
-- since we are stil here, we had a successful write.
return true
end
--[[
Read the contents of a file.
@return {string} - file contents, or nil on failure.
]]
cext.read = function(this, file)
local inode = this.fs.data:match(file..",[a-z,0-9-]+")
if inode == nil then
-- doesn't exist.
return nil
end
this.print("got: "..inode)
local m = string.split(inode, ",")
this.print("contents are on: "..m[2])
return this:readLines(m[2])
end
cext.readLines = function(this, linesep)
local nl = string.split(this.fs.data, "\n")
local m = string.split(linesep, '-')
local min = tonumber(m[1])
local max = tonumber(m[2])
local d = ""
for i,v in pairs(nl) do
if i > min and i < max then
d = d..v.."\n"
end
end
if d == "" then
return false
end
return d
end
-- TESTING
fs.delete("test.fs")
cext:createFS("test.fs", 1000)
cext:open("test.fs")
cext:write("nothing", "hacky fix")
cext:close()
cext:write("ccdocker", fs.open("ccdocker", "r").readAll())
cext:close()
-- test file read.
cext:read("ccdocker")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment