#!/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 XorFS < RFuse::Fuse def initialize(mnt,kernelopt,libopt,parts) @parts = parts.collect { |part| [File.new(part,(part == parts[0] and "r+" or "r")),File.stat(part).size] } @size = @parts.collect { |x| x[1] }.min 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) xor(@parts.collect { |part,partSize| part.seek(offset) part.read(size) }) end def write(ctx,path,buf,offset,fi) @parts[0][0].seek(offset) @parts[0][0].write(xor([buf]+@parts[1..-1].collect { |part,partSize| part.seek(offset) part.read(buf.size) })) buf.length end private def xor(strings) strings[1..-1].inject(NArray.to_na(strings[0],"byte")) { |result,string| result ^= NArray.to_na(string,"byte") }.to_s end end if ARGV.size < 2 puts "minimum 2 parameters needed:" puts "\truby xorfs.rb MOUNT_POINT WRITABLE_FILE_OR_DEV [ READABLE_FILE_OR_DEV ]*" exit end fo = XorFS.new(ARGV[0],["default_permissions"],[],ARGV[1..-1]); #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