Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't use File.OpenWrite when trying to overwrite a file #93744

Merged
merged 1 commit into from
Oct 20, 2023

Conversation

akoeplinger
Copy link
Member

@akoeplinger akoeplinger commented Oct 19, 2023

File.OpenWrite will open an existing file which means if you write data that is shorter than the existing data the file will be corrupt. This is documented on https://learn.microsoft.com/en-us/dotnet/api/system.io.file.openwrite

The OpenWrite method opens a file if one already exists for the file path, or creates a new file if one does not exist. For an existing file, it does not append the new text to the existing text. Instead, it overwrites the existing characters with the new characters. If you overwrite a longer string (such as "This is a test of the OpenWrite method") with a shorter string (such as "Second run"), the file will contain a mix of the strings ("Second runtest of the OpenWrite method").

The fix is to use the FileMode.Create option in cases where the intention is to create a new file or overwrite an existing one.

I noticed this recently when using the illink feature to dump a dependency analysis xml, the next time I ran the build after making a change that'd reduce dependencies the xml would get corrupted since it wrote less data than the existing file had.

There were a couple instances across the codebase, this fixes them.

File.OpenWrite will open an existing file which means if you write data that is shorter than the existing data the file will be corrupt.
This is documented on https://learn.microsoft.com/en-us/dotnet/api/system.io.file.openwrite

> The OpenWrite method opens a file if one already exists for the file path, or creates a new file if one does not exist. For an existing file, it does not append the new text to the existing text. Instead, it overwrites the existing characters with the new characters. If you overwrite a longer string (such as "This is a test of the OpenWrite method") with a shorter string (such as "Second run"), the file will contain a mix of the strings ("Second runtest of the OpenWrite method").
@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Oct 19, 2023
@ghost ghost assigned akoeplinger Oct 19, 2023
@akoeplinger akoeplinger added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Oct 19, 2023
@ghost
Copy link

ghost commented Oct 19, 2023

Tagging subscribers to this area: @agocke, @sbomer, @vitek-karas
See info in area-owners.md if you want to be subscribed.

Issue Details

File.OpenWrite will open an existing file which means if you write data that is shorter than the existing data the file will be corrupt. This is documented on https://learn.microsoft.com/en-us/dotnet/api/system.io.file.openwrite

The OpenWrite method opens a file if one already exists for the file path, or creates a new file if one does not exist. For an existing file, it does not append the new text to the existing text. Instead, it overwrites the existing characters with the new characters. If you overwrite a longer string (such as "This is a test of the OpenWrite method") with a shorter string (such as "Second run"), the file will contain a mix of the strings ("Second runtest of the OpenWrite method").

The fix is to use the FileMode.Create option in cases where the intention is to create a new file or overwrite an existing one.

I noticed this recently when using the illink feature to dump a dependency analysis xml, the next time I ran the build after making a change that'd reduce dependencies the xml would get corrupted since it wrote less data than the existing file had.

Author: akoeplinger
Assignees: akoeplinger
Labels:

area-Tools-ILLink, needs-area-label

Milestone: -

@akoeplinger akoeplinger removed the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Oct 19, 2023
@radical
Copy link
Member

radical commented Oct 19, 2023

Good catch!

Copy link
Member

@MichalStrehovsky MichalStrehovsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@@ -28,7 +28,7 @@ public ILAssemblyGeneratingMethodDebugInfoProvider(string fileName, DebugInforma
{
_wrappedProvider = wrappedProvider;
_fileName = fileName;
_tw = new StreamWriter(File.OpenWrite(fileName));
_tw = new StreamWriter(new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More compact alternative:

Suggested change
_tw = new StreamWriter(new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None));
_tw = new StreamWriter(File.Create(fileName));

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that it matters here but File.Create() uses FileAccess.ReadWrite instead of FileAccess.Write so it is slightly different.
I'll keep this as is to avoid restarting CI but let me know if you feel strongly.

@akoeplinger akoeplinger merged commit 111b089 into dotnet:main Oct 20, 2023
188 of 191 checks passed
@akoeplinger akoeplinger deleted the file-openwrite branch October 20, 2023 09:28
@lewing
Copy link
Member

lewing commented Oct 20, 2023

we should probably backport the parts that apply to 8.0

@MichalStrehovsky
Copy link
Member

we should probably backport the parts that apply to 8.0

The nativeaot part is related to producing a file used to debug the compiler. The code is under a non-public switch. The risk is pretty much none, but it's also not important for 8.0. This obviously only applies to that part of this pr.

@akoeplinger
Copy link
Member Author

Backported the relevant changes in #93854

@ghost ghost locked as resolved and limited conversation to collaborators Nov 22, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Tools-ILLink .NET linker development as well as trimming analyzers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants