Skip to content

Commit

Permalink
Fix invalid parameters in SetFileInformationByHandle (#95235)
Browse files Browse the repository at this point in the history
* Add failing unit test for SetFileInformationByHandle (#95096)

* Fix invalid parameters in SetFileInformationByHandle (#95096)

According to MS-FSCC, "a value of -1 indicates to the server that it MUST NOT change this attribute for all subsequent operations"
This behavior is incorrect in the scope of .NET, since a call to File.SetLastAccessTime on a open file handle
will cause all future writes to not update the last write tim.

It also triggers STATUS_INVALID_PARAMETER on NFS, since the Windows driver doesn't seem to implement the -1 value (tracking of subsequent write operations)

Fixes #95096
  • Loading branch information
smx-smx authored Nov 27, 2023
1 parent 2f991a6 commit 721c445
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -464,10 +464,10 @@ public static unsafe void SetAttributes(SafeFileHandle fileHandle, FileAttribute
private static void SetFileTime(
string fullPath,
bool asDirectory,
long creationTime = -1,
long lastAccessTime = -1,
long lastWriteTime = -1,
long changeTime = -1,
long creationTime = 0,
long lastAccessTime = 0,
long lastWriteTime = 0,
long changeTime = 0,
uint fileAttributes = 0)
{
using SafeFileHandle handle = OpenHandleToWriteAttributes(fullPath, asDirectory);
Expand All @@ -477,10 +477,10 @@ private static void SetFileTime(
private static unsafe void SetFileTime(
SafeFileHandle fileHandle,
string? fullPath = null,
long creationTime = -1,
long lastAccessTime = -1,
long lastWriteTime = -1,
long changeTime = -1,
long creationTime = 0,
long lastAccessTime = 0,
long lastWriteTime = 0,
long changeTime = 0,
uint fileAttributes = 0)
{
var basicInfo = new Interop.Kernel32.FILE_BASIC_INFO
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
using Xunit;

Expand Down Expand Up @@ -91,15 +92,34 @@ protected override DateTime GetLastWriteTimeUtc(string path)
return File.GetLastWriteTimeUtc(fileHandle);
}

[Fact]
public async Task WritingShouldUpdateWriteTime_After_SetLastAccessTime()
{
string filePath = GetTestFilePath();
using var handle = OpenFileHandle(filePath, FileAccess.ReadWrite);

File.SetLastAccessTime(handle, DateTime.Now.Subtract(TimeSpan.FromDays(1)));
var timeBeforeWrite = File.GetLastWriteTime(handle);

using var writer = new StreamWriter(new FileStream(handle, FileAccess.ReadWrite));
writer.AutoFlush = true;
writer.WriteLine("now: " + DateTime.Now);
await Task.Delay(2000);
writer.WriteLine("now: " + DateTime.Now);

var timeAfterWrite = File.GetLastWriteTime(handle);
Assert.True(timeAfterWrite > timeBeforeWrite);
}

[Fact]
public void NullArgumentValidation()
{
Assert.Throws<ArgumentNullException>("fileHandle", static () => File.GetCreationTime(default(SafeFileHandle)!));
Assert.Throws<ArgumentNullException>("fileHandle", static () => File.SetCreationTime(default(SafeFileHandle)!, DateTime.Now));

Assert.Throws<ArgumentNullException>("fileHandle", static () => File.GetCreationTimeUtc(default(SafeFileHandle)!));
Assert.Throws<ArgumentNullException>("fileHandle", static () => File.SetCreationTimeUtc(default(SafeFileHandle)!, DateTime.Now));

Assert.Throws<ArgumentNullException>("fileHandle", static () => File.GetLastAccessTime(default(SafeFileHandle)!));
Assert.Throws<ArgumentNullException>("fileHandle", static () => File.SetLastAccessTime(default(SafeFileHandle)!, DateTime.Now));

Expand Down

0 comments on commit 721c445

Please sign in to comment.