FileStream's FileMode.OpenOrCreate overwrites file
Asked Answered
C

2

8

Documentation says FileMode.OpenOrCreate "specifies that the operating system should open a file if it exists; otherwise, a new file should be created", which sounds like it will open the file and write to it. Instead, the file seems to be overwritten.

How do I add to the file, rather than overwrite it?

class Logger : IDisposable
{
    private FileStream fs;
    private StreamWriter sw;
    
    public Logger()
    {
        // INTENT (but not reality): Will create file if one does not exist, otherwise opens existing file to append text
        fs = new FileStream("log.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
        sw = new StreamWriter(fs, Encoding.UTF8);
    }

    public void Log(string message)
    {
        sw.WriteLine(message);
        sw.Flush();
        fs.Flush();
    }
    
    public void Dispose()
    {
        sw?.Dispose();
        fs?.Dispose();
    }
}
Careerism answered 25/8, 2018 at 19:1 Comment(1)
well, the merit and 'uniqueness' of the answer withstanding, I think the question has merit in that it sympathizes to a misconception that FileMode.OpenOrCreate might be thought to overwrite a file. (afterall FileMode.Create will). so I'm removing my comment above regarding duplication (links are probably still associate to this question though)Calc
C
16

The documentation is correct – but note that open and append are not synonymous.
FileMode.OpenOrCreate is not causing the FileStream constructor to delete the preexisting file wholesale; rather, it causes the stream to start at the beginning of the file. What you are observing is the file contents being overwritten by the StreamWriter, not the FileStream constructor replacing the file immediately.

You have to move the stream position to the end of the file to add text at the end. To do this, you could move the position with FileStream.Seek() or change the FileMode to FileMode.Append. FileMode.Append is likely more idiomatic but also requires making the FileAccess write-only, rather than read-write. Otherwise, an exception is thrown upon calling the constructor.

Option 1 (FileMode.OpenOrCreate, FileAccess.ReadWrite)

public Logger()
{
    // Will create file if one does not exist, otherwise opens existing file
    fs = new FileStream("log.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
    // Set stream position to end-of-file
    fs.Seek(0, SeekOrigin.End);
    sw = new StreamWriter(fs, Encoding.UTF8);
}

Option 2 (FileMode.Append, FileAccess.Write)

public Logger()
{
    // Will create file if one does not exist, otherwise appends to existing file
    fs = new FileStream("log.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
    sw = new StreamWriter(fs, Encoding.UTF8);
}
Careerism answered 25/8, 2018 at 19:1 Comment(0)
B
2

I experienced this issue in a different way, I used OpenOrCreate when I needed plain Create.

Based on the bug I fixed by changing OpenOrCreate to just Create, I'm convinced that the aCropalypse vulnerability was also caused by this confusion.

Since OpenOrCreate doesn't truncate existing files and just starts writing your stream over the top of the original file from the beginning of the file, if you write less bytes then the original file size, you get part of the old file left at the end, which in the aCropalypse case was PNG image data, since PNG files end with an IEND block, image readers don't always throw an error they just ignore anything after IEND

Blackwell answered 27/3, 2023 at 13:33 Comment(2)
Yes, that's more-or-less what happened with aCropalypse.Careerism
Having issues with this exact fact and stumbled upon this, thanks for clarifying!Panache

© 2022 - 2025 — McMap. All rights reserved.