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

[API Implementation]: SafeFileHandle overloads for SetCreationTime, SetLastAccessTime, SetLastWriteTime #60507

Merged
merged 142 commits into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from 141 commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
80246ea
Implement proposal
deeprobin Oct 16, 2021
eb56003
Undo auto-reformat
deeprobin Oct 16, 2021
f2b6e90
Add ref
deeprobin Oct 16, 2021
c1c0161
Fix Parameter Naming in FileSystem.Unix.cs
deeprobin Oct 16, 2021
d250908
Partial test implementation
deeprobin Oct 16, 2021
d711b3d
Fix typo
deeprobin Oct 16, 2021
3ef0ee4
Fix parameter naming
deeprobin Oct 16, 2021
ea88c03
Naming fix and remove unnessecary method
deeprobin Oct 16, 2021
ec7d870
Add more test cases
deeprobin Oct 17, 2021
b656f06
Implement usage of Kernel32 Method GetFileInformationByHandle and str…
deeprobin Oct 17, 2021
4299f3d
Fix Test SetLastWriteTimeTicks_SafeFileHandle
deeprobin Oct 17, 2021
318167e
Disable SafeFileHandle specific-tests on other platforms than Windows
deeprobin Oct 22, 2021
53f5d93
Add Common reference to project file of System.IO.AccessControl
deeprobin Oct 22, 2021
a928024
fix
deeprobin Oct 24, 2021
bf7d67f
Add preprocessor variable check
deeprobin Oct 24, 2021
9dc972d
Add check for preprocessor var
deeprobin Oct 24, 2021
03fa5d1
Fix CS0649
deeprobin Oct 27, 2021
1ad32c2
Remove preprozessor variable checks
deeprobin Oct 28, 2021
f424ec2
Fix tab/spaces conflict in Interop.BY_HANDLE_FILE_INFORMATION
deeprobin Oct 28, 2021
007e068
Fix misplaced StructLayoutAttribute
deeprobin Nov 3, 2021
6a88e68
Fix project file identation
deeprobin Nov 3, 2021
092b32a
Remove preprocessor variable checks for GetSetAttributes test
deeprobin Nov 3, 2021
eb27249
Use ThrowHelper instead of direct throwing
deeprobin Nov 3, 2021
b67d6a0
Use ThrowHelper instead of direct throwing
deeprobin Nov 3, 2021
a1ce489
Add null-forgiving operator to fix build-failures
deeprobin Nov 3, 2021
39febf3
Fix build failures
deeprobin Nov 3, 2021
25ffa08
Win32 interop improvements
deeprobin Nov 6, 2021
ba42e76
Specify FileAccess in File.OpenHandle calls for testing purposes
deeprobin Nov 18, 2021
5824743
Implement GetLastWin32Error proposal / @Jozkee
deeprobin Nov 18, 2021
01c405c
Merge branch 'main' into issue-20234
deeprobin Dec 16, 2021
3484035
Fix build
deeprobin Dec 16, 2021
273ce50
Usage of kernel32.dll!SetFileInformationByHandle in FileSystem.SetFil…
deeprobin Dec 16, 2021
af3955b
Usage of access parameter in GetSetTimes tests
deeprobin Dec 17, 2021
af923e1
Remove auto-generated trash
deeprobin Dec 19, 2021
001f45c
Add localization string to NativeAOT Strings.resx
deeprobin Dec 19, 2021
1f41f14
Unix Implementation of GetAttributes, SetAttributes, GetCreationTime,…
deeprobin Dec 19, 2021
bce7faa
Unix Implementation SetLastAccessTime and SetLastWriteTime
deeprobin Dec 19, 2021
b7d7b4a
Add error handling to FChMod call
deeprobin Dec 19, 2021
c48c855
Revert "Unix Implementation SetLastAccessTime and SetLastWriteTime"
deeprobin Dec 19, 2021
51e462e
Unix Implementation SetLastAccessTime and SetLastWriteTime
deeprobin Dec 19, 2021
d1752d4
Merge branch 'main' into issue-20234
deeprobin Jan 17, 2022
e7b5b15
Merge remote-tracking branch 'origin/main' into issue-20234
tmds Jan 31, 2022
b2eec67
Move SafeHandle implementation to FileStatus.
tmds Feb 1, 2022
6a3e131
`SafeFileHandle` `FileStatus` (#4)
deeprobin Feb 1, 2022
8de7a3c
Revert "`SafeFileHandle` `FileStatus` (#4)" (#5)
deeprobin Feb 1, 2022
d5bb271
Merge branch 'main' into issue-20234
deeprobin Feb 1, 2022
3e2e401
Merge branch 'safehandle_filestatus' of https://github.com/tmds/runti…
deeprobin Feb 1, 2022
b8b85cb
Merge branch 'tmds-safehandle_filestatus' into issue-20234
deeprobin Feb 1, 2022
34d11e9
Usage of `GetHandlePath`
deeprobin Feb 2, 2022
02dbad3
Usage of `CULong`
deeprobin Feb 2, 2022
ef86395
Usage of `CULong`
deeprobin Feb 2, 2022
dcfd29a
Add SystemNative_FChflags
tmds Feb 2, 2022
3e08682
Fix `OpenHandle` calls in test
deeprobin Feb 5, 2022
1a65bf0
Add symlink resolving in `TimeFunctions` tests
deeprobin Feb 5, 2022
485d516
Use `futimes` fallback for `futimens`
deeprobin Feb 11, 2022
797c8f0
Use right preprocessor var
deeprobin Feb 11, 2022
72c2156
Add new line at EOF in `Interop.BY_HANDLE_FILE_INFORMATION`
deeprobin Feb 11, 2022
7a31c6c
Remove `FileOptions.DeleteOnClose` in Test cases for post-disposal tests
deeprobin Feb 12, 2022
68e4bde
Add non linux condition to `SetDateTimeMax_SafeFileHandle` test
deeprobin Feb 12, 2022
12823fe
Exclude pagefile.sys tests for `SafeFileHandle`
deeprobin Feb 12, 2022
31e374e
Fix `BaseGetSetTimes` tests
deeprobin Feb 12, 2022
384d8af
Use `GeneratedDllImport` over `DllImport`
deeprobin Feb 13, 2022
4984e30
Remove redundant charset spec. from `Interop.UTimensat`
deeprobin Feb 14, 2022
708b8b0
Remove extra line
deeprobin Feb 14, 2022
6510050
Add explicit parameter names
deeprobin Feb 14, 2022
3a336b7
Add explanatory comment to `TimeFunctions`
deeprobin Feb 14, 2022
38e27bf
Improve test cases
deeprobin Feb 14, 2022
1ca813c
Remove more redundant charset specifications
deeprobin Feb 14, 2022
1ab9f08
Merge branch 'main' into issue-20234
deeprobin Feb 14, 2022
84c19d8
Refactoring Tests
deeprobin Feb 16, 2022
e81aa89
Merge branch 'main' into issue-20234
deeprobin Feb 16, 2022
136b78f
Refactor Tests
deeprobin Feb 16, 2022
f4bab25
Fix tests
deeprobin Feb 17, 2022
d32db10
Improve test structure
deeprobin Feb 24, 2022
b97d6e7
Merge branch 'main' into issue-20234
deeprobin Feb 24, 2022
6c27da1
Remove `ExactSpelling`
deeprobin Feb 24, 2022
9da4cba
Merge branch 'main' into issue-20234
deeprobin Mar 10, 2022
4ad6973
Rename `GeneratedDllImportAttribute` to `LibraryImportAttribute`
deeprobin Mar 10, 2022
1e85d14
Apply suggestions
deeprobin Mar 11, 2022
985347e
Reduce code duplication in `FileSystem.Windows.cs`
deeprobin Mar 11, 2022
214a00f
Fix tests
deeprobin Mar 12, 2022
bae3af1
Address feedback
deeprobin Mar 15, 2022
dc9ecb5
Address more feedback
deeprobin Mar 15, 2022
99d6dd9
Merge branch 'main' into issue-20234
deeprobin Mar 26, 2022
1d177a4
Apply suggestions & try to avoid reordering
deeprobin Mar 28, 2022
fc06d5b
Merge branch 'main' into issue-20234
deeprobin Apr 6, 2022
1bdec8f
Fix tests
deeprobin Apr 6, 2022
5b7691f
Fix tests
deeprobin Apr 7, 2022
56bdc14
Refactor `BaseGetSetTimes`
deeprobin Apr 19, 2022
d43e8c2
Merge branch 'main' into issue-20234
deeprobin May 2, 2022
64641ce
Merge branch 'main' into issue-20234
deeprobin May 2, 2022
9362956
Test style changes
deeprobin May 2, 2022
f9687ac
Don't try to verify link properties don't change when we can't retrie…
tmds May 3, 2022
695070e
Apply suggestions
deeprobin May 4, 2022
9fe781b
Regenerate reference source
deeprobin May 4, 2022
b56a5ff
Merge branch 'main' into issue-20234
deeprobin May 4, 2022
d763b4d
Merge branch 'main' into issue-20234
deeprobin May 4, 2022
78bf14a
Apply suggestions
deeprobin May 4, 2022
0e8e25d
Reorder project items
deeprobin May 8, 2022
12a6e8a
Apply suggestions
deeprobin May 8, 2022
95114b3
Apply suggestions
deeprobin May 8, 2022
1f3886e
Apply suggestions
deeprobin May 9, 2022
b9a310b
Merge branch 'main' into issue-20234
deeprobin May 9, 2022
9886bbb
Fix `fsetattrlist`
deeprobin May 9, 2022
2388c49
Apply suggestions
deeprobin May 12, 2022
34e4c24
Fix error path resolution
deeprobin May 12, 2022
2b5d54a
Merge branch 'main' into issue-20234
deeprobin May 12, 2022
175d0a4
Merge branch 'main' into issue-20234
deeprobin May 12, 2022
053842b
(Try to) fix OSX failures
deeprobin May 12, 2022
db1ff57
Fix OSX method signature
deeprobin May 13, 2022
fdd0b9c
Apply suggestions
deeprobin May 16, 2022
1700cdd
Apply suggestions (using GitHub UI)
deeprobin May 17, 2022
83e7017
Apply suggestions (using GitHub UI)
deeprobin May 17, 2022
377b3ad
Merge branch 'main' into issue-20234
deeprobin Jun 4, 2022
6542494
FileStatus: avoid ToString allocation for setattrlist
tmds Jun 1, 2022
92b75e3
Remove unnecessary message
deeprobin Jun 4, 2022
4fc1b4f
Apply suggestions
deeprobin Jun 4, 2022
4fd5f7b
Merge branch 'main' into issue-20234
deeprobin Jun 4, 2022
67aab4e
Fix corruption due merge
deeprobin Jun 4, 2022
b0d5a73
Apply suggestions
deeprobin Jun 7, 2022
1fe2043
Fix Unix tests
deeprobin Jun 8, 2022
168626d
Merge branch 'main' into issue-20234
deeprobin Jun 9, 2022
afe8cfe
Merge branch 'main' into issue-20234
deeprobin Jun 9, 2022
5b59c8f
Fix tests
deeprobin Jun 9, 2022
88efa3b
Update src/libraries/System.Private.CoreLib/src/System/IO/FileStatus.…
deeprobin Jun 10, 2022
af9b7e7
Merge remote-tracking branch 'origin/main' into issue-20234
tmds Jun 23, 2022
edf948e
Fix Unix test failures.
tmds Jun 23, 2022
5776aad
Sfh fix tests (#8)
deeprobin Jun 23, 2022
3cc1261
Revert "Sfh fix tests (#8)" (#9)
deeprobin Jun 23, 2022
4378d0b
Merge pull request #10 from tmds/sfh_fix_tests
deeprobin Jun 23, 2022
3e8b4ba
Apply interop suggestions
deeprobin Jun 30, 2022
a9321a8
Verify non-nullability either of handle or path
deeprobin Jun 30, 2022
2dbd741
Small syntax fix
deeprobin Jul 4, 2022
3a1dc2e
Merge branch 'main' into issue-20234
deeprobin Jul 4, 2022
f0697b5
Merge branch 'main' into issue-20234
deeprobin Jul 11, 2022
231c838
Add null-validation
deeprobin Jul 11, 2022
c63501b
Move population into private helper method
deeprobin Jul 11, 2022
f865a87
Fix null-handling
deeprobin Jul 11, 2022
880011b
Add more debug assertions
deeprobin Jul 11, 2022
312b199
Add code documentation
deeprobin Jul 11, 2022
0d405b4
Address documentation feedback
deeprobin Jul 12, 2022
7cfc7e1
Update src/libraries/System.Private.CoreLib/src/System/IO/File.cs
Jozkee Jul 12, 2022
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
17 changes: 17 additions & 0 deletions src/libraries/Common/src/Interop/OSX/Interop.libc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,22 @@ internal struct AttrList
internal static unsafe partial int setattrlist(string path, AttrList* attrList, void* attrBuf, nint attrBufSize, CULong options);

internal const uint FSOPT_NOFOLLOW = 0x00000001;
[LibraryImport(Libraries.libc, EntryPoint = "fsetattrlist", SetLastError = true)]
private static unsafe partial int fsetattrlist(int fd, AttrList* attrList, void* attrBuf, nint attrBufSize, CULong options);

internal static unsafe int fsetattrlist(SafeHandle handle, AttrList* attrList, void* attrBuf, nint attrBufSize, CULong options)
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
{
bool refAdded = false;
try
{
handle.DangerousAddRef(ref refAdded);
return fsetattrlist(handle.DangerousGetHandle().ToInt32(), attrList, attrBuf, attrBufSize, options);
}
finally
{
if (refAdded)
handle.DangerousRelease();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ internal enum UserFlags : uint
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_LChflags", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)]
internal static partial int LChflags(string path, uint flags);

[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_FChflags", SetLastError = true)]
internal static partial int FChflags(SafeHandle fd, uint flags);

internal static readonly bool CanSetHiddenFlag = (LChflagsCanSetHiddenFlag() != 0);

[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_LChflagsCanSetHiddenFlag")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,8 @@ internal struct TimeSpec
/// </returns>
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_UTimensat", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)]
internal static unsafe partial int UTimensat(string path, TimeSpec* times);

[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_FUTimens", SetLastError = true)]
internal static unsafe partial int FUTimens(SafeHandle fd, TimeSpec* times);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class Kernel32
{
[StructLayout(LayoutKind.Sequential)]
internal struct BY_HANDLE_FILE_INFORMATION
{
internal uint dwFileAttributes;
internal FILE_TIME ftCreationTime;
internal FILE_TIME ftLastAccessTime;
internal FILE_TIME ftLastWriteTime;
internal uint dwVolumeSerialNumber;
internal uint nFileSizeHigh;
internal uint nFileSizeLow;
internal uint nNumberOfLinks;
internal uint nFileIndexHigh;
internal uint nFileIndexLow;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class Kernel32
{
[LibraryImport(Libraries.Kernel32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static unsafe partial bool GetFileInformationByHandle(SafeFileHandle hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ public void InvalidParameters()
[Theory, MemberData(nameof(TrailingCharacters))]
public void SetAttributes_MissingFile(char trailingChar)
{
if (!CanBeReadOnly) return;
Assert.Throws<FileNotFoundException>(() => SetAttributes(GetTestFilePath() + trailingChar, FileAttributes.ReadOnly));
}

[Theory, MemberData(nameof(TrailingCharacters))]
public void SetAttributes_MissingDirectory(char trailingChar)
{
if (!CanBeReadOnly) return;
Assert.Throws<DirectoryNotFoundException>(() => SetAttributes(Path.Combine(GetTestFilePath(), "file" + trailingChar), FileAttributes.ReadOnly));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace System.IO.Tests
{
public abstract class BaseGetSetAttributes : FileSystemTest
{
protected abstract bool CanBeReadOnly { get; }
protected abstract FileAttributes GetAttributes(string path);
protected abstract void SetAttributes(string path, FileAttributes attributes);

Expand Down
56 changes: 41 additions & 15 deletions src/libraries/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public abstract class BaseGetSetTimes<T> : FileSystemTest

protected abstract T CreateSymlink(string path, string pathToTarget);

// When the item is a link, indicates whether the .NET API will get/set the link itself, or its target.
protected virtual bool ApiTargetsLink => true;

protected T CreateSymlinkToItem(T item)
{
// Creates a Symlink to 'item' (target may or may not exist)
Expand All @@ -37,6 +40,7 @@ protected T CreateSymlinkToItem(T item)

protected abstract string GetItemPath(T item);

// requiresRoundtripping defines whether to convert DateTimeFormat 'a' to 'b' and then back to 'a' to verify the DateTimeFormat-conversion
public abstract IEnumerable<TimeFunction> TimeFunctions(bool requiresRoundtripping = false);

public class TimeFunction : Tuple<SetTime, GetTime, DateTimeKind>
Expand All @@ -52,17 +56,27 @@ public static TimeFunction Create(SetTime setter, GetTime getter, DateTimeKind k
public SetTime Setter => Item1;
public GetTime Getter => Item2;
public DateTimeKind Kind => Item3;

public override string ToString()
{
return $"TimeFunction DateTimeKind.{Kind} Setter: {Setter.Method.Name} Getter: {Getter.Method.Name}";
}
}

private void SettingUpdatesPropertiesCore(T item)
private void SettingUpdatesPropertiesCore(T item, T? linkTarget = default)
{
Assert.All(TimeFunctions(requiresRoundtripping: true), (function) =>
{
bool isLink = linkTarget is not null;

// Checking that milliseconds are not dropped after setter.
// Emscripten drops milliseconds in Browser
DateTime dt = new DateTime(2014, 12, 1, 12, 3, 3, LowTemporalResolution ? 0 : 321, function.Kind);
function.Setter(item, dt);
DateTime result = function.Getter(item);

T getTarget = !isLink || ApiTargetsLink ? item : linkTarget;
DateTime result = function.Getter(getTarget);

Assert.Equal(dt, result);
Assert.Equal(dt.ToLocalTime(), result.ToLocalTime());

Expand Down Expand Up @@ -102,7 +116,7 @@ public void SettingUpdatesPropertiesWhenReadOnly()
[PlatformSpecific(~TestPlatforms.Browser)] // Browser is excluded as it doesn't support symlinks
[InlineData(false)]
[InlineData(true)]
public void SettingUpdatesPropertiesOnSymlink(bool targetExists)
public void SettingPropertiesOnSymlink(bool targetExists)
{
// This test is in this class since it needs all of the time functions.
// This test makes sure that the times are set on the symlink itself.
Expand All @@ -119,25 +133,37 @@ public void SettingUpdatesPropertiesOnSymlink(bool targetExists)
T link = CreateSymlinkToItem(target);
if (!targetExists)
{
SettingUpdatesPropertiesCore(link);
// Don't check when settings update the target.
if (ApiTargetsLink)
{
SettingUpdatesPropertiesCore(link, target);
}
}
else
{
// Get the target's initial times
IEnumerable<TimeFunction> timeFunctions = TimeFunctions(requiresRoundtripping: true);
DateTime[] initialTimes = timeFunctions.Select((funcs) => funcs.Getter(target)).ToArray();
// When properties update link, verify the target properties don't change.
IEnumerable<TimeFunction>? timeFunctions = null;
DateTime[]? initialTimes = null;
if (ApiTargetsLink)
{
timeFunctions = TimeFunctions(requiresRoundtripping: true);
initialTimes = timeFunctions.Select((funcs) => funcs.Getter(target)).ToArray();
}

SettingUpdatesPropertiesCore(link);
SettingUpdatesPropertiesCore(link, target);

// Ensure that we have the latest times.
if (target is FileSystemInfo fsi)
// Ensure target properties haven't changed.
if (ApiTargetsLink)
{
fsi.Refresh();
// Ensure that we have the latest times.
if (target is FileSystemInfo fsi)
{
fsi.Refresh();
}

DateTime[] updatedTimes = timeFunctions.Select((funcs) => funcs.Getter(target)).ToArray();
Assert.Equal(initialTimes, updatedTimes);
}

// Ensure the target's times haven't changed.
DateTime[] updatedTimes = timeFunctions.Select((funcs) => funcs.Getter(target)).ToArray();
Assert.Equal(initialTimes, updatedTimes);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,21 @@ namespace System.IO.Tests
// Tests that are valid for File and FileInfo
public abstract class FileGetSetAttributes : BaseGetSetAttributes
{
[Theory]
[InlineData(FileAttributes.ReadOnly)]
[InlineData(FileAttributes.Normal)]
[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)]
public void SettingAttributes_Unix(FileAttributes attributes)
public void SettingAttributes_Unix_Normal()
{
string path = CreateItem();
AssertSettingAttributes(path, attributes);
AssertSettingAttributes(path, FileAttributes.Normal);
}

[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)]
public void SettingAttributes_Unix_ReadOnly()
{
if (!CanBeReadOnly) return;
string path = CreateItem();
AssertSettingAttributes(path, FileAttributes.ReadOnly);
}

[Theory]
Expand All @@ -28,20 +35,29 @@ public void SettingAttributes_OSXAndFreeBSD(FileAttributes attributes)
}

[Theory]
[InlineData(FileAttributes.ReadOnly)]
[InlineData(FileAttributes.Hidden)]
[InlineData(FileAttributes.System)]
[InlineData(FileAttributes.Archive)]
[InlineData(FileAttributes.Normal)]
[InlineData(FileAttributes.Temporary)]
[InlineData(FileAttributes.ReadOnly | FileAttributes.Hidden)]
[PlatformSpecific(TestPlatforms.Windows)]
public void SettingAttributes_Windows(FileAttributes attributes)
{
string path = CreateItem();
AssertSettingAttributes(path, attributes);
}

[Theory]
[InlineData(FileAttributes.ReadOnly)]
[InlineData(FileAttributes.ReadOnly | FileAttributes.Hidden)]
[PlatformSpecific(TestPlatforms.Windows)]
public void SettingAttributes_Windows_ReadOnly(FileAttributes attributes)
{
if (!CanBeReadOnly) return;
string path = CreateItem();
AssertSettingAttributes(path, attributes);
}

private void AssertSettingAttributes(string path, FileAttributes attributes)
{
SetAttributes(path, attributes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ protected override string CreateItem(string path = null, [CallerMemberName] stri
protected override DirectoryInfo CreateInfo(string path) => new DirectoryInfo(path);
protected override void DeleteItem(string path) => Directory.Delete(path);
protected override bool IsDirectory => true;
protected override bool CanBeReadOnly => true;

[Theory]
[InlineData(FileAttributes.ReadOnly)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class File_GetSetAttributes : BaseGetSetAttributes
{
protected override FileAttributes GetAttributes(string path) => File.GetAttributes(path);
protected override void SetAttributes(string path, FileAttributes attributes) => File.SetAttributes(path, attributes);
protected override bool CanBeReadOnly => false;

// Getting only throws for File, not FileInfo
[Theory, MemberData(nameof(TrailingCharacters))]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Win32.SafeHandles;
using Xunit;

namespace System.IO.Tests
{
public class GetSetAttributes_SafeFileHandle : FileGetSetAttributes
{
protected virtual SafeFileHandle OpenFileHandle(string path, FileAccess fileAccess) =>
File.OpenHandle(
path,
FileMode.OpenOrCreate,
fileAccess,
FileShare.None);

protected override bool CanBeReadOnly => false;

protected override FileAttributes GetAttributes(string path)
{
using SafeFileHandle fileHandle = OpenFileHandle(path, FileAccess.Read);
return File.GetAttributes(fileHandle);
}

protected override void SetAttributes(string path, FileAttributes attributes)
{
using SafeFileHandle fileHandle = OpenFileHandle(path, FileAccess.ReadWrite);
File.SetAttributes(fileHandle, attributes);
}

[Fact]
public void NullArgumentValidation()
{
Assert.Throws<ArgumentNullException>("fileHandle", static () => File.GetAttributes(default(SafeFileHandle)!));
Assert.Throws<ArgumentNullException>("fileHandle", static () => File.SetAttributes(default(SafeFileHandle)!, (FileAttributes)0));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
namespace System.IO.Tests
{
// Concrete class to run common file attributes tests on the File class
public class File_GetSetAttributesCommon : FileGetSetAttributes
public sealed class File_GetSetAttributes_String : FileGetSetAttributes
{
protected override FileAttributes GetAttributes(string path) => File.GetAttributes(path);
protected override void SetAttributes(string path, FileAttributes attributes) => File.SetAttributes(path, attributes);
protected override bool CanBeReadOnly => true;
}
}
Loading