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

[release/6.0] Fix setting timestamp on Windows on readonly files #62922

Merged
merged 3 commits into from
Jan 7, 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
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ internal static partial class FileOperations
internal const int FILE_FLAG_OVERLAPPED = 0x40000000;

internal const int FILE_LIST_DIRECTORY = 0x0001;
}

internal const int FILE_WRITE_ATTRIBUTES = 0x100;
}
}
}
28 changes: 23 additions & 5 deletions src/libraries/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ public abstract class BaseGetSetTimes<T> : FileSystemTest
protected static bool LowTemporalResolution => PlatformDetection.IsBrowser || isHFS;
protected static bool HighTemporalResolution => !LowTemporalResolution;

protected abstract T GetExistingItem();
protected abstract bool CanBeReadOnly { get; }

protected abstract T GetExistingItem(bool readOnly = false);
protected abstract T GetMissingItem();

protected abstract string GetItemPath(T item);
Expand All @@ -42,11 +44,8 @@ public static TimeFunction Create(SetTime setter, GetTime getter, DateTimeKind k
public DateTimeKind Kind => Item3;
}

[Fact]
public void SettingUpdatesProperties()
private void SettingUpdatesPropertiesCore(T item)
{
T item = GetExistingItem();

Assert.All(TimeFunctions(requiresRoundtripping: true), (function) =>
{
// Checking that milliseconds are not dropped after setter.
Expand All @@ -70,6 +69,25 @@ public void SettingUpdatesProperties()
});
}

[Fact]
public void SettingUpdatesProperties()
{
T item = GetExistingItem();
SettingUpdatesPropertiesCore(item);
}

[Fact]
public void SettingUpdatesPropertiesWhenReadOnly()
{
if (!CanBeReadOnly)
{
return; // directories can't be read only, so automatic pass
}

T item = GetExistingItem(readOnly: true);
SettingUpdatesPropertiesCore(item);
}

[Fact]
public void CanGetAllTimesAfterCreation()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ namespace System.IO.Tests
{
public class Directory_GetSetTimes : StaticGetSetTimes
{
protected override string GetExistingItem() => Directory.CreateDirectory(GetTestFilePath()).FullName;
protected override bool CanBeReadOnly => false;

protected override string GetExistingItem(bool _) => Directory.CreateDirectory(GetTestFilePath()).FullName;

public override IEnumerable<TimeFunction> TimeFunctions(bool requiresRoundtripping = false)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ namespace System.IO.Tests
{
public class DirectoryInfo_GetSetTimes : InfoGetSetTimes<DirectoryInfo>
{
protected override DirectoryInfo GetExistingItem() => Directory.CreateDirectory(GetTestFilePath());
protected override bool CanBeReadOnly => false;

protected override DirectoryInfo GetExistingItem(bool _) => Directory.CreateDirectory(GetTestFilePath());

protected override DirectoryInfo GetMissingItem() => new DirectoryInfo(GetTestFilePath());

Expand Down
10 changes: 9 additions & 1 deletion src/libraries/System.IO.FileSystem/tests/File/GetSetTimes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,22 @@ namespace System.IO.Tests
{
public class File_GetSetTimes : StaticGetSetTimes
{
protected override bool CanBeReadOnly => true;

// OSX has the limitation of setting upto 2262-04-11T23:47:16 (long.Max) date.
// 32bit Unix has time_t up to ~ 2038.
private static bool SupportsLongMaxDateTime => PlatformDetection.IsWindows || (!PlatformDetection.Is32BitProcess && !PlatformDetection.IsOSXLike);

protected override string GetExistingItem()
protected override string GetExistingItem(bool readOnly = false)
{
string path = GetTestFilePath();
File.Create(path).Dispose();

if (readOnly)
{
File.SetAttributes(path, FileAttributes.ReadOnly);
}

return path;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ namespace System.IO.Tests
{
public class FileInfo_GetSetTimes : InfoGetSetTimes<FileInfo>
{
protected override FileInfo GetExistingItem()
protected override bool CanBeReadOnly => true;

protected override FileInfo GetExistingItem(bool readOnly = false)
{
string path = GetTestFilePath();
File.Create(path).Dispose();

if (readOnly)
{
File.SetAttributes(path, FileAttributes.ReadOnly);
}

return new FileInfo(path);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,18 +145,17 @@ public static void MoveFile(string sourceFullPath, string destFullPath, bool ove
}
}

private static SafeFileHandle OpenHandle(string fullPath, bool asDirectory)
private static SafeFileHandle OpenHandleToWriteAttributes(string fullPath, bool asDirectory)
{
string root = fullPath.Substring(0, PathInternal.GetRootLength(fullPath.AsSpan()));
if (root == fullPath && root[1] == Path.VolumeSeparatorChar)
if (fullPath.Length == PathInternal.GetRootLength(fullPath.AsSpan()) && fullPath[1] == Path.VolumeSeparatorChar)
{
// intentionally not fullpath, most upstack public APIs expose this as path.
throw new ArgumentException(SR.Arg_PathIsVolume, "path");
}

SafeFileHandle handle = Interop.Kernel32.CreateFile(
fullPath,
Interop.Kernel32.GenericOperations.GENERIC_WRITE,
Interop.Kernel32.FileOperations.FILE_WRITE_ATTRIBUTES,
FileShare.ReadWrite | FileShare.Delete,
FileMode.Open,
asDirectory ? Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS : 0);
Expand Down Expand Up @@ -382,7 +381,7 @@ private static unsafe void SetFileTime(
long changeTime = -1,
uint fileAttributes = 0)
{
using (SafeFileHandle handle = OpenHandle(fullPath, asDirectory))
using (SafeFileHandle handle = OpenHandleToWriteAttributes(fullPath, asDirectory))
{
var basicInfo = new Interop.Kernel32.FILE_BASIC_INFO()
{
Expand Down