Bug 11699

Summary: FileStream writing incorrectly (some internal position is wrong)
Product: [Mono] Class Libraries Reporter: Andrii Nakryiko <andrii.nakryiko>
Component: mscorlibAssignee: marcos.henrich
Status: RESOLVED FIXED    
Severity: normal CC: marcos.henrich, masafa, miguel, mono-bugs+mono, tom.philpot
Priority: ---    
Version: unspecified   
Target Milestone: Untriaged   
Hardware: PC   
OS: Linux   
Tags: Is this bug a regression?: ---
Last known good build:

Description Andrii Nakryiko 2013-04-10 05:48:29 UTC
We've had a problems with FileStream under Mono (different versions, both 2.10.x and 3.0.x). It seems like some internal positioning problem, because instead of overwriting some parts of file, it just appends written data. It is very critical that the amount of bytes written is larger than internal buffer size. I've managed to create small isolated test case that shows both what is necessary for bug to occur and easy workaround for that. But hopefully this will be fixed as this is core functionality.

    using System;
    using System.IO;
    using NUnit.Framework;

    [TestFixture]
    public class mono_filestream_bug
    {
        [Test]
        public void show_time()
        {
            const int pos = 1;
            const int bufferSize = 128;

            var filename = Path.GetTempFileName();
            File.WriteAllBytes(filename, new byte[pos + 1]); // init file with zeros

            var bytes = new byte[bufferSize + 1 /* THIS IS WHAT MAKES A BIG DIFFERENCE */];
            new Random().NextBytes(bytes);

            using (var file = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.Read,
                                             bufferSize, FileOptions.SequentialScan))
            {
                file.Read(new byte[pos], 0, pos); // THIS READ IS CRITICAL, WITHOUT IT EVERYTHING WORKS
                Assert.AreEqual(pos, file.Position); // !!! here it says position is correct, but writes at different position !!!
                // file.Position = pos; // !!! this fixes test !!!
                file.Write(bytes, 0, bytes.Length);

                //Assert.AreEqual(pos + bytes.Length, file.Length); -- fails
            }

            using (var filestream = File.Open(filename, FileMode.Open, FileAccess.Read))
            {
                var bb = new byte[bytes.Length];
                filestream.Position = pos;
                filestream.Read(bb, 0, bb.Length);
                Assert.AreEqual(bytes, bb);
            }
        }
    }
Comment 1 Marek Safar 2013-04-24 03:38:19 UTC
We are mixing reading and writing buffer offsets here. We need to split buf_offset to be read and write specific
Comment 2 Andrii Nakryiko 2013-06-25 09:56:43 UTC
Any updates on this issue?
Comment 3 Miguel de Icaza [MSFT] 2015-05-20 16:12:51 UTC
The issue in this particular bug is that our first call to Read() will fill the buffer with as much data as possible, in this case 2 bytes.

The reason why file.Position returns 1 instead of 2 is because we are not actually calling the OS seek functionality.  We only do that when the Handle has been surfaced (isExposed).

So the right fix, like Marek suggests is to keep two separate poiters.

For reference, this is what the CoreFX does: 

https://github.com/dotnet/corefx/blob/master/src/System.IO.FileSystem/src/System/IO/FileStream.cs
Comment 4 marcos.henrich 2016-02-25 18:56:22 UTC
After a successful Read the buffer position is updated to the char after the last one read.
The problem as mentioned by Miguel is that the position does not reflect the actual file handle position.

The pull request below fixes this by doing a seek before the write. 
https://github.com/mono/mono/pull/2678
Comment 5 marcos.henrich 2016-02-29 14:01:58 UTC
Fixed in mono master 7013a709c30ab2f1e1eee56b7aa8d4088e7f555e.
https://github.com/mono/mono/commit/7013a709c30ab2f1e1eee56b7aa8d4088e7f555e

Should be included in Cycle 8, Mono 4.6.