Skip to content

Legacy FileStream mode and symlink breaking changes #31602

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

Merged
merged 5 commits into from
Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/core/compatibility/7.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ If you're migrating an app to .NET 7, the breaking changes listed here might aff
| [Collectible Assembly in non-collectible AssemblyLoadContext](core-libraries/7.0/collectible-assemblies.md) | ❌ | ✔️ | Preview 5 |
| [Equals method behavior change for NaN](core-libraries/7.0/equals-nan.md) | ❌ | ✔️ | Preview 5 |
| [Generic type constraint on PatternContext\<T>](core-libraries/7.0/patterncontext-generic-constraint.md) | ❌ | ❌ | Preview 3 |
| [Legacy FileStream strategy removed](core-libraries/7.0/filestream-compat-switch.md) | ❌ | ✔️ | Preview 1 |
| [Library support for older frameworks](core-libraries/7.0/old-framework-support.md) | ❌ | ❌ | Preview 1 |
| [Maximum precision for numeric format strings](core-libraries/7.0/max-precision-numeric-format-strings.md) | ❌ | ✔️ | RC 1 |
| [SerializationFormat.Binary is obsolete](core-libraries/7.0/serializationformat-binary.md) | ❌ | ❌ | Preview 2 |
| [Time fields on symbolic links](core-libraries/7.0/symbolic-link-timestamps.md) | ❌ | ✔️ | Preview 1 |
| [Tracking linked cache entries](core-libraries/7.0/memorycache-tracking.md) | ❌ | ✔️ | Preview 1 |
| [Validate CompressionLevel for BrotliStream](core-libraries/7.0/compressionlevel-validation.md) | ❌ | ✔️ | Preview 1 |

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: ".NET 6 breaking change: FileStream doesn't synchronize file offset with OS"
description: Learn about the .NET 6 breaking change in core .NET libraries where FileStream doesn't synchronize the file offset with the operating system.
ms.date: 05/05/2021
ms.date: 10/04/2022
---
# FileStream no longer synchronizes file offset with OS

Expand Down Expand Up @@ -65,6 +65,9 @@ With this change, <xref:System.IO.FileStream.ReadAsync%2A> operations are up to
set DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM=1
```

> [!NOTE]
> This switch is only available in .NET 6. It was [removed in .NET 7](../7.0/filestream-compat-switch.md).

## Affected APIs

None.
Expand All @@ -73,15 +76,3 @@ None.

- [SetFilePointer function](/windows/win32/api/fileapi/nf-fileapi-setfilepointer)
- [SetFilePointerEx function](/windows/win32/api/fileapi/nf-fileapi-setfilepointerex)

<!--

### Category

- Core .NET libraries

### Affected APIs

Not detectible via API analysis.

-->
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: ".NET 6 breaking change: FileStream.Position updated after ReadAsync or WriteAsync completion"
description: Learn about the .NET 6 breaking change in core .NET libraries where FileStream.Position is updated after ReadAsync or WriteAsync completion.
ms.date: 05/05/2021
ms.date: 10/04/2022
---
# FileStream.Position updates after ReadAsync or WriteAsync completes

Expand Down Expand Up @@ -68,18 +68,9 @@ Now, when buffering is enabled (that is, the `bufferSize` argument that's passed
set DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM=1
```

> [!NOTE]
> This switch is only available in .NET 6. It was [removed in .NET 7](../7.0/filestream-compat-switch.md).

## Affected APIs

- <xref:System.IO.FileStream.Position?displayProperty=fullName>

<!--

### Category

- Core .NET libraries

### Affected APIs

- `P:System.IO.FileStream.Position`

-->
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
title: ".NET 7 breaking change: Legacy FileStream strategy removed"
description: Learn about the .NET 7 breaking change in core .NET libraries where the the ability to use the legacy `FileStream` implementation has been removed.
ms.date: 10/04/2022
---
# Legacy FileStream strategy removed

The `AppContext` switch `System.IO.UseNet5CompatFileStream` and the ability to use the legacy <xref:System.IO.FileStream> implementation were removed.

## Previous behavior

The legacy `FileStream` implementation was available and you could opt into it by using the `UseNet5CompatFileStream` switch or the `DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM` environment variable.

## New behavior

Starting in .NET 7, you can no longer opt in to use the legacy `FileStream` implementation.

## Version introduced

.NET 7 Preview 1

## Type of breaking change

This change can affect [binary compatibility](../../categories.md#binary-compatibility).

## Reason for change

The `UseNet5CompatFileStream` switch and `DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM` environment variable were included in .NET 6 in case the new implementation caused breaking changes. Any breaking changes have now been fixed. Since there are no more bugs introduced by the `FileStream` changes, the compatibility mode was removed and with it all the legacy code, which makes the codebase easier to maintain.

## Recommended action

If you're currently using the switch (or the `DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM` environment variable) to opt into legacy code and are upgrading to .NET 7, the switch will no longer have any effect and you should remove it.

## Affected APIs

- <xref:System.IO.FileStream?displayProperty=fullName>
- <xref:System.IO.File.Create(System.String)?displayProperty=fullName>
- <xref:System.IO.File.Create(System.String,System.Int32)?displayProperty=fullName>
- <xref:System.IO.File.Create(System.String,System.Int32,System.IO.FileOptions)?displayProperty=fullName>
- <xref:System.IO.File.Create(System.String,System.Int32,System.IO.FileOptions,System.Security.AccessControl.FileSecurity)?displayProperty=fullName>
- <xref:System.IO.File.Open(System.String,System.IO.FileMode)?displayProperty=fullName>
- <xref:System.IO.File.Open(System.String,System.IO.FileStreamOptions)?displayProperty=fullName>
- <xref:System.IO.File.Open(System.String,System.IO.FileMode,System.IO.FileAccess)?displayProperty=fullName>
- <xref:System.IO.File.Open(System.String,System.IO.FileMode,System.IO.FileAccess,System.IO.FileShare)?displayProperty=fullName>
- <xref:System.IO.File.OpenRead(System.String)?displayProperty=fullName>
- <xref:System.IO.File.OpenWrite(System.String)?displayProperty=fullName>
- <xref:System.IO.FileSystemAclExtensions.Create(System.IO.FileInfo,System.IO.FileMode,System.Security.AccessControl.FileSystemRights,System.IO.FileShare,System.Int32,System.IO.FileOptions,System.Security.AccessControl.FileSecurity)?displayProperty=fullName>
- <xref:System.IO.FileInfo.Create?displayProperty=fullName>
- <xref:System.IO.FileInfo.Open%2A?displayProperty=fullName>
- <xref:System.IO.FileInfo.OpenRead?displayProperty=fullName>
- <xref:System.IO.FileInfo.OpenWrite?displayProperty=fullName>

## See also

- [FileStream no longer synchronizes file offset with OS (.NET 6)](../6.0/filestream-doesnt-sync-offset-with-os.md)
- [FileStream.Position updates after ReadAsync or WriteAsync completes (.NET 6)](../6.0/filestream-position-updates-after-readasync-writeasync-completion.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
---
title: ".NET 7 breaking change: Time fields on symbolic links"
description: Learn about the .NET 7 breaking change in core .NET libraries where updating CreationTime[Utc], LastAccessTime[Utc], and LastWriteTime[Utc] on a symbolic link no longer affects the target .
ms.date: 10/04/2022
---
# Time fields on symbolic links

When changes are made to the following time-related fields on a symbolic link ("symlink"), the updates now affect the symlink itself and not the target:

- <xref:System.IO.FileSystemInfo.CreationTime>
- <xref:System.IO.FileSystemInfo.CreationTimeUtc>
- <xref:System.IO.FileSystemInfo.LastAccessTime>
- <xref:System.IO.FileSystemInfo.LastAccessTimeUtc>
- <xref:System.IO.FileSystemInfo.LastWriteTime>
- <xref:System.IO.FileSystemInfo.LastWriteTimeUtc>

## Previous behavior

Previously, updating any of the time-related fields on a symlink affected the fields of the symlink's target.

Consider the following program that prints the various time field values on a file and its symbolic link, updates the symlink's time field values to 1 day later, and then reprints the time field values on both the file and symlink.

```csharp
string filename = "file";
string linkname = "link";

// Create a file and symlink.
File.Create(filename).Dispose();
File.CreateSymbolicLink(linkname, filename);

Console.WriteLine("Before update:");
PrintMetadata(filename);
PrintMetadata(linkname);

UpdateMetadata(linkname);

Console.WriteLine("\nAfter update:");
PrintMetadata(filename);
PrintMetadata(linkname);

static void UpdateMetadata(string filename)
{
DateTime tomorrow = DateTime.Now.AddDays(1);

File.SetCreationTime(filename, tomorrow);
File.SetLastAccessTime(filename, tomorrow);
File.SetLastWriteTime(filename, tomorrow);
File.SetAttributes(filename, File.GetAttributes(filename) | FileAttributes.Offline);
}

static void PrintMetadata(string filename)
{
Console.WriteLine($"---{filename}---");
Console.WriteLine("Creation:\t" + File.GetCreationTime(filename));
Console.WriteLine("Last access:\t" + File.GetLastAccessTime(filename));
Console.WriteLine("Last write:\t" + File.GetLastWriteTime(filename));
Console.WriteLine("Attributes:\t" + File.GetAttributes(filename));
}
```

Previously, after updating the values on the symlink, only the target file's time fields were updated. The output of the preceding program was as follows:

```output
Before update:
---file---
Creation: 9/29/2022 10:35:40 AM
Last access: 9/29/2022 10:35:40 AM
Last write: 9/29/2022 10:35:40 AM
Attributes: Archive
---link---
Creation: 9/29/2022 10:35:40 AM
Last access: 9/29/2022 10:35:40 AM
Last write: 9/29/2022 10:35:40 AM
Attributes: Archive, ReparsePoint

After update:
---file---
Creation: 9/30/2022 10:35:40 AM
Last access: 9/30/2022 10:35:40 AM
Last write: 9/30/2022 10:35:40 AM
Attributes: Archive
---link---
Creation: 9/29/2022 10:35:40 AM
Last access: 9/29/2022 10:35:40 AM
Last write: 9/29/2022 10:35:40 AM
Attributes: Archive, ReparsePoint, Offline
```

## New behavior

Starting in .NET 7, updating any of the time-related fields on a symlink affects the fields of the symlink itself and not the target file.

The output of the program shown in the [Previous behavior](#previous-behavior) section is as follows:

```output
Before update:
---file---
Creation: 9/29/2022 10:33:39 AM
Last access: 9/29/2022 10:33:39 AM
Last write: 9/29/2022 10:33:39 AM
Attributes: Archive
---link---
Creation: 9/29/2022 10:33:39 AM
Last access: 9/29/2022 10:33:39 AM
Last write: 9/29/2022 10:33:39 AM
Attributes: Archive, ReparsePoint

After update:
---file---
Creation: 9/29/2022 10:33:39 AM
Last access: 9/29/2022 10:33:39 AM
Last write: 9/29/2022 10:33:39 AM
Attributes: Archive
---link---
Creation: 9/30/2022 10:33:39 AM
Last access: 9/30/2022 10:33:39 AM
Last write: 9/30/2022 10:33:39 AM
Attributes: Archive, ReparsePoint, Offline
```

## Version introduced

.NET 7 Preview 1

## Type of breaking change

This change can affect [binary compatibility](../../categories.md#binary-compatibility).

## Reason for change

The previous behavior was unexpected and undesirable in some cases:

- It was inconsistent with the behavior of the properties and methods that get the same fields.
- It was also impossible to actually update the fields in the symlink itself using .NET APIs.

## Recommended action

If you were relying on this behavior to set values on the symlink's target, setting one of the `*Time` fields in a symlink will no longer affect the target. You can use the new symbolic link APIs to obtain the target of a symlink and then update that file system object instead.

```csharp
FileSystemInfo? targetInfo = linkInfo.ResolveLinkTarget(returnFinalTarget: true);
if (targetInfo != null)
{
// Update the properties accordingly.
targetInfo.LastWriteTime = DateTime.Now;
}
```

## Affected APIs

- <xref:System.IO.Directory.SetCreationTime(System.String,System.DateTime)?displayProperty=fullName>
- <xref:System.IO.Directory.SetCreationTimeUtc(System.String,System.DateTime)?displayProperty=fullName>
- <xref:System.IO.Directory.SetLastAccessTime(System.String,System.DateTime)?displayProperty=fullName>
- <xref:System.IO.Directory.SetLastAccessTimeUtc(System.String,System.DateTime)?displayProperty=fullName>
- <xref:System.IO.Directory.SetLastWriteTime(System.String,System.DateTime)?displayProperty=fullName>
- <xref:System.IO.Directory.SetLastWriteTimeUtc(System.String,System.DateTime)?displayProperty=fullName>
- <xref:System.IO.File.SetCreationTime(System.String,System.DateTime)?displayProperty=fullName>
- <xref:System.IO.File.SetCreationTimeUtc(System.String,System.DateTime)?displayProperty=fullName>
- <xref:System.IO.File.SetLastAccessTime(System.String,System.DateTime)?displayProperty=fullName>
- <xref:System.IO.File.SetLastAccessTimeUtc(System.String,System.DateTime)?displayProperty=fullName>
- <xref:System.IO.File.SetLastWriteTime(System.String,System.DateTime)?displayProperty=fullName>
- <xref:System.IO.File.SetLastWriteTimeUtc(System.String,System.DateTime)?displayProperty=fullName>
- <xref:System.IO.FileSystemInfo.CreationTime?displayProperty=fullName>
- <xref:System.IO.FileSystemInfo.CreationTimeUtc?displayProperty=fullName>
- <xref:System.IO.FileSystemInfo.LastAccessTime?displayProperty=fullName>
- <xref:System.IO.FileSystemInfo.LastAccessTimeUtc?displayProperty=fullName>
- <xref:System.IO.FileSystemInfo.LastWriteTime?displayProperty=fullName>
- <xref:System.IO.FileSystemInfo.LastWriteTimeUtc?displayProperty=fullName>
8 changes: 8 additions & 0 deletions docs/core/compatibility/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ items:
href: core-libraries/7.0/equals-nan.md
- name: Generic type constraint on PatternContext<T>
href: core-libraries/7.0/patterncontext-generic-constraint.md
- name: Legacy FileStream strategy removed
href: core-libraries/7.0/filestream-compat-switch.md
- name: Library support for older frameworks
href: core-libraries/7.0/old-framework-support.md
- name: Maximum precision for numeric format strings
Expand All @@ -77,6 +79,8 @@ items:
href: core-libraries/7.0/reflection-invoke-exceptions.md
- name: SerializationFormat.Binary is obsolete
href: core-libraries/7.0/serializationformat-binary.md
- name: Time fields on symbolic links
href: core-libraries/7.0/symbolic-link-timestamps.md
- name: Tracking linked cache entries
href: core-libraries/7.0/memorycache-tracking.md
- name: Validate CompressionLevel for BrotliStream
Expand Down Expand Up @@ -849,6 +853,8 @@ items:
href: core-libraries/7.0/equals-nan.md
- name: Generic type constraint on PatternContext<T>
href: core-libraries/7.0/patterncontext-generic-constraint.md
- name: Legacy FileStream strategy removed
href: core-libraries/7.0/filestream-compat-switch.md
- name: Library support for older frameworks
href: core-libraries/7.0/old-framework-support.md
- name: Maximum precision for numeric format strings
Expand All @@ -857,6 +863,8 @@ items:
href: core-libraries/7.0/reflection-invoke-exceptions.md
- name: SerializationFormat.Binary is obsolete
href: core-libraries/7.0/serializationformat-binary.md
- name: Time fields on symbolic links
href: core-libraries/7.0/symbolic-link-timestamps.md
- name: Tracking linked cache entries
href: core-libraries/7.0/memorycache-tracking.md
- name: Validate CompressionLevel for BrotliStream
Expand Down