-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Cannot change LastWriteTime or LastAccessTime of a symlink #38824
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
@jeffschwMSFT this doesn't seem related to Setup, can you reassign to FileSystem owners? |
@Liturgist, allow me to provide a more succinct repro: Run either in PowerShell [Core] v6+ or in Windows PowerShell, in which case the session must be elevated: # Create a test target file.
$null > tmp.txt
# Create a symlink to it.
$link = New-Item -Force -ItemType SymbolicLink tmpL.txt -Target tmp.txt
# Try to modify the *symlink*'s LastWriteTime.
$link.LastWriteTime = '2020-01-01'
# List both the link and the target, which shows that the *target*'s
# LastWriteTime was modified.
Get-Item tmp.txt, tmpL.txt -OutVariable result
# Clean up.
$result | Remove-Item In PowerShell Core, you'll get output such as the following, showing that the target was modified, not the link itself:
See also: The proposed symlink API in #24271 |
Thanks for the details. I know in Linux, there are ways to manipulate the attributes of the symbolic link itself, For example you can read the file attributes using What I would like to investigate is if Windows has similar options to modify symbolic link attributes. I know the Windows methods to create symbolic links, but I can't find a method to edit an existing symbolic link. @Liturgist can you share more information about why you want to edit the symbolic link attributes, and specifically, the timestamps? |
@carlossanlop I've implemented fixes for this in #49555. |
Thank you so much for offering your help, @hamarb123 ! I assigned this issue to you per your request. I'm looking forward to reviewing your PR. |
|
Just to clarify my reasoning as to why I think we should proceed with the changes in #49555 and #52639 (setting it on the reparse point itself) is because (in no particular order):
This is why I think we should set and get from the link itself in my opinion. |
…#52639) * Implement most of the fix for #38824 Reverts the changes in the seperate PR a617a01 to fix #38824. Does not re-enable the test because that relies on #49555, will add a seperate commit to whichever is merged last to enable the SettingUpdatesPropertiesOnSymlink test. * Most of the code for PR #52639 to fix #38824 • Deal with merge conflicts • Add FSOPT_NOFOLLOW for macOS and use it in setattrlist • Use AT_SYMLINK_NOFOLLOW with utimensat (note: there doesn't seem to be an equivalent for utimes) • Add SettingUpdatesPropertiesOnSymlink test to test if it actually sets it on the symlink itself (to the best that we can anyway ie. write + read the times) • Specify FILE_FLAG_OPEN_REPARSE_POINT for CreateFile in FileSystem.Windows.cs (is there any other CreateFile calls that need this?) * Remove additional FILE_FLAG_OPEN_REPARSE_POINT I added FILE_FLAG_OPEN_REPARSE_POINT before and it was then added in another PR, this removes the duplicate entry. * Add missing override keywords Add missing override keywords for abstract members in the tests. * Fix access modifiers Fix access modifiers for tests - were meant to be protected rather than public * Add more symlink tests, rearrange files • Move symlink creation api to better spot in non-base files • Add IsDirectory property for some of the new tests • Change abstract symlink api to CreateSymlink that accepts path and target • Create a CreateSymlinkToItem method that creates a symlink to an item that may be relative that uses the new/modified abstract CreateSymlink method • Add SettingUpdatesPropertiesCore to avoid code duplication • Add tests for the following variants: normal/symlink, target exists/doesn't exist, absolute/relative target • Exclude nonexistent symlink target tests on Unix for Directories since they are counted as files * Fix return type of CreateSymlink in File/GetSetTimes.cs * Remove browser from new symlink tests as it doesn't support creation of symlinks Remove browser from new symlink tests as it doesn't support creation of symlinks * Use lutimes, improve code readability, simplify tests • Use lutimes when it's available • Extract dwFlagsAndAttributes to a variable • Use same year for all tests • Checking to delete old symlink is unnecessary, so don't • Replace var with explicit type * Change year in test to 2014 to reduce diff * Rename symlink tests, use 1 core symlink times function, and check that target times don't change Rename symlink tests, use 1 core symlink times function, and check that target times don't change * Inline RunSymlinkTestPart 'function' Inline RunSymlinkTestPart 'function' so that the code can be reordered so the access time test can be valid. * Share CreateSymlinkToItem call in tests and update comment for clarity * Update symlink time tests • Make SettingUpdatesPropertiesOnSymlink a theory • Remove special case for Unix due to #52639 (comment) (will revert if fails) • Rename isRelative to targetIsRelative for clarity * Remove unnecessary Assert.All * Changes to SettingUpdatesPropertiesOnSymlink test • Rename item field to link field • Don't use if one-liner • Use all time functions since only using UTC isn't necessary • Remove the now-defunct IsDirectory property since we aren't checking it anymore * Remove unnecessary fsi.Refresh() • Remove unnecessary fsi.Refresh() since atime is only updated when reading a file * Updates to test and pal_time.c • Remove targetIsRelative cases • Multi-line if statement • Combine HAVE_LUTIMES and #else conditions to allow more code charing * Remove trailing space
Is there a quick workaround for |
You can use the pinvokes yourself to write a function that updates the symlink rather than the target, for Windows that would be: using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
using System.Runtime.InteropServices;
const int FileBasicInfo = 0;
SafeFileHandle handle =
CreateFilePrivate(
"C:/your-link",
0x40000000 /* Write */,
FileShare.ReadWrite | FileShare.Delete,
lpSecurityAttributes: 0,
FileMode.Open,
0x00200000 /* Open reparse point */,
IntPtr.Zero);
var basicInfo = new FILE_BASIC_INFO()
{
CreationTime = -1,
LastAccessTime = -1,
LastWriteTime = DateTime.UtcNow.AddDays(1).ToFileTime(),
ChangeTime = -1,
FileAttributes = 0
};
unsafe
{
if (!SetFileInformationByHandle(handle, FileBasicInfo, &basicInfo, (uint)sizeof(FILE_BASIC_INFO)))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
Console.WriteLine("Success!");
// interop code:
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
static unsafe extern SafeFileHandle CreateFilePrivate(
string lpFileName,
int dwDesiredAccess,
FileShare dwShareMode,
uint lpSecurityAttributes,
FileMode dwCreationDisposition,
int dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
static unsafe extern bool SetFileInformationByHandle(
SafeFileHandle hFile,
int FileInformationClass,
void* lpFileInformation,
uint dwBufferSize);
struct FILE_BASIC_INFO
{
internal long CreationTime;
internal long LastAccessTime;
internal long LastWriteTime;
internal long ChangeTime;
internal uint FileAttributes;
} You can expand above code to also work with other OSes by looking at what .NET uses internally. |
Description
It does not appear to be possible to change the LastWriteTime or LastAccessTime of a symlink. It works on hardlinks.
Configuration
Regression?
Unknown
Other information
The text was updated successfully, but these errors were encountered: