修改NTFS交接点的属性

时间:2014-07-22 10:56:53

标签: c# pinvoke ntfs junction reparsepoint

我正在使用this libray来创建或获取我的交接点。获得此JunctionPoint个实例后,我可以从DirectoryInfo (*)路径创建Link并阅读{ {1}}财产。 现在我想将此时间戳设置为另一个日期,但它不起作用:值保持不变并且不会抛出异常。

(*):这是您找到交接点的位置的路径。

我怀疑,你需要使用一些PInvoke来完成这项工作。通过在创建时或之后使用第二种方法指定所需的时间戳。所以我查找了LastWriteTimeUtc CreateFile(),用于创建交接点,希望能找到有用的东西。 dwFlagsAndAttributes参数似乎接受了一些标记/数值,而SetFileAttributes()方法的doc看起来并不好看。

我在使用PInvoke方面有很多经验,一些好的建议真的很值得赞赏。

修改

  

"我不想下载代码。我希望在问题中看到足够的代码才能回答"

这是用于创建联结的相关代码。它与 codeproject 的代码略有不同,因为我需要进行一些调整才能将其嵌入到我自己的库中。 Link的类型为DirectoryInfo

public void Create(bool overwrite = true)
    {
        Link.Refresh();
        if (Link.Exists)
        {
            if (!overwrite) throw new IOException("Directory already exists and overwrite parameter is false.");
        }
        else Link.Create();

        using (var handle = OpenReparsePoint(Link.FullName, EFileAccess.GenericWrite))
        {
            var targetDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Target.FullName);

            var reparseDataBuffer = new REPARSE_DATA_BUFFER();

            reparseDataBuffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
            reparseDataBuffer.ReparseDataLength = (ushort) (targetDirBytes.Length + 12);
            reparseDataBuffer.SubstituteNameOffset = 0;
            reparseDataBuffer.SubstituteNameLength = (ushort) targetDirBytes.Length;
            reparseDataBuffer.PrintNameOffset = (ushort) (targetDirBytes.Length + 2);
            reparseDataBuffer.PrintNameLength = 0;
            reparseDataBuffer.PathBuffer = new byte[0x3ff0];
            Array.Copy(targetDirBytes, reparseDataBuffer.PathBuffer, targetDirBytes.Length);

            var inBufferSize = Marshal.SizeOf(reparseDataBuffer);
            var inBuffer = Marshal.AllocHGlobal(inBufferSize);

            try
            {
                Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false);

                int bytesReturned;
                var result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT,
                                             inBuffer, targetDirBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);

                if (!result) ThrowLastWin32Error("Unable to create junction point.");
            }
            finally
            {
                Marshal.FreeHGlobal(inBuffer);
            }
        }
    }

private static SafeFileHandle OpenReparsePoint(string reparsePoint, EFileAccess accessMode)
    {
        var reparsePointHandle = new SafeFileHandle(CreateFile(reparsePoint, accessMode,
                                                               EFileShare.Read | EFileShare.Write | EFileShare.Delete,
                                                               IntPtr.Zero, ECreationDisposition.OpenExisting,
                                                               EFileAttributes.BackupSemantics |
                                                               EFileAttributes.OpenReparsePoint, IntPtr.Zero), true);

        if (Marshal.GetLastWin32Error() != 0) ThrowLastWin32Error("Unable to open reparse point.");
        return reparsePointHandle;
    }

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern IntPtr CreateFile(
        string lpFileName,
        EFileAccess dwDesiredAccess,
        EFileShare dwShareMode,
        IntPtr lpSecurityAttributes,
        ECreationDisposition dwCreationDisposition,
        EFileAttributes dwFlagsAndAttributes,
        IntPtr hTemplateFile);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
                                               IntPtr InBuffer, int nInBufferSize,
                                               IntPtr OutBuffer, int nOutBufferSize,
                                               out int pBytesReturned, IntPtr lpOverlapped);

1 个答案:

答案 0 :(得分:1)

最近我不得不回到这个老问题,并通过使用Win32 API找到了以下解决方案:

为此,您需要创建联结的SafeFileHandle。所以我修改了Create()方法以返回OpenReparsePoint()的结果而不处理它(之后将会这样做)。要访问Win32 API,您需要声明一个extern方法,该方法与要调用的API method相匹配。

[DllImport("kernel32.dll", SetLastError = true)]
[ResourceExposure(ResourceScope.None)]
private static extern bool SetFileTime(SafeFileHandle hFile,
                                       ref long lpCreationTime,
                                       ref long lpLastAccessTime,
                                       ref long lpLastWriteTime);

现在您可以像这样设置文件时间

string link;
string target;
var junctionPoint = new JunctionPoint(ling, target);

long creationTime; // ticks in FileTime format
long accessTime;
long lastWriteTime
using(SafeFileHandle hFile = junctionPoint.Create()) 
{ 
     SetFileTime(SafeFileHandle hFile, 
                     ref creationTime,
                   ref lastAccessTime,
                    ref lastWriteTime);
}