[Mono-dev] Patch for ZipPackagePart.GetStreamCore in System.IO.Packaging (olive)

Yves Dhondt yves.dhondt at gmail.com
Mon Mar 16 15:21:27 EDT 2009


Issue:
-------
ZipPackagePart.GetStreamCore in System.IO.Packaging does not handle
Filemode mode input parameter (correctly).

Test program (requires an existing openxml document, for example
test.docx as input):
-------------------
I'm not that good with unit testing, but the following code should be
easily translated into 4 tests.


using System;
using System.IO;
using System.IO.Packaging;

namespace MonoOpenXmlExperiment
{
  class Program
  {
    static void Main(string[] args)
    {
      // Warning do this with a document which can be damaged!!!
      Package pack = Package.Open(args[0]);
      PackagePart part = pack.CreatePart(new Uri("/somepart.xml",
UriKind.Relative), "application/xml");

      try
      {
        Stream s2 = part.GetStream(FileMode.CreateNew, FileAccess.Write);
      }
      catch (Exception ex)
      {
        // An ArgumentException should be throw as CreateNew is an
unsupported mode.
        Console.WriteLine("Expected ArgumentException, got {0}.",
ex.GetType().ToString());
      }

      try
      {
        Stream s3 = part.GetStream(FileMode.Truncate, FileAccess.Write);
      }
      catch (Exception ex)
      {
        // An ArgumentException should be throw as Truncate is an
unsupported mode.
        Console.WriteLine("Expected ArgumentException, got {0}.",
ex.GetType().ToString());
      }

      try
      {
        Stream s4 = part.GetStream(FileMode.Append, FileAccess.Write);
      }
      catch (Exception ex)
      {
        // An ArgumentException should be throw as Append is an
unsupported mode.
        Console.WriteLine("Expected ArgumentException, got {0}.",
ex.GetType().ToString());
      }

      Stream s = part.GetStream(FileMode.OpenOrCreate, FileAccess.Write);
      StreamWriter sw = new StreamWriter(s);
      sw.Write("<test>aaaaaaa</test>");
      sw.Flush();

      Stream s5 = part.GetStream(FileMode.Create, FileAccess.ReadWrite);
      StreamWriter sw2 = new StreamWriter(s5);
      sw2.Write("<test>bbb</test>");
      sw2.Flush();

      // Verify that the part got overwritten correctly.
      Stream s6 = part.GetStream();
      StreamReader sr = new StreamReader(s6);
      Console.WriteLine("Expected <test>bbb</test>, got {0}.", sr.ReadToEnd());

      pack.Close();
    }
  }
}



Expected output:
------------------------
Expected ArgumentException, got System.ArgumentException.
Expected ArgumentException, got System.ArgumentException.
Expected ArgumentException, got System.ArgumentException.
Expected <test>bbb</test>, got <test>bbb</test>.

Output currently given by mono:
---------------------------------------------
Expected <test>bbb</test>, got <test>bbb</test>.

Remarks:
-------------
The last test is especially important as it indicates that a create
would only overwrite part of stream.

Solution:
------------
Replace function by:

protected override Stream GetStreamCore (FileMode mode, FileAccess access)
    {
      MemoryStream stream;

      switch (mode)
      {
        case FileMode.CreateNew:
          throw new ArgumentException("FileMode not supported", "CreateNew");
        case FileMode.Truncate:
          throw new ArgumentException("FileMode not supported", "Truncate");
        case FileMode.Append:
          throw new ArgumentException("FileMode not supported", "Append");
        case FileMode.Open:
          if (Package.PartStreams.TryGetValue(Uri, out stream)) {
            return new ZipPartStream(Package, stream, access);
          }
          else {
            throw new IOException(); // Verify that this is the
correct exception.
          }
        case FileMode.OpenOrCreate:
        case FileMode.Create:
          // Check if a stream is already loaded. If not, load it or
create an empty one.
          if (!Package.PartStreams.TryGetValue(Uri, out stream)) {
            stream = new MemoryStream();

            try
            {
              using (UnzipArchive archive = new
UnzipArchive(Package.PackageStream))
              {
                foreach (string file in archive.GetFiles())
                {
                  if (file != Uri.ToString().Substring(1))
                    continue;

                  using (Stream archiveStream = archive.GetStream(file))
                  {
                    int read = 0;
                    byte[] buffer = new
byte[Math.Min(archiveStream.Length, 2 * 1024)];
                    while ((read = archiveStream.Read(buffer, 0,
buffer.Length)) != 0)
                      stream.Write(buffer, 0, read);
                  }
                }
              }
            }
            catch
            {
              // The zipfile is invalid, so just create the file
              // as if it didn't exist
              stream.SetLength(0);
            }

            Package.PartStreams.Add(Uri, stream);
          }

   // This ensures that an existing stream to which a create
   // is applied will be overwritten.
   if (mode == FileMode.Create)
            stream.SetLength(0);

          return new ZipPartStream(Package, stream, access);
 default:
   throw new ArgumentOutOfRangeException("mode");
      }
}


More information about the Mono-devel-list mailing list