Skip to content

Commit

Permalink
Fix setting creation date for OSX-like platforms (#49555)
Browse files Browse the repository at this point in the history
* Fix setting creation date for OSX

Fix for #39132

* Fix setting creation date for OSX - fix for setattrlist

This commit adds a fallback for setattrlist failing.
It can sometimes fail on specific volume types, so this is a workaround to use the old behaviour on unsupported filesystems.

Fix for #39132

* Fix setting creation date for OSX - fix for caching when set time & possible stack overflow

This commit adds _fileStatusInitialized = -1; which resets the cache for the file status after setting a time value to the file using the setattrlist api version so that it will not simply return the last cached time.
It also fixes SetCreationTime_OtherUnix method so that it calls SetLastWriteTime_OtherUnix directly instead of SetLastWriteTime because otherwise you'll get stuck in an infinite loop on OSX when the fallback is theoretically needed.

Fix for #39132

* Fix setting creation date for OSX - changes for PR 1

Fix the behaviour of SetLastWriteTime (OSX) when setting lastwritetime before creationtime.
Use Invalidate() instead of _fileStatusInitialized = -1, and only when appropriate as per PR #49555.
Add SettingUpdatesPropertiesAfterAnother test to test things such as setting lastwritetime to before creationtime.
Rename the added _OtherUnix methods to _StandardUnixImpl, a more logical name given their purpose (#49555)

Fix for #39132 and issues in #49555.

* Fix setting creation date for OSX - changes for PR 2

Added a new test to test the behaviour of the file time functions on symlinks as per standard Windows behaviour, and also what I use in my code today. It makes sure that the times are set on the symlink itself, not the target. It is likely to fail on many unix platforms, so these will be given the [ActiveIssue] attribute when we discover which ones.
Make access time be set using setattrlist on OSX to fix following symlinks issue that would've failed the new test.
Fix and update wording of comment in SetAccessOrWriteTime.
Add T CreateSymlinkToItem(T item) to BaseGetSetTimes<T> (and implementation in inheritors) for the new test.
Made the SettingUpdatesPropertiesAfterAnother test skip on browser since it only, in effect, has one date attribute.

Fix for #39132 and issues in #49555.

* Fix setting creation date for OSX - changes for PR 3

Revert change in last commit: Make access time be set using setattrlist on OSX to fix following symlinks issue that would've failed the new test. It is now using the other API as otherwise some file watcher tests fail.
Set AT_SYMLINK_NOFOLLOW in pal_time.c to not follow symlinks for access time on OSX, or any times on other unix systems.

* Fix setting creation date for OSX - fix test for Windows and browser + update comments

Fix the SettingUpdatesPropertiesOnSymlink test for Windows by setting FILE_FLAG_OPEN_REPARSE_POINT in the CreateFile used for setting times.
Disable the SettingUpdatesPropertiesOnSymlink test for Browser as it can't use symlinks with the current API in the test project.
Update comments.
Add FILE_FLAG_OPEN_REPARSE_POINT.

Fix for #39132 and issues in #49555.

* Fix setting creation date for OSX - make modification time use normal unix APIs

Only implement creation time setting with setattrlist.
Here is why: eg. the old code doesn't account for setting creation time to 2002, then setting modification time to 2001, and then setting access time (since access time didn't have the creation date check).
I've split up SetAccessOrWriteTime into 3 parts - SetAccessOrWriteTime, SetAccessOrWriteTime_StandardUnixImpl, and SetAccessOrWriteTimeImpl (this last one contains the logic of the method without the things like refreshing the times so it's not called twice on OSX). In this process I replaced the _fileStatusInitialized = -1 calls with Invalidate() calls. And to make sure that if the creation time is needed to be set by SetAccessOrWriteTime, then we don't get in an infinite loop in case setattrlist fails, so we call SetAccessOrWriteTime_StandardUnixImpl.

* Fix setting creation date for OSX - hotfix: Remove ATTR_CMN_MODTIME

Adding this commit now to avoid 2x CI.
It was meant to be in the last one, but I forgot to save the file.

Fix for #39132 and issues in #49555.

* Fix setting creation date for OSX - revert windows changes & simplify code

Revert the changes for Windows & disable the SettingUpdatesPropertiesOnSymlink test on Windows.
Remove unnecessary setting to 0 on the attrList variable.
Remove the unnecessary MarshalAs attributes in the AttrList type.

Fix for #39132 and issues in #49555.

* Fix setting creation date for OSX - hotfix: change to CULong for marshalling with setattrlist

Use CULong as per #49555 (comment)

Fix for #39132 and issues in #49555.

* Fix setting creation date for OSX - hotfix: change to CULong for marshalling with setattrlist 2

Use CULong as per #49555 (comment)
Forgot to save the other file's changes

Fix for #39132 and issues in #49555.

* Fix setting creation date for OSX - consolidate unix nanosecond calculation

Consolidate unix nanosecond calculation into UnixTimeSecondsToNanoseconds as per #49555 (comment).

Fix for #39132 and issues in #49555.

* Use InvalidateCaches instead of Invalidate and EnsureCachesInitialized instead of EnsureStatInitialized

* Fixes for reviews in PR #49555

Add an identifying field to the TimeFunction tuple and a function to get a TimeFunction from the name.
This function is then used so that SettingUpdatesPropertiesAfterAnother can be written as a Theory, and is a lot more readable.
Fix some comments that were incorrect.

* Hotfix for missing parameter

* Fix naming

* Revert changes re SettingUpdatesPropertiesAfterAnother (mainly) and revert using a Theory

Reversion as per #49555 (comment)
This change can be unreverted if that's what's decided.

* Fix errors of #49555 due to missing variables

* Remove the parameters that were meant to be removed

* Finish merge

* Remove duplicate file

* Add custom error handling for setattrlist

Add custom error handling for setattrlist as per #49555 (comment).

ENOTDIR means it is not a directory, so directory not found seems logical to me, I think the other errors can be dealt with by an IOException, happy to change this.

* Remove symlink specific code from this PR

Removes the symlink specific code as per #49555 (comment).
This will be reverted (and modified) as per #49555 (comment) into #52639.

* Remove custom ENOTDIR handling, enable tests for other OSX-like platforms + move items in project file

• Removed ENOTDIR handling as per #49555 (comment)
• Enabled tests for other OSX-like platforms since the code was included on them (IsOSXLike is presumably defined at src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs). If it fails on any of these platforms, they'll be fully explicitly excluded everywhere for this PR or fixed.
• Move item in project file (System.Private.CoreLib.Shared.projitems) as per #49555 (comment)

* Rename files according to review

Rename files according to #49555 (comment) (and nearby comments).

* Change names of functions to improve readability and refactor code

This commit changes and refactors the core functions of this PR to have clearer names and to create a more logical flow of the code. It also updates some comments and fixes some oversights. Changes are based on the discussion #49555 (comment) (read whole conversation).

* Fix the wrongly named variable and add the missing 'unsafe' keyword

Fix the wrongly named variable and add the missing 'unsafe' keyword

* Better comments and minor improvements

Changes according to #49555 (review)
• Use explicit type `IEnumerable<TimeFunction>` instead of var
• Use PlatformDetection rather than OperatingSystem
• Move the InvalidateCaches code for creation time setting to avoid unnecessary checks
• Update explanatory comments to be more useful

* Add additional tests and improvements

- Use tuple destruction to swap variables
- Add SettingOneTimeMaintainsOtherDefaultTimes and AllDefaultTimesAreSame test as per #49555 (comment)
- Change TFM(/s) as per #49555 (comment)
- Avoid unnecessary call to `UnixTimeToDateTimeOffset` as per #49555 (comment)
- Use InvalidOperationException as per #49555 (comment)

* Fix typos

* Revert to <TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser</TargetFrameworks>

Revert to using <TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser</TargetFrameworks> since <TargetFramework>$(NetCoreAppCurrent)</TargetFramework> failed; I wouldn't be surprised if this failed too.

* Remove SettingOneTimeMaintainsOtherDefaultTimes and AllDefaultTimesAreSame tests

Remove SettingOneTimeMaintainsOtherDefaultTimes and AllDefaultTimesAreSame tests since they don't work on windows because it is apparently an invalid time. Additionally, the function SettingOneTimeMaintainsOtherDefaultTimes wasn't working and needed to be fixed.

* Fix: setattrlist can only return 0 or -1

As per #49555 (comment), use GetLastErrorInfo to get the error when setattrlist indicates that there is one.

Co-authored-by: David Cantú <dacantu@microsoft.com>

* Update code comment for SettingUpdatesPropertiesAfterAnother test

The comment is now clearer and grammatically correct :)

* Update code comment for SettingUpdatesPropertiesAfterAnother test

Describe how utimensat change more dates than would be desired

* Update comment for SettingUpdatesPropertiesAfterAnother for consistency

Update for consistency with the comment in FileStatus.Unix.cs

* Fixes for compilation and update to comment

• Fix trailing spaces and incorrect capitalisation in FileStatus.SetTimes.OSX.cs
• Add more info to the comment in the SettingUpdatesPropertiesAfterAnother test

* Update FileStatus.SetTimes.OSX.cs

Use the Error property

* Move comments and add explicit types to SettingUpdatesPropertiesAfterAnother test

Move comments and add explicit types to SettingUpdatesPropertiesAfterAnother test

Co-authored-by: David Cantú <dacantu@microsoft.com>
  • Loading branch information
hamarb123 and jozkee authored Oct 20, 2021
1 parent 26c9b28 commit 7cd8f19
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 24 deletions.
1 change: 1 addition & 0 deletions src/libraries/Common/src/Interop/OSX/Interop.Libraries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ internal static partial class Libraries
internal const string SystemConfigurationLibrary = "/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration";
internal const string AppleCryptoNative = "libSystem.Security.Cryptography.Native.Apple";
internal const string MsQuic = "libmsquic.dylib";
internal const string libc = "libc";
}
}
29 changes: 29 additions & 0 deletions src/libraries/Common/src/Interop/OSX/Interop.libc.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class libc
{
[StructLayout(LayoutKind.Sequential)]
internal struct AttrList
{
public ushort bitmapCount;
public ushort reserved;
public uint commonAttr;
public uint volAttr;
public uint dirAttr;
public uint fileAttr;
public uint forkAttr;

public const ushort ATTR_BIT_MAP_COUNT = 5;
public const uint ATTR_CMN_CRTIME = 0x00000200;
}

[DllImport(Libraries.libc, EntryPoint = "setattrlist", SetLastError = true)]
internal static unsafe extern int setattrlist(string path, AttrList* attrList, void* attrBuf, nint attrBufSize, CULong options);
}
}
57 changes: 57 additions & 0 deletions src/libraries/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Xunit;

Expand Down Expand Up @@ -70,6 +71,62 @@ public void SettingUpdatesProperties()
});
}

[Fact]
[PlatformSpecific(~TestPlatforms.Browser)] // Browser is excluded as there is only 1 effective time store.
public void SettingUpdatesPropertiesAfterAnother()
{
T item = GetExistingItem();

// These linq calls make an IEnumerable of pairs of functions that are not identical
// (eg. not (creationtime, creationtime)), includes both orders as seperate entries
// as they it have different behavior in reverse order (of functions), in addition
// to the pairs of functions, there is a reverse bool that allows a test for both
// increasing and decreasing timestamps as to not limit the test unnecessarily.
// Only testing with utc because it would be hard to check if lastwrite utc was the
// same type of method as lastwrite local since their .Getter fields are different.
// This test is required as some apis change more dates than would be desired (eg.
// utimes()/utimensat() set the write and access times, but as a side effect of
// the implementation, it sets creation time too when the write time is less than
// the creation time). There were issues related to the order in which the dates are
// set, so this test should almost fully eliminate any possibilities of that in the
// future by having a proper test for it. Also, it should be noted that the
// combination (A, B, false) is not the same as (B, A, true).

// The order that these LINQ expression creates is (when all 3 are available):
// [0] = (creation, access, False), [1] = (creation, access, True), [2] = (creation, write, False),
// [3] = (creation, write, True), [4] = (access, creation, False), [5] = (access, creation, True),
// [6] = (access, write, False), [7] = (access, write, True), [8] = (write, creation, False),
// [9] = (write, creation, True), [10] = (write, access, False), [11] = (write, access, True)
// Or, when creation time setting is not available:
// [0] = (access, write, False), [1] = (access, write, True),
// [2] = (write, access, False), [3] = (write, access, True)

IEnumerable<TimeFunction> timeFunctionsUtc = TimeFunctions(requiresRoundtripping: true).Where((f) => f.Kind == DateTimeKind.Utc);
bool[] booleanArray = new bool[] { false, true };
Assert.All(timeFunctionsUtc.SelectMany((x) => timeFunctionsUtc.SelectMany((y) => booleanArray.Select((reverse) => (x, y, reverse)))).Where((fs) => fs.x.Getter != fs.y.Getter), (functions) =>
{
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);
DateTime dt3 = new DateTime(2000, 12, 1, 12, 3, 3, LowTemporalResolution ? 0 : 321, DateTimeKind.Utc);
if (reverse) //reverse the order of setting dates
{
(dt1, dt3) = (dt3, dt1);
}
function1.Setter(item, dt1);
function2.Setter(item, dt2);
function1.Setter(item, dt3);
DateTime result1 = function1.Getter(item);
DateTime result2 = function2.Getter(item);
Assert.Equal(dt3, result1);
Assert.Equal(dt2, result2);
});
}

[Fact]
public void CanGetAllTimesAfterCreation()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

internal static class IOInputs
{
public static bool SupportsSettingCreationTime => OperatingSystem.IsWindows();
public static bool SupportsGettingCreationTime => OperatingSystem.IsWindows() || OperatingSystem.IsMacOS();
public static bool SupportsSettingCreationTime => PlatformDetection.IsWindows || PlatformDetection.IsOSXLike;
public static bool SupportsGettingCreationTime => PlatformDetection.IsWindows || PlatformDetection.IsOSXLike;

// Max path length (minus trailing \0). Unix values vary system to system; just using really long values here likely to be more than on the average system.
public static readonly int MaxPath = OperatingSystem.IsWindows() ? 259 : 10000;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2152,6 +2152,9 @@
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Unix.Android.cs" Condition="'$(TargetsAndroid)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Unix.NonAndroid.cs" Condition="'$(TargetsAndroid)' != 'true'" />
</ItemGroup>
<ItemGroup Condition="('$(TargetsUnix)' == 'true' or '$(TargetsBrowser)' == 'true') and '$(IsOSXLike)' != 'true'">
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStatus.SetTimes.OtherUnix.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetEUid.cs">
<Link>Common\Interop\Unix\System.Native\Interop.GetEUid.cs</Link>
Expand Down Expand Up @@ -2199,6 +2202,10 @@
<Compile Include="$(CommonPath)Interop\OSX\Interop.Libraries.cs">
<Link>Common\Interop\OSX\Interop.Libraries.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\OSX\Interop.libc.cs">
<Link>Common\Interop\OSX\Interop.libc.cs</Link>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStatus.SetTimes.OSX.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsMacCatalyst)' == 'true'">
<Compile Include="$(CommonPath)Interop\OSX\System.Native\Interop.iOSSupportVersion.cs" Link="Common\Interop\OSX\System.Native\Interop.iOSSupportVersion.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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;

namespace System.IO
{
internal partial struct FileStatus
{
internal void SetCreationTime(string path, DateTimeOffset time)
{
// Try to set the attribute on the file system entry using setattrlist,
// if we get ENOTSUP then it means that "The volume does not support
// setattrlist()", so we fall back to the method used on other unix
// platforms, otherwise we throw an error if we get one, or invalidate
// the cache if successful because otherwise it has invalid information.
// Note: the unix fallback implementation doesn't have a test as we are
// yet to determine which volume types it can fail on, so modify with
// great care.
long seconds = time.ToUnixTimeSeconds();
long nanoseconds = UnixTimeSecondsToNanoseconds(time, seconds);
Interop.Error error = SetCreationTimeCore(path, seconds, nanoseconds);

if (error == Interop.Error.SUCCESS)
{
InvalidateCaches();
}
else if (error == Interop.Error.ENOTSUP)
{
SetAccessOrWriteTimeCore(path, time, isAccessTime: false, checkCreationTime: false);
}
else
{
Interop.CheckIo(error, path, InitiallyDirectory);
}
}

private unsafe Interop.Error SetCreationTimeCore(string path, long seconds, long nanoseconds)
{
Interop.Sys.TimeSpec timeSpec = default;

timeSpec.TvSec = seconds;
timeSpec.TvNsec = nanoseconds;

Interop.libc.AttrList attrList = default;
attrList.bitmapCount = Interop.libc.AttrList.ATTR_BIT_MAP_COUNT;
attrList.commonAttr = Interop.libc.AttrList.ATTR_CMN_CRTIME;

Interop.Error error =
Interop.libc.setattrlist(path, &attrList, &timeSpec, sizeof(Interop.Sys.TimeSpec), default(CULong)) == 0 ?
Interop.Error.SUCCESS :
Interop.Sys.GetLastErrorInfo().Error;

return error;
}

private void SetAccessOrWriteTime(string path, DateTimeOffset time, bool isAccessTime) =>
SetAccessOrWriteTimeCore(path, time, isAccessTime, checkCreationTime: true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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;

namespace System.IO
{
internal partial struct FileStatus
{
internal void SetCreationTime(string path, DateTimeOffset time) =>
SetLastWriteTime(path, time);

private void SetAccessOrWriteTime(string path, DateTimeOffset time, bool isAccessTime) =>
SetAccessOrWriteTimeCore(path, time, isAccessTime, checkCreationTime: false);

// This is not used on these platforms, but is needed for source compat
private Interop.Error SetCreationTimeCore(string path, long seconds, long nanoseconds) =>
throw new InvalidOperationException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace System.IO
{
internal struct FileStatus
internal partial struct FileStatus
{
private const int NanosecondsPerTick = 100;

Expand Down Expand Up @@ -245,20 +245,6 @@ internal DateTimeOffset GetCreationTime(ReadOnlySpan<char> path, bool continueOn
return UnixTimeToDateTimeOffset(_fileCache.CTime, _fileCache.CTimeNsec);
}

internal void SetCreationTime(string path, DateTimeOffset time)
{
// Unix provides APIs to update the last access time (atime) and last modification time (mtime).
// There is no API to update the CreationTime.
// Some platforms (e.g. Linux) don't store a creation time. On those platforms, the creation time
// is synthesized as the oldest of last status change time (ctime) and last modification time (mtime).
// We update the LastWriteTime (mtime).
// This triggers a metadata change for FileSystemWatcher NotifyFilters.CreationTime.
// Updating the mtime, causes the ctime to be set to 'now'. So, on platforms that don't store a
// CreationTime, GetCreationTime will return the value that was previously set (when that value
// wasn't in the future).
SetLastWriteTime(path, time);
}

internal DateTimeOffset GetLastAccessTime(ReadOnlySpan<char> path, bool continueOnError = false)
{
EnsureCachesInitialized(path, continueOnError);
Expand All @@ -284,20 +270,29 @@ private DateTimeOffset UnixTimeToDateTimeOffset(long seconds, long nanoseconds)
return DateTimeOffset.FromUnixTimeSeconds(seconds).AddTicks(nanoseconds / NanosecondsPerTick);
}

private unsafe void SetAccessOrWriteTime(string path, DateTimeOffset time, bool isAccessTime)
private unsafe void SetAccessOrWriteTimeCore(string path, DateTimeOffset time, bool isAccessTime, bool checkCreationTime)
{
// This api is used to set creation time on non OSX platforms, and as a fallback for OSX platforms.
// The reason why we use it to set 'creation time' is the below comment:
// Unix provides APIs to update the last access time (atime) and last modification time (mtime).
// There is no API to update the CreationTime.
// Some platforms (e.g. Linux) don't store a creation time. On those platforms, the creation time
// is synthesized as the oldest of last status change time (ctime) and last modification time (mtime).
// We update the LastWriteTime (mtime).
// This triggers a metadata change for FileSystemWatcher NotifyFilters.CreationTime.
// Updating the mtime, causes the ctime to be set to 'now'. So, on platforms that don't store a
// CreationTime, GetCreationTime will return the value that was previously set (when that value
// wasn't in the future).

// force a refresh so that we have an up-to-date times for values not being overwritten
_initializedFileCache = -1;
InvalidateCaches();
EnsureCachesInitialized(path);

// we use utimes()/utimensat() to set the accessTime and writeTime
Interop.Sys.TimeSpec* buf = stackalloc Interop.Sys.TimeSpec[2];

long seconds = time.ToUnixTimeSeconds();

const long TicksPerMillisecond = 10000;
const long TicksPerSecond = TicksPerMillisecond * 1000;
long nanoseconds = (time.UtcDateTime.Ticks - DateTimeOffset.UnixEpoch.Ticks - seconds * TicksPerSecond) * NanosecondsPerTick;
long nanoseconds = UnixTimeSecondsToNanoseconds(time, seconds);

#if TARGET_BROWSER
buf[0].TvSec = seconds;
Expand All @@ -321,7 +316,28 @@ private unsafe void SetAccessOrWriteTime(string path, DateTimeOffset time, bool
}
#endif
Interop.CheckIo(Interop.Sys.UTimensat(path, buf), path, InitiallyDirectory);
_initializedFileCache = -1;

// On OSX-like platforms, when the modification time is less than the creation time (including
// when the modification time is already less than but access time is being set), the creation
// time is set to the modification time due to the api we're currently using; this is not
// desirable behaviour since it is inconsistent with windows behaviour and is not logical to
// the programmer (ie. we'd have to document it), so these api calls revert the creation time
// when it shouldn't be set (since we're setting modification time and access time here).
// checkCreationTime is only true on OSX-like platforms.
// allowFallbackToLastWriteTime is ignored on non OSX-like platforms.
bool updateCreationTime = checkCreationTime && (_fileCache.Flags & Interop.Sys.FileStatusFlags.HasBirthTime) != 0 &&
(buf[1].TvSec < _fileCache.BirthTime || (buf[1].TvSec == _fileCache.BirthTime && buf[1].TvNsec < _fileCache.BirthTimeNsec));

InvalidateCaches();

if (updateCreationTime)
{
Interop.Error error = SetCreationTimeCore(path, _fileCache.BirthTime, _fileCache.BirthTimeNsec);
if (error != Interop.Error.SUCCESS && error != Interop.Error.ENOTSUP)
{
Interop.CheckIo(error, path, InitiallyDirectory);
}
}
}

internal long GetLength(ReadOnlySpan<char> path, bool continueOnError = false)
Expand Down Expand Up @@ -399,6 +415,13 @@ private void ThrowOnCacheInitializationError(ReadOnlySpan<char> path)
}
}

private static long UnixTimeSecondsToNanoseconds(DateTimeOffset time, long seconds)
{
const long TicksPerMillisecond = 10000;
const long TicksPerSecond = TicksPerMillisecond * 1000;
return (time.UtcDateTime.Ticks - DateTimeOffset.UnixEpoch.Ticks - seconds * TicksPerSecond) * NanosecondsPerTick;
}

private bool TryRefreshFileCache(ReadOnlySpan<char> path) =>
VerifyStatCall(Interop.Sys.LStat(path, out _fileCache), out _initializedFileCache);

Expand Down

0 comments on commit 7cd8f19

Please sign in to comment.