Skip to content

Fix setting timestamp on Windows on readonly files #62638

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 3 commits into from
Dec 11, 2021
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;
}
}
}
18 changes: 16 additions & 2 deletions src/libraries/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,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 T CreateSymlink(string path, string pathToTarget);
Expand Down Expand Up @@ -84,6 +86,18 @@ public void SettingUpdatesProperties()
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);
}

[ConditionalTheory(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))]
[PlatformSpecific(~TestPlatforms.Browser)] // Browser is excluded as it doesn't support symlinks
[InlineData(false)]
Expand Down Expand Up @@ -164,7 +178,7 @@ public void SettingUpdatesPropertiesAfterAnother()
TimeFunction function1 = functions.x;
TimeFunction function2 = functions.y;
bool reverse = functions.reverse;

// Checking that milliseconds are not dropped after setter.
DateTime dt1 = new DateTime(2002, 12, 1, 12, 3, 3, LowTemporalResolution ? 0 : 321, DateTimeKind.Utc);
DateTime dt2 = new DateTime(2001, 12, 1, 12, 3, 3, LowTemporalResolution ? 0 : 321, DateTimeKind.Utc);
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;

protected override string CreateSymlink(string path, string pathToTarget) => Directory.CreateSymbolicLink(path, pathToTarget).FullName;

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 @@ -182,10 +182,9 @@ 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) && fullPath[1] == Path.VolumeSeparatorChar)
{
// intentionally not fullpath, most upstack public APIs expose this as path.
throw new ArgumentException(SR.Arg_PathIsVolume, "path");
Expand All @@ -199,7 +198,7 @@ private static SafeFileHandle OpenHandle(string fullPath, bool asDirectory)

SafeFileHandle handle = Interop.Kernel32.CreateFile(
fullPath,
Interop.Kernel32.GenericOperations.GENERIC_WRITE,
Interop.Kernel32.FileOperations.FILE_WRITE_ATTRIBUTES,
FileShare.ReadWrite | FileShare.Delete,
FileMode.Open,
dwFlagsAndAttributes);
Expand Down Expand Up @@ -425,7 +424,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