about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-11-02 20:36:47 +0000
committerEric Wong <normalperson@yhbt.net>2011-11-02 22:32:55 +0000
commit909a2ae12d11593d59fccf30b7c2143cc9f441ab (patch)
tree9627742fbdfa7d9bd86b0de28a6a7bc5b4895701
parent46355ddfecb4e210b4dd3b6f91dde330e8d509ff (diff)
downloadmogilefs-client-909a2ae12d11593d59fccf30b7c2143cc9f441ab.tar.gz
syswrloop and it's filter arg is going away, but we're
stuck supporting bigfiles in read-only mode for eternity.
-rw-r--r--lib/mogilefs/bigfile.rb53
-rw-r--r--lib/mogilefs/bigfile/filter.rb58
2 files changed, 74 insertions, 37 deletions
diff --git a/lib/mogilefs/bigfile.rb b/lib/mogilefs/bigfile.rb
index 34af8ee..1be36a5 100644
--- a/lib/mogilefs/bigfile.rb
+++ b/lib/mogilefs/bigfile.rb
@@ -1,12 +1,14 @@
 # -*- encoding: binary -*-
-require 'zlib'
-require 'digest/md5'
 require 'uri'
 require 'mogilefs/util'
+require 'mogilefs/network'
+
+# Used for reading deprecated "bigfile" objects generated by the deprecated
+# mogtool(1) utility.  Not recommended for new projects.
 
 module MogileFS::Bigfile
-  GZIP_HEADER = "\x1f\x8b".freeze # mogtool(1) has this
   # VALID_TYPES = %w(file tarball partition).map { |x| x.freeze }.freeze
+  include MogileFS::Network
 
   # returns a big_info hash if successful
   def bigfile_stat(key)
@@ -14,37 +16,17 @@ module MogileFS::Bigfile
   end
 
   # returns total bytes written and the big_info hash if successful, raises an
-  # exception if not wr_io is expected to be an IO-like object capable of
-  # receiving the syswrite method.
+  # exception if not.  wr_io is expected to be an IO-like object capable of
+  # receiving the write method.
   def bigfile_write(key, wr_io, opts = { :verify => false })
     info = bigfile_stat(key)
-    zi = nil
-    md5 = opts[:verify] ? Digest::MD5.new : nil
     total = 0
 
     # we only decode raw zlib deflated streams that mogtool (unfortunately)
     # generates.  tarballs and gzip(1) are up to to the application to decrypt.
-    filter = Proc.new do |buf|
-      if zi == nil
-        if info[:compressed] && info[:type] == 'file' &&
-             buf.length >= 2 && buf[0,2] != GZIP_HEADER
-          zi = Zlib::Inflate.new
-
-          # mogtool(1) seems to have a bug that causes it to generate bogus
-          # MD5s if zlib deflate is used.  Don't trust those MD5s for now...
-          md5 = nil
-        else
-          zi = false
-        end
-      end
-      buf ||= ''
-      if zi
-        zi.inflate(buf)
-      else
-        md5 << buf
-        buf
-      end
-    end if (info[:compressed] || md5)
+    if info[:compressed] || opts[:verify]
+      wr_io = MogileFS::Bigfile::Filter.new(wr_io, info, opts)
+    end
 
     info[:parts].each_with_index do |part,part_nr|
       next if part_nr == 0 # info[:parts][0] is always empty
@@ -58,16 +40,13 @@ module MogileFS::Bigfile
       end
 
       sock = http_read_sock(uris[0])
-      md5.reset if md5
-      w = sysrwloop(sock, wr_io, filter)
+      w = copy_stream(sock, wr_io)
 
-      if md5 && md5.hexdigest != part[:md5]
-        raise MogileFS::ChecksumMismatchError, "#{md5} != #{part[:md5]}"
-      end
+      wr_io.respond_to?(:md5_check!) and wr_io.md5_check!(part[:md5])
       total += w
     end
-
-    syswrite_full(wr_io, zi.finish) if zi
+    wr_io.flush
+    total += wr_io.flushed_bytes if wr_io.respond_to?(:flushed_bytes)
 
     [ total, info ]
   end
@@ -100,8 +79,8 @@ module MogileFS::Bigfile
 
       rv
     end
-
-end # module MogileFS::Bigfile
+end
+require "mogilefs/bigfile/filter"
 
 __END__
 # Copied from mogtool:
diff --git a/lib/mogilefs/bigfile/filter.rb b/lib/mogilefs/bigfile/filter.rb
new file mode 100644
index 0000000..e95a47e
--- /dev/null
+++ b/lib/mogilefs/bigfile/filter.rb
@@ -0,0 +1,58 @@
+# -*- encoding: binary -*-
+require 'zlib'
+require 'digest/md5'
+
+# Filter class to wrap IO objects and uncompress DEFLATE'd files
+#
+# This is used for reading "bigfile" objects generated by the
+# (deprecated) mogtool(1)
+class MogileFS::Bigfile::Filter
+  GZIP_HEADER = "\x1f\x8b"
+  INFLATABLE_TYPES = { "file" => true }
+  attr_reader :flushed_bytes
+
+  def initialize(io, info, opts)
+    @io = io
+    @info = info
+    @md5 = opts[:verify] ? Digest::MD5.new : nil
+    @zi = nil
+    @flushed_bytes = 0
+  end
+
+  def md5_check!(expect)
+    return unless @md5
+    current = @md5.hexdigest
+    current == expect or
+      raise MogileFS::ChecksumMismatchError, "#{current} != #{expect}"
+    @md5.reset
+  end
+
+  def flush
+    @flushed_bytes = @io.write(@zi.finish) if @zi
+    @io.flush
+  end
+
+  def write(buf)
+    if nil == @zi
+      if @info[:compressed] &&
+         INFLATABLE_TYPES.include?(@info[:type]) &&
+         buf.bytesize >= 2 &&
+         buf[0,2] != GZIP_HEADER
+
+        @zi = Zlib::Inflate.new
+
+        # mogtool(1) seems to have a bug that causes it to generate bogus
+        # MD5s if zlib deflate is used.  Don't trust those MD5s for now...
+        @md5 = nil
+      else
+        @zi = false
+      end
+    end
+    if @zi
+      buf = @zi.inflate(buf)
+    else
+      @md5 << buf
+    end
+    @io.write(buf)
+  end
+end