Skip to content

Commit

Permalink
Fix setting timestamp on Windows on readonly files (dotnet#62638)
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/libraries/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs
  • Loading branch information
danmoseley committed Dec 31, 2021
1 parent 3b71fd8 commit 762a479
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 11 deletions.
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;
}
}
}
16 changes: 15 additions & 1 deletion 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 Down Expand Up @@ -70,6 +72,18 @@ public void SettingUpdatesProperties()
});
}

[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) && 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

0 comments on commit 762a479

Please sign in to comment.