-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSymLinkUtilities.cs
132 lines (114 loc) · 5.26 KB
/
SymLinkUtilities.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace SymLinkMirror
{
public static partial class SymLinkUtilities
{
// ------------------------------------------------------------
// API declarations
// ------------------------------------------------------------
private enum SymbolicLinkFlag : uint
{
File = 0,
Directory = 1,
}
[LibraryImport("kernel32.dll", EntryPoint = "CreateSymbolicLinkW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
[return: MarshalAs(UnmanagedType.I1)]
private static partial bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, SymbolicLinkFlag dwFlags);
private static readonly EnumerationOptions _enumerationOptions = new();
// ------------------------------------------------------------
// Custom exceptions
// ------------------------------------------------------------
/// <summary>
/// The exception that is thrown when an error occurs while creating a symbolic link.
/// </summary>
public sealed class SymLinkException : IOException
{
public int Win32ErrorCode
{
get
{
return _Win32ErrorCode;
}
}
private readonly int _Win32ErrorCode = 0;
public SymLinkException(int win32ErrorCode) : base("Creation of the symbolic link failed. You may not have permission to create a symbolic link at the destination. Creation of symbolic links requires administrator privileges and elevation.") { _Win32ErrorCode = win32ErrorCode; }
public override string Message
{
get
{
return base.Message + " (" + _Win32ErrorCode.ToString() + ")";
}
}
}
// ------------------------------------------------------------
// Extension methods
// ------------------------------------------------------------
/// <summary>
/// Recursively mirrors a directory structure to another location by creating symbolic links instead of copying files.
/// </summary>
/// <param name="source">The source directory to mirror.</param>
/// <param name="destination">The destination where symbolic links will be created.</param>
public static void MirrorUsingSymLinks(this DirectoryInfo source, string destination)
{
// First, make sure that the destination folder exists.
if (!Directory.Exists(destination))
Directory.CreateDirectory(destination);
// First, mirror all files in this folder.
foreach (FileInfo sourceFile in source.EnumerateFiles("*", _enumerationOptions))
sourceFile.SymLinkTo(Path.Combine(destination, sourceFile.Name));
// Then, recursively mirror all of the subfolders of this folder.
foreach (DirectoryInfo sourceFolder in source.EnumerateDirectories("*", _enumerationOptions))
sourceFolder.MirrorUsingSymLinks(Path.Combine(destination, sourceFolder.Name));
}
/// <summary>
/// Creates a file symbolic link.
/// </summary>
/// <param name="source">The existing file to link to.</param>
/// <param name="destination">The path to the symbolic link to be created.</param>
/// <remarks>
/// If the destination file already exists, it will be deleted before the symbolic link is created.
/// File attributes and access times will also be mirrored, but ACLs will not be.
/// </remarks>
public static void SymLinkTo(this FileInfo source, string destinationPath)
{
if (File.Exists(destinationPath))
File.Delete(destinationPath);
if (!CreateSymbolicLink(destinationPath, source.FullName, SymbolicLinkFlag.File))
throw new SymLinkException(Marshal.GetLastWin32Error());
//source.CopyAttributesAndTimestampsTo(new FileInfo(destinationPath));
}
/// <summary>
/// Creates a directory symbolic link.
/// </summary>
/// <param name="source">The existing directory to link to.</param>
/// <param name="destination">The path to the symbolic link to be created.</param>
/// <remarks>If the destination directory already exists, this method will fail.</remarks>
public static void SymLinkTo(this DirectoryInfo source, string destinationPath)
{
if (!CreateSymbolicLink(destinationPath, source.FullName, SymbolicLinkFlag.Directory))
throw new SymLinkException(Marshal.GetLastWin32Error());
//source.CopyAttributesAndTimestampsTo(new DirectoryInfo(destinationPath));
}
/// <summary>
/// Copies the file system attributes, creation time, last write time, and last access time from
/// one FileSystemInfo object to another of the same type.
/// </summary>
/// <param name="source">The source of the file system attributes to copy.</param>
/// <param name="destination">The destination object whose attributes are to be changed.</param>
public static void CopyAttributesAndTimestampsTo(this FileSystemInfo source, FileSystemInfo destination)
{
if (destination == null)
throw new ArgumentNullException(nameof(destination));
bool sourceIsFile = source is FileInfo;
bool destIsFile = destination is FileInfo;
if ((sourceIsFile && !destIsFile) || (!sourceIsFile && destIsFile))
throw new ArgumentOutOfRangeException(nameof(destination), "The source and destination FileSystemInfo objects must be of the same derived type.");
destination.Attributes = source.Attributes;
destination.CreationTimeUtc = source.CreationTimeUtc;
destination.LastWriteTimeUtc = source.LastWriteTimeUtc;
destination.LastAccessTimeUtc = source.LastAccessTimeUtc;
}
}
}