#!/usr/bin/ruby1.8 # # yunta83@gmail.com # BSD license # require 'rubygems' require 'rfuse_ng' require 'narray' class Stat attr_accessor :uid,:gid,:mode,:size,:atime,:mtime,:ctime,:dev,:ino,:nlink,:rdev,:blksize,:blocks def initialize(mode=0,size=0) @mode,@size,@uid,@gid,@atime,@mtime,@ctime,@dev,@ino,@nlink,@rdev,@blksize,@blocks = [mode,size]+[0]*11 end end class E2fsFsFs < RFuse::Fuse def initialize(mnt,kernelopt,libopt,dev) dump = `dumpe2fs '#{dev}' 2>/dev/null` dump =~ /^Block size:\s+([0-9]+)/m @blockSize = $1.to_i dump =~ /^Free blocks: (.*)/ freeBlocks = $1.to_i ranges = [] dump.scan(/^ Free blocks: (.*)/) { ranges += $1.split(",").collect { |range| range = range.split("-").collect{ |x| x.to_i } range << range[0] if range.size == 1 range } } @size = ranges.collect { |a,b| b-a }.inject(0) { |a,b| a+b } * @blockSize @map = NArray.int(freeBlocks) @sizes = NArray.int(freeBlocks) first = last = 0 ranges.sort.reverse.each { |a,b| last = first + b - a + 1 @map[first..last-1] = NArray[a..b] @sizes[first..last-1] = NArray[(b-a+1)..1] first = last } @device = File.new(dev,"r+") super(mnt,kernelopt,libopt) end def readdir(ctx,path,filler,offset,ffi) return if offset > 0 filler.push("data",Stat.new(0100777),0) end def getattr(ctx,path) raise Errno::ENOTDIR.new(path) if path != "/" and path != "/data" Stat.new((path == "/" and 040777 or 0100777),(path == "/" and 0 or @size)) end def open(ctx,path,ffi) end def read(ctx,path,size,offset,fi) # STDERR.write("READ: size %i offset %i\n"%[size,offset]) out = [] transfer(size,offset) { |physicalAddress,toRead| # STDERR.write("reading %i from %i\n"%[toRead,physicalAddress]) @device.seek(physicalAddress) out << @device.read(toRead) } out.join("") end def write(ctx,path,buf,offset,fi) last = 0 transfer(buf.size,offset) { |physicalAddress,toWrite| # STDERR.write("writing %i to %i\n"%[toWrite,physicalAddress]) @device.seek(physicalAddress) @device.write(buf[last..(last+toWrite-1)]) last += toWrite } return buf.length end private def transfer(size,offset) logicalBlock = offset / @blockSize initialOffset = offset % @blockSize physicalAddress = @map[logicalBlock]*@blockSize+initialOffset transferSize = [size,@sizes[logicalBlock]*@blockSize-initialOffset].min while true # STDERR.write("logicalBlock %i available blocks: %i physicalBlock: %i transferBlocks: %i+%i "%[logicalBlock,@sizes[logicalBlock],@map[logicalBlock],transferSize/@blockSize,transferSize%@blockSize]) yield physicalAddress,transferSize size -= transferSize break if size <= 0 logicalBlock += transferSize / @blockSize physicalAddress = @map[logicalBlock]*@blockSize transferSize = [size,@sizes[logicalBlock]*@blockSize].min end raise :WTF if size < 0 end end if ARGV.size != 2 puts "2 parameters needed:" puts "\truby e2fsfsfs.rb DEVICE MOUNTPOINT" exit end fo = E2fsFsFs.new(ARGV[1],["default_permissions"],[],ARGV[0]); #kernel: default_permissions,allow_other,kernel_cache,large_read,direct_io # max_read=N,fsname=NAME #library: debug,hard_remove Signal.trap("TERM") do fo.exit fo.unmount end begin fo.loop rescue print "Error:" + $! end