From 45bf02a843fb221f77c888cafb7294aa3f3d3eb8 Mon Sep 17 00:00:00 2001 From: XeonSquared Date: Sun, 1 Oct 2023 13:40:26 +1000 Subject: [PATCH] rtfs v1 adding fragmentation. restructure the rtfs libraries, for reasons that will become clear. --- .../rtfs.lua => rtfs-v0/lib/fs/rtfs/v0.lua | 49 +- rtfs-v0/package.cfg | 3 + rtfs/lib/fs/rtfs/init.lua | 66 +++ rtfs/lib/fs/rtfs/v1.lua | 508 ++++++++++++++++++ 4 files changed, 582 insertions(+), 44 deletions(-) rename rtfs/lib/rtfs.lua => rtfs-v0/lib/fs/rtfs/v0.lua (93%) create mode 100644 rtfs-v0/package.cfg create mode 100644 rtfs/lib/fs/rtfs/init.lua create mode 100644 rtfs/lib/fs/rtfs/v1.lua diff --git a/rtfs/lib/rtfs.lua b/rtfs-v0/lib/fs/rtfs/v0.lua similarity index 93% rename from rtfs/lib/rtfs.lua rename to rtfs-v0/lib/fs/rtfs/v0.lua index 40aeb6e..1736c14 100644 --- a/rtfs/lib/rtfs.lua +++ b/rtfs-v0/lib/fs/rtfs/v0.lua @@ -1,6 +1,5 @@ -local rtfs, internal = {}, {} -local proxy = {} -proxy.cacheSize = 8 +local common = require "fs.rtfs" +local rtfs, proxy = {}, setmetatable({},{__index=common.proxy}) local ieformat = ">BI8I8c47" -- ftype, start, len, name local iesize = string.packsize(ieformat) local sbformat = ">c4I2I8c18" -- "rtfs" magic, version, index size in entries, label. label length subject to change. @@ -18,9 +17,7 @@ function proxy:log(message,level) end -- data mangling functions -local function fnormalize(s) - return table.concat(fs.segments(s),"/") -end +local fnormalize = common.fnormalize function proxy:cSOI(n) n=n-1 local ms = self.capacity / self.blockSize @@ -31,42 +28,6 @@ function proxy:cSOI(n) return ms - s, so+1, eo end --- just for ease-of-use -local function getProxy(addr) - if type(addr) == "string" then - return component.proxy(component.get(addr,"partition") or component.get(addr) or addr) - end - return addr -end - --- cache stuff -function proxy:cacheClean() - while #self.cache > self.cacheSize do - table.remove(self.cache, 1) - end -end -function proxy:cachedRead(s) - for k,v in ipairs(self.cache) do - if v[1] == s then - self.cacheHits = self.cacheHits + 1 - return v[2] - end - end - self.cache[#self.cache + 1] = {s, self.d.readSector(s)} - self.cacheMisses = self.cacheMisses + 1 - return self.cache[#self.cache][2] -end -function proxy:cachedWrite(s,d) - for k,v in ipairs(self.cache) do - if v[1] == s then - table.remove(self.cache, k) - end - end - self.cache[#self.cache + 1] = {s, d} - self:cacheClean() - return self.d.writeSector(s,d) -end - -- superblock stuff function proxy:updateSB() self:cachedWrite(1,string.pack(sbformat, "rtfs", 0, self.isize, self.label)) @@ -412,7 +373,7 @@ end ---------- function rtfs.mount(d) - d=getProxy(d) + d=common.getProxy(d) local p = setmetatable({}, {__index=proxy}) local magic, version, isize, label = string.unpack(sbformat,d.readSector(1)) assert(magic == "rtfs", "incorrect magic") @@ -536,7 +497,7 @@ function rtfs.mount(d) end function rtfs.format(d,label) - d=getProxy(d) + d=common.getProxy(d) print(d.address) d.writeSector(1, string.pack(sbformat,"rtfs", 0, 1, label or "")) rtfs.mount(d):writeIEntry(1, ftypes.empty, 2, (d.getCapacity() / d.getSectorSize()) - 2, "") diff --git a/rtfs-v0/package.cfg b/rtfs-v0/package.cfg new file mode 100644 index 0000000..f43856e --- /dev/null +++ b/rtfs-v0/package.cfg @@ -0,0 +1,3 @@ +{["name"]="rtfs-v0", + ["description"]="RT-11 filesystem clone", + ["authors"]="Izaya"} diff --git a/rtfs/lib/fs/rtfs/init.lua b/rtfs/lib/fs/rtfs/init.lua new file mode 100644 index 0000000..a037187 --- /dev/null +++ b/rtfs/lib/fs/rtfs/init.lua @@ -0,0 +1,66 @@ +local cache = (package.loaded["fs.rtfs"] or {}).cache or {hitsR=0, hitsW=0, missesR=0, missesW=0} +local proxy = {} +local rtfs = {proxy=proxy, cache=cache} +local sbformat = ">c4I2I8c18" -- "rtfs" magic, version, index size in entries, label. label length subject to change. + +rtfs.cacheSize = computer.totalMemory() // 1024 // 16 + +-- data mangling functions +function rtfs.fnormalize(s) + return table.concat(fs.segments(s),"/") +end +function proxy:log(message,level) + syslog(message,level or syslog.debug,"rtfs:"..self.label) +end + + +-- just for ease-of-use +function rtfs.getProxy(addr) + if type(addr) == "string" then + return component.proxy(component.get(addr,"partition") or component.get(addr) or addr) + end + return addr +end + +-- cache stuff +local function cacheClean() + while #cache > rtfs.cacheSize do + table.remove(cache, 1) + end +end +function proxy:cachedRead(s) + for k,v in ipairs(cache) do + if v[1] == self.d.address and v[2] == s then + rtfs.cache.hitsR = rtfs.cache.hitsR + 1 + return v[3] + end + end + cache[#cache + 1] = {self.d.address, s, self.d.readSector(s)} + rtfs.cache.missesR = rtfs.cache.missesR + 1 + cacheClean() + return cache[#cache][3] +end +function proxy:cachedWrite(s,d) + for k,v in ipairs(cache) do + if v[1] == self.d.address and v[2] == s then + table.remove(cache, k) + end + end + cache[#cache + 1] = {self.d.address, s, d} + cacheClean() + return self.d.writeSector(s,d) +end + +function rtfs.mount(a) + local d = rtfs.getProxy(a) + local magic, version = string.unpack(sbformat, d.readSector(1)) + assert(magic == "rtfs","not an rtfs filesystem") + local w, l = pcall(require, string.format("fs.rtfs.v%i", version)) + if w then return l.mount(a) end + error("incompatible rtfs version") +end +function rtfs.format(a,l) + return require("fs.rtfs.v1").format(a,l) +end + +return rtfs diff --git a/rtfs/lib/fs/rtfs/v1.lua b/rtfs/lib/fs/rtfs/v1.lua new file mode 100644 index 0000000..70358da --- /dev/null +++ b/rtfs/lib/fs/rtfs/v1.lua @@ -0,0 +1,508 @@ +local common = require "fs.rtfs" +local rtfs, proxy = {}, setmetatable({},{__index=common.proxy}) +local ieformat = ">I2I8I8c46" -- type (4 bits) and index (12 bits), start sector (32 bits), lenth in bytes (32 bits), name/path (46 bytes) +local sbformat = ">c4I2I8c18" -- "rtfs" magic, version, index size in entries, label. label length subject to change. +local iesize = string.packsize(ieformat) +local ftypes = { + empty = 0, + dfile = 1, + dfext = 2, + ddir = 3, + ddex = 4, + dtfile = 5, + dtext = 6, + dlink = 7, + unused = 8, + file = 9, + fext = 10, + dir = 11, + dex = 12, + tfile = 13, + text = 14, + link = 15 +} + +-- data mangling functions +local fnormalize = common.fnormalize +function proxy:cSOI(n) + n=n-1 + local ms = self.capacity / self.blockSize + local ePS = self.blockSize // iesize + local s = n // ePS + local so = self.blockSize - iesize - ((n % ePS) * iesize) + local eo = so + iesize + return ms - s, so+1, eo +end + +-- superblock stuff +function proxy:updateSB() + self:cachedWrite(1,string.pack(sbformat, "rtfs", 1, self.isize, self.label)) +end +function proxy:setISize(n) + if self.isize ~= n then + self.isize = n + self:updateSB() + end +end + +-- index I/O +function proxy:readIEntry(n) + local sector, sstart, send = self:cSOI(n) + local rt = {string.unpack(ieformat,self:cachedRead(sector):sub(sstart, send))} + local et, ex = rt[1]>>12, rt[1]&0x1FF + rt[4] = rt[4]:gsub("\0","") + rt[5] = nil + return et, ex, table.unpack(rt,2) +end +function proxy:writeIEntry(n,et,ex,es,el,en) -- index, type, extent index, extent start, extent length, extent name + local ne = string.pack(ieformat, (et<<12) | (ex & 0x1FF), es, el, en) + local sector, sstart, send = self:cSOI(n) + -- check if there's an extent in the way of this entry + while self:findIEntry(nil,nil,nil,nil,nil,sector) do + self:log("auto-compact triggered") + -- compactIndex will shrink the extent if it is safe to overwrite (deleted, free, etc) + n = self:compactIndex(n) or self.isize + 1 + local newn = n == self.isize + 1 + -- if that fails, because it can't be shrunk, we need to move it ourselves + local fi, ft = self:findIEntry(nil,nil,nil,nil,nil,sector) + if fi and ft[1] > ftypes.unused then + self:log("auto-compact unsuccessful, trying to relocate extent") + -- try to find a suitable free space + local lfi, lfis = self:bestFree() + local lf = {self:readIEntry(lfi)} + -- if it's not big enough, give up. + assert(lf[4] >= ft[4], "unable to relocate extent to write index entry") + -- if it is, create an extent structure at the end of the free space + local de = {ft[1], ft[2], lf[3] + lf[4]//self.blockSize - math.ceil(ft[4]/self.blockSize), ft[4], ft[5]} + lf[4] = lf[4] - (math.ceil(ft[4] / self.blockSize) * self.blockSize) + -- move the data to the new extent + for i = 0, ft[4]//self.blockSize do + self:writeExtent(de, i, self:readExtent(ft, i)) + end + -- overwrite the old entries, which shouldn't get us back here + self:writeIEntry(fi, table.unpack(de)) + self:writeIEntry(lfi, table.unpack(lf)) + -- and finally, make a new entry for the now old data, sans one sector + self:writeIEntry(self:nextEntry(), ft[1]-8, ft[2], ft[3], ft[4] - self.blockSize, ft[5]) + end + -- if the aim was to make a new entry, update n so it will do so + n = newn and self.isize + 1 or n + sector, sstart, send = self:cSOI(n) + end + local pes = self:cachedRead(sector) + local ns = pes:sub(1, sstart - 1) .. ne .. pes:sub(send + 1) + self:cachedWrite(sector, ns) + self:setISize(math.max(self.isize, n)) + return n +end +function proxy:allIEntries() + local i = 0 + return function() + if i < self.isize then + i = i + 1 + return i, self:readIEntry(i) + end + return nil + end +end + +-- for debugging +function proxy:dumpIndex() + local t = {} + for i, tp, ei, st, sl, n in self:allIEntries() do + t[#t+1] = {i,tp,ei,st,sl,(st+math.ceil(sl/self.blockSize))-1,n} + end + table.sort(t, function(a,b) return a[4] < b[4] end) + print(" # ty ext# init len end name") + for k,v in ipairs(t) do + if k % 23 == 0 then + io.read() + print(" # ty ext# init len end name") + end + print(string.format("%2i %1x %4i %4i %8i %4i %s",table.unpack(v))) + end +end + +-- index searching +function proxy:findIEntry(et,ex,es,el,en,ef) -- number number number string number -- Find an entry matching all provided parameters and ignoring any not specified. + local ri, rt + for i, tp, ix, st, sl, n in self:allIEntries() do + local sf = st + math.ceil(sl/self.blockSize) - 1 + if (et or tp) == tp and (ex or ix) == ix and (es or st) == st and (el or sl) == sl and (en or n) == n and (ef or sf) == sf then + ri, rt = i, {self:readIEntry(i)} + break + end + end + return ri, rt +end +function proxy:nextEntry() + local ni = self.isize+1 + for i, tp in self:allIEntries() do + if tp == ftypes.unused then + ni = i + break + end + end + return ni +end + +-- extent allocation +function proxy:bestFree(last) + local ri, sri + local bs, sbs = 0, 0 + if last then + ri, rs = self:findIEntry(nil,nil,last[3]+math.ceil(last[4]/self.blockSize)) + if ri and rs and rs[1] <= ftypes.ddex then + return ri, false, true + end + end + for i, tp, ei, st, sl, n in self:allIEntries() do + if tp <= ftypes.ddex then + if sl > bs then + sri, sbs = ri, bs + ri, bs = i, sl + end + end + end + return bs/2 > sbs and ri or sri, bs/2 > sbs +end +function proxy:allocateExtent(name, etype, ei, esize, last) + etype, ei, esize = etype or ftypes.tfile, ei or 0, math.ceil(math.max(4096,esize or self.capacity / 256)/self.blockSize)*self.blockSize + self:compactIndex() + local lfi,lfis,lfext = self:bestFree(last) + local lf = {self:readIEntry(lfi)} + local esize = math.min(esize, lf[4]) + local lfs, es = lf[4]//self.blockSize, esize//self.blockSize + -- if biggest available area, split and put extent in the middle + if lfis then + local new = {etype, ei, lf[3] + math.floor(lfs/2), esize, name} + lf[4] = math.floor(lfs/2) * self.blockSize + local free = {ftypes.empty, 0, new[3] + es, lfs*self.blockSize - new[4] - lf[4], ""} + self:writeIEntry(lfi, table.unpack(lf)) + self:writeIEntry(self:nextEntry(), table.unpack(new)) + self:writeIEntry(self:nextEntry(), table.unpack(free)) + return self:findIEntry(etype, ei, nil, nil, name) + else -- otherwise, put it at the start + local new = {etype, ei, lf[3], esize, name} + local free = {ftypes.empty, 0, new[3] + es, lfs*self.blockSize - new[4], ""} + local efi, eff + if last then + last={table.unpack(last)} + efi, eff = self:findIEntry(table.unpack(last)) + end + -- if a previous entry was provided and the name matches, continue the extent + if lfext and efi and name == eff[5] then + last[4] = math.ceil((last[4] + new[4]) / self.blockSize) * self.blockSize + new = last + self:writeIEntry(efi, table.unpack(new)) + self:writeIEntry(lfi, table.unpack(free)) + lfi=efi + -- failing that, replace the free space entry with the new extent and + -- write a new free space entry + else + self:writeIEntry(lfi, table.unpack(new)) + self:writeIEntry(self:nextEntry(), table.unpack(free)) + end + return lfi, new, lfext and new[4] + end + return false +end + +function proxy:writeExtent(ft, o, d) -- write a sector to an extent + assert(o >= 0 and o < (ft[4]//self.blockSize) + 1, "out of range") + assert(#d <= self.blockSize, "block too large") + return self.d.writeSector(ft[3] + o, d .. ("\0"):rep(self.blockSize - #d)) +end +function proxy:readExtent(ft, o) -- read a sector from an extent + assert(o >= 0 and o < (ft[4]//self.blockSize) + 1, "out of range") + return self.d.readSector(ft[3] + o):sub(1, math.min(self.blockSize, ft[4] - (o*self.blockSize))) +end + +function proxy:openHandle(name, mode, fmt) + name, fmt = fnormalize(name), fmt or {ftypes.file, ftypes.fext, ftypes.tfile, ftypes.text} + local handle, xt = {aft={},fmt=fmt,buffer="",bytes=0} + for c in mode:gmatch(".") do + handle[c] = true + end + local fi, oft = self:findIEntry(fmt[1], nil, nil, nil, name) + if handle.r then + if not fi then + return false, "file not found" + end + handle.ft, handle.aft[1] = oft, oft + elseif handle.w or handle.a then + if handle.a and fi then + local lfi, lf = self:findIEntry(fmt[2], oft[2], nil, nil, oft[5]) + fi, handle.ft, xt = self:allocateExtent(name, fmt[4], oft[2] + 1, nil, lf or oft) + -- if in append mode, put the file extent into the "all extents" table to be updated later + handle.aft[1], handle.bytes = oft, xt and (lfi and lf[4] or oft[4]) or 0 + -- read the last sector into the write buffer so it can be written back to disk with new data + -- then rewind the counter so it does get written + if handle.bytes%self.blockSize ~= 0 then + handle.buffer = self:readExtent(lf or oft, handle.bytes//512) + handle.bytes = handle.bytes - #handle.buffer + end + else + -- allocate the new extent to write into + fi, handle.ft = self:allocateExtent(name, fmt[3]) + end + handle.aft[#handle.aft+(xt and 0 or 1)] = handle.ft + end + local ni = #self.handles+1 + self.handles[ni] = handle + return ni +end + +function proxy:closeHandle(h) + local handle = self.handles[h] + if handle.w or handle.a then + -- flush the buffer just in case + self:writeHandle(h, "", true) + local fs = handle.ft[4] - handle.bytes + -- update each index entry + self:compactIndex() + -- mark any old entries with a matching name as deleted when not appending + if not handle.a then + for i, tp, ei, st, sl, n in self:allIEntries() do + if n == handle.aft[1][5] and tp > 8 then + self:writeIEntry(i, tp-8, ei, st, sl, n) + end + end + end + for k,v in ipairs(handle.aft) do + local fi, ft = self:findIEntry(table.unpack(v)) + self:writeIEntry(fi, + -- determine if it's the file or extent part + (v[1] == handle.fmt[3] or v[1] == handle.fmt[1]) and handle.fmt[1] or handle.fmt[2], + -- file part wants the number of extents, extents want their index + (v[1] == handle.fmt[3] or v[1] == handle.fmt[1]) and handle.ft[2] or v[2], + -- the rest is fine as is, unless it's the last item + v[3], v == handle.ft and handle.bytes or v[4], v[5] + ) + end + if fs > self.blockSize then + self:writeIEntry(self:nextEntry(), ftypes.empty, 0, handle.ft[3] + math.ceil(handle.bytes / self.blockSize), (fs//self.blockSize)*self.blockSize, "") + self:compactIndex() + end + end + self.handles[h] = nil +end + +function proxy:writeHandle(h,d,f) + local handle = self.handles[h] + assert(handle.w or handle.a, "file not open in writable mode") + handle.buffer = handle.buffer .. d + local rv + -- only attempt to write when there's enough data, or forced + while #handle.buffer >= self.blockSize or f do + f = false + -- allocate a new extent if this one is full + if handle.bytes >= handle.ft[4] then + local fi, ft, xt = self:allocateExtent(handle.ft[5], handle.fmt[4], handle.ft[2] + 1, nil, handle.ft) + if xt then + handle.aft[#handle.aft], handle.ft = ft, ft + else + handle.aft[#handle.aft+1], handle.ft, handle.bytes = ft, ft, 0 + end + end + -- write a block and increment the counter + local wb = handle.buffer:sub(1, self.blockSize) + handle.buffer = handle.buffer:sub(self.blockSize + 1) + rv = self:writeExtent(handle.ft, handle.bytes//self.blockSize, wb) + handle.bytes = handle.bytes + #wb + end + return rv +end + +function proxy:readHandle(h) + local handle = self.handles[h] + assert(handle.r, "file not open in read mode") + -- if there's no more data in the extent, find the next + if handle.bytes >= handle.ft[4] then + local ce = handle.ft[1] == handle.fmt[2] and handle.ft[2] or 0 + if ce >= handle.aft[1][2] then + return nil + end + local fi, ft = self:findIEntry(handle.fmt[2], ce + 1, nil, nil, handle.ft[5]) + handle.aft[#handle.aft+1], handle.ft, handle.bytes = ft, ft, 0 + end + local rb = self:readExtent(handle.ft, handle.bytes//512) + handle.bytes = handle.bytes + #rb + return rb +end + +function proxy:compactIndex(ti) + -- compact contingous free space + for i, tp, ei, st, sl, n in self:allIEntries() do + if tp <= ftypes.ddex then + sl=math.max(sl/self.blockSize) + local ni, nt = self:findIEntry(nil,nil,st+sl) + if ni and nt and nt[1] <= ftypes.ddex then + self:writeIEntry(i,tp,ei,st,(sl*self.blockSize)+nt[4],n) + self:writeIEntry(ni,ftypes.unused,0,0,0,"") + elseif tp <= ftypes.ddex and sl < 1 then + self:writeIEntry(i,ftypes.unused,0,0,0,"") + end + end + end + local ri + -- reverse walk the index to find holes + for i = self.isize, 1, -1 do + local ne = self:nextEntry() or self.isize + local ft = {self:readIEntry(i)} + if ne < i then + self:writeIEntry(ne, table.unpack(ft)) + self:setISize(i-1) + ri = (ti == i) and ne or ri + end + end + + -- resize space at end of disk + local li, ls, lt = 0, 0, nil + for i, tp, ei, st, sl, n in self:allIEntries() do + sl=math.max(sl/self.blockSize) + if st+sl > ls then + li, ls = i, st+sl + lt = {tp, ei, st, sl, n} + end + end + if lt[1] <= ftypes.text then + local lastsec = (self.capacity / self.blockSize) - math.ceil(self.isize / (self.blockSize / iesize)) + self:writeIEntry(li, lt[1], lt[2], lt[3], (lastsec - lt[3])*self.blockSize, lt[5]) + end + return ri +end + +function rtfs.mount(p) + local d = common.getProxy(p) + local p = setmetatable({}, {__index=proxy}) + p.d = d + p.fstype, p.version, p.isize, p.label = string.unpack(sbformat,d.readSector(1)) + assert(p.fstype == "rtfs", "incorrect magic") + assert(p.version == 1, "incompatible rtfs version") + p.d = d + p.cache, p.handles = {}, {} + p.cacheHits, p.cacheMisses = 0, 0 + p.blockSize, p.capacity, p.label = d.getSectorSize(), d.getCapacity(), p.label:gsub("\0","") + + -- FS proxy functions + function p.exists(name) + name = fnormalize(name) + for i, tp, ex, st, sl, n in p:allIEntries() do + if (tp == ftypes.file or tp == ftypes.dir) and n == name then + return true + end + end + end + function p.isDirectory(name) + name = fnormalize(name) + return name == "" and true or p:findIEntry(ftypes.dir, nil, nil, nil, name) ~= nil + end + function p.size(name) + name = fnormalize(name) + local rv = 0 + for i, tp, ex, st, sl, n in p:allIEntries() do + if n == name and tp > ftypes.unused then + rv = rv + sl + end + end + return rv + end + function p.spaceUsed() + local c = 0 + for i, tp, ex, st, sl, n in p:allIEntries() do + if tp > ftypes.unused then + c = c + sl + end + end + return c + end + function p.spaceTotal() + return p.capacity - (math.ceil(p.isize / (p.blockSize / iesize))*p.blockSize) - p.blockSize + end + p.isReadOnly = p.d.isReadOnly + + function p.list(name) + name = fnormalize(name) + local rt = {} + for i, tp, ex, st, sl, n in p:allIEntries() do + local seg = fs.segments(n) + local pn = table.concat(seg,"/",1,#seg-1) + local fn = seg[#seg] + if (tp == ftypes.file or tp == ftypes.dir) and pn == name then + rt[#rt+1] = fn .. ((tp == ftypes.dir and "/") or "") + end + end + return rt + end + function p.makeDirectory(name) + name = fnormalize(name) + if #name < 1 or p:findIEntry(nil,nil,nil,nil,name) then return false end + local seg = fs.segments(name) + for j = 1, #seg-1 do + p.makeDirectory(table.concat(seg, "/", 1, j)) + end + if not p.isDirectory(table.concat(seg,"/",1,#seg-1)) then return false end + local ni = p:nextEntry() + p:writeIEntry(ni, ftypes.dir, 0, 0, 0, name) +-- p:setISize(math.max(p.isize, ni)) + return true + end + + function p.remove(name) + name = fnormalize(name) + for i, tp, ex, st, sl, n in p:allIEntries() do + if n == name and tp > ftypes.unused then + p:writeIEntry(i, tp-8, ex, st, sl, n) + end + end + end + + function p.rename(from, to) + from, to = fnormalize(from), fnormalize(to) + if p.exists(from) then + if p.exists(to) then + p.remove(to) + end + local fi, ft = p:findIEntry(p.isDirectory(name) and ftypes.dir or ftypes.file, nil, nil, to) + ft[5] = to + p:writeIEntry(fi, table.unpack(ft)) + end + end + function p.lastModified() + return 0 + end + + function p.open(name, mode) + name, mode = fnormalize(name), mode or "r" + return p:openHandle(name, mode) + end + function p.write(h,v) + return p:writeHandle(h,v) + end + function p.read(h,v) + return p:readHandle(h,v) + end + function p.close(h) + return p:closeHandle(h) + end + + function p.getLabel() + return p.label + end + function p.setLabel(label) + p.label = label + p:updateSB() + end + + return p +end + +function rtfs.format(p, label) + local d = common.getProxy(p) + print(d.address) + d.writeSector(1, string.pack(sbformat,"rtfs", 1, 1, label or "")) + rtfs.mount(d):writeIEntry(1, ftypes.empty, 0, 2, d.getCapacity() - (d.getSectorSize() * 2), "") +end + +return rtfs