├── .gitattributes
├── .gitignore
├── Austin.WindowsProjectedFileSystem
├── Austin.WindowsProjectedFileSystem.csproj
├── Compatibility.cs
├── FileBasicInfo.cs
├── IProjectedFileSystemCallbacks.cs
├── Interop
│ ├── HResult.cs
│ ├── Interop.cs
│ ├── NtDll.cs
│ └── ProjFs
│ │ ├── PRJ_CALLBACKS.cs
│ │ ├── PRJ_CALLBACK_DATA.cs
│ │ ├── PRJ_CALLBACK_DATA_FLAGS.cs
│ │ ├── PRJ_CANCEL_COMMAND_CB.cs
│ │ ├── PRJ_END_DIRECTORY_ENUMERATION_CB.cs
│ │ ├── PRJ_FILE_BASIC_INFO.cs
│ │ ├── PRJ_GET_DIRECTORY_ENUMERATION_CB.cs
│ │ ├── PRJ_GET_FILE_DATA_CB.cs
│ │ ├── PRJ_GET_PLACEHOLDER_INFO_CB.cs
│ │ ├── PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT.cs
│ │ ├── PRJ_NOTIFICATION.cs
│ │ ├── PRJ_NOTIFICATION_CB.cs
│ │ ├── PRJ_NOTIFICATION_PARAMETERS.cs
│ │ ├── PRJ_NOTIFY_TYPES.cs
│ │ ├── PRJ_PLACEHOLDER_INFO.cs
│ │ ├── PRJ_PLACEHOLDER_VERSION_INFO.cs
│ │ ├── PRJ_QUERY_FILE_NAME_CB.cs
│ │ ├── PRJ_START_DIRECTORY_ENUMERATION_CB.cs
│ │ └── ProjFs.cs
├── ProjectedFileSystem.EnumStatus.cs
└── ProjectedFileSystem.cs
├── README.markdown
├── ZFSOnDiskFormat.pdf
├── ZfsDokan
├── Dokan
│ ├── DokanNet.cs
│ ├── DokanOperations.cs
│ └── Proxy.cs
├── Program.cs
├── ZfsDokan.cs
└── ZfsDokan.csproj
├── ZfsProjFs
├── Program.cs
├── ProjectedItemInfo.cs
└── ZfsProjFs.csproj
├── ZfsSharp.sln
├── ZfsSharp
├── Program.cs
└── ZfsSharp.csproj
└── ZfsSharpLib
├── -
├── Guard.cs
├── System
│ ├── DataStructures
│ │ ├── AvlTree.cs
│ │ ├── AvlTreeNode.cs
│ │ ├── BinaryTreeBase.cs
│ │ ├── BinaryTreeNode.cs
│ │ └── FuncComparer.cs
│ └── IComparableExtensions.cs
└── Throw.cs
├── CRC.cs
├── Checksum
├── Flecter4.cs
├── NoChecksum.cs
└── Sha256.cs
├── Compression
├── GZip.cs
├── LZ4.cs
├── Lzjb.cs
└── NoCompression.cs
├── DNode.cs
├── DataSetType.cs
├── DatasetDirectory.cs
├── Extensions.cs
├── HardDisk
├── FileHardDisk.cs
├── GptHardDrive.cs
├── HardDisk.cs
├── MbrHardDisk.cs
├── OffsetHardDisk.cs
├── OffsetTableHardDisk.cs
├── VdiHardDisk.cs
├── VhdHardDisk.cs
└── VhdxHardDisk.cs
├── LeafVdevInfo.cs
├── MetaSlabs.cs
├── NvList.cs
├── NvListBinaryReader.cs
├── ObjectSet.cs
├── Program.cs
├── RangeMap.cs
├── Structs.Ddt.cs
├── Structs.Dmu.cs
├── Structs.Dsl.cs
├── Structs.SpaceMaps.cs
├── Structs.Zio.cs
├── Structs.Zpl.cs
├── VirtualDevices
├── HddVdev.cs
├── MirrorVdev.cs
├── RaidzVdev.cs
└── Vdev.cs
├── Zap.cs
├── Zfs.cs
├── ZfsSharpLib.csproj
├── Zio.cs
├── Zpl.cs
└── lz4net
├── LZ4.portable
└── LZ4Codec.portable.cs
├── LZ4
├── ILZ4Service.cs
├── LZ4Codec.cs
├── LZ4StreamFlags.cs
├── LZ4StreamMode.cs
└── Services
│ ├── Safe32LZ4Service.cs
│ └── Safe64LZ4Service.cs
└── LZ4ps
├── LZ4Codec.Safe.cs
├── LZ4Codec.Safe32.Dirty.cs
├── LZ4Codec.Safe32HC.Dirty.cs
├── LZ4Codec.Safe64.Dirty.cs
├── LZ4Codec.Safe64HC.Dirty.cs
└── LZ4Codec.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.suo
2 | *proj.user
3 | bin
4 | obj
5 | *.cache
6 | TestResults
7 | packages
8 | *.psess
9 | *.vsp
10 | .vs/
11 | *.vspx
12 | launchSettings.json
13 |
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Austin.WindowsProjectedFileSystem.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | true
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Compatibility.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 |
6 | namespace Austin.WindowsProjectedFileSystem
7 | {
8 | public enum CompatibilityStatus
9 | {
10 | UnsupportedPlatform,
11 | UnsupportedArchitecture,
12 | UnsupportedOsVersion,
13 | FeatureNotEnabled,
14 | Supported,
15 | }
16 |
17 | public static class Compatibility
18 | {
19 | public static CompatibilityStatus Status { get; } = getStatus();
20 |
21 | static CompatibilityStatus getStatus()
22 | {
23 | if (Environment.OSVersion.Platform != PlatformID.Win32NT)
24 | {
25 | return CompatibilityStatus.UnsupportedPlatform;
26 | }
27 |
28 | if (!Environment.Is64BitProcess)
29 | {
30 | //It appears that ProjectedFSLib.dll only exists as a 64-bit DLL.
31 | return CompatibilityStatus.UnsupportedArchitecture;
32 | }
33 |
34 | //In .NET Core versions before 3, the host executable does not contain a manifest that specifies supportedOs,
35 | //so Environment.OSVersion.Version lies. So we have to call RtlGetVersion ourselves.
36 | //In .NET Core 3 a proper manfiest is included:
37 | //https://github.com/dotnet/core-setup/issues/5106
38 | var winVer = Interop.NtDll.RtlGetVersion();
39 | if (winVer.Major < 10)
40 | {
41 | //TODO: do we need a tigher version check?
42 | return CompatibilityStatus.UnsupportedOsVersion;
43 | }
44 |
45 |
46 | //TODO: maybe use DISM API to figure out if the feature is enabled.
47 | if (!File.Exists(Path.Combine(Environment.SystemDirectory, Interop.ProjectedFfLibDll)))
48 | {
49 | return CompatibilityStatus.FeatureNotEnabled;
50 | }
51 |
52 | return CompatibilityStatus.Supported;
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/FileBasicInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace Austin.WindowsProjectedFileSystem
5 | {
6 | public class FileBasicInfo
7 | {
8 | public string Name { get; set; }
9 | public bool IsDirectory { get; set; }
10 | public long FileSize { get; set; }
11 | public DateTime CreationTime { get; set; }
12 | public DateTime LastAccessTime { get; set; }
13 | public DateTime LastWriteTime { get; set; }
14 | public DateTime ChangeTime { get; set; }
15 | public FileAttributes Attributes { get; set; }
16 |
17 | internal Interop.ProjFs.PRJ_FILE_BASIC_INFO GoNative()
18 | {
19 | var ret = new Interop.ProjFs.PRJ_FILE_BASIC_INFO()
20 | {
21 | FileSize = this.FileSize,
22 | IsDirectory = this.IsDirectory,
23 | FileAttributes = (uint)this.Attributes,
24 | };
25 | if (this.ChangeTime.Year >= 1601)
26 | ret.ChangeTime = this.ChangeTime.ToFileTimeUtc();
27 | if (this.CreationTime.Year >= 1601)
28 | ret.CreationTime = this.CreationTime.ToFileTimeUtc();
29 | if (this.LastAccessTime.Year >= 1601)
30 | ret.LastAccessTime = this.LastAccessTime.ToFileTimeUtc();
31 | if (this.LastWriteTime.Year >= 1601)
32 | ret.LastWriteTime = this.LastWriteTime.ToFileTimeUtc();
33 | return ret;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/IProjectedFileSystemCallbacks.cs:
--------------------------------------------------------------------------------
1 | namespace Austin.WindowsProjectedFileSystem
2 | {
3 | public interface IProjectedFileSystemCallbacks
4 | {
5 | FileBasicInfo[] EnumerateDirectory(bool isWildCardExpression, string directory, string searchExpression);
6 | bool FileExists(string fileName);
7 | //TODO: add support for extended attributes, etc.
8 | FileBasicInfo QueryFileInfo(string fileName);
9 | bool GetFileData(string fileName, byte[] buf, ulong offset, uint length);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/HResult.cs:
--------------------------------------------------------------------------------
1 | namespace Austin.WindowsProjectedFileSystem
2 | {
3 | partial class Interop
4 | {
5 | public static class HResult
6 | {
7 | const int FACILITY_NT_BIT = 0x10000000;
8 | static int HRESULT_FROM_NT(uint status)
9 | {
10 | return (int)(status | FACILITY_NT_BIT);
11 | }
12 |
13 | const int FACILITY_WIN32 = 7;
14 | static int HRESULT_FROM_WIN32(uint x)
15 | {
16 | return (int)(x) <= 0 ? (int)(x) : (int)(((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000);
17 | }
18 |
19 | public static readonly int STATUS_CANNOT_DELETE = HRESULT_FROM_NT(0xC0000121);
20 | public static readonly int ERROR_FILE_NOT_FOUND = HRESULT_FROM_WIN32(2);
21 | public static readonly int ERROR_INVALID_PARAMETER = HRESULT_FROM_WIN32(87);
22 | public static readonly int ERROR_INSUFFICIENT_BUFFER = HRESULT_FROM_WIN32(122);
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/Interop.cs:
--------------------------------------------------------------------------------
1 | namespace Austin.WindowsProjectedFileSystem
2 | {
3 | static partial class Interop
4 | {
5 | public const string ProjectedFfLibDll = "ProjectedFSLib.dll";
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/NtDll.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Austin.WindowsProjectedFileSystem
5 | {
6 | partial class Interop
7 | {
8 | public static class NtDll
9 | {
10 | [StructLayout(LayoutKind.Sequential)]
11 | internal struct RTL_OSVERSIONINFOEX
12 | {
13 | internal uint dwOSVersionInfoSize;
14 | internal uint dwMajorVersion;
15 | internal uint dwMinorVersion;
16 | internal uint dwBuildNumber;
17 | internal uint dwPlatformId;
18 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
19 | internal string szCSDVersion;
20 | }
21 |
22 | [DllImport("ntdll")]
23 | private static extern int RtlGetVersion(out RTL_OSVERSIONINFOEX lpVersionInformation);
24 |
25 | internal static Version RtlGetVersion()
26 | {
27 | RTL_OSVERSIONINFOEX v = new RTL_OSVERSIONINFOEX();
28 | v.dwOSVersionInfoSize = (uint)Marshal.SizeOf(v);
29 | if (RtlGetVersion(out v) == 0)
30 | {
31 | return new Version((int)v.dwMajorVersion, (int)v.dwMinorVersion, (int)v.dwBuildNumber, (int)v.dwPlatformId);
32 | }
33 | else
34 | {
35 | throw new Exception("RtlGetVersion failed!");
36 | }
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_CALLBACKS.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Austin.WindowsProjectedFileSystem
5 | {
6 | partial class Interop
7 | {
8 | partial class ProjFs
9 | {
10 | [StructLayout(LayoutKind.Sequential)]
11 | public class PRJ_CALLBACKS
12 | {
13 | public PRJ_START_DIRECTORY_ENUMERATION_CB StartDirectoryEnumerationCallback;
14 | public PRJ_END_DIRECTORY_ENUMERATION_CB EndDirectoryEnumerationCallback;
15 | public PRJ_GET_DIRECTORY_ENUMERATION_CB GetDirectoryEnumerationCallback;
16 | public PRJ_GET_PLACEHOLDER_INFO_CB GetPlaceholderInfoCallback;
17 | public PRJ_GET_FILE_DATA_CB GetFileDataCallback;
18 |
19 | public PRJ_QUERY_FILE_NAME_CB QueryFileNameCallback;
20 | public PRJ_NOTIFICATION_CB NotificationCallback;
21 | public PRJ_CANCEL_COMMAND_CB CancelCommandCallback;
22 | }
23 | [StructLayout(LayoutKind.Sequential)]
24 | public class PRJ_CALLBACKS_INTPTR
25 | {
26 | public IntPtr StartDirectoryEnumerationCallback;
27 | public IntPtr EndDirectoryEnumerationCallback;
28 | public IntPtr GetDirectoryEnumerationCallback;
29 | public IntPtr GetPlaceholderInfoCallback;
30 | public IntPtr GetFileDataCallback;
31 |
32 | public IntPtr QueryFileNameCallback;
33 | public IntPtr NotificationCallback;
34 | public IntPtr CancelCommandCallback;
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_CALLBACK_DATA.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.InteropServices;
4 | using System.Text;
5 |
6 | namespace Austin.WindowsProjectedFileSystem
7 | {
8 | partial class Interop
9 | {
10 | partial class ProjFs
11 | {
12 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
13 | public struct PRJ_CALLBACK_DATA
14 | {
15 | public UInt32 Size;
16 | public PRJ_CALLBACK_DATA_FLAGS Flags;
17 | public IntPtr NamespaceVirtualizationContext;
18 | public Int32 CommandId;
19 | public Guid FileId;
20 | public Guid DataStreamId;
21 | public string FilePathName;
22 | IntPtr VersionInfo;
23 | public UInt32 TriggeringProcessId;
24 | public string TriggeringProcessImageFileName;
25 | IntPtr InstanceContext;
26 | }
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_CALLBACK_DATA_FLAGS.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Austin.WindowsProjectedFileSystem
4 | {
5 | partial class Interop
6 | {
7 | partial class ProjFs
8 | {
9 | [Flags]
10 | public enum PRJ_CALLBACK_DATA_FLAGS : UInt32
11 | {
12 | None = 0,
13 | RESTART_SCAN = 0x00000001,
14 | RETURN_SINGLE_ENTRY = 0x00000002,
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_CANCEL_COMMAND_CB.cs:
--------------------------------------------------------------------------------
1 | namespace Austin.WindowsProjectedFileSystem
2 | {
3 | partial class Interop
4 | {
5 | partial class ProjFs
6 | {
7 | public delegate void PRJ_CANCEL_COMMAND_CB(in PRJ_CALLBACK_DATA callbackData);
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_END_DIRECTORY_ENUMERATION_CB.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Austin.WindowsProjectedFileSystem
4 | {
5 | partial class Interop
6 | {
7 | partial class ProjFs
8 | {
9 | public delegate Int32 PRJ_END_DIRECTORY_ENUMERATION_CB(in PRJ_CALLBACK_DATA callbackData, in Guid enumerationId);
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_FILE_BASIC_INFO.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Austin.WindowsProjectedFileSystem
5 | {
6 | partial class Interop
7 | {
8 | partial class ProjFs
9 | {
10 | [StructLayout(LayoutKind.Sequential)]
11 | public struct PRJ_FILE_BASIC_INFO
12 | {
13 | [MarshalAs(UnmanagedType.U1)]
14 | public bool IsDirectory;
15 | public Int64 FileSize;
16 | public Int64 CreationTime;
17 | public Int64 LastAccessTime;
18 | public Int64 LastWriteTime;
19 | public Int64 ChangeTime;
20 | public UInt32 FileAttributes;
21 | }
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_GET_DIRECTORY_ENUMERATION_CB.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Austin.WindowsProjectedFileSystem
5 | {
6 | partial class Interop
7 | {
8 | partial class ProjFs
9 | {
10 | public delegate Int32 PRJ_GET_DIRECTORY_ENUMERATION_CB(in PRJ_CALLBACK_DATA callbackData, in Guid enumerationId, [MarshalAs(UnmanagedType.LPWStr)] string searchExpression, IntPtr dirEntryBufferHandle);
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_GET_FILE_DATA_CB.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Austin.WindowsProjectedFileSystem
4 | {
5 | partial class Interop
6 | {
7 | partial class ProjFs
8 | {
9 | public delegate Int32 PRJ_GET_FILE_DATA_CB(in PRJ_CALLBACK_DATA callbackData, UInt64 byteOffset, UInt32 length);
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_GET_PLACEHOLDER_INFO_CB.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Austin.WindowsProjectedFileSystem
4 | {
5 | partial class Interop
6 | {
7 | partial class ProjFs
8 | {
9 | public delegate Int32 PRJ_GET_PLACEHOLDER_INFO_CB(in PRJ_CALLBACK_DATA callbackData);
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32.SafeHandles;
2 |
3 | namespace Austin.WindowsProjectedFileSystem
4 | {
5 | partial class Interop
6 | {
7 | partial class ProjFs
8 | {
9 | public class PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT : SafeHandleZeroOrMinusOneIsInvalid
10 | {
11 | public PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT()
12 | : base(true)
13 | {
14 | }
15 |
16 | protected override bool ReleaseHandle()
17 | {
18 | PrjStopVirtualizing(this.handle);
19 | return true;
20 | }
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_NOTIFICATION.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Austin.WindowsProjectedFileSystem
4 | {
5 | partial class Interop
6 | {
7 | partial class ProjFs
8 | {
9 | public enum PRJ_NOTIFICATION : UInt32
10 | {
11 | FILE_OPENED = 0x00000002,
12 | NEW_FILE_CREATED = 0x00000004,
13 | FILE_OVERWRITTEN = 0x00000008,
14 | PRE_DELETE = 0x00000010,
15 | PRE_RENAME = 0x00000020,
16 | PRE_SET_HARDLINK = 0x00000040,
17 | FILE_RENAMED = 0x00000080,
18 | HARDLINK_CREATED = 0x00000100,
19 | FILE_HANDLE_CLOSED_NO_MODIFICATION = 0x00000200,
20 | FILE_HANDLE_CLOSED_FILE_MODIFIED = 0x00000400,
21 | FILE_HANDLE_CLOSED_FILE_DELETED = 0x00000800,
22 | FILE_PRE_CONVERT_TO_FULL = 0x00001000,
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_NOTIFICATION_CB.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Austin.WindowsProjectedFileSystem
5 | {
6 | partial class Interop
7 | {
8 | partial class ProjFs
9 | {
10 | public delegate Int32 PRJ_NOTIFICATION_CB(
11 | in PRJ_CALLBACK_DATA callbackData,
12 | [MarshalAs(UnmanagedType.U1)] bool isDirectory,
13 | PRJ_NOTIFICATION notification,
14 | [MarshalAs(UnmanagedType.LPWStr)] string destinationFileName,
15 | ref PRJ_NOTIFICATION_PARAMETERS operationParameters);
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_NOTIFICATION_PARAMETERS.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace Austin.WindowsProjectedFileSystem
4 | {
5 | partial class Interop
6 | {
7 | partial class ProjFs
8 | {
9 | [StructLayout(LayoutKind.Explicit)]
10 | public struct PRJ_NOTIFICATION_PARAMETERS
11 | {
12 | [FieldOffset(0)]
13 | public PRJ_NOTIFY_TYPES PostCreate;
14 | [FieldOffset(0)]
15 | public PRJ_NOTIFY_TYPES FileRenamed;
16 | [FieldOffset(0), MarshalAs(UnmanagedType.U1)]
17 | public bool FileDeletedOnHandleClose;
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_NOTIFY_TYPES.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Austin.WindowsProjectedFileSystem
4 | {
5 | partial class Interop
6 | {
7 | partial class ProjFs
8 | {
9 | [Flags]
10 | public enum PRJ_NOTIFY_TYPES : UInt32
11 | {
12 | None = 0x00000000,
13 | SUPPRESS_NOTIFICATIONS = 0x00000001,
14 | FILE_OPENED = 0x00000002,
15 | NEW_FILE_CREATED = 0x00000004,
16 | FILE_OVERWRITTEN = 0x00000008,
17 | PRE_DELETE = 0x00000010,
18 | PRE_RENAME = 0x00000020,
19 | PRE_SET_HARDLINK = 0x00000040,
20 | FILE_RENAMED = 0x00000080,
21 | HARDLINK_CREATED = 0x00000100,
22 | FILE_HANDLE_CLOSED_NO_MODIFICATION = 0x00000200,
23 | FILE_HANDLE_CLOSED_FILE_MODIFIED = 0x00000400,
24 | FILE_HANDLE_CLOSED_FILE_DELETED = 0x00000800,
25 | FILE_PRE_CONVERT_TO_FULL = 0x00001000,
26 | USE_EXISTING_MASK = 0xFFFFFFFF
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_PLACEHOLDER_INFO.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Austin.WindowsProjectedFileSystem
5 | {
6 | partial class Interop
7 | {
8 | partial class ProjFs
9 | {
10 | [StructLayout(LayoutKind.Sequential)]
11 | public unsafe struct PRJ_PLACEHOLDER_INFO
12 | {
13 | public PRJ_FILE_BASIC_INFO FileBasicInfo;
14 | public UInt32 EaBufferSize;
15 | public UInt32 OffsetToFirstEa;
16 | public UInt32 SecurityBufferSize;
17 | public UInt32 OffsetToSecurityDescriptor;
18 | public UInt32 StreamsInfoBufferSize;
19 | public UInt32 OffsetToFirstStreamInfo;
20 | public PRJ_PLACEHOLDER_VERSION_INFO VersionInfo;
21 | fixed byte VariableData[1];
22 | }
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_PLACEHOLDER_VERSION_INFO.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace Austin.WindowsProjectedFileSystem
4 | {
5 | partial class Interop
6 | {
7 | public static partial class ProjFs
8 | {
9 | [StructLayout(LayoutKind.Sequential)]
10 | public unsafe struct PRJ_PLACEHOLDER_VERSION_INFO
11 | {
12 | public const int PRJ_PLACEHOLDER_ID_LENGTH = 128;
13 | fixed byte ProviderID[PRJ_PLACEHOLDER_ID_LENGTH];
14 | fixed byte ContentID[PRJ_PLACEHOLDER_ID_LENGTH];
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_QUERY_FILE_NAME_CB.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Austin.WindowsProjectedFileSystem
4 | {
5 | partial class Interop
6 | {
7 | partial class ProjFs
8 | {
9 | public delegate Int32 PRJ_QUERY_FILE_NAME_CB(in PRJ_CALLBACK_DATA callbackData);
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/PRJ_START_DIRECTORY_ENUMERATION_CB.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Austin.WindowsProjectedFileSystem
4 | {
5 | partial class Interop
6 | {
7 | partial class ProjFs
8 | {
9 | public delegate Int32 PRJ_START_DIRECTORY_ENUMERATION_CB(in PRJ_CALLBACK_DATA callbackData, in Guid enumerationId);
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/Interop/ProjFs/ProjFs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Austin.WindowsProjectedFileSystem
5 | {
6 | partial class Interop
7 | {
8 | public static partial class ProjFs
9 | {
10 | [DllImport(ProjectedFfLibDll, ExactSpelling = true, CharSet = CharSet.Unicode)]
11 | public static extern Int32 PrjMarkDirectoryAsPlaceholder(string rootPathName, string targetPathName, IntPtr versionInfo, in Guid virtualizationInstanceID);
12 |
13 | [DllImport(ProjectedFfLibDll, ExactSpelling = true, CharSet = CharSet.Unicode)]
14 | public static extern Int32 PrjStartVirtualizing(string virtualizationRootPath, IntPtr callbacks, IntPtr instanceContext, IntPtr options, out PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT namespaceVirtualizationContext);
15 |
16 | [DllImport(ProjectedFfLibDll, ExactSpelling = true, CharSet = CharSet.Unicode)]
17 | static extern Int32 PrjStopVirtualizing(IntPtr namespaceVirtualizationContext);
18 |
19 | [DllImport(ProjectedFfLibDll, ExactSpelling = true, CharSet = CharSet.Unicode)]
20 | [return: MarshalAs(UnmanagedType.U1)]
21 | public static extern bool PrjFileNameMatch(string fileNameToCheck, string pattern);
22 |
23 | [DllImport(ProjectedFfLibDll, ExactSpelling = true, CharSet = CharSet.Unicode)]
24 | public static extern Int32 PrjFillDirEntryBuffer(string fileName, in PRJ_FILE_BASIC_INFO fileBasicInfo, IntPtr dirEntryBufferHandle);
25 |
26 | [DllImport(ProjectedFfLibDll, ExactSpelling = true, CharSet = CharSet.Unicode)]
27 | [return: MarshalAs(UnmanagedType.U1)]
28 | public static extern bool PrjDoesNameContainWildCards(string fileName);
29 |
30 | [DllImport(ProjectedFfLibDll, ExactSpelling = true, CharSet = CharSet.Unicode)]
31 | public static extern Int32 PrjWritePlaceholderInfo(IntPtr namespaceVirtualizationContext, string destinationFileName, in PRJ_PLACEHOLDER_INFO placeholderInfo, UInt32 placeholderInfoSize);
32 |
33 | [DllImport(ProjectedFfLibDll, ExactSpelling = true, CharSet = CharSet.Unicode)]
34 | public static extern unsafe Int32 PrjWriteFileData(IntPtr namespaceVirtualizationContext, in Guid dataStreamId, byte* buffer, UInt64 byteOffset, UInt32 length);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Austin.WindowsProjectedFileSystem/ProjectedFileSystem.EnumStatus.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Austin.WindowsProjectedFileSystem
6 | {
7 | partial class ProjectedFileSystem
8 | {
9 | class EnumerationStatus
10 | {
11 | public FileBasicInfo[] Entries;
12 | public int CurrentIndex;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | What is this?
2 | =============
3 |
4 | This is a C# program that reads ZFS file systems. Writing is explicitly a non-goal.
5 | Several types of disk images are supported: raw, VHD, VHDX, and VDI.
6 | RAIDZ, mirror, and stripe vdevs are supported.
7 |
8 | Code Layout
9 | -----------
10 |
11 | * ZfsSharpLib: A library for reading ZFS files.
12 | * ZfsProjFs: Mount a pool using [Windows Projected Filesystem][ProjFS].
13 | * Austin.WindowsProjectedFileSystem: A library for using Windows Projected Filesystem. Not specific
14 | to ZFS.
15 | * ZfsDokan: Mount a pool using [Dokan].
16 | * ZfsSharp: Mostly used for testing and benchmarking.
17 |
18 | Future plans
19 | ------------
20 |
21 | * Improve Windows Project Filesystem support. Currently it works mostly ok, but for some reason
22 | directory entries are duplicated.
23 | * Add support for FUSE.
24 | * Add `async` support to parrelleize checksumming and decompression.
25 |
26 | What I'm learning
27 | -----------------
28 |
29 | It was exciting to get enough code working so I could read the uberblock in disk label. However it
30 | was a long way from there to reading any information out of the pool as I had to implement
31 | decompression and checksumming.
32 | This made obvious how fundamentally data integrity is backed into ZFS.
33 |
34 | The second moment of zen was to see the bock pointer's abstraction of 128k blocks of copy-on-write
35 | data being used by dnodes to create an abstraction of arbitrarily large pieces of data.
36 | By merely implementing the dnode and blkptr abstractions you get access to the fundamental data management tools
37 | of ZFS. Everything else in ZFS is just reading out of those.
38 |
39 | I found it interesting that ZFS incorporates three different ways of storing key-value pairs: ZAP, XDR, and SA.
40 | Each system is designed for different performance profiles:
41 |
42 | * SA: compactly storing small amounts of data, where many objects use the same key
43 | * ZAP: fast lookup by key to handle large directories
44 | * XDR: Config data that is rarely read or written. Probably used because it was lying around in
45 | Solaris for NFS, so why create something new.
46 |
47 | When I implemented the third system I started to get a feeling of Déjà vu.
48 |
49 | What parts of ZFS I wish were better documented
50 | -----------------------------------------------
51 |
52 | During the creation of this I found the [ZFS On-Disk Specification][ZfsSpec] to be quite helpful.
53 | However it describes the initial version of zpool and zfs. OpenZFS is forked off OpenSolaris at
54 | zpool version 28 and zfs 5. Since then OpenZFS has added many [features][ZfsFeatures], so there are
55 | several aspects of the system that are lacking diagrams in the On-Disk Specification document.
56 | Eventually I'd like to make a blog post full of pretty pictures to describe these structures.
57 |
58 | An example of outdated documentation in the On-Disk Specification is the `znode_phys_t` structure.
59 | In zfs v5 this was replaced with the system attribute (SA) system. The comment at the start of sa.c
60 | is pretty good so it was not difficult to figure out. I think a nice diagram would have made it
61 | even easier to understand.
62 |
63 | The description of the Fat ZAP makes sense now that I've implemented it. However I remember it
64 | being somewhat obtuse while implementing it.
65 |
66 | The best documentation on how XDR worked was looking at GRUB's code. Since it only needs to
67 | implement enough to boot the system, it quickly gets to the heart of the matter.
68 | So XDR could benefit from more documentation.
69 |
70 | [ZfsSpec]: ZFSOnDiskFormat.pdf
71 | [ProjFS]: https://docs.microsoft.com/en-us/windows/desktop/projfs/projected-file-system
72 | [Dokan]: https://dokan-dev.github.io/
73 | [ZfsFeatures]: http://www.open-zfs.org/wiki/Feature_Flags
74 |
--------------------------------------------------------------------------------
/ZFSOnDiskFormat.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustinWise/ZfsSharp/895b991b35d133b1b233755297eb39f2bdc4d4fa/ZFSOnDiskFormat.pdf
--------------------------------------------------------------------------------
/ZfsDokan/Dokan/DokanNet.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Runtime.InteropServices;
5 |
6 | namespace Dokan
7 | {
8 | public class DokanOptions
9 | {
10 | public ushort Version;
11 | public ushort ThreadCount;
12 | public bool DebugMode;
13 | public bool UseStdErr;
14 | public bool UseAltStream;
15 | public bool UseKeepAlive;
16 | public bool NetworkDrive;
17 | public bool RemovableDrive;
18 | public string VolumeLabel;
19 | public string MountPoint;
20 | }
21 |
22 |
23 | // this struct must be the same layout as DOKAN_OPERATIONS
24 | [StructLayout(LayoutKind.Sequential, Pack = 4)]
25 | struct DOKAN_OPERATIONS
26 | {
27 | public Proxy.CreateFileDelegate CreateFile;
28 | public Proxy.OpenDirectoryDelegate OpenDirectory;
29 | public Proxy.CreateDirectoryDelegate CreateDirectory;
30 | public Proxy.CleanupDelegate Cleanup;
31 | public Proxy.CloseFileDelegate CloseFile;
32 | public Proxy.ReadFileDelegate ReadFile;
33 | public Proxy.WriteFileDelegate WriteFile;
34 | public Proxy.FlushFileBuffersDelegate FlushFileBuffers;
35 | public Proxy.GetFileInformationDelegate GetFileInformation;
36 | public Proxy.FindFilesDelegate FindFiles;
37 | public IntPtr FindFilesWithPattern; // this is not used in DokanNet
38 | public Proxy.SetFileAttributesDelegate SetFileAttributes;
39 | public Proxy.SetFileTimeDelegate SetFileTime;
40 | public Proxy.DeleteFileDelegate DeleteFile;
41 | public Proxy.DeleteDirectoryDelegate DeleteDirectory;
42 | public Proxy.MoveFileDelegate MoveFile;
43 | public Proxy.SetEndOfFileDelegate SetEndOfFile;
44 | public Proxy.SetAllocationSizeDelegate SetAllocationSize;
45 | public Proxy.LockFileDelegate LockFile;
46 | public Proxy.UnlockFileDelegate UnlockFile;
47 | public Proxy.GetDiskFreeSpaceDelegate GetDiskFreeSpace;
48 | public Proxy.GetVolumeInformationDelegate GetVolumeInformation;
49 | public Proxy.UnmountDelegate Unmount;
50 | public Proxy.GetFileSecurityDelegate GetFileSecurity;
51 | public Proxy.SetFileSecurityDelegate SetFileSecurity;
52 | }
53 |
54 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
55 | struct DOKAN_OPTIONS
56 | {
57 | public ushort Version;
58 | public ushort ThreadCount; // number of threads to be used
59 | public uint Options;
60 | public ulong Dummy1;
61 | [MarshalAs(UnmanagedType.LPWStr)]
62 | public string MountPoint;
63 | }
64 |
65 |
66 | class Dokan
67 | {
68 | [DllImport("dokan.dll")]
69 | public static extern int DokanMain(ref DOKAN_OPTIONS options, ref DOKAN_OPERATIONS operations);
70 |
71 | [DllImport("dokan.dll")]
72 | public static extern int DokanUnmount(int driveLetter);
73 |
74 | [DllImport("dokan.dll")]
75 | public static extern int DokanRemoveMountPoint(
76 | [MarshalAs(UnmanagedType.LPWStr)] string mountPoint);
77 |
78 | [DllImport("dokan.dll")]
79 | public static extern uint DokanVersion();
80 |
81 | [DllImport("dokan.dll")]
82 | public static extern uint DokanDriveVersion();
83 |
84 | [DllImport("dokan.dll")]
85 | public static extern bool DokanResetTimeout(uint timeout, ref DOKAN_FILE_INFO rawFileInfo);
86 | }
87 |
88 |
89 | public class DokanNet
90 | {
91 | public const int ERROR_FILE_NOT_FOUND = 2;
92 | public const int ERROR_PATH_NOT_FOUND = 3;
93 | public const int ERROR_ACCESS_DENIED = 5;
94 | public const int ERROR_SHARING_VIOLATION = 32;
95 | public const int ERROR_INVALID_NAME = 123;
96 | public const int ERROR_FILE_EXISTS = 80;
97 | public const int ERROR_ALREADY_EXISTS = 183;
98 |
99 | public const int DOKAN_SUCCESS = 0;
100 | public const int DOKAN_ERROR = -1; // General Error
101 | public const int DOKAN_DRIVE_LETTER_ERROR = -2; // Bad Drive letter
102 | public const int DOKAN_DRIVER_INSTALL_ERROR = -3; // Can't install driver
103 | public const int DOKAN_START_ERROR = -4; // Driver something wrong
104 | public const int DOKAN_MOUNT_ERROR = -5; // Can't assign drive letter
105 |
106 | public const int DOKAN_VERSION = 600; // ver 0.6.0
107 |
108 | private const uint DOKAN_OPTION_DEBUG = 1;
109 | private const uint DOKAN_OPTION_STDERR = 2;
110 | private const uint DOKAN_OPTION_ALT_STREAM = 4;
111 | private const uint DOKAN_OPTION_KEEP_ALIVE = 8;
112 | private const uint DOKAN_OPTION_NETWORK = 16;
113 | private const uint DOKAN_OPTION_REMOVABLE = 32;
114 |
115 | public static int DokanMain(DokanOptions options, DokanOperations operations)
116 | {
117 | if (options.VolumeLabel == null)
118 | {
119 | options.VolumeLabel = "DOKAN";
120 | }
121 |
122 | Proxy proxy = new Proxy(options, operations);
123 |
124 | DOKAN_OPTIONS dokanOptions = new DOKAN_OPTIONS();
125 |
126 | dokanOptions.Version = options.Version;
127 | if (dokanOptions.Version == 0)
128 | {
129 | dokanOptions.Version = DOKAN_VERSION;
130 | }
131 | dokanOptions.ThreadCount = options.ThreadCount;
132 | dokanOptions.Options |= options.DebugMode ? DOKAN_OPTION_DEBUG : 0;
133 | dokanOptions.Options |= options.UseStdErr ? DOKAN_OPTION_STDERR : 0;
134 | dokanOptions.Options |= options.UseAltStream ? DOKAN_OPTION_ALT_STREAM : 0;
135 | dokanOptions.Options |= options.UseKeepAlive ? DOKAN_OPTION_KEEP_ALIVE : 0;
136 | dokanOptions.Options |= options.NetworkDrive ? DOKAN_OPTION_NETWORK : 0;
137 | dokanOptions.Options |= options.RemovableDrive ? DOKAN_OPTION_REMOVABLE : 0;
138 | dokanOptions.MountPoint = options.MountPoint;
139 |
140 | DOKAN_OPERATIONS dokanOperations = new DOKAN_OPERATIONS();
141 | dokanOperations.CreateFile = proxy.CreateFileProxy;
142 | dokanOperations.OpenDirectory = proxy.OpenDirectoryProxy;
143 | dokanOperations.CreateDirectory = proxy.CreateDirectoryProxy;
144 | dokanOperations.Cleanup = proxy.CleanupProxy;
145 | dokanOperations.CloseFile = proxy.CloseFileProxy;
146 | dokanOperations.ReadFile = proxy.ReadFileProxy;
147 | dokanOperations.WriteFile = proxy.WriteFileProxy;
148 | dokanOperations.FlushFileBuffers = proxy.FlushFileBuffersProxy;
149 | dokanOperations.GetFileInformation = proxy.GetFileInformationProxy;
150 | dokanOperations.FindFiles = proxy.FindFilesProxy;
151 | dokanOperations.SetFileAttributes = proxy.SetFileAttributesProxy;
152 | dokanOperations.SetFileTime = proxy.SetFileTimeProxy;
153 | dokanOperations.DeleteFile = proxy.DeleteFileProxy;
154 | dokanOperations.DeleteDirectory = proxy.DeleteDirectoryProxy;
155 | dokanOperations.MoveFile = proxy.MoveFileProxy;
156 | dokanOperations.SetEndOfFile = proxy.SetEndOfFileProxy;
157 | dokanOperations.SetAllocationSize = proxy.SetAllocationSizeProxy;
158 | dokanOperations.LockFile = proxy.LockFileProxy;
159 | dokanOperations.UnlockFile = proxy.UnlockFileProxy;
160 | dokanOperations.GetDiskFreeSpace = proxy.GetDiskFreeSpaceProxy;
161 | dokanOperations.GetVolumeInformation = proxy.GetVolumeInformationProxy;
162 | dokanOperations.Unmount = proxy.UnmountProxy;
163 |
164 | return Dokan.DokanMain(ref dokanOptions, ref dokanOperations);
165 | }
166 |
167 |
168 | public static int DokanUnmount(char driveLetter)
169 | {
170 | return Dokan.DokanUnmount(driveLetter);
171 | }
172 |
173 | public static int DokanRemoveMountPoint(string mountPoint)
174 | {
175 | return Dokan.DokanRemoveMountPoint(mountPoint);
176 | }
177 |
178 | public static uint DokanVersion()
179 | {
180 | return Dokan.DokanVersion();
181 | }
182 |
183 | public static uint DokanDriverVersion()
184 | {
185 | return Dokan.DokanDriveVersion();
186 | }
187 |
188 | public static bool DokanResetTimeout(uint timeout, DokanFileInfo fileinfo)
189 | {
190 | DOKAN_FILE_INFO rawFileInfo = new DOKAN_FILE_INFO();
191 | rawFileInfo.DokanContext = fileinfo.DokanContext;
192 | return Dokan.DokanResetTimeout(timeout, ref rawFileInfo);
193 | }
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/ZfsDokan/Dokan/DokanOperations.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Collections;
4 |
5 | namespace Dokan
6 | {
7 | public class DokanFileInfo
8 | {
9 | public Object Context;
10 | public bool IsDirectory;
11 | public ulong InfoId;
12 | public uint ProcessId;
13 | public bool DeleteOnClose;
14 | public bool PagingIo;
15 | public bool SynchronousIo;
16 | public bool Nocache;
17 | public bool WriteToEndOfFile;
18 | public readonly ulong DokanContext; // for internal use
19 |
20 | public DokanFileInfo(ulong dokanContext)
21 | {
22 | Context = null;
23 | IsDirectory = false;
24 | DeleteOnClose = false;
25 | PagingIo = false;
26 | SynchronousIo = false;
27 | Nocache = false;
28 | WriteToEndOfFile = false;
29 | InfoId = 0;
30 | DokanContext = dokanContext;
31 | }
32 | }
33 |
34 |
35 | public class FileInformation
36 | {
37 | public FileAttributes Attributes;
38 | public DateTime CreationTime;
39 | public DateTime LastAccessTime;
40 | public DateTime LastWriteTime;
41 | public long Length;
42 | public string FileName;
43 | /*
44 | public FileInformation()
45 | {
46 | Attributes = FileAttributes.Normal;
47 | Length = 0;
48 | }
49 | */
50 | }
51 |
52 | public interface DokanOperations
53 | {
54 | int CreateFile(
55 | string filename,
56 | FileAccess access,
57 | FileShare share,
58 | FileMode mode,
59 | FileOptions options,
60 | DokanFileInfo info);
61 |
62 | int OpenDirectory(
63 | string filename,
64 | DokanFileInfo info);
65 |
66 | int CreateDirectory(
67 | string filename,
68 | DokanFileInfo info);
69 |
70 | int Cleanup(
71 | string filename,
72 | DokanFileInfo info);
73 |
74 | int CloseFile(
75 | string filename,
76 | DokanFileInfo info);
77 |
78 | int ReadFile(
79 | string filename,
80 | byte[] buffer,
81 | ref uint readBytes,
82 | long offset,
83 | DokanFileInfo info);
84 |
85 | int WriteFile(
86 | string filename,
87 | byte[] buffer,
88 | ref uint writtenBytes,
89 | long offset,
90 | DokanFileInfo info);
91 |
92 | int FlushFileBuffers(
93 | string filename,
94 | DokanFileInfo info);
95 |
96 | int GetFileInformation(
97 | string filename,
98 | FileInformation fileinfo,
99 | DokanFileInfo info);
100 |
101 | int FindFiles(
102 | string filename,
103 | ArrayList files,
104 | DokanFileInfo info);
105 |
106 | int SetFileAttributes(
107 | string filename,
108 | FileAttributes attr,
109 | DokanFileInfo info);
110 |
111 | int SetFileTime(
112 | string filename,
113 | DateTime ctime,
114 | DateTime atime,
115 | DateTime mtime,
116 | DokanFileInfo info);
117 |
118 | int DeleteFile(
119 | string filename,
120 | DokanFileInfo info);
121 |
122 | int DeleteDirectory(
123 | string filename,
124 | DokanFileInfo info);
125 |
126 | int MoveFile(
127 | string filename,
128 | string newname,
129 | bool replace,
130 | DokanFileInfo info);
131 |
132 | int SetEndOfFile(
133 | string filename,
134 | long length,
135 | DokanFileInfo info);
136 |
137 | int SetAllocationSize(
138 | string filename,
139 | long length,
140 | DokanFileInfo info);
141 |
142 | int LockFile(
143 | string filename,
144 | long offset,
145 | long length,
146 | DokanFileInfo info);
147 |
148 | int UnlockFile(
149 | string filename,
150 | long offset,
151 | long length,
152 | DokanFileInfo info);
153 |
154 | int GetDiskFreeSpace(
155 | ref ulong freeBytesAvailable,
156 | ref ulong totalBytes,
157 | ref ulong totalFreeBytes,
158 | DokanFileInfo info);
159 |
160 | int Unmount(
161 | DokanFileInfo info);
162 |
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/ZfsDokan/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Dokan;
6 | using ZfsSharpLib;
7 |
8 | namespace ZfsDokan
9 | {
10 | class Program
11 | {
12 | static void Main(string[] args)
13 | {
14 | //args = new string[] { @"C:\VPC\SmartOs\" };
15 | if (args.Length == 0)
16 | {
17 | Console.WriteLine("Usage: ZfsSharp.exe ");
18 | return;
19 | }
20 |
21 | using (var zfs = new Zfs(args[0]))
22 | {
23 | DokanOptions opt = new DokanOptions();
24 | opt.MountPoint = "z:\\";
25 | opt.DebugMode = true;
26 | opt.UseStdErr = true;
27 | opt.VolumeLabel = "ZFS";
28 | int status = DokanNet.DokanMain(opt, new ZfsDokan(zfs));
29 | switch (status)
30 | {
31 | case DokanNet.DOKAN_DRIVE_LETTER_ERROR:
32 | Console.WriteLine("Drvie letter error");
33 | break;
34 | case DokanNet.DOKAN_DRIVER_INSTALL_ERROR:
35 | Console.WriteLine("Driver install error");
36 | break;
37 | case DokanNet.DOKAN_MOUNT_ERROR:
38 | Console.WriteLine("Mount error");
39 | break;
40 | case DokanNet.DOKAN_START_ERROR:
41 | Console.WriteLine("Start error");
42 | break;
43 | case DokanNet.DOKAN_ERROR:
44 | Console.WriteLine("Unknown error");
45 | break;
46 | case DokanNet.DOKAN_SUCCESS:
47 | Console.WriteLine("Success");
48 | break;
49 | default:
50 | Console.WriteLine("Unknown status: {0}", status);
51 | break;
52 |
53 | }
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/ZfsDokan/ZfsDokan.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Dokan;
6 | using System.Collections;
7 | using System.IO;
8 | using ZfsSharpLib;
9 |
10 | namespace ZfsDokan
11 | {
12 | class ZfsDokan : DokanOperations
13 | {
14 | private Dictionary mItems = new Dictionary(StringComparer.OrdinalIgnoreCase);
15 | private Dictionary mRevItems = new Dictionary();
16 |
17 | public ZfsDokan(Zfs zfs)
18 | {
19 | foreach (var ds in zfs.GetAllDataSets().Where(ds => ds.Type == DataSetType.ZFS && ds.Name.Contains("var")))
20 | {
21 |
22 | loadItems(ds.GetHeadZfs().Root);
23 |
24 | break;
25 | }
26 | }
27 |
28 | void loadItems(Zpl.ZfsItem root)
29 | {
30 | var path = root.FullPath.Replace('/', '\\');
31 | if (mItems.ContainsKey(path))
32 | return;
33 |
34 | if (!FilterItem(root))
35 | return;
36 |
37 | mItems.Add(path, root);
38 | mRevItems.Add(root, path);
39 |
40 | if (root is Zpl.ZfsDirectory)
41 | {
42 | foreach (var f in ((Zpl.ZfsDirectory)root).GetChildren())
43 | {
44 | loadItems(f);
45 | }
46 | }
47 | }
48 |
49 | static bool FilterItem(Zpl.ZfsItem item)
50 | {
51 | return item is Zpl.ZfsDirectory || (item is Zpl.ZfsFile && ((Zpl.ZfsFile)item).Type == ZfsItemType.S_IFREG);
52 | }
53 |
54 | public int CreateFile(string filename, FileAccess access, FileShare share, FileMode mode, FileOptions options, DokanFileInfo info)
55 | {
56 | if (!mItems.ContainsKey(filename))
57 | return -1;
58 | return 0;
59 | }
60 |
61 | public int OpenDirectory(string filename, DokanFileInfo info)
62 | {
63 | return 0;
64 | }
65 |
66 | public int CreateDirectory(string filename, DokanFileInfo info)
67 | {
68 | return -1;
69 | }
70 |
71 | public int Cleanup(string filename, DokanFileInfo info)
72 | {
73 | return 0;
74 | }
75 |
76 | public int CloseFile(string filename, DokanFileInfo info)
77 | {
78 | return 0;
79 | }
80 |
81 | public int ReadFile(string filename, byte[] buffer, ref uint readBytes, long offset, DokanFileInfo info)
82 | {
83 | //TODO
84 | if (!mItems.ContainsKey(filename))
85 | return -1;
86 |
87 | var item = mItems[filename] as Zpl.ZfsFile;
88 | if (item == null)
89 | return -1;
90 |
91 | if (offset >= item.Length || buffer.Length == 0)
92 | return -1;
93 |
94 | int bytesToRead = buffer.Length;
95 | if (offset + bytesToRead > item.Length)
96 | {
97 | bytesToRead = (int)(item.Length - offset);
98 | }
99 |
100 | item.GetContents(new Span(buffer, 0, bytesToRead), offset);
101 | readBytes = (uint)bytesToRead;
102 | return 0;
103 | }
104 |
105 | public int WriteFile(string filename, byte[] buffer, ref uint writtenBytes, long offset, DokanFileInfo info)
106 | {
107 | return -1;
108 | }
109 |
110 | public int FlushFileBuffers(string filename, DokanFileInfo info)
111 | {
112 | return -1;
113 | }
114 |
115 | public int GetFileInformation(string filename, FileInformation fileinfo, DokanFileInfo info)
116 | {
117 | if (!mItems.ContainsKey(filename))
118 | return -1;
119 |
120 | var item = mItems[filename];
121 |
122 | SetAttributes(item, fileinfo);
123 |
124 | return 0;
125 | }
126 |
127 | private static void SetAttributes(Zpl.ZfsItem item, FileInformation finfo)
128 | {
129 | if (item is Zpl.ZfsDirectory)
130 | finfo.Attributes = FileAttributes.Directory;
131 | else if (item is Zpl.ZfsFile)
132 | finfo.Attributes = FileAttributes.Normal;
133 | else
134 | throw new NotSupportedException();
135 |
136 | finfo.LastAccessTime = item.ATIME;
137 | finfo.LastWriteTime = item.MTIME;
138 | finfo.CreationTime = item.CTIME;
139 | if (item is Zpl.ZfsFile)
140 | {
141 | var zplFile = (Zpl.ZfsFile)item;
142 | finfo.Length = zplFile.Length;
143 | }
144 | }
145 |
146 | public int FindFiles(string filename, ArrayList files, DokanFileInfo info)
147 | {
148 | if (!mItems.ContainsKey(filename))
149 | return -1;
150 |
151 | var dir = mItems[filename] as Zpl.ZfsDirectory;
152 | if (dir == null)
153 | return -1;
154 |
155 | foreach (var item in dir.GetChildren().Where(FilterItem))
156 | {
157 | FileInformation finfo = new FileInformation();
158 | finfo.FileName = item.Name;
159 | SetAttributes(item, finfo);
160 | files.Add(finfo);
161 | }
162 |
163 | return 0;
164 | }
165 |
166 | public int SetFileAttributes(string filename, FileAttributes attr, DokanFileInfo info)
167 | {
168 | return -1;
169 | }
170 |
171 | public int SetFileTime(string filename, DateTime ctime, DateTime atime, DateTime mtime, DokanFileInfo info)
172 | {
173 | return -1;
174 | }
175 |
176 | public int DeleteFile(string filename, DokanFileInfo info)
177 | {
178 | return -1;
179 | }
180 |
181 | public int DeleteDirectory(string filename, DokanFileInfo info)
182 | {
183 | return -1;
184 | }
185 |
186 | public int MoveFile(string filename, string newname, bool replace, DokanFileInfo info)
187 | {
188 | return -1;
189 | }
190 |
191 | public int SetEndOfFile(string filename, long length, DokanFileInfo info)
192 | {
193 | return -1;
194 | }
195 |
196 | public int SetAllocationSize(string filename, long length, DokanFileInfo info)
197 | {
198 | return -1;
199 | }
200 |
201 | public int LockFile(string filename, long offset, long length, DokanFileInfo info)
202 | {
203 | return 0;
204 | }
205 |
206 | public int UnlockFile(string filename, long offset, long length, DokanFileInfo info)
207 | {
208 | return 0;
209 | }
210 |
211 | public int GetDiskFreeSpace(ref ulong freeBytesAvailable, ref ulong totalBytes, ref ulong totalFreeBytes, DokanFileInfo info)
212 | {
213 | freeBytesAvailable = 512 * 1024 * 1024;
214 | totalBytes = 1024 * 1024 * 1024;
215 | totalFreeBytes = 512 * 1024 * 1024;
216 | return 0;
217 | }
218 |
219 | public int Unmount(DokanFileInfo info)
220 | {
221 | return 0;
222 | }
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/ZfsDokan/ZfsDokan.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ZfsProjFs/Program.cs:
--------------------------------------------------------------------------------
1 | using Austin.WindowsProjectedFileSystem;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using ZfsSharpLib;
6 |
7 | namespace ZfsProjFs
8 | {
9 | class Program : IProjectedFileSystemCallbacks
10 | {
11 | Zfs mZfs;
12 | ProjectedFileSystem mProjFs;
13 | Dictionary mCache = new Dictionary();
14 |
15 | static void Usage()
16 | {
17 | Console.WriteLine("Usage: ZfsProjFs.exe ");
18 | Console.WriteLine(" zfs disk image directory: A directory containing VHD, VHDX, VDI, or ZFS files.");
19 | Console.WriteLine(" target directory: Wherte to mount the ZFS file system.");
20 | }
21 |
22 | static int Main(string[] args)
23 | {
24 | try
25 | {
26 | return new Program().Run(args);
27 | }
28 | catch (Exception ex)
29 | {
30 | Console.WriteLine("Program crashed horribly:");
31 | Console.WriteLine(ex);
32 | return 1;
33 | }
34 | }
35 |
36 | int Run(string[] args)
37 | {
38 | if (args.Length < 2)
39 | {
40 | Usage();
41 | return 1;
42 | }
43 |
44 | string zfsDiskDir = args[0];
45 | string targetDir = args[1];
46 |
47 | if (!(Directory.Exists(zfsDiskDir) || File.Exists(zfsDiskDir)) || !Directory.Exists(targetDir))
48 | {
49 | Console.WriteLine("Both directories must already exist.");
50 | Console.WriteLine();
51 | Usage();
52 | return 1;
53 | }
54 |
55 | if (Compatibility.Status != CompatibilityStatus.Supported)
56 | {
57 | Console.WriteLine("ProjectedFS is not supported: " + Compatibility.Status);
58 | return 1;
59 | }
60 |
61 | using (mZfs = new Zfs(zfsDiskDir))
62 | {
63 | populateDatasets("", mZfs.GetRootDataset());
64 | using (mProjFs = new ProjectedFileSystem(targetDir, this))
65 | {
66 | Console.WriteLine("Start virtualizing in " + targetDir);
67 | Console.WriteLine("Press enter to exit.");
68 | Console.ReadLine();
69 | }
70 | }
71 |
72 | return 0;
73 | }
74 |
75 | ProjectedItemInfo populateDatasets(string basePath, DatasetDirectory dsd)
76 | {
77 | var rootDir = dsd.GetHeadZfs().Root;
78 | var info = new ProjectedItemInfo()
79 | {
80 | ZfsItem = rootDir,
81 | ProjectedForm = new FileBasicInfo()
82 | {
83 | IsDirectory = true,
84 | Name = dsd.Name,
85 | },
86 | FullName = basePath,
87 | };
88 | mCache.Add(info.FullName, info);
89 |
90 | var childItems = getChildren(info.FullName, rootDir);
91 |
92 | var childDatasets = dsd.GetChildren();
93 | foreach (var childDs in childDatasets)
94 | {
95 | if (childDs.Value.Type != DataSetType.ZFS)
96 | continue;
97 | childItems.Add(populateDatasets(basePath == "" ? childDs.Key : basePath + "\\" + childDs.Key, childDs.Value));
98 | }
99 | info.Children = childItems.ToArray();
100 |
101 | return info;
102 | }
103 |
104 | List getChildren(string baseName, Zpl.ZfsDirectory dir)
105 | {
106 | var projectedChildren = new List();
107 | foreach (var c in dir.GetChildren())
108 | {
109 | var type = c.Type;
110 | if (type != ZfsItemType.S_IFREG && type != ZfsItemType.S_IFDIR)
111 | continue;
112 |
113 | var childInfo = new ProjectedItemInfo();
114 | childInfo.FullName = baseName == "" ? c.Name : baseName + "\\" + c.Name;
115 | childInfo.ProjectedForm = new FileBasicInfo()
116 | {
117 | Name = c.Name,
118 | IsDirectory = type == ZfsItemType.S_IFDIR,
119 | FileSize = type == ZfsItemType.S_IFREG ? ((Zpl.ZfsFile)c).Length : 0,
120 | Attributes = FileAttributes.ReadOnly,
121 | ChangeTime = c.MTIME,
122 | CreationTime = c.CTIME,
123 | LastAccessTime = c.ATIME,
124 | LastWriteTime = c.MTIME,
125 | };
126 | childInfo.ZfsItem = c;
127 | projectedChildren.Add(childInfo);
128 | lock (mCache)
129 | {
130 | mCache[childInfo.FullName] = childInfo;
131 | }
132 | }
133 | return projectedChildren;
134 | }
135 |
136 | public FileBasicInfo[] EnumerateDirectory(bool isWildCardExpression, string directory, string searchExpression)
137 | {
138 | var ret = new List();
139 |
140 | if (isWildCardExpression)
141 | {
142 | //TODO
143 | }
144 | else
145 | {
146 | ProjectedItemInfo info;
147 | lock (mCache)
148 | {
149 | mCache.TryGetValue(directory, out info);
150 | }
151 | if (info != null)
152 | {
153 | if (info.Children == null)
154 | {
155 | if (info.ZfsItem is Zpl.ZfsDirectory dir)
156 | {
157 | info.Children = getChildren(info.FullName, dir).ToArray();
158 | }
159 | else
160 | {
161 | info.Children = new ProjectedItemInfo[0];
162 | }
163 | }
164 |
165 | foreach (var c in info.Children)
166 | {
167 | if (mProjFs.FileNameMatch(c.FullName, searchExpression))
168 | ret.Add(c.ProjectedForm);
169 | }
170 | }
171 | }
172 | return ret.ToArray();
173 | }
174 |
175 | public bool FileExists(string fileName)
176 | {
177 | lock (mCache)
178 | {
179 | return mCache.ContainsKey(fileName);
180 | }
181 | }
182 |
183 | public FileBasicInfo QueryFileInfo(string fileName)
184 | {
185 | ProjectedItemInfo ret;
186 | lock (mCache)
187 | {
188 | mCache.TryGetValue(fileName, out ret);
189 | }
190 | return ret?.ProjectedForm;
191 | }
192 |
193 | public bool GetFileData(string fileName, byte[] buf, ulong offset, uint length)
194 | {
195 | ProjectedItemInfo ret;
196 | lock (mCache)
197 | {
198 | mCache.TryGetValue(fileName, out ret);
199 | }
200 | var file = ret?.ZfsItem as Zpl.ZfsFile;
201 | if (file == null)
202 | return false;
203 | checked
204 | {
205 | file.GetContents(new Span(buf, 0, (int)length), (long)offset);
206 | }
207 | return true;
208 | }
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/ZfsProjFs/ProjectedItemInfo.cs:
--------------------------------------------------------------------------------
1 | using Austin.WindowsProjectedFileSystem;
2 | using ZfsSharpLib;
3 |
4 | namespace ZfsProjFs
5 | {
6 | class ProjectedItemInfo
7 | {
8 | public string FullName;
9 | public FileBasicInfo ProjectedForm;
10 | public Zpl.ZfsItem ZfsItem;
11 | public ProjectedItemInfo[] Children;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ZfsProjFs/ZfsProjFs.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ZfsSharp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27703.2042
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZfsSharpLib", "ZfsSharpLib\ZfsSharpLib.csproj", "{6A0F1BE1-B663-4A9C-B57F-40602A5B221A}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZfsSharp", "ZfsSharp\ZfsSharp.csproj", "{2927DC5D-C5E3-4556-875C-C54BD77E052F}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZfsDokan", "ZfsDokan\ZfsDokan.csproj", "{DE824396-0149-4CC2-B5DF-E496EEFACE97}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZfsProjFs", "ZfsProjFs\ZfsProjFs.csproj", "{D517BADE-DA8C-4215-8486-40A994345E25}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Austin.WindowsProjectedFileSystem", "Austin.WindowsProjectedFileSystem\Austin.WindowsProjectedFileSystem.csproj", "{42E809F5-D3E8-43B7-BD0E-BA49FD73AD38}"
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Debug|x64 = Debug|x64
20 | Release|Any CPU = Release|Any CPU
21 | Release|x64 = Release|x64
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {6A0F1BE1-B663-4A9C-B57F-40602A5B221A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {6A0F1BE1-B663-4A9C-B57F-40602A5B221A}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {6A0F1BE1-B663-4A9C-B57F-40602A5B221A}.Debug|x64.ActiveCfg = Debug|Any CPU
27 | {6A0F1BE1-B663-4A9C-B57F-40602A5B221A}.Debug|x64.Build.0 = Debug|Any CPU
28 | {6A0F1BE1-B663-4A9C-B57F-40602A5B221A}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {6A0F1BE1-B663-4A9C-B57F-40602A5B221A}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {6A0F1BE1-B663-4A9C-B57F-40602A5B221A}.Release|x64.ActiveCfg = Release|Any CPU
31 | {6A0F1BE1-B663-4A9C-B57F-40602A5B221A}.Release|x64.Build.0 = Release|Any CPU
32 | {2927DC5D-C5E3-4556-875C-C54BD77E052F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {2927DC5D-C5E3-4556-875C-C54BD77E052F}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {2927DC5D-C5E3-4556-875C-C54BD77E052F}.Debug|x64.ActiveCfg = Debug|Any CPU
35 | {2927DC5D-C5E3-4556-875C-C54BD77E052F}.Debug|x64.Build.0 = Debug|Any CPU
36 | {2927DC5D-C5E3-4556-875C-C54BD77E052F}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {2927DC5D-C5E3-4556-875C-C54BD77E052F}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {2927DC5D-C5E3-4556-875C-C54BD77E052F}.Release|x64.ActiveCfg = Release|Any CPU
39 | {2927DC5D-C5E3-4556-875C-C54BD77E052F}.Release|x64.Build.0 = Release|Any CPU
40 | {DE824396-0149-4CC2-B5DF-E496EEFACE97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {DE824396-0149-4CC2-B5DF-E496EEFACE97}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {DE824396-0149-4CC2-B5DF-E496EEFACE97}.Debug|x64.ActiveCfg = Debug|Any CPU
43 | {DE824396-0149-4CC2-B5DF-E496EEFACE97}.Debug|x64.Build.0 = Debug|Any CPU
44 | {DE824396-0149-4CC2-B5DF-E496EEFACE97}.Release|Any CPU.ActiveCfg = Release|Any CPU
45 | {DE824396-0149-4CC2-B5DF-E496EEFACE97}.Release|Any CPU.Build.0 = Release|Any CPU
46 | {DE824396-0149-4CC2-B5DF-E496EEFACE97}.Release|x64.ActiveCfg = Release|Any CPU
47 | {DE824396-0149-4CC2-B5DF-E496EEFACE97}.Release|x64.Build.0 = Release|Any CPU
48 | {D517BADE-DA8C-4215-8486-40A994345E25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {D517BADE-DA8C-4215-8486-40A994345E25}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {D517BADE-DA8C-4215-8486-40A994345E25}.Debug|x64.ActiveCfg = Debug|Any CPU
51 | {D517BADE-DA8C-4215-8486-40A994345E25}.Debug|x64.Build.0 = Debug|Any CPU
52 | {D517BADE-DA8C-4215-8486-40A994345E25}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 | {D517BADE-DA8C-4215-8486-40A994345E25}.Release|Any CPU.Build.0 = Release|Any CPU
54 | {D517BADE-DA8C-4215-8486-40A994345E25}.Release|x64.ActiveCfg = Release|Any CPU
55 | {D517BADE-DA8C-4215-8486-40A994345E25}.Release|x64.Build.0 = Release|Any CPU
56 | {42E809F5-D3E8-43B7-BD0E-BA49FD73AD38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57 | {42E809F5-D3E8-43B7-BD0E-BA49FD73AD38}.Debug|Any CPU.Build.0 = Debug|Any CPU
58 | {42E809F5-D3E8-43B7-BD0E-BA49FD73AD38}.Debug|x64.ActiveCfg = Debug|Any CPU
59 | {42E809F5-D3E8-43B7-BD0E-BA49FD73AD38}.Debug|x64.Build.0 = Debug|Any CPU
60 | {42E809F5-D3E8-43B7-BD0E-BA49FD73AD38}.Release|Any CPU.ActiveCfg = Release|Any CPU
61 | {42E809F5-D3E8-43B7-BD0E-BA49FD73AD38}.Release|Any CPU.Build.0 = Release|Any CPU
62 | {42E809F5-D3E8-43B7-BD0E-BA49FD73AD38}.Release|x64.ActiveCfg = Release|Any CPU
63 | {42E809F5-D3E8-43B7-BD0E-BA49FD73AD38}.Release|x64.Build.0 = Release|Any CPU
64 | EndGlobalSection
65 | GlobalSection(SolutionProperties) = preSolution
66 | HideSolutionNode = FALSE
67 | EndGlobalSection
68 | GlobalSection(ExtensibilityGlobals) = postSolution
69 | SolutionGuid = {D6D77C51-8677-402C-A63B-F7E96FFA9BD3}
70 | EndGlobalSection
71 | GlobalSection(Performance) = preSolution
72 | HasPerformanceSessions = true
73 | EndGlobalSection
74 | EndGlobal
75 |
--------------------------------------------------------------------------------
/ZfsSharp/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using ZfsSharpLib;
8 |
9 | namespace ZfsSharp
10 | {
11 | class Program
12 | {
13 | static void Main(string[] args)
14 | {
15 | if (args.Length == 0)
16 | {
17 | Console.WriteLine("Usage: ZfsSharp.exe ");
18 | return;
19 | }
20 |
21 | var sw = Stopwatch.StartNew();
22 | sw.Stop();
23 |
24 | using (var zfs = new Zfs(args[0]))
25 | {
26 | sw = Stopwatch.StartNew();
27 | foreach (var ds in zfs.GetAllDataSets())
28 | {
29 | Console.WriteLine("{0}: {1}", ds.Type, ds.Name);
30 |
31 | if (ds.Type != DataSetType.ZFS)
32 | continue;
33 |
34 | var zpl = ds.GetHeadZfs();
35 | printContent(ds.Name, zpl.Root);
36 |
37 | if (ds.Name == "zones/var")
38 | Console.WriteLine(Encoding.ASCII.GetString(zpl.GetFileContents(@"/svc/log/svc.startd.log")));
39 |
40 | foreach (var snap in ds.GetZfsSnapShots())
41 | {
42 | var snapName = ds.Name + "@" + snap.Key;
43 | Console.WriteLine(snapName);
44 | printContent(snapName, snap.Value.Root);
45 | }
46 | }
47 | sw.Stop();
48 | Console.WriteLine("time: " + sw.ElapsedMilliseconds);
49 | }
50 |
51 | Console.WriteLine();
52 | }
53 |
54 | private static void DumpContents(string outPath, Zpl.ZfsItem item)
55 | {
56 | Console.WriteLine(item.FullPath);
57 | var dir = item as Zpl.ZfsDirectory;
58 | var file = item as Zpl.ZfsFile;
59 |
60 | var dest = Path.Combine(outPath, item.FullPath.Substring(1));
61 |
62 | if (file != null)
63 | {
64 | File.WriteAllBytes(dest, file.GetContents());
65 | }
66 |
67 | if (dir == null)
68 | return;
69 | if (!Directory.Exists(dest))
70 | Directory.CreateDirectory(dest);
71 | foreach (var d in dir.GetChildren())
72 | {
73 | DumpContents(outPath, d);
74 | }
75 | }
76 |
77 | private static void BenchmarkFileReading(Zfs zfs)
78 | {
79 | var varzpl = zfs.GetAllDataSets().Where(k => k.Name == "zones/var").Select(ds => ds.GetHeadZfs()).Single();
80 | Stopwatch st = Stopwatch.StartNew();
81 | for (int i = 0; i < 1000; i++)
82 | {
83 | varzpl.GetFileContents(@"/svc/log/svc.startd.log");
84 | }
85 | st.Stop();
86 |
87 | Console.WriteLine(st.Elapsed.TotalSeconds);
88 | }
89 |
90 | static void printContent(string namePrefix, Zpl.ZfsItem item)
91 | {
92 | //Console.WriteLine(namePrefix + item.FullPath);
93 | var dir = item as Zpl.ZfsDirectory;
94 | var file = item as Zpl.ZfsFile;
95 |
96 | if (file != null)
97 | {
98 | int length = (int)file.Length;
99 | byte[] bytes = ArrayPool.Shared.Rent(length);
100 | file.GetContents(new Span(bytes, 0, length), 0);
101 | ArrayPool.Shared.Return(bytes);
102 | }
103 |
104 | if (dir == null)
105 | return;
106 | foreach (var d in dir.GetChildren())
107 | {
108 | printContent(namePrefix, d);
109 | }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/ZfsSharp/ZfsSharp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ZfsSharpLib/-/System/DataStructures/AvlTreeNode.cs:
--------------------------------------------------------------------------------
1 | namespace System.DataStructures
2 | {
3 | ///
4 | /// Node used by .
5 | ///
6 | /// Type of the data stored in the node.
7 | internal class AvlTreeNode : IBinaryTreeNode, T>
8 | {
9 | ///
10 | /// Initializes a new instance of the class.
11 | ///
12 | /// Value of node
13 | public AvlTreeNode(T value)
14 | {
15 | Value = value;
16 | Height = 1;
17 | }
18 |
19 | ///
20 | /// Gets the height of the node.
21 | ///
22 | public int Height { get; internal set; }
23 |
24 | ///
25 | /// Gets or sets the left node reference.
26 | ///
27 | public AvlTreeNode Left { get; set; }
28 |
29 | ///
30 | /// Gets or sets the right node reference.
31 | ///
32 | public AvlTreeNode Right { get; set; }
33 |
34 | ///
35 | /// Gets or sets the value of the node.
36 | ///
37 | public T Value { get; set; }
38 | }
39 | }
--------------------------------------------------------------------------------
/ZfsSharpLib/-/System/DataStructures/BinaryTreeNode.cs:
--------------------------------------------------------------------------------
1 | namespace System.DataStructures
2 | {
3 | ///
4 | /// Interface for the nodes that are used in a .
5 | ///
6 | /// Type of the node.
7 | /// Type of the value.
8 | ///
9 | internal interface IBinaryTreeNode
10 | {
11 | ///
12 | /// Gets or sets the left node reference.
13 | ///
14 | /// The left.
15 | ///
16 | TNode Left { get; set; }
17 |
18 | ///
19 | /// Gets or sets the right node reference.
20 | ///
21 | /// The right.
22 | ///
23 | TNode Right { get; set; }
24 |
25 | ///
26 | /// Gets or sets the value of the .
27 | ///
28 | /// The value.
29 | ///
30 | TValue Value { get; set; }
31 | }
32 |
33 | ///
34 | /// Node used by a binary tree.
35 | ///
36 | /// Type of the node.
37 | ///
38 | internal class BinaryTreeNode : IBinaryTreeNode, T>
39 | {
40 | ///
41 | /// Initializes a new instance of the class.
42 | ///
43 | /// The value.
44 | ///
45 | public BinaryTreeNode(T value) {
46 | Value = value;
47 | }
48 |
49 | ///
50 | /// Gets or sets the left node reference.
51 | ///
52 | /// The left.
53 | ///
54 | public BinaryTreeNode Left { get; set; }
55 |
56 | ///
57 | /// Gets or sets the right node reference.
58 | ///
59 | /// The right.
60 | ///
61 | public BinaryTreeNode Right { get; set; }
62 |
63 | ///
64 | /// Gets or sets the value of the node.
65 | ///
66 | /// The value.
67 | ///
68 | public T Value { get; set; }
69 | }
70 | }
--------------------------------------------------------------------------------
/ZfsSharpLib/-/System/DataStructures/FuncComparer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace System.DataStructures
4 | {
5 | ///
6 | /// Compares various functions of two objects
7 | ///
8 | /// The type of function comparer
9 | internal class FuncComparer : IComparer, IEqualityComparer
10 | {
11 | private readonly Func _compare;
12 |
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The comparer.
17 | public FuncComparer(Func comparer)
18 | {
19 | _compare = comparer;
20 | }
21 |
22 | ///
23 | /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
24 | ///
25 | /// The first object to compare.
26 | /// The second object to compare.
27 | ///
28 | /// Value Condition Less than zero is less than .Zero equals .Greater than zero is greater than .
29 | ///
30 | public int Compare(T x, T y)
31 | {
32 | return _compare(x, y);
33 | }
34 |
35 | ///
36 | /// Determines whether the specified objects are equal.
37 | ///
38 | /// The first object of type T to compare.
39 | /// The second object of type T to compare.
40 | ///
41 | /// true if the specified objects are equal; otherwise, false.
42 | ///
43 | public bool Equals(T x, T y)
44 | {
45 | return (_compare(x, y) == 0);
46 | }
47 |
48 | ///
49 | /// Returns a hash code for the specified object.
50 | ///
51 | /// The for which a hash code is to be returned.
52 | /// A hash code for the specified object.
53 | /// The type of is a reference type and is null.
54 | public int GetHashCode(T obj)
55 | {
56 | return obj.GetHashCode();
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/ZfsSharpLib/-/Throw.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 |
5 | internal static class Throw
6 | {
7 | [DebuggerStepThrough]
8 | internal static void ArgumentEmptyException(string paramName)
9 | {
10 | throw new ArgumentException(string.Format("{0} cannot be empty.", paramName), paramName);
11 | }
12 |
13 | [DebuggerStepThrough]
14 | internal static void ArgumentNullException(string paramName)
15 | {
16 | throw new ArgumentNullException(paramName, string.Format("{0} cannot be null.", paramName));
17 | }
18 |
19 | [DebuggerStepThrough]
20 | internal static void Exception(string message)
21 | {
22 | throw new Exception(message);
23 | }
24 |
25 | [DebuggerStepThrough]
26 | internal static void FileNotFoundException(string filename)
27 | {
28 | string message = string.Format("File [{0}] was not found.", filename);
29 | #if !CompactFramework && !SILVERLIGHT
30 | throw new FileNotFoundException(message, filename);
31 | #else
32 | throw new FileNotFoundException(message);
33 | #endif
34 | }
35 |
36 | [DebuggerStepThrough]
37 | internal static void GuardAgainstFailure(string message)
38 | {
39 | throw new Exception(string.Format("Guard Against: {0}", message));
40 | }
41 |
42 | [DebuggerStepThrough]
43 | internal static void GuardAssertFailure(string message)
44 | {
45 | throw new Exception(string.Format("Guard Assert: {0}", message));
46 | }
47 |
48 | [DebuggerStepThrough]
49 | internal static void GuardFailureNotGreaterThan(string name, T value, T minimum)
50 | {
51 | string message = string.Format("Guard Greater: {0} is '{1}' but must be greater than '{2}'.", name, value, minimum);
52 | throw new Exception(message);
53 | }
54 |
55 | [DebuggerStepThrough]
56 | internal static void GuardFailureNotGreaterThanOrEqualTo(string name, T value, T minimum)
57 | {
58 | string message = string.Format("Guard Greater or Equal: {0} is '{1}' but must be greater than or equal to '{2}'.",
59 | name,
60 | value,
61 | minimum);
62 | throw new Exception(message);
63 | }
64 |
65 | [DebuggerStepThrough]
66 | internal static void GuardFailureNotLessThan(string name, T value, T maximum)
67 | {
68 | string message = string.Format("Guard Less: {0} is '{1}' but must be less than '{2}'.", name, value, maximum);
69 | throw new Exception(message);
70 | }
71 |
72 | [DebuggerStepThrough]
73 | internal static void GuardFailureNotLessThanOrEqualTo(string name, T value, T maximum)
74 | {
75 | string message = string.Format("Guard Less or Equal: {0} is '{1}' but must be less than or equal to '{2}'.",
76 | name,
77 | value,
78 | maximum);
79 | throw new Exception(message);
80 | }
81 |
82 | [DebuggerStepThrough]
83 | internal static void GuardTypeAssignmentFailure(Type typeToAssign, Type targetType, string name)
84 | {
85 | string message = string.Format("Guard Type Assignment: {0} is '{1}' but does not {2} '{3}'.",
86 | name,
87 | typeToAssign,
88 | targetType.IsInterface ? "implement required interface" : "convert to required type",
89 | targetType);
90 | throw new Exception(message);
91 | }
92 | }
--------------------------------------------------------------------------------
/ZfsSharpLib/CRC.cs:
--------------------------------------------------------------------------------
1 | /* This is .NET safe implementation of Crc32C algorithm.
2 | * This implementation was found fastest from some variants, based on Robert Vazan native implementations
3 | * Also, it is good for x64 and for x86, so, it seems, there is no sense to do 2 different realizations.
4 | * Reference speed: Hardware: 20GB/s, Software Native: 2GB/s, this: 1GB/s
5 | *
6 | * Max Vysokikh, 2016
7 | */
8 |
9 | namespace Crc32C
10 | {
11 | static class CRC
12 | {
13 | private const uint Poly = 0x82f63b78;
14 |
15 | private static readonly uint[] _table = new uint[16 * 256];
16 |
17 | static CRC()
18 | {
19 | uint[] table = _table;
20 | for (uint i = 0; i < 256; i++)
21 | {
22 | uint res = i;
23 | for (int t = 0; t < 16; t++)
24 | {
25 | for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? Poly ^ (res >> 1) : (res >> 1);
26 | table[(t * 256) + i] = res;
27 | }
28 | }
29 | }
30 |
31 | public static uint Compute(byte[] bytes)
32 | {
33 | return Append(0, bytes, 0, bytes.Length);
34 | }
35 |
36 | static uint Append(uint crc, byte[] input, int offset, int length)
37 | {
38 | uint crcLocal = uint.MaxValue ^ crc;
39 |
40 | uint[] table = _table;
41 | while (length >= 16)
42 | {
43 | crcLocal = table[(15 * 256) + ((crcLocal ^ input[offset]) & 0xff)]
44 | ^ table[(14 * 256) + (((crcLocal >> 8) ^ input[offset + 1]) & 0xff)]
45 | ^ table[(13 * 256) + (((crcLocal >> 16) ^ input[offset + 2]) & 0xff)]
46 | ^ table[(12 * 256) + (((crcLocal >> 24) ^ input[offset + 3]) & 0xff)]
47 | ^ table[(11 * 256) + input[offset + 4]]
48 | ^ table[(10 * 256) + input[offset + 5]]
49 | ^ table[(9 * 256) + input[offset + 6]]
50 | ^ table[(8 * 256) + input[offset + 7]]
51 | ^ table[(7 * 256) + input[offset + 8]]
52 | ^ table[(6 * 256) + input[offset + 9]]
53 | ^ table[(5 * 256) + input[offset + 10]]
54 | ^ table[(4 * 256) + input[offset + 11]]
55 | ^ table[(3 * 256) + input[offset + 12]]
56 | ^ table[(2 * 256) + input[offset + 13]]
57 | ^ table[(1 * 256) + input[offset + 14]]
58 | ^ table[(0 * 256) + input[offset + 15]];
59 | offset += 16;
60 | length -= 16;
61 | }
62 |
63 | while (--length >= 0)
64 | crcLocal = table[(crcLocal ^ input[offset++]) & 0xff] ^ crcLocal >> 8;
65 | return crcLocal ^ uint.MaxValue;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Checksum/Flecter4.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace ZfsSharpLib
5 | {
6 | class Flecter4 : IChecksum
7 | {
8 | public unsafe zio_cksum_t Calculate(ArraySegment input)
9 | {
10 | if (input.Count % 4 != 0)
11 | throw new ArgumentException("Input must have a length that is a multiple of 4.");
12 |
13 | ulong a, b, c, d;
14 | a = b = c = d = 0;
15 | fixed (byte* ptr = input.Array)
16 | {
17 | int size = input.Count / 4;
18 | uint* intPtr = (uint*)(ptr + input.Offset);
19 | for (int i = 0; i < size; i++)
20 | {
21 | a += intPtr[i];
22 | b += a;
23 | c += b;
24 | d += c;
25 | }
26 | }
27 |
28 | zio_cksum_t ret = new zio_cksum_t()
29 | {
30 | word1 = a,
31 | word2 = b,
32 | word3 = c,
33 | word4 = d
34 | };
35 | return ret;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Checksum/NoChecksum.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ZfsSharpLib
4 | {
5 | class NoChecksum : IChecksum
6 | {
7 | public zio_cksum_t Calculate(ArraySegment input)
8 | {
9 | return new zio_cksum_t();
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Checksum/Sha256.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Cryptography;
3 |
4 | namespace ZfsSharpLib
5 | {
6 | class Sha256 : IChecksum
7 | {
8 | public zio_cksum_t Calculate(ArraySegment input)
9 | {
10 | byte[] checksumBytes;
11 | using (var sha = SHA256.Create())
12 | {
13 | checksumBytes = sha.ComputeHash(input.Array, input.Offset, input.Count);
14 | }
15 | return Program.ToStructByteSwap(checksumBytes);
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Compression/GZip.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ZfsSharpLib
4 | {
5 | class GZip : ICompression
6 | {
7 | public void Decompress(Span input, Span output)
8 | {
9 | //GZip is not very common,
10 | //so I'm dropping support for it as part of the Span<> upgrade.
11 | throw new NotImplementedException();
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Compression/LZ4.cs:
--------------------------------------------------------------------------------
1 | using LZ4ps;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace ZfsSharpLib
8 | {
9 | class LZ4 : ICompression
10 | {
11 | public void Decompress(Span input, Span output)
12 | {
13 | var bufsiz = input[0] << 24 | input[1] << 16 | input[2] << 8 | input[3];
14 | if (bufsiz + 4 > input.Length || bufsiz < 0)
15 | throw new ArgumentOutOfRangeException("Not enough bytes in input.");
16 | int ret = LZ4Codec.Decode64(input, 4, bufsiz, output, 0, output.Length, true);
17 | if (ret != output.Length)
18 | throw new Exception("Did not decompress the right number of bytes.");
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Compression/Lzjb.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ZfsSharpLib
4 | {
5 | class Lzjb : ICompression
6 | {
7 | const int NBBY = 8;
8 |
9 | const int MATCH_BITS = 6;
10 | const int MATCH_MIN = 3;
11 | const int MATCH_MAX = ((1 << MATCH_BITS) + (MATCH_MIN - 1));
12 | const int OFFSET_MASK = ((1 << (16 - MATCH_BITS)) - 1);
13 | const int LEMPEL_SIZE = 1024;
14 |
15 | public unsafe void Decompress(Span input, Span output)
16 | {
17 | fixed (byte* s_start = input)
18 | {
19 | fixed (byte* d_start = output)
20 | {
21 | int ret = lzjb_decompress(s_start, d_start, input.Length, output.Length, 0);
22 | if (ret != 0)
23 | throw new Exception($"LZJB failed to decompress: {ret}");
24 | }
25 | }
26 | }
27 | unsafe int lzjb_decompress(byte* s_start, byte* d_start, long s_len, long d_len, int n)
28 | {
29 | byte* src = s_start;
30 | byte* dst = d_start;
31 | byte* d_end = d_start + d_len;
32 | byte* cpy;
33 | byte copymap = 0;
34 | int copymask = 1 << (NBBY - 1);
35 |
36 | while (dst < d_end)
37 | {
38 | if ((copymask <<= 1) == (1 << NBBY))
39 | {
40 | copymask = 1;
41 | copymap = *src++;
42 | }
43 | if ((int)(copymap & copymask) != 0)
44 | {
45 | int mlen = (src[0] >> (NBBY - MATCH_BITS)) + MATCH_MIN;
46 | int offset = ((src[0] << NBBY) | src[1]) & OFFSET_MASK;
47 | src += 2;
48 | if ((cpy = dst - offset) < d_start)
49 | return (-1);
50 | while (--mlen >= 0 && dst < d_end)
51 | *dst++ = *cpy++;
52 | }
53 | else
54 | {
55 | *dst++ = *src++;
56 | }
57 | }
58 | return (0);
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Compression/NoCompression.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ZfsSharpLib
4 | {
5 | class NoCompression : ICompression
6 | {
7 | public void Decompress(Span input, Span output)
8 | {
9 | input.CopyTo(output);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ZfsSharpLib/DNode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace ZfsSharpLib
5 | {
6 | class DNode
7 | {
8 | readonly Zio mZio;
9 | readonly Func mGetBlockKey;
10 | readonly Program.BlockReader mReadBlock;
11 | dnode_phys_t mPhys;
12 |
13 | public DNode(Zio zio, dnode_phys_t phys)
14 | {
15 | if (phys.NLevels == 0)
16 | throw new ArgumentOutOfRangeException(nameof(phys), "Expect dnode's NLevels to be > 0");
17 |
18 | mZio = zio;
19 | mGetBlockKey = getBlockKey;
20 | mReadBlock = readBlock;
21 | mPhys = phys;
22 | }
23 |
24 | public dmu_object_type_t Type
25 | {
26 | get { return mPhys.Type; }
27 | }
28 |
29 | public dmu_object_type_t BonusType
30 | {
31 | get { return mPhys.BonusType; }
32 | }
33 |
34 | public bool IsNewType
35 | {
36 | get { return mPhys.IsNewType; }
37 | }
38 |
39 | public dmu_object_byteswap NewType
40 | {
41 | get { return mPhys.NewType; }
42 | }
43 |
44 | public int BlockSizeInBytes
45 | {
46 | get { return mPhys.BlockSizeInBytes; }
47 | }
48 |
49 | ///
50 | /// Maximum amount of data that can be read from this DNode.
51 | ///
52 | public long AvailableDataSize
53 | {
54 | get { return mPhys.AvailableDataSize; }
55 | }
56 |
57 | public DnodeFlags Flags
58 | {
59 | get { return mPhys.Flags; }
60 | }
61 |
62 | public dmu_object_type_t SpillType
63 | {
64 | get { return (mPhys.Flags & DnodeFlags.SpillBlkptr) == 0 ? dmu_object_type_t.NONE : mPhys.Spill.Type; }
65 | }
66 |
67 | public int SpillSize
68 | {
69 | get
70 | {
71 | if ((mPhys.Flags & DnodeFlags.SpillBlkptr) == 0)
72 | {
73 | throw new NotSupportedException("DNode does not have a spill block pointer.");
74 | }
75 | return mPhys.Spill.LogicalSizeBytes;
76 | }
77 | }
78 |
79 | unsafe void CalculateBonusSize(out int bonusOffset, out int maxBonusSize)
80 | {
81 | if (mPhys.BonusType == dmu_object_type_t.NONE)
82 | throw new Exception("No bonus type.");
83 |
84 | bonusOffset = (mPhys.NBlkPtrs - 1) * sizeof(blkptr_t);
85 | maxBonusSize = dnode_phys_t.DN_MAX_BONUSLEN - bonusOffset;
86 | if ((mPhys.Flags & DnodeFlags.SpillBlkptr) != 0)
87 | {
88 | maxBonusSize -= sizeof(blkptr_t);
89 | }
90 | }
91 |
92 | unsafe public T GetBonus() where T : struct
93 | {
94 | Type t = typeof(T);
95 | int structSize = Program.SizeOf();
96 | int bonusOffset;
97 | int maxBonusSize;
98 | CalculateBonusSize(out bonusOffset, out maxBonusSize);
99 |
100 | if (structSize > maxBonusSize)
101 | throw new ArgumentOutOfRangeException();
102 | if (structSize > mPhys.BonusLen)
103 | throw new ArgumentOutOfRangeException();
104 |
105 | fixed (byte* pBonus = mPhys.Bonus)
106 | {
107 | return Program.ToStruct(pBonus, bonusOffset, maxBonusSize);
108 | }
109 | }
110 |
111 | public void ReadSpill(Span dest)
112 | {
113 | if ((mPhys.Flags & DnodeFlags.SpillBlkptr) == 0)
114 | {
115 | throw new NotSupportedException("DNode does not have a spill block pointer.");
116 | }
117 |
118 | var spill = mPhys.Spill;
119 | if (spill.fill != 1)
120 | {
121 | throw new NotImplementedException("Only spill pointers with fill = 1 supported.");
122 | }
123 |
124 | mZio.Read(spill, dest);
125 | }
126 |
127 | ///
128 | ///
129 | ///
130 | /// A buffer that should be return to
131 | unsafe public ArraySegment RentBonus()
132 | {
133 | int bonusOffset;
134 | int maxBonusSize;
135 | CalculateBonusSize(out bonusOffset, out maxBonusSize);
136 |
137 | if (mPhys.BonusLen > maxBonusSize)
138 | throw new Exception("Specified bonus size is larger than the dnode can hold.");
139 |
140 | var bonus = Program.RentBytes(mPhys.BonusLen);
141 | fixed (byte* pBonus = mPhys.Bonus)
142 | {
143 | Marshal.Copy(new IntPtr(pBonus + bonusOffset), bonus.Array, bonus.Offset, bonus.Count);
144 | }
145 | return bonus;
146 | }
147 |
148 | public byte[] Read(long offset, int size)
149 | {
150 | var ret = new byte[size];
151 | Read(ret, offset, size);
152 | return ret;
153 | }
154 |
155 | public void Read(byte[] buffer, long offset, int size)
156 | {
157 | Read(new Span(buffer, 0, size), offset);
158 | }
159 |
160 | public void Read(ArraySegment dest, long offset)
161 | {
162 | Read((Span)dest, offset);
163 | }
164 |
165 | public void Read(Span dest, long offset)
166 | {
167 | if (offset < 0 || dest.Length < 0)
168 | throw new ArgumentOutOfRangeException();
169 | if ((offset + dest.Length) > mPhys.AvailableDataSize)
170 | throw new ArgumentOutOfRangeException();
171 |
172 | Program.MultiBlockCopy(dest, offset, mPhys.BlockSizeInBytes, mGetBlockKey, mReadBlock);
173 | }
174 |
175 | private void readBlock(Span dest, blkptr_t blkptr, int startNdx)
176 | {
177 | if (blkptr.IsHole)
178 | return;
179 | int logicalBlockSize = blkptr.LogicalSizeBytes;
180 | if (logicalBlockSize == dest.Length)
181 | {
182 | mZio.Read(blkptr, dest);
183 | }
184 | else
185 | {
186 | var src = Program.RentBytes(logicalBlockSize);
187 | mZio.Read(blkptr, src);
188 | new Span(src.Array, src.Offset + startNdx, dest.Length).CopyTo(dest);
189 | Program.ReturnBytes(src);
190 | }
191 | }
192 |
193 | private blkptr_t getBlockKey(long blockId)
194 | {
195 | int indirBlockShift = mPhys.IndirectBlockShift - blkptr_t.SPA_BLKPTRSHIFT;
196 | int indirMask = (1 << indirBlockShift) - 1;
197 | int indirSize = 1 << mPhys.IndirectBlockShift;
198 |
199 | blkptr_t ptr = default(blkptr_t);
200 |
201 | for (int i = 0; i < mPhys.NLevels; i++)
202 | {
203 | int indirectNdx = (int)(blockId >> ((mPhys.NLevels - i - 1) * indirBlockShift)) & indirMask;
204 | if (i == 0)
205 | {
206 | ptr = mPhys.GetBlkptr(indirectNdx);
207 | }
208 | else
209 | {
210 | var indirBlockRent = Program.RentBytes(indirSize);
211 | Span indirBlock = indirBlockRent;
212 | mZio.Read(ptr, indirBlock);
213 | const int BP_SIZE = 1 << blkptr_t.SPA_BLKPTRSHIFT;
214 | ptr = Program.ToStruct(indirBlock.Slice(indirectNdx * BP_SIZE, BP_SIZE));
215 | Program.ReturnBytes(indirBlockRent);
216 | }
217 |
218 | if (ptr.IsHole)
219 | break;
220 | }
221 |
222 | return ptr;
223 | }
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/ZfsSharpLib/DataSetType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace ZfsSharpLib
7 | {
8 | public enum DataSetType
9 | {
10 | MetaData,
11 | ZFS,
12 | ZVOL,
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ZfsSharpLib/DatasetDirectory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace ZfsSharpLib
6 | {
7 | public class DatasetDirectory
8 | {
9 | private ObjectSet mMos;
10 | private Zio mZio;
11 | private dsl_dir_phys_t mDslDir;
12 | private Dictionary mSnapShots = new Dictionary();
13 |
14 | internal DatasetDirectory(ObjectSet mos, long objectid, string name, Zio zio)
15 | {
16 | this.mMos = mos;
17 | this.mZio = zio;
18 | this.Name = name;
19 | this.Type = DataSetType.MetaData;
20 |
21 | var rootDslObj = mos.ReadEntry(objectid);
22 | if (rootDslObj.Type != dmu_object_type_t.DSL_DIR)
23 | throw new NotSupportedException("Expected DSL_DIR dnode.");
24 | if (rootDslObj.BonusType != dmu_object_type_t.DSL_DIR)
25 | throw new NotSupportedException("Expected DSL_DIR bonus.");
26 | mDslDir = rootDslObj.GetBonus();
27 | var rootDslProps = Zap.Parse(mos, mDslDir.props_zapobj);
28 |
29 | Dictionary clones;
30 | if (mDslDir.clones != 0)
31 | {
32 | clones = Zap.GetDirectoryEntries(mos, mDslDir.clones);
33 | }
34 |
35 | if (mDslDir.head_dataset_obj == 0)
36 | return; //probably meta data, like $MOS or $FREE
37 | var rootDataSetObj = mos.ReadEntry(mDslDir.head_dataset_obj);
38 | if (!IsDataSet(rootDataSetObj))
39 | throw new Exception("Not a dataset!");
40 | if (rootDataSetObj.BonusType != dmu_object_type_t.DSL_DATASET)
41 | throw new Exception("Missing dataset bonus!");
42 | var headDs = rootDataSetObj.GetBonus();
43 |
44 | if (headDs.bp.IsHole && mDslDir.origin_obj == 0)
45 | return; //this is $ORIGIN
46 |
47 | if (headDs.snapnames_zapobj != 0)
48 | {
49 | mSnapShots = Zap.GetDirectoryEntries(mMos, headDs.snapnames_zapobj);
50 | }
51 |
52 | if (headDs.bp.Type != dmu_object_type_t.OBJSET)
53 | throw new Exception("Expected OBJSET.");
54 | var headDsObjset = zio.Get(headDs.bp);
55 | switch (headDsObjset.Type)
56 | {
57 | case dmu_objset_type_t.DMU_OST_ZFS:
58 | this.Type = DataSetType.ZFS;
59 | break;
60 | case dmu_objset_type_t.DMU_OST_ZVOL:
61 | this.Type = DataSetType.ZVOL;
62 | break;
63 | default:
64 | throw new Exception("Unknow dataset type: " + headDsObjset.Type.ToString());
65 | }
66 | }
67 |
68 | public DataSetType Type { get; private set; }
69 | public string Name { get; private set; }
70 |
71 | public Zpl GetHeadZfs()
72 | {
73 | return GetZfs(mDslDir.head_dataset_obj);
74 | }
75 |
76 | private Zpl GetZfs(long objectid)
77 | {
78 | return new Zpl(mMos, objectid, mZio);
79 | }
80 |
81 | public IEnumerable> GetZfsSnapShots()
82 | {
83 | return mSnapShots.Select(snap => new KeyValuePair(snap.Key, GetZfs(snap.Value)));
84 | }
85 |
86 | internal IEnumerable> GetChildIds()
87 | {
88 | if (mDslDir.child_dir_zapobj == 0)
89 | return Enumerable.Empty>();
90 | return Zap.GetDirectoryEntries(mMos, mDslDir.child_dir_zapobj);
91 | }
92 |
93 | public Dictionary GetChildren()
94 | {
95 | var ret = new Dictionary();
96 | if (mDslDir.child_dir_zapobj != 0)
97 | {
98 | foreach (var child in Zap.GetDirectoryEntries(mMos, mDslDir.child_dir_zapobj))
99 | {
100 | ret.Add(child.Key, new DatasetDirectory(mMos, child.Value, child.Key, mZio));
101 | }
102 | }
103 | return ret;
104 | }
105 |
106 | internal static bool IsDataSet(DNode dn)
107 | {
108 | return dn.Type == dmu_object_type_t.DSL_DATASET || (dn.IsNewType && dn.NewType == dmu_object_byteswap.DMU_BSWAP_ZAP);
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.CompilerServices;
5 | using System.Text;
6 |
7 | namespace ZfsSharpLib
8 | {
9 | static class Extensions
10 | {
11 | public static ArraySegment SubSegment(this ArraySegment parent, int offset, int count)
12 | {
13 | if (parent.Array == null)
14 | throw new ArgumentNullException(nameof(parent), "Parent array is null.");
15 | if (offset < 0)
16 | throw new ArgumentOutOfRangeException(nameof(offset), "Negative offset.");
17 | if (count < 0)
18 | throw new ArgumentOutOfRangeException(nameof(count), "Negative count.");
19 |
20 | if (offset >= parent.Count)
21 | throw new ArgumentOutOfRangeException(nameof(offset), "Offset is beyond the end of the parent.");
22 | if (offset + count > parent.Count)
23 | throw new ArgumentOutOfRangeException(nameof(count), "Sub segment extends beyond the end of the parent.");
24 |
25 | return new ArraySegment(parent.Array, parent.Offset + offset, count);
26 | }
27 |
28 | public static T Get(this ArraySegment seg, int offset)
29 | {
30 | if (seg.Offset + offset >= seg.Count)
31 | throw new ArgumentOutOfRangeException(nameof(offset));
32 | return seg.Array[seg.Offset + offset];
33 | }
34 |
35 | public static void Set(this ArraySegment seg, int offset, T value)
36 | {
37 | if (seg.Offset + offset >= seg.Count)
38 | throw new ArgumentOutOfRangeException(nameof(offset));
39 | seg.Array[seg.Offset + offset] = value;
40 | }
41 |
42 | public unsafe static void ZeroMemory(this ArraySegment dest)
43 | {
44 | fixed (byte* pDest = dest.Array)
45 | {
46 | Unsafe.InitBlock(pDest + dest.Offset, 0, (uint)dest.Count);
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/ZfsSharpLib/HardDisk/FileHardDisk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.MemoryMappedFiles;
4 |
5 | namespace ZfsSharpLib.HardDisks
6 | {
7 | unsafe class FileHardDisk : HardDisk
8 | {
9 | private MemoryMappedFile mFile;
10 | private MemoryMappedViewAccessor mViewAcessor;
11 | private byte* mPointer;
12 | private long mSize;
13 |
14 | public FileHardDisk(string path)
15 | {
16 | mFile = MemoryMappedFile.CreateFromFile(path, FileMode.Open);
17 | mViewAcessor = mFile.CreateViewAccessor();
18 | long fileSize = new FileInfo(path).Length;
19 | //Limit the range of data we read to the Capacity of the ViewAccessor
20 | //in the unlikly case that it is smaller than the file size we read.
21 | //We can't just use the Capacity though, as it is round up to the page size.
22 | mSize = Math.Min(mViewAcessor.Capacity, fileSize);
23 | mPointer = null;
24 | mViewAcessor.SafeMemoryMappedViewHandle.AcquirePointer(ref mPointer);
25 | }
26 |
27 | public override void ReadBytes(Span dest, long offset)
28 | {
29 | CheckOffsets(offset, dest.Length);
30 | new Span(mPointer + offset, dest.Length).CopyTo(dest);
31 | }
32 |
33 | public override long Length
34 | {
35 | get { return mSize; }
36 | }
37 |
38 | public override void Dispose()
39 | {
40 | mPointer = null;
41 | mViewAcessor.SafeMemoryMappedViewHandle.ReleasePointer();
42 | mViewAcessor.Dispose();
43 | mFile.Dispose();
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ZfsSharpLib/HardDisk/GptHardDrive.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.InteropServices;
5 | using System.Text;
6 |
7 | namespace ZfsSharpLib.HardDisks
8 | {
9 | class GptHardDrive : OffsetHardDisk
10 | {
11 | readonly static SortedSet sZfsPartitionTypes = new SortedSet(new[] {
12 | new Guid("6A898CC3-1DD2-11B2-99A6-080020736631"), //Solaris /usr (SmartOS partitions pools this way), also Mac ZFS apperently
13 | new Guid("516E7CBA-6ECF-11D6-8FF8-00022D09712B"), //FreeBSD ZFS
14 | });
15 | readonly static Guid SolarisUsrPartitionId = new Guid("6A898CC3-1DD2-11B2-99A6-080020736631");
16 |
17 | const string EfiMagic = "EFI PART";
18 | const int CurrentRevision = 0x00010000;
19 | const int CurrentHeaderSize = 92;
20 | const int ParitionEntrySize = 128;
21 | const long SectorSize = 512; //TODO: DEAL WITH IT
22 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
23 | unsafe struct GptHeader
24 | {
25 | fixed byte signature[8];
26 | public int Revision;
27 | public int HeaderSize;
28 | public int Crc;
29 | int Zero1;
30 | public long CurrentLba;
31 | public long BackupLba;
32 | public long FirstUsableLba;
33 | public long LastUsableLba;
34 | public Guid DiskGuid;
35 | public long StartingLbaOfPartitionEntries;
36 | public int NumberOfPartitions;
37 | public int SizeOfPartitionEntry;
38 | public int CrcOfPartitionEntry;
39 |
40 | public string Signature
41 | {
42 | get
43 | {
44 | fixed (byte* bytes = signature)
45 | return Marshal.PtrToStringAnsi(new IntPtr(bytes), 8);
46 | }
47 | }
48 | }
49 |
50 | [Flags]
51 | enum PartitionAttributes : long
52 | {
53 | None = 0,
54 | System = 1,
55 | Active = 1 << 2,
56 | ReadOnly = 1 << 60,
57 | Hidden = 1 << 62,
58 | DoNotAutomount = 1 << 63,
59 | }
60 |
61 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
62 | unsafe struct PartitionEntry
63 | {
64 | const int NameSize = 72;
65 |
66 | public Guid Type;
67 | public Guid ID;
68 | public long FirstLba;
69 | public long LastLba;
70 | public PartitionAttributes Attributes;
71 | fixed byte name[NameSize];
72 |
73 | public string Name
74 | {
75 | get
76 | {
77 | byte[] copiedBytes = new byte[NameSize];
78 | fixed (byte* bytes = name)
79 | {
80 | Marshal.Copy(new IntPtr(bytes), copiedBytes, 0, NameSize);
81 | }
82 | string ret = Encoding.GetEncoding("utf-16").GetString(copiedBytes, 0, NameSize);
83 | int subStr = ret.IndexOf('\0');
84 | if (subStr != -1)
85 | ret = ret.Substring(0, subStr);
86 | return ret;
87 | }
88 | }
89 | }
90 |
91 | private GptHeader mHeader;
92 | private PartitionEntry mPartition;
93 |
94 | public GptHardDrive(HardDisk hdd)
95 | {
96 | //check for MBR protective partition
97 | if (!MbrHardDisk.IsMbr(hdd))
98 | throw new Exception("Not MBR.");
99 | if (MbrHardDisk.GetType(hdd, 0) != MbrPartitionType.GptProtective)
100 | throw new Exception("Not GPT.");
101 |
102 | mHeader = LoadHeader(hdd, SectorSize);
103 | var backup = LoadHeader(hdd, hdd.Length - SectorSize);
104 |
105 | List parts = new List();
106 | for (int i = 0; i < mHeader.NumberOfPartitions; i++)
107 | {
108 | var partEnt = GetLba(hdd, mHeader.StartingLbaOfPartitionEntries, i * mHeader.SizeOfPartitionEntry);
109 | if (partEnt.Type == Guid.Empty)
110 | continue;
111 | parts.Add(partEnt);
112 | }
113 |
114 | mPartition = parts.Where(p => sZfsPartitionTypes.Contains(p.Type)).Single();
115 |
116 | Init(hdd, SectorSize * mPartition.FirstLba, SectorSize * (mPartition.LastLba - mPartition.FirstLba));
117 | }
118 |
119 | private GptHeader LoadHeader(HardDisk hdd, long offset)
120 | {
121 | GptHeader ret;
122 |
123 | hdd.Get(offset, out ret); //LBA 1
124 | if (ret.Signature != EfiMagic)
125 | throw new Exception("Not a GPT.");
126 | if (ret.Revision != CurrentRevision)
127 | throw new Exception("Wrong rev.");
128 | if (ret.HeaderSize < CurrentHeaderSize)
129 | throw new Exception("Wrong header size.");
130 | //TODO: check crc
131 | if (ret.SizeOfPartitionEntry != ParitionEntrySize)
132 | throw new Exception("Wrong ParitionEntrySize.");
133 | //TODO: check partition entry CRC
134 |
135 | if (ret.NumberOfPartitions == 0)
136 | throw new Exception("No partitions!");
137 |
138 | return ret;
139 | }
140 |
141 | private static T GetLba(HardDisk hdd, long absoluteLba, long extraOffset) where T : struct
142 | {
143 | long byteOffset = absoluteLba * SectorSize + extraOffset;
144 | T ret;
145 | hdd.Get(byteOffset, out ret);
146 | return ret;
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/ZfsSharpLib/HardDisk/HardDisk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ZfsSharpLib
4 | {
5 | abstract class HardDisk : IDisposable
6 | {
7 | protected void CheckOffsets(long offset, long size)
8 | {
9 | if (offset < 0 || size <= 0 || offset + size > Length)
10 | throw new ArgumentOutOfRangeException();
11 | }
12 |
13 | public void Get(long offset, out T @struct) where T : struct
14 | {
15 | int structSize = Program.SizeOf();
16 | CheckOffsets(offset, structSize);
17 | var bytes = Program.RentBytes(structSize);
18 | ReadBytes(bytes, offset);
19 | @struct = Program.ToStruct(bytes);
20 | Program.ReturnBytes(bytes);
21 | }
22 |
23 | ///
24 | /// Reads and verifies data from a label.
25 | ///
26 | ///
27 | ///
28 | /// true if the checksum is valid, false otherwise
29 | public bool ReadLabelBytes(ArraySegment dest, long offset)
30 | {
31 | ReadBytes(dest, offset);
32 | var verifier = new zio_cksum_t()
33 | {
34 | word1 = (ulong)offset,
35 | word2 = 0,
36 | word3 = 0,
37 | word4 = 0,
38 | };
39 | return Zio.IsEmbeddedChecksumValid(dest, verifier);
40 | }
41 |
42 | public byte[] ReadBytes(long offset, int count)
43 | {
44 | var ret = new byte[count];
45 | ReadBytes(new Span(ret), offset);
46 | return ret;
47 | }
48 |
49 | public abstract void ReadBytes(Span dest, long offset);
50 |
51 | public abstract long Length
52 | {
53 | get;
54 | }
55 |
56 | public abstract void Dispose();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/ZfsSharpLib/HardDisk/MbrHardDisk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Runtime.InteropServices;
7 | using System.IO;
8 |
9 | namespace ZfsSharpLib.HardDisks
10 | {
11 | enum MbrPartitionType : byte
12 | {
13 | Empty = 0,
14 | Ntfs = 7,
15 | Solaris = 0xbf,
16 | GptProtective = 0xee,
17 | }
18 | #region StructStuff
19 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
20 | unsafe struct CHS
21 | {
22 | short Stuff1;
23 | byte Stuff2;
24 | }
25 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
26 | unsafe struct PartitionEntry
27 | {
28 | public byte Status;
29 | public CHS FirstSector;
30 | public MbrPartitionType Type;
31 | public CHS LastSector;
32 | public uint FirstSectorLba;
33 | public uint NumberOfSectors;
34 | }
35 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
36 | unsafe struct MbrHeader
37 | {
38 | public const ushort MbrMagic = 0xaa55;
39 | public const long SectorSize = 512;
40 |
41 | fixed byte BootstrapCode1[218];
42 | short Zeros1;
43 | public byte OriginalPhysicalDrive;
44 | public byte Seconds;
45 | public byte Minutes;
46 | public byte Hours;
47 | fixed byte BootStrapCode2[216];
48 | public int DiskSig;
49 | short Zeros2;
50 | public PartitionEntry Partition1;
51 | public PartitionEntry Partition2;
52 | public PartitionEntry Partition3;
53 | public PartitionEntry Partition4;
54 | public ushort BootSig;
55 |
56 | public PartitionEntry GetPartition(int index)
57 | {
58 | switch (index)
59 | {
60 | case 0:
61 | return Partition1;
62 | case 1:
63 | return Partition2;
64 | case 2:
65 | return Partition3;
66 | case 3:
67 | return Partition4;
68 | default:
69 | throw new ArgumentOutOfRangeException();
70 | }
71 | }
72 | }
73 | #endregion
74 |
75 | class MbrHardDisk : OffsetHardDisk
76 | {
77 | private MbrHeader mHeader;
78 | private PartitionEntry mPartition;
79 |
80 | public MbrHardDisk(HardDisk hdd, int partition)
81 | {
82 | hdd.Get(0, out mHeader);
83 |
84 | //for now, always assume a GPT partition
85 | if (!MbrHardDisk.IsMbr(hdd))
86 | throw new Exception("Expected a MBR hdd.");
87 | if (MbrHardDisk.GetType(hdd, 0) != MbrPartitionType.GptProtective)
88 | throw new Exception("Expected a GPT protective MBR entry.");
89 |
90 | mPartition = mHeader.GetPartition(partition);
91 | Init(hdd, (long)mPartition.FirstSectorLba * MbrHeader.SectorSize, (long)mPartition.NumberOfSectors * MbrHeader.SectorSize);
92 | }
93 |
94 | public static bool IsMbr(HardDisk hdd)
95 | {
96 | MbrHeader h;
97 | hdd.Get(0, out h);
98 | return h.BootSig == MbrHeader.MbrMagic;
99 | }
100 |
101 | public static MbrPartitionType GetType(HardDisk hdd, int index)
102 | {
103 | MbrHeader h;
104 | hdd.Get(0, out h);
105 | return h.GetPartition(index).Type;
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/ZfsSharpLib/HardDisk/OffsetHardDisk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ZfsSharpLib.HardDisks
4 | {
5 | class OffsetHardDisk : HardDisk
6 | {
7 | HardDisk mHdd;
8 | long mOffset;
9 | long mSize;
10 |
11 | public static HardDisk Create(HardDisk hdd, long offset, long size)
12 | {
13 | while (hdd is OffsetHardDisk)
14 | {
15 | var off = (OffsetHardDisk)hdd;
16 | offset += off.mOffset;
17 | hdd = off.mHdd;
18 | }
19 | return new OffsetHardDisk(hdd, offset, size);
20 | }
21 |
22 | private OffsetHardDisk(HardDisk hdd, long offset, long size)
23 | {
24 | Init(hdd, offset, size);
25 | }
26 |
27 | protected OffsetHardDisk()
28 | {
29 | }
30 |
31 | protected void Init(HardDisk hdd, long offset, long size)
32 | {
33 | if (offset < 0)
34 | throw new ArgumentOutOfRangeException();
35 | if (offset + size > hdd.Length)
36 | throw new ArgumentOutOfRangeException();
37 |
38 | mHdd = hdd;
39 | mOffset = offset;
40 | mSize = size;
41 | }
42 |
43 | public override void ReadBytes(Span dest, long offset)
44 | {
45 | CheckOffsets(offset, dest.Length);
46 | mHdd.ReadBytes(dest, mOffset + offset);
47 | }
48 |
49 | public override long Length
50 | {
51 | get { return mSize; }
52 | }
53 |
54 | public override void Dispose()
55 | {
56 | mHdd.Dispose();
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ZfsSharpLib/HardDisk/OffsetTableHardDisk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace ZfsSharpLib
8 | {
9 | abstract class OffsetTableHardDisk : HardDisk
10 | {
11 | protected readonly HardDisk mHdd;
12 | readonly Program.BlockReader mReadBlock;
13 | readonly Func mGetBlockKey;
14 |
15 | //need to be set by subclass
16 | protected long[] mBlockOffsets;
17 | protected int mBlockSize;
18 |
19 | protected OffsetTableHardDisk(HardDisk hdd)
20 | {
21 | if (hdd == null)
22 | throw new ArgumentNullException(nameof(hdd));
23 | mHdd = hdd;
24 | mReadBlock = readBlock;
25 | mGetBlockKey = getBlockKey;
26 | }
27 |
28 | public override long Length
29 | {
30 | get
31 | {
32 | return mBlockSize * mBlockOffsets.LongLength;
33 | }
34 | }
35 |
36 | public override void Dispose()
37 | {
38 | mHdd.Dispose();
39 | }
40 |
41 | public override void ReadBytes(Span dest, long offset)
42 | {
43 | CheckOffsets(offset, dest.Length);
44 | Program.MultiBlockCopy(dest, offset, mBlockSize, mGetBlockKey, mReadBlock);
45 | }
46 |
47 | long getBlockKey(long blockId)
48 | {
49 | return mBlockOffsets[blockId];
50 | }
51 |
52 | void readBlock(Span array, long blockOffset, int blockStartNdx)
53 | {
54 | if (blockOffset == -1)
55 | {
56 | array.Fill(0);
57 | }
58 | else
59 | {
60 | mHdd.ReadBytes(array, blockOffset + blockStartNdx);
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/ZfsSharpLib/HardDisk/VdiHardDisk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace ZfsSharpLib.HardDisks
6 | {
7 | class VdiHardDisk : OffsetTableHardDisk
8 | {
9 | enum ImageType : uint
10 | {
11 | Dynamic = 0x01,
12 | Fixed = 0x02,
13 | }
14 |
15 | const uint VdiMagic = 0xbeda107f;
16 | const uint VdiHeadSize = 0x190;
17 | const uint VdiVersion = 0x00010001;
18 |
19 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
20 | unsafe struct VdiHeader
21 | {
22 | fixed byte Text[0x40];
23 |
24 | public uint ImageSig;
25 | public uint Version;
26 | public uint SizeOfHeader;
27 | public ImageType ImageType;
28 |
29 | public uint ImageFlags;
30 | fixed byte ImageDescription[0x100];
31 | public uint OffsetBlocks;
32 | public uint OffsetData;
33 | uint Cylinders;
34 |
35 | uint Heads;
36 | uint Sectors;
37 | public uint SectorSize;
38 | uint pad;
39 |
40 | public ulong DiskSizeInBytes;
41 | public uint BlockSize;
42 | uint BlockExtraData;
43 |
44 | public uint BlocksInHdd;
45 | public uint BlocksAllocated;
46 | public Guid Uuid;
47 | public Guid UuidOfLastSnap;
48 | public Guid UuidLink;
49 | public Guid UuidParent;
50 | }
51 |
52 | unsafe public VdiHardDisk(HardDisk hdd)
53 | : base(hdd)
54 | {
55 | var headBytes = hdd.ReadBytes(0, sizeof(VdiHeader));
56 | VdiHeader head = Program.ToStruct(headBytes);
57 |
58 | if (head.ImageSig != VdiMagic)
59 | throw new Exception("Wrong magic.");
60 | if (head.Version != VdiVersion)
61 | throw new Exception("Wrong version.");
62 | if (head.SizeOfHeader != VdiHeadSize)
63 | throw new Exception("Wrong size.");
64 |
65 | if (head.ImageType != ImageType.Dynamic)
66 | throw new NotImplementedException("Only dynamic is supported.");
67 |
68 | var dataOffset = head.OffsetData;
69 | mBlockOffsets = new long[head.BlocksInHdd];
70 | mBlockSize = (int)head.BlockSize;
71 |
72 | for (long i = 0; i < head.BlocksInHdd; i++)
73 | {
74 | uint blockLoc;
75 | hdd.Get(head.OffsetBlocks + i * 4, out blockLoc);
76 | if (blockLoc == ~0u)
77 | mBlockOffsets[i] = -1;
78 | else
79 | mBlockOffsets[i] = dataOffset + blockLoc * mBlockSize;
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/ZfsSharpLib/HardDisk/VhdHardDisk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace ZfsSharpLib.HardDisks
6 | {
7 | static class VhdHardDisk
8 | {
9 | #region Structs
10 | [Flags]
11 | enum Features : int
12 | {
13 | None = 0,
14 | Temp = 1,
15 | Reserved = 2,
16 | }
17 | enum CreatorOs : int
18 | {
19 | Win = 0x5769326B, //(Wi2k)
20 | Max = 0x4D616320,
21 | }
22 | enum DiskType : int
23 | {
24 | None = 0,
25 | Fixed = 2,
26 | Dynamic = 3,
27 | Differencing = 4,
28 | }
29 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
30 | unsafe struct VhdHeader
31 | {
32 | public fixed byte Cookie[8];
33 | public Features Features;
34 | public int FileFormatVersion;
35 | public long DataOffset;
36 | public int TimeStamp;
37 | public fixed byte CreatorApp[4];
38 | public int CreatorVersion;
39 | public CreatorOs CreatorOs;
40 | public long OriginalSize;
41 | public long CurrentSize;
42 | public int DiskGeometry;
43 | public DiskType DiskType;
44 | public int Checksum;
45 | public fixed byte UniqueId[16];
46 | public byte SavedState;
47 | fixed byte Reserved[427];
48 |
49 | public string CookieStr
50 | {
51 | get
52 | {
53 | fixed (byte* bytes = Cookie)
54 | {
55 | return Marshal.PtrToStringAnsi(new IntPtr(bytes), 8);
56 | }
57 | }
58 | }
59 |
60 | public string CreatorAppStr
61 | {
62 | get
63 | {
64 | fixed (byte* bytes = CreatorApp)
65 | {
66 | return Marshal.PtrToStringAnsi(new IntPtr(bytes), 4);
67 | }
68 | }
69 | }
70 | }
71 |
72 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
73 | unsafe struct DynamicHeader
74 | {
75 | fixed byte Cookie[8];
76 | public ulong DataOffset;
77 | public long TableOffset;
78 | public int HeaderVersion;
79 | public int MaxTableEntries;
80 | public int BlockSize;
81 | public int Checksum;
82 | public Guid ParentUniqueID;
83 | public int ParentTimeStamp;
84 | int Reserved;
85 | fixed byte ParentUnicodeName[512];
86 | fixed byte LocatorEntries[8 * 24]; // for differencing disks only
87 | fixed byte Reserved2[256];
88 |
89 | public string CookieStr
90 | {
91 | get
92 | {
93 | fixed (byte* bytes = Cookie)
94 | {
95 | return Marshal.PtrToStringAnsi(new IntPtr(bytes), 8);
96 | }
97 | }
98 | }
99 |
100 | public string ParentUnicodeNameStr
101 | {
102 | get
103 | {
104 | fixed (byte* bytes = ParentUnicodeName)
105 | {
106 | return Marshal.PtrToStringAnsi(new IntPtr(bytes), 512);
107 | }
108 | }
109 | }
110 | }
111 | #endregion
112 |
113 | public static HardDisk Create(HardDisk hdd)
114 | {
115 | byte[] headBytes = hdd.ReadBytes(hdd.Length - 512, 512);
116 | VhdHeader head = Program.ToStructFromBigEndian(headBytes);
117 | if (head.CookieStr != "conectix")
118 | throw new Exception("missing magic string");
119 | if (head.FileFormatVersion != 0x00010000)
120 | throw new Exception("upsupported version");
121 | //TODO: validate checksum
122 |
123 | if (head.DiskType == DiskType.Fixed)
124 | {
125 | return new FixedVhd(hdd, in head);
126 | }
127 | else if (head.DiskType == DiskType.Dynamic)
128 | {
129 | return new DynamicVhd(hdd, in head);
130 | }
131 | else
132 | {
133 | throw new Exception("Only fixed size VHDs are supported.");
134 | }
135 | }
136 |
137 | class FixedVhd : OffsetHardDisk
138 | {
139 | public FixedVhd(HardDisk hdd, in VhdHeader head)
140 | {
141 | long size = hdd.Length - 512;
142 |
143 | if (head.CurrentSize != size)
144 | throw new Exception();
145 |
146 | Init(hdd, 0, size);
147 | }
148 | }
149 |
150 | class DynamicVhd : OffsetTableHardDisk
151 | {
152 | const int SECTOR_SIZE = 512;
153 |
154 | long mSize;
155 |
156 | public DynamicVhd(HardDisk hdd, in VhdHeader head)
157 | : base(hdd)
158 | {
159 | int dySize = Program.SizeOf();
160 | DynamicHeader dyhead = Program.ToStructFromBigEndian(hdd.ReadBytes(head.DataOffset, dySize));
161 | if (dyhead.CookieStr != "cxsparse")
162 | throw new Exception("missing magic string");
163 | if (dyhead.HeaderVersion != 0x00010000)
164 | throw new NotSupportedException("wrong version");
165 | if (dyhead.ParentUniqueID != Guid.Empty)
166 | throw new NotSupportedException("Differencing disks not supported.");
167 | //TODO: validate checksum
168 |
169 | mSize = head.CurrentSize;
170 | mBlockSize = dyhead.BlockSize;
171 | if (mBlockSize % SECTOR_SIZE != 0)
172 | throw new Exception("Block size is not a multiple of sector size.");
173 | int sectorBitmapSize = (mBlockSize / SECTOR_SIZE) / 8;
174 |
175 | //Round up if we have a partial block
176 | int numberOfBlocks = (int)((mSize + mBlockSize - 1) / mBlockSize);
177 | if (numberOfBlocks != dyhead.MaxTableEntries)
178 | throw new Exception("Our calculated number of blocks does not match the MaxTableEntries. Is that right?");
179 | mBlockOffsets = new long[numberOfBlocks];
180 |
181 | var bat = hdd.ReadBytes(dyhead.TableOffset, numberOfBlocks * 4);
182 |
183 | for (int i = 0; i < numberOfBlocks; i++)
184 | {
185 | long batEntry = BinaryPrimitives.ReadInt32BigEndian(new ReadOnlySpan(bat, i * 4, 4));
186 | if (batEntry != -1)
187 | {
188 | batEntry *= SECTOR_SIZE;
189 | //skip the sector bitmap, since we don't support differencing disks
190 | batEntry += sectorBitmapSize;
191 | }
192 | mBlockOffsets[i] = batEntry;
193 | }
194 | }
195 |
196 | public override long Length
197 | {
198 | get { return mSize; }
199 | }
200 | }
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/ZfsSharpLib/LeafVdevInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using ZfsSharpLib.HardDisks;
7 |
8 | namespace ZfsSharpLib
9 | {
10 | class LeafVdevInfo : IDisposable
11 | {
12 | const int VDEV_PAD_SIZE = (8 << 10);
13 | /* 2 padding areas (vl_pad1 and vl_pad2) to skip */
14 | const int VDEV_SKIP_SIZE = VDEV_PAD_SIZE * 2;
15 | const int VDEV_PHYS_SIZE = (112 << 10);
16 |
17 | //uberblocks can be between 1k and 8k
18 | const int UBERBLOCK_SHIFT = 10;
19 | const int MAX_UBERBLOCK_SHIFT = 13;
20 | const int VDEV_UBERBLOCK_RING = (128 << 10);
21 |
22 | public LeafVdevInfo(HardDisk hdd)
23 | {
24 | this.HDD = hdd;
25 |
26 | var rentedBytes = Program.RentBytes(VDEV_PHYS_SIZE);
27 |
28 | try
29 | {
30 | if (!hdd.ReadLabelBytes(rentedBytes, VDEV_SKIP_SIZE))
31 | throw new Exception("Invalid checksum on lable config data!");
32 | Config = new NvList(rentedBytes);
33 | }
34 | finally
35 | {
36 | Program.ReturnBytes(rentedBytes);
37 | rentedBytes = default(ArraySegment);
38 | }
39 |
40 | //figure out how big the uber blocks are
41 | var vdevTree = Config.Get("vdev_tree");
42 | var ubShift = (int)vdevTree.Get("ashift");
43 | ubShift = Math.Max(ubShift, UBERBLOCK_SHIFT);
44 | ubShift = Math.Min(ubShift, MAX_UBERBLOCK_SHIFT);
45 | var ubSize = 1 << ubShift;
46 | var ubCount = VDEV_UBERBLOCK_RING >> ubShift;
47 |
48 | List blocks = new List();
49 | var ubBytes = Program.RentBytes(ubSize);
50 | try
51 | {
52 | for (long i = 0; i < ubCount; i++)
53 | {
54 | var offset = VDEV_SKIP_SIZE + VDEV_PHYS_SIZE + ubSize * i;
55 | if (!hdd.ReadLabelBytes(ubBytes, offset))
56 | continue;
57 | uberblock_t b = Program.ToStruct(ubBytes.Array, ubBytes.Offset);
58 | if (b.Magic == uberblock_t.UbMagic)
59 | {
60 | blocks.Add(b);
61 | }
62 | }
63 | }
64 | finally
65 | {
66 | Program.ReturnBytes(ubBytes);
67 | ubBytes = default(ArraySegment);
68 | }
69 | this.Uberblock = blocks.OrderByDescending(u => u.Txg).ThenByDescending(u => u.TimeStamp).First();
70 |
71 | const int VDevLableSizeStart = 4 << 20;
72 | const int VDevLableSizeEnd = 512 << 10;
73 | hdd = OffsetHardDisk.Create(hdd, VDevLableSizeStart, hdd.Length - VDevLableSizeStart - VDevLableSizeEnd);
74 | this.HDD = hdd;
75 | }
76 |
77 | public ulong Guid { get; private set; }
78 | public uberblock_t Uberblock { get; private set; }
79 | public HardDisk HDD { get; private set; }
80 | public NvList Config { get; private set; }
81 |
82 | public void Dispose()
83 | {
84 | HDD.Dispose();
85 | }
86 |
87 | static readonly Dictionary> sFileFormats = new Dictionary>(StringComparer.OrdinalIgnoreCase)
88 | {
89 | { ".vhd", fileHdd => new GptHardDrive(VhdHardDisk.Create(fileHdd)) },
90 | { ".vhdx", fileHdd => new GptHardDrive(new VhdxHardDisk(fileHdd)) },
91 | { ".vdi", fileHdd => new GptHardDrive(new VdiHardDisk(fileHdd)) },
92 | { ".zfs", fileHdd => fileHdd },
93 | };
94 |
95 | public static List GetLeafVdevs(string dirOrFile)
96 | {
97 | var ret = new List();
98 |
99 | try
100 | {
101 | FileInfo[] files;
102 | if (File.Exists(dirOrFile))
103 | {
104 | files = new FileInfo[] { new FileInfo(dirOrFile) };
105 | }
106 | else
107 | {
108 | files = new DirectoryInfo(dirOrFile).GetFiles();
109 | }
110 |
111 | foreach (var fi in files)
112 | {
113 | Func factory;
114 | if (!sFileFormats.TryGetValue(fi.Extension, out factory))
115 | continue;
116 |
117 | var file = new FileHardDisk(fi.FullName);
118 | var partition = factory(file);
119 | var vdev = new LeafVdevInfo(partition);
120 | ret.Add(vdev);
121 | }
122 | }
123 | catch
124 | {
125 | foreach (var leaf in ret)
126 | {
127 | leaf.Dispose();
128 | }
129 | throw;
130 | }
131 |
132 | return ret;
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/ZfsSharpLib/MetaSlabs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ZfsSharpLib
4 | {
5 | class MetaSlabs
6 | {
7 | RangeMap[] mRangeMap;
8 | ObjectSet mMos;
9 | long mSlabSize;
10 |
11 | public MetaSlabs(ObjectSet mos, long metaSlabArray, int metaSlabShift, int aShift)
12 | {
13 | mMos = mos;
14 | mSlabSize = 1L << metaSlabShift;
15 |
16 | var slabDnode = mos.ReadEntry(metaSlabArray);
17 | var someBytes = Program.RentBytes(checked((int)slabDnode.AvailableDataSize));
18 | slabDnode.Read(someBytes, 0);
19 |
20 | int numberOfSlabs = someBytes.Count / 8;
21 | mRangeMap = new RangeMap[numberOfSlabs];
22 | long[] ids = new long[numberOfSlabs];
23 | Buffer.BlockCopy(someBytes.Array, someBytes.Offset, ids, 0, someBytes.Count);
24 |
25 | Program.ReturnBytes(someBytes);
26 | someBytes = default(ArraySegment);
27 |
28 | for (int i = 0; i < numberOfSlabs; i++)
29 | {
30 | var id = ids[i];
31 | RangeMap map;
32 | if (id == 0)
33 | {
34 | map = new RangeMap();
35 | }
36 | else
37 | {
38 | map = LoadEntrysForMetaSlab(id, aShift);
39 | }
40 | mRangeMap[i] = map;
41 | }
42 | }
43 |
44 | public bool ContainsRange(long offset, long range)
45 | {
46 | long slabNdx = offset / mSlabSize;
47 | offset = offset % mSlabSize;
48 | return mRangeMap[slabNdx].ContainsRange((ulong)offset, (ulong)range);
49 | }
50 |
51 | unsafe RangeMap LoadEntrysForMetaSlab(long dnEntry, int sm_shift)
52 | {
53 | RangeMap ret = new RangeMap();
54 |
55 | var dn = mMos.ReadEntry(dnEntry);
56 | if (dn.Type != dmu_object_type_t.SPACE_MAP || dn.BonusType != dmu_object_type_t.SPACE_MAP_HEADER)
57 | throw new Exception("Not a space map.");
58 |
59 | var head = dn.GetBonus();
60 |
61 | if (head.smo_object != dnEntry)
62 | throw new Exception();
63 |
64 | if (head.smo_objsize > int.MaxValue)
65 | throw new Exception("Holy cow, this space map is greater than 2GB, what is wrong with your VDev!?!?");
66 |
67 | var someBytes = Program.RentBytes((int)head.smo_objsize);
68 | dn.Read(someBytes, 0);
69 | for (int i = 0; i < someBytes.Count; i += 8)
70 | {
71 | var ent = Program.ToStruct(someBytes.SubSegment(i, sizeof(spaceMapEntry)));
72 | if (ent.IsDebug)
73 | continue;
74 |
75 | ulong offset = (ent.Offset << sm_shift);
76 | ulong range = ent.Run << sm_shift;
77 | //Console.WriteLine("\t [{4,6}] {0} range: {1:x10}-{2:x10} size: {3:x6}", ent.Type, offset, offset + range, range, i / 8);
78 | if (ent.Type == SpaceMapEntryType.A)
79 | {
80 | ret.AddRange(offset, range);
81 | }
82 | else if (ent.Type == SpaceMapEntryType.F)
83 | {
84 | ret.RemoveRange(offset, range);
85 | }
86 | }
87 | Program.ReturnBytes(someBytes);
88 |
89 | return ret;
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/ZfsSharpLib/NvList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 |
6 | namespace ZfsSharpLib
7 | {
8 | //based on usr\src\uts\common\rpc\xdr.c
9 |
10 | ///
11 | /// Decodes an XDR representation of a NVList.
12 | ///
13 | class NvList : IEnumerable>
14 | {
15 | const int NV_VERSION = 0;
16 |
17 | enum NvDataType
18 | {
19 | UNKNOWN = 0,
20 | BOOLEAN,
21 | BYTE,
22 | INT16,
23 | UINT16,
24 | INT32,
25 | UINT32,
26 | INT64,
27 | UINT64,
28 | STRING,
29 | BYTE_ARRAY,
30 | INT16_ARRAY,
31 | UINT16_ARRAY,
32 | INT32_ARRAY,
33 | UINT32_ARRAY,
34 | INT64_ARRAY,
35 | UINT64_ARRAY,
36 | STRING_ARRAY,
37 | HRTIME,
38 | NVLIST,
39 | NVLIST_ARRAY,
40 | BOOLEAN_VALUE,
41 | INT8,
42 | UINT8,
43 | BOOLEAN_ARRAY,
44 | INT8_ARRAY,
45 | UINT8_ARRAY,
46 | }
47 |
48 | [Flags]
49 | enum NvFlags : int
50 | {
51 | None = 0,
52 | UNIQUE_NAME = 0x1,
53 | UNIQUE_NAME_TYPE = 0x2,
54 | }
55 |
56 | enum NV_ENCODE : byte
57 | {
58 | NATIVE = 0,
59 | XDR = 1,
60 | }
61 |
62 | private Dictionary mVals = new Dictionary();
63 |
64 | public NvList(ArraySegment bytes)
65 | : this(new MemoryStream(bytes.Array, bytes.Offset, bytes.Count))
66 | {
67 | }
68 |
69 | public NvList(byte[] bytes)
70 | : this(new MemoryStream(bytes))
71 | {
72 | }
73 |
74 | public NvList(Stream s)
75 | {
76 | var r = new NvListBinaryReader(s, Encoding.ASCII);
77 |
78 | NV_ENCODE encoding = (NV_ENCODE)r.ReadByte();
79 | if (encoding != NV_ENCODE.XDR)
80 | throw new Exception("Is not encoding in XDR.");
81 | byte endian = r.ReadByte();
82 | if (endian != 1)
83 | throw new Exception("Incorrect endianness.");
84 | short reserved = r.ReadInt16(); //reserved fields
85 |
86 | Load(r);
87 | }
88 |
89 | private NvList(NvListBinaryReader r)
90 | {
91 | Load(r);
92 | }
93 |
94 |
95 | private void Load(NvListBinaryReader r)
96 | {
97 | int version = r.ReadInt32();
98 |
99 | if (version != NV_VERSION)
100 | throw new NotSupportedException("Unsupport NVList version!");
101 |
102 | NvFlags flags = (NvFlags)r.ReadInt32();
103 |
104 | while (true)
105 | {
106 | int encodedSize = r.ReadInt32();
107 | int decodedSize = r.ReadInt32();
108 |
109 | if (encodedSize == 0 && decodedSize == 0)
110 | break;
111 |
112 | string name = r.ReadString();
113 | NvDataType type = (NvDataType)r.ReadInt32();
114 | int numberOfElements = r.ReadInt32();
115 |
116 | object val;
117 | switch (type)
118 | {
119 | case NvDataType.STRING:
120 | val = r.ReadString();
121 | break;
122 | case NvDataType.UINT64:
123 | val = r.ReadUInt64();
124 | break;
125 | case NvDataType.NVLIST:
126 | val = new NvList(r);
127 | break;
128 | case NvDataType.NVLIST_ARRAY:
129 | var array = new NvList[numberOfElements];
130 | for (int i = 0; i < numberOfElements; i++)
131 | {
132 | array[i] = new NvList(r);
133 | }
134 | val = array;
135 | break;
136 | case NvDataType.BOOLEAN:
137 | val = true;
138 | break;
139 | case NvDataType.BOOLEAN_VALUE:
140 | val = r.ReadInt32() != 0;
141 | break;
142 | default:
143 | throw new NotImplementedException();
144 | }
145 | mVals.Add(name, val);
146 | }
147 | }
148 |
149 | public T Get(string name)
150 | {
151 | return (T)mVals[name];
152 | }
153 |
154 | public T? GetOptional(string name) where T : struct
155 | {
156 | if (!mVals.ContainsKey(name))
157 | return new Nullable();
158 | return (T)mVals[name];
159 | }
160 |
161 | public IEnumerator> GetEnumerator()
162 | {
163 | return mVals.GetEnumerator();
164 | }
165 |
166 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
167 | {
168 | return mVals.GetEnumerator();
169 | }
170 |
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/ZfsSharpLib/NvListBinaryReader.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 |
4 | namespace ZfsSharpLib
5 | {
6 | class NvListBinaryReader : BinaryReader
7 | {
8 | readonly Encoding mEnc;
9 | public NvListBinaryReader(Stream s)
10 | : base(s, Encoding.UTF8)
11 | {
12 | }
13 | public NvListBinaryReader(Stream s, Encoding enc)
14 | : base(s, enc)
15 | {
16 | mEnc = enc;
17 | }
18 |
19 | public override short ReadInt16()
20 | {
21 | byte b0 = ReadByte();
22 | byte b1 = ReadByte();
23 | return (short)((b0 << 8) | b1);
24 | }
25 |
26 | public override int ReadInt32()
27 | {
28 | byte b0 = ReadByte();
29 | byte b1 = ReadByte();
30 | byte b2 = ReadByte();
31 | byte b3 = ReadByte();
32 | return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
33 | }
34 |
35 | public override long ReadInt64()
36 | {
37 | long b0 = ReadByte();
38 | long b1 = ReadByte();
39 | long b2 = ReadByte();
40 | long b3 = ReadByte();
41 | long b4 = ReadByte();
42 | long b5 = ReadByte();
43 | long b6 = ReadByte();
44 | long b7 = ReadByte();
45 | return (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
46 | }
47 |
48 | public override ushort ReadUInt16()
49 | {
50 | byte b0 = ReadByte();
51 | byte b1 = ReadByte();
52 | return (ushort)((b0 << 8) | b1);
53 | }
54 |
55 | public override uint ReadUInt32()
56 | {
57 | byte b0 = ReadByte();
58 | byte b1 = ReadByte();
59 | byte b2 = ReadByte();
60 | byte b3 = ReadByte();
61 | return (uint)((b0 << 24) | (b1 << 16) | (b2 << 8) | b3);
62 | }
63 |
64 | public override ulong ReadUInt64()
65 | {
66 | ulong b0 = ReadByte();
67 | ulong b1 = ReadByte();
68 | ulong b2 = ReadByte();
69 | ulong b3 = ReadByte();
70 | ulong b4 = ReadByte();
71 | ulong b5 = ReadByte();
72 | ulong b6 = ReadByte();
73 | ulong b7 = ReadByte();
74 | return (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
75 | }
76 |
77 | public override string ReadString()
78 | {
79 | int size = ReadInt32();
80 | byte[] bytes = ReadBytes((size + 3) & ~3);
81 | return mEnc.GetString(bytes, 0, size);
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/ZfsSharpLib/ObjectSet.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace ZfsSharpLib
7 | {
8 | class ObjectSet
9 | {
10 | readonly DNode mMetaDNode;
11 | readonly Zio mZio;
12 | readonly dmu_objset_type_t mType;
13 |
14 | public ObjectSet(Zio zio, objset_phys_t os)
15 | {
16 | if (zio == null)
17 | throw new ArgumentNullException("zio");
18 |
19 | mZio = zio;
20 | mType = os.Type;
21 | mMetaDNode = new DNode(zio, os.MetaDnode);
22 | }
23 |
24 | public dmu_objset_type_t Type
25 | {
26 | get { return mType; }
27 | }
28 |
29 | public unsafe DNode ReadEntry(long index)
30 | {
31 | var buf = Program.RentBytes(sizeof(dnode_phys_t));
32 | try
33 | {
34 | mMetaDNode.Read(buf, index << dnode_phys_t.DNODE_SHIFT);
35 | return new DNode(mZio, Program.ToStruct(buf));
36 | }
37 | finally
38 | {
39 | Program.ReturnBytes(buf);
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Reflection;
6 | using System.Runtime.CompilerServices;
7 | using System.Runtime.InteropServices;
8 |
9 | namespace ZfsSharpLib
10 | {
11 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
12 | struct uberblock_t
13 | {
14 | public ulong Magic;
15 | public ulong Version;
16 | public ulong Txg;
17 | public ulong GuidSum;
18 | public ulong TimeStamp;
19 | public blkptr_t rootbp;
20 |
21 | public const ulong UbMagic = 0x00bab10c;
22 | }
23 |
24 | static class Program
25 | {
26 | static readonly ArrayPool sBytePool = ArrayPool.Shared;
27 |
28 | public static ArraySegment RentBytes(int size)
29 | {
30 | return new ArraySegment(sBytePool.Rent(size), 0, size);
31 | }
32 |
33 | public static void ReturnBytes(ArraySegment buffer)
34 | {
35 | sBytePool.Return(buffer.Array);
36 | }
37 |
38 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
39 | public static int SizeOf()
40 | {
41 | return Unsafe.SizeOf();
42 | }
43 |
44 | public static T ToStruct(byte[] bytes) where T : struct
45 | {
46 | return ToStruct(bytes, 0);
47 | }
48 |
49 | public unsafe static T ToStruct(byte[] bytes, long offset) where T : struct
50 | {
51 | fixed (byte* ptr = bytes)
52 | return ToStruct(ptr, offset, bytes.Length);
53 | }
54 |
55 | public static T ToStruct(ArraySegment bytes) where T : struct
56 | {
57 | if (Unsafe.SizeOf() != bytes.Count)
58 | throw new ArgumentOutOfRangeException();
59 | return ToStruct(bytes.Array, bytes.Offset);
60 | }
61 |
62 | public static T ToStruct(Span bytes) where T : struct
63 | {
64 | if (Unsafe.SizeOf() != bytes.Length)
65 | throw new ArgumentOutOfRangeException();
66 | return MemoryMarshal.Cast(bytes)[0];
67 | }
68 |
69 | public unsafe static T ToStruct(byte* ptr, long offset, long ptrLength) where T : struct
70 | {
71 | if (offset < 0 || ptrLength <= 0)
72 | throw new ArgumentOutOfRangeException();
73 | if (offset + Unsafe.SizeOf() > ptrLength)
74 | throw new ArgumentOutOfRangeException();
75 | return Unsafe.Read(ptr + offset);
76 | }
77 |
78 | public static T ToStructFromBigEndian(byte[] bytes) where T : struct
79 | {
80 | if (BitConverter.IsLittleEndian)
81 | {
82 | return ToStructByteSwap(bytes);
83 | }
84 | else
85 | {
86 | return ToStruct(bytes);
87 | }
88 | }
89 |
90 | public static T ToStructByteSwap(byte[] bytes) where T : struct
91 | {
92 | var copy = new byte[bytes.Length];
93 | Buffer.BlockCopy(bytes, 0, copy, 0, copy.Length);
94 | foreach (var f in typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
95 | {
96 | ByteSwapField(f.Name, f.FieldType, copy);
97 | }
98 | return ToStruct(copy);
99 | }
100 |
101 | static Dictionary sStructSize = new Dictionary()
102 | {
103 | { typeof(byte), 1 },
104 | { typeof(sbyte), 1 },
105 | { typeof(short), 2 },
106 | { typeof(ushort), 2 },
107 | { typeof(int), 4 },
108 | { typeof(uint), 4 },
109 | { typeof(long), 8 },
110 | { typeof(ulong), 8 },
111 | { typeof(Guid), 16 },
112 | };
113 |
114 | static void ByteSwapField(string fieldName, Type fieldType, byte[] byteArray) where T : struct
115 | {
116 | var itemOffset = Marshal.OffsetOf(typeof(T), fieldName).ToInt32();
117 | ByteSwap(fieldType, byteArray, itemOffset);
118 | }
119 |
120 | public static void ByteSwap(Type type, byte[] byteArray, int itemOffset)
121 | {
122 | int itemSize;
123 | if (!sStructSize.TryGetValue(type, out itemSize))
124 | {
125 | if (type.IsEnum)
126 | {
127 | var realType = type.GetEnumUnderlyingType();
128 | ByteSwap(realType, byteArray, itemOffset);
129 | return;
130 | }
131 | else if (type.GetCustomAttributes(typeof(UnsafeValueTypeAttribute), false).Length != 0)
132 | {
133 | return;
134 | //ignore fixed size buffers
135 | }
136 | else
137 | throw new NotSupportedException();
138 | }
139 |
140 | for (int byteNdx = 0; byteNdx < itemSize / 2; byteNdx++)
141 | {
142 | int lowerNdx = itemOffset + byteNdx;
143 | int higherNdx = itemOffset + itemSize - byteNdx - 1;
144 | byte b = byteArray[lowerNdx];
145 | byteArray[lowerNdx] = byteArray[higherNdx];
146 | byteArray[higherNdx] = b;
147 | }
148 | }
149 |
150 | ///
151 | ///
152 | ///
153 | /// The place to store the read data.
154 | /// An identifier for the block.
155 | /// The offset within the block to start reading from.
156 | public delegate void BlockReader(Span dest, T blockKey, int startNdx);
157 |
158 | static long roundup(long x, long y)
159 | {
160 | return ((x + (y - 1)) / y) * y;
161 | }
162 |
163 | static long rounddown(long x, long y)
164 | {
165 | return (x / y) * y;
166 | }
167 |
168 | ///
169 | /// Given a large amount of data stored in equal sized blocks, reads a subset of that data efficiently.
170 | ///
171 | ///
172 | /// The place to store the read data.
173 | /// The byte offset into the blocks to read.
174 | /// The size of the blocks.
175 | /// Given a block offset returns a key for reading that block.
176 | /// Given a block key, reads the block.
177 | public static void MultiBlockCopy(Span dest, long offset, int blockSize, Func GetBlockKey, BlockReader ReadBlock)
178 | {
179 | if (offset < 0)
180 | throw new ArgumentOutOfRangeException("offset");
181 | if (blockSize <= 0)
182 | throw new ArgumentOutOfRangeException("blockSize");
183 |
184 | long firstBlock = offset / blockSize;
185 | int numBlocks = (int)((roundup(offset + dest.Length, blockSize) - rounddown(offset, blockSize)) / blockSize);
186 |
187 | int remaingBytes = dest.Length;
188 | int destOffset = 0;
189 | for (int i = 0; i < numBlocks; i++)
190 | {
191 | int blockOffset = (int)(offset % blockSize);
192 | int size = Math.Min(remaingBytes, blockSize - blockOffset);
193 |
194 | var key = GetBlockKey(firstBlock + i);
195 | ReadBlock(dest.Slice(destOffset, size), key, blockOffset);
196 |
197 | destOffset += size;
198 | offset += size;
199 | remaingBytes -= size;
200 | }
201 | }
202 |
203 | public const int SPA_MINBLOCKSHIFT = 9;
204 | public const int SPA_MAXBLOCKSHIFT = 17;
205 | public const int SPA_MINBLOCKSIZE = (1 << SPA_MINBLOCKSHIFT);
206 | const long SPA_MAXBLOCKSIZE = (1L << SPA_MAXBLOCKSHIFT);
207 | }
208 |
209 |
210 | }
211 |
--------------------------------------------------------------------------------
/ZfsSharpLib/RangeMap.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.DataStructures;
3 | using System.Diagnostics;
4 |
5 | namespace ZfsSharpLib
6 | {
7 | class RangeMap
8 | {
9 | private readonly AvlTree mTree = new AvlTree();
10 |
11 | #if DEBUG
12 | static RangeMap()
13 | {
14 | var space = new SpaceRange(100, 100);
15 |
16 | Debug.Assert(space.Intersects(new SpaceRange(150, 25)));
17 | Debug.Assert(space.Intersects(new SpaceRange(100, 25)));
18 | Debug.Assert(space.Intersects(new SpaceRange(50, 200)));
19 | Debug.Assert(space.Intersects(new SpaceRange(190, 10)));
20 |
21 | Debug.Assert(space.Intersects(new SpaceRange(90, 20)));
22 | Debug.Assert(space.Intersects(new SpaceRange(190, 20)));
23 |
24 | Debug.Assert(!space.Intersects(new SpaceRange(50, 25)));
25 | Debug.Assert(!space.Intersects(new SpaceRange(90, 10)));
26 | Debug.Assert(!space.Intersects(new SpaceRange(200, 10)));
27 | }
28 | #endif
29 |
30 | public RangeMap()
31 | {
32 | }
33 |
34 | public void AddRange(ulong offset, ulong range)
35 | {
36 | ValidateParams(offset, range);
37 |
38 | if (mTree.Count == 0)
39 | {
40 | mTree.Add(new SpaceRange(offset, range));
41 | return;
42 | }
43 |
44 | var newRange = new SpaceRange(offset, range);
45 | var near = mTree.FindNearestValues(newRange);
46 |
47 | //try to merge with the left
48 | if (near.Item1.IsValid)
49 | {
50 | if (near.Item1.Intersects(newRange))
51 | throw new Exception("Range already added");
52 | if (near.Item1.Offset + near.Item1.Range == newRange.Offset)
53 | {
54 | if (!mTree.Remove(near.Item1))
55 | throw new Exception("Failed to remove node, this should not happen.");
56 | newRange = new SpaceRange(near.Item1.Offset, near.Item1.Range + newRange.Range);
57 | }
58 | }
59 |
60 | //try to merge with the right
61 | if (near.Item2.IsValid)
62 | {
63 | if (near.Item2.Intersects(newRange))
64 | throw new Exception("Range already added.");
65 | if (newRange.Offset + newRange.Range == near.Item2.Offset)
66 | {
67 | if (!mTree.Remove(near.Item2))
68 | throw new Exception("Failed to remove node, this should not happen.");
69 | newRange = new SpaceRange(newRange.Offset, newRange.Range + near.Item2.Range);
70 | }
71 | }
72 |
73 | mTree.Add(newRange);
74 | }
75 |
76 | public void RemoveRange(ulong offset, ulong range)
77 | {
78 | ValidateParams(offset, range);
79 |
80 | if (mTree.Count == 0)
81 | throw new Exception("Empty tree.");
82 |
83 | var removeRange = new SpaceRange(offset, range);
84 |
85 | var near = mTree.FindNearestValues(removeRange);
86 |
87 | if (near.Item1.IsValid && near.Item1.Contains(removeRange))
88 | {
89 | SplitNode(near.Item1, removeRange);
90 | return;
91 | }
92 | else if (near.Item2.IsValid && near.Item2.Contains(removeRange))
93 | {
94 | SplitNode(near.Item2, removeRange);
95 | return;
96 | }
97 |
98 | throw new Exception("Could not find range to remove.");
99 | }
100 |
101 | private void SplitNode(SpaceRange existingRange, SpaceRange removeRange)
102 | {
103 | var newLowerRange = new SpaceRange(existingRange.Offset, removeRange.Offset - existingRange.Offset);
104 | var newUpperRange = new SpaceRange(removeRange.Offset + removeRange.Range, existingRange.Range - newLowerRange.Range - removeRange.Range);
105 | if (!mTree.Remove(existingRange))
106 | throw new Exception("Failed to remove range.");
107 | if (newLowerRange.Range != 0)
108 | mTree.Add(newLowerRange);
109 | if (newUpperRange.Range != 0)
110 | mTree.Add(newUpperRange);
111 | }
112 |
113 | void ValidateParams(ulong offset, ulong range)
114 | {
115 | if (range == 0)
116 | throw new ArgumentOutOfRangeException("range", "Range cannot be zero.");
117 | if (offset > (offset + range))
118 | throw new OverflowException();
119 | }
120 |
121 | public bool ContainsRange(ulong offset, ulong range)
122 | {
123 | if (mTree.Count == 0)
124 | return false;
125 | var search = new SpaceRange(offset, range);
126 | var near = mTree.FindNearestValues(search);
127 | return (near.Item1.IsValid && near.Item1.Contains(search)) || (near.Item2.IsValid && near.Item2.Contains(search));
128 | }
129 |
130 | public void Print()
131 | {
132 | foreach (var sr in mTree.GetInorderEnumerator())
133 | {
134 | Console.WriteLine(sr);
135 | }
136 | Console.WriteLine();
137 | }
138 |
139 | struct SpaceRange : IComparable
140 | {
141 | public readonly ulong Offset;
142 | public readonly ulong Range;
143 |
144 | public SpaceRange(ulong offset, ulong range)
145 | {
146 | this.Offset = offset;
147 | this.Range = range;
148 | }
149 |
150 | public bool IsValid => Range != 0;
151 |
152 | public int CompareTo(SpaceRange other)
153 | {
154 | if (this.Offset < other.Offset)
155 | return -1;
156 | if (this.Offset > other.Offset)
157 | return 1;
158 | return 0;
159 | }
160 |
161 | public bool Intersects(SpaceRange other)
162 | {
163 | ulong myEnd = this.Offset + this.Range;
164 | if (other.Offset >= this.Offset && other.Offset < myEnd)
165 | return true;
166 | ulong otherEnd = other.Offset + other.Range;
167 | if (otherEnd > this.Offset && otherEnd <= myEnd)
168 | return true;
169 |
170 | if (other.Offset < this.Offset && otherEnd > myEnd)
171 | return true;
172 |
173 | return false;
174 | }
175 |
176 | public bool Contains(SpaceRange other)
177 | {
178 | ulong myEnd = this.Offset + this.Range;
179 | ulong otherEnd = other.Offset + other.Range;
180 | return other.Offset >= this.Offset && otherEnd <= myEnd;
181 | }
182 |
183 | public override string ToString()
184 | {
185 | return string.Format("{{ {0:x}, {1:x} }}", Offset, Range);
186 | }
187 | }
188 | }
189 |
190 |
191 | }
192 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Structs.Ddt.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace ZfsSharpLib
5 | {
6 | [StructLayout(LayoutKind.Sequential)]
7 | struct ddt_key_t
8 | {
9 | public zio_cksum_t cksum;
10 | ulong prop;
11 |
12 | public zio_compress Compress
13 | {
14 | get { return (zio_compress)((prop >> 32) & 0xff); }
15 | }
16 |
17 | public ushort PSize
18 | {
19 | get { return (ushort)((prop >> 16) & 0xffff); }
20 | }
21 |
22 | public ushort LSize
23 | {
24 | get { return (ushort)(prop & 0xffff); }
25 | }
26 | }
27 |
28 | [StructLayout(LayoutKind.Sequential)]
29 | unsafe struct ddt_phys_t
30 | {
31 | public dva_t dva1;
32 | public dva_t dva2;
33 | public dva_t dva3;
34 | public UInt64 refcnt;
35 | public UInt64 phys_birth;
36 | }
37 |
38 | enum ddt_type
39 | {
40 | DDT_TYPE_ZAP = 0,
41 | DDT_TYPES
42 | }
43 |
44 | enum ddt_phys_type
45 | {
46 | DDT_PHYS_DITTO = 0,
47 | DDT_PHYS_SINGLE = 1,
48 | DDT_PHYS_DOUBLE = 2,
49 | DDT_PHYS_TRIPLE = 3,
50 | DDT_PHYS_TYPES
51 | }
52 |
53 | enum ddt_class
54 | {
55 | DDT_CLASS_DITTO = 0,
56 | DDT_CLASS_DUPLICATE,
57 | DDT_CLASS_UNIQUE,
58 | DDT_CLASSES
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Structs.Dsl.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace ZfsSharpLib
5 | {
6 | [Flags]
7 | enum DD_FLAG : ulong
8 | {
9 | None = 0,
10 | USED_BREAKDOWN = 1 << 0
11 | }
12 |
13 | enum dd_used_t
14 | {
15 | DD_USED_HEAD,
16 | DD_USED_SNAP,
17 | DD_USED_CHILD,
18 | DD_USED_CHILD_RSRV,
19 | DD_USED_REFRSRV,
20 | //DD_USED_NUM
21 | }
22 |
23 | [StructLayout(LayoutKind.Sequential)]
24 | unsafe struct dsl_dir_phys_t
25 | {
26 | const int DD_USED_NUM = 0x00000005;
27 |
28 | ulong creation_time; /* not actually used */
29 | public long head_dataset_obj;
30 | public long parent_obj;
31 | public long origin_obj;
32 | public long child_dir_zapobj;
33 | /*
34 | * how much space our children are accounting for; for leaf
35 | * datasets, == physical space used by fs + snaps
36 | */
37 | public ulong used_bytes;
38 | public ulong compressed_bytes;
39 | public ulong uncompressed_bytes;
40 | /* Administrative quota setting */
41 | public ulong quota;
42 | /* Administrative reservation setting */
43 | public ulong reserved;
44 | public long props_zapobj;
45 | public ulong deleg_zapobj; /* dataset delegation permissions */
46 | public DD_FLAG flags;
47 | public fixed ulong used_breakdown[DD_USED_NUM];
48 | public long clones; /* dsl_dir objects */
49 | fixed ulong pad[13]; /* pad out to 256 bytes for good measure */
50 | }
51 |
52 | [Flags]
53 | enum DS_FLAG : ulong
54 | {
55 | None = 0,
56 | INCONSISTENT = (1UL << 0),
57 | ///
58 | /// Do not allow this dataset to be promoted.
59 | ///
60 | NOPROMOTE = (1UL << 1),
61 | ///
62 | /// UNIQUE_ACCURATE is set if ds_unique_bytes has been correctly
63 | /// calculated for head datasets (starting with SPA_VERSION_UNIQUE_ACCURATE,
64 | /// refquota/refreservations).
65 | ///
66 | UNIQUE_ACCURATE = (1UL << 2),
67 | ///
68 | /// DEFER_DESTROY is set after 'zfs destroy -d' has been called
69 | /// on a dataset. This allows the dataset to be destroyed using 'zfs release'.
70 | ///
71 | DEFER_DESTROY = (1UL << 3),
72 | ///
73 | /// CI_DATASET is set if the dataset contains a file system whose
74 | /// name lookups should be performed case-insensitively.
75 | ///
76 | CI_DATASET = (1UL << 16),
77 | }
78 |
79 | [StructLayout(LayoutKind.Sequential)]
80 | unsafe struct dsl_dataset_phys_t
81 | {
82 | public long dir_obj; /* DMU_OT_DSL_DIR */
83 | public long prev_snap_obj; /* DMU_OT_DSL_DATASET */
84 | public ulong prev_snap_txg;
85 | public long next_snap_obj; /* DMU_OT_DSL_DATASET */
86 | public long snapnames_zapobj; /* DMU_OT_DSL_SNAP_MAP 0 for snaps */
87 | public ulong num_children; /* clone/snap children; ==0 for head */
88 | public ulong creation_time; /* seconds since 1970 */
89 | public ulong creation_txg;
90 | public long deadlist_obj; /* DMU_OT_DEADLIST */
91 | /*
92 | * referenced_bytes, compressed_bytes, and uncompressed_bytes
93 | * include all blocks referenced by this dataset, including those
94 | * shared with any other datasets.
95 | */
96 | public ulong referenced_bytes;
97 | public ulong compressed_bytes;
98 | public ulong uncompressed_bytes;
99 | public ulong unique_bytes; /* only relevant to snapshots */
100 | /*
101 | * The fsid_guid is a 56-bit ID that can change to avoid
102 | * collisions. The guid is a 64-bit ID that will never
103 | * change, so there is a small probability that it will collide.
104 | */
105 | public ulong fsid_guid;
106 | public ulong guid;
107 | public DS_FLAG flags;
108 | public blkptr_t bp;
109 | public long next_clones_obj; /* DMU_OT_DSL_CLONES */
110 | public long props_obj; /* DMU_OT_DSL_PROPS for snaps */
111 | public long userrefs_obj; /* DMU_OT_USERREFS */
112 | fixed ulong pad[5]; /* pad out to 320 bytes for good measure */
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Structs.SpaceMaps.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace ZfsSharpLib
4 | {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | struct space_map_obj
7 | {
8 | public long smo_object; /* on-disk space map object */
9 | public long smo_objsize; /* size of the object */
10 | public long smo_alloc; /* space allocated from the map */
11 | }
12 |
13 | enum SpaceMapEntryType
14 | {
15 | A = 0,
16 | F = 1,
17 | }
18 |
19 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
20 | struct spaceMapEntry
21 | {
22 | ulong mData;
23 |
24 | public spaceMapEntry(SpaceMapEntryType type, ulong offset, ulong run)
25 | {
26 | mData = 0;
27 | }
28 |
29 | public bool IsDebug
30 | {
31 | get
32 | {
33 | return (mData >> 63) == 1;
34 | }
35 | }
36 |
37 | // non-debug fields
38 |
39 | public ulong Offset
40 | {
41 | get
42 | {
43 | ulong mask = ~0UL >> 1;
44 | return (mData & mask) >> 16;
45 | }
46 | }
47 |
48 | public SpaceMapEntryType Type
49 | {
50 | get
51 | {
52 | return (SpaceMapEntryType)((mData >> 15) & 1);
53 | }
54 | }
55 |
56 | public ulong Run
57 | {
58 | get
59 | {
60 | return (mData & 0x7fff) + 1;
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Structs.Zpl.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace ZfsSharpLib
5 | {
6 | public enum ZfsItemType
7 | {
8 | None = 0,
9 | ///
10 | /// Fifo
11 | ///
12 | S_IFIFO = 0x1,
13 | ///
14 | /// Character Special Device
15 | ///
16 | S_IFCHR = 0x2,
17 | ///
18 | /// Directory
19 | ///
20 | S_IFDIR = 0x4,
21 | ///
22 | /// Block special device
23 | ///
24 | S_IFBLK = 0x6,
25 | ///
26 | /// Regular file
27 | ///
28 | S_IFREG = 0x8,
29 | ///
30 | /// Symbolic Link
31 | ///
32 | S_IFLNK = 0xA,
33 | ///
34 | /// Socket
35 | ///
36 | S_IFSOCK = 0xC,
37 | ///
38 | /// Door
39 | ///
40 | S_IFDOOR = 0xD,
41 | ///
42 | /// Event Port
43 | ///
44 | S_IFPORT = 0xE,
45 | }
46 |
47 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
48 | unsafe struct sa_hdr_phys_t
49 | {
50 | const uint SA_MAGIC = 0x2F505A;
51 |
52 | uint sa_magic;
53 | ushort sa_layout_info; /* Encoded with hdrsize and layout number */
54 | public fixed ushort sa_lengths[1]; /* optional sizes for variable length attrs */
55 | /* ... Data follows the lengths. */
56 |
57 | public int hdrsz
58 | {
59 | get { return (sa_layout_info >> 10) * 8; }
60 | }
61 | public int layout
62 | {
63 | get { return sa_layout_info & 0x3FF; }
64 | }
65 |
66 | public void VerifyMagic()
67 | {
68 | if (sa_magic != SA_MAGIC)
69 | throw new Exception();
70 | }
71 | }
72 | enum zpl_attr_t : short
73 | {
74 | ZPL_ATIME = 0,
75 | ZPL_MTIME,
76 | ZPL_CTIME,
77 | ZPL_CRTIME,
78 | ZPL_GEN,
79 | ZPL_MODE,
80 | ZPL_SIZE,
81 | ZPL_PARENT,
82 | ZPL_LINKS,
83 | ZPL_XATTR,
84 | ZPL_RDEV,
85 | ZPL_FLAGS,
86 | ZPL_UID,
87 | ZPL_GID,
88 | ZPL_PAD,
89 | ZPL_ZNODE_ACL,
90 | ZPL_DACL_COUNT,
91 | ZPL_SYMLINK,
92 | ZPL_SCANSTAMP,
93 | ZPL_DACL_ACES,
94 | //ZPL_END
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/ZfsSharpLib/VirtualDevices/HddVdev.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.IO;
6 | using ZfsSharpLib.HardDisks;
7 |
8 | namespace ZfsSharpLib.VirtualDevices
9 | {
10 | class HddVdev : Vdev
11 | {
12 | readonly HardDisk mHdd;
13 | public HddVdev(NvList config, LeafVdevInfo hdd)
14 | : base(config)
15 | {
16 | this.mHdd = hdd.HDD;
17 | }
18 |
19 | protected override void ReadBytesCore(Span dest, long offset)
20 | {
21 | mHdd.ReadBytes(dest, offset);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ZfsSharpLib/VirtualDevices/MirrorVdev.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace ZfsSharpLib.VirtualDevices
7 | {
8 | class MirrorVdev : Vdev
9 | {
10 | readonly Vdev[] mVdevs;
11 | public MirrorVdev(NvList config, Dictionary leafs)
12 | : base(config)
13 | {
14 | this.mVdevs = config.Get("children")
15 | .Select(child => Vdev.Create(child, leafs))
16 | .ToArray();
17 | }
18 |
19 | protected override void ReadBytesCore(Span dest, long offset)
20 | {
21 | //TODO: use more than one child
22 | mVdevs[0].ReadBytes(dest, offset);
23 | }
24 |
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ZfsSharpLib/VirtualDevices/RaidzVdev.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Runtime.InteropServices;
6 | using System.Text;
7 |
8 | namespace ZfsSharpLib.VirtualDevices
9 | {
10 | class RaidzVdev : Vdev
11 | {
12 | readonly Vdev[] mVdevs;
13 | readonly ulong mNparity;
14 | readonly int mUnitShift; //ashift
15 | public RaidzVdev(NvList config, Dictionary leafs)
16 | : base(config)
17 | {
18 | this.mVdevs = config.Get("children")
19 | .Select(child => Vdev.Create(child, leafs))
20 | .OrderBy(child => child.ID)
21 | .ToArray();
22 |
23 | mNparity = config.Get("nparity");
24 | mUnitShift = (int)config.Get("ashift");
25 | }
26 |
27 | protected override void ReadBytesCore(Span dest, long offset)
28 | {
29 | var rm = vdev_raidz_map_alloc((ulong)dest.Length, (ulong)offset, mUnitShift, (ulong)mVdevs.Length, mNparity);
30 | int ptr = 0;
31 | for (ulong i = rm.rm_firstdatacol; i < rm.rm_cols; i++)
32 | {
33 | var col = rm.rm_col[i];
34 | if (col.rc_size > int.MaxValue)
35 | throw new NotSupportedException("RaidZ column too big.");
36 | mVdevs[col.rc_devidx].ReadBytes(dest.Slice(0, (int)col.rc_size), (long)col.rc_offset);
37 | ptr += (int)col.rc_size;
38 | }
39 | }
40 |
41 | //All below is copy-pasted with some modification from usr\src\uts\common\fs\zfs\vdev_raidz.c
42 | //Inspired by http://www.joyent.com/blog/zfs-raidz-striping
43 |
44 | [StructLayout(LayoutKind.Sequential)]
45 | struct raidz_col
46 | {
47 | public ulong rc_devidx; /* child device index for I/O */
48 | public ulong rc_offset; /* device offset */
49 | public ulong rc_size; /* I/O size */
50 | //public void* rc_data; /* I/O data */
51 | //public void* rc_gdata; /* used to store the "good" version */
52 | public int rc_error; /* I/O error for this device */
53 | public byte rc_tried; /* Did we attempt this I/O column? */
54 | public byte rc_skipped; /* Did we skip this I/O column? */
55 | }
56 |
57 | [StructLayout(LayoutKind.Sequential)]
58 | struct raidz_map
59 | {
60 | public ulong rm_cols; /* Regular column count */
61 | public ulong rm_scols; /* Count including skipped columns */
62 | public ulong rm_bigcols; /* Number of oversized columns */
63 | public ulong rm_asize; /* Actual total I/O size */
64 | public ulong rm_missingdata; /* Count of missing data devices */
65 | public ulong rm_missingparity; /* Count of missing parity devices */
66 | public ulong rm_firstdatacol; /* First data column/parity count */
67 | public ulong rm_nskip; /* Skipped sectors for padding */
68 | public ulong rm_skipstart; /* Column index of padding start */
69 | //public void* rm_datacopy; /* rm_asize-buffer of copied data */
70 | public UIntPtr rm_reports; /* # of referencing checksum reports */
71 | public byte rm_freed; /* map no longer has referencing ZIO */
72 | public byte rm_ecksuminjected; /* checksum error was injected */
73 | public raidz_col[] rm_col; /* Flexible array of I/O columns */
74 | }
75 |
76 | static ulong roundup(ulong x, ulong y)
77 | {
78 | return ((((x) + ((y) - 1)) / (y)) * (y));
79 | }
80 |
81 | static raidz_map vdev_raidz_map_alloc(ulong size, ulong offset, int unit_shift, ulong dcols, ulong nparity)
82 | {
83 | raidz_map rm;
84 | ulong b = offset >> unit_shift;
85 | ulong s = size >> unit_shift;
86 | ulong f = b % dcols;
87 | ulong o = (b / dcols) << unit_shift;
88 | ulong q, r, c, bc, col, acols, scols, coff, devidx, asize, tot;
89 |
90 | q = s / (dcols - nparity);
91 | r = s - q * (dcols - nparity);
92 | bc = (r == 0 ? 0 : r + nparity);
93 | tot = s + nparity * (q + (ulong)(r == 0 ? 0 : 1));
94 |
95 | if (q == 0)
96 | {
97 | acols = bc;
98 | scols = Math.Min(dcols, roundup(bc, nparity + 1));
99 | }
100 | else
101 | {
102 | acols = dcols;
103 | scols = dcols;
104 | }
105 |
106 |
107 | Debug.Assert(acols <= scols);
108 |
109 | rm = default(raidz_map);
110 | rm.rm_col = new raidz_col[scols];
111 |
112 | rm.rm_cols = acols;
113 | rm.rm_scols = scols;
114 | rm.rm_bigcols = bc;
115 | rm.rm_skipstart = bc;
116 | rm.rm_missingdata = 0;
117 | rm.rm_missingparity = 0;
118 | rm.rm_firstdatacol = nparity;
119 | //rm.rm_datacopy = (void*)0;
120 | rm.rm_reports = UIntPtr.Zero;
121 | rm.rm_freed = 0;
122 | rm.rm_ecksuminjected = 0;
123 |
124 | asize = 0;
125 |
126 | for (c = 0; c < scols; c++)
127 | {
128 | col = f + c;
129 | coff = o;
130 | if (col >= dcols)
131 | {
132 | col -= dcols;
133 | coff += 1UL << unit_shift;
134 | }
135 | rm.rm_col[c].rc_devidx = col;
136 | rm.rm_col[c].rc_offset = coff;
137 | //rm.rm_col[c].rc_data = NULL;
138 | //rm.rm_col[c].rc_gdata = NULL;
139 | rm.rm_col[c].rc_error = 0;
140 | rm.rm_col[c].rc_tried = 0;
141 | rm.rm_col[c].rc_skipped = 0;
142 |
143 | if (c >= acols)
144 | rm.rm_col[c].rc_size = 0;
145 | else if (c < bc)
146 | rm.rm_col[c].rc_size = (q + 1) << unit_shift;
147 | else
148 | rm.rm_col[c].rc_size = q << unit_shift;
149 |
150 | asize += rm.rm_col[c].rc_size;
151 | }
152 |
153 | Debug.Assert(asize == (tot << unit_shift));
154 | rm.rm_asize = roundup(asize, (nparity + 1) << unit_shift);
155 | rm.rm_nskip = roundup(tot, nparity + 1) - tot;
156 | Debug.Assert((rm.rm_asize - asize) == (rm.rm_nskip << unit_shift));
157 | Debug.Assert(rm.rm_nskip <= nparity);
158 |
159 | /*
160 | * If all data stored spans all columns, there's a danger that parity
161 | * will always be on the same device and, since parity isn't read
162 | * during normal operation, that that device's I/O bandwidth won't be
163 | * used effectively. We therefore switch the parity every 1MB.
164 | *
165 | * ... at least that was, ostensibly, the theory. As a practical
166 | * matter unless we juggle the parity between all devices evenly, we
167 | * won't see any benefit. Further, occasional writes that aren't a
168 | * multiple of the LCM of the number of children and the minimum
169 | * stripe width are sufficient to avoid pessimal behavior.
170 | * Unfortunately, this decision created an implicit on-disk format
171 | * requirement that we need to support for all eternity, but only
172 | * for single-parity RAID-Z.
173 | *
174 | * If we intend to skip a sector in the zeroth column for padding
175 | * we must make sure to note this swap. We will never intend to
176 | * skip the first column since at least one data and one parity
177 | * column must appear in each row.
178 | */
179 | Debug.Assert(rm.rm_cols >= 2);
180 | Debug.Assert(rm.rm_col[0].rc_size == rm.rm_col[1].rc_size);
181 |
182 | if (rm.rm_firstdatacol == 1 && (offset & (1UL << 20)) != 0)
183 | {
184 | devidx = rm.rm_col[0].rc_devidx;
185 | o = rm.rm_col[0].rc_offset;
186 | rm.rm_col[0].rc_devidx = rm.rm_col[1].rc_devidx;
187 | rm.rm_col[0].rc_offset = rm.rm_col[1].rc_offset;
188 | rm.rm_col[1].rc_devidx = devidx;
189 | rm.rm_col[1].rc_offset = o;
190 |
191 | if (rm.rm_skipstart == 0)
192 | rm.rm_skipstart = 1;
193 | }
194 |
195 | return (rm);
196 | }
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/ZfsSharpLib/VirtualDevices/Vdev.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace ZfsSharpLib.VirtualDevices
7 | {
8 | abstract class Vdev
9 | {
10 | private MetaSlabs mMetaSlabs = null;
11 |
12 | protected Vdev(NvList config)
13 | {
14 | this.Guid = config.Get("guid");
15 | this.ID = config.Get("id");
16 |
17 | MetaSlabArray = config.GetOptional("metaslab_array");
18 | MetaSlabShift = config.GetOptional("metaslab_shift");
19 | AShift = config.GetOptional("ashift");
20 | ASize = config.GetOptional("asize");
21 | }
22 |
23 | //a bit of a layering violation
24 | public void InitMetaSlabs(ObjectSet mos)
25 | {
26 | mMetaSlabs = new MetaSlabs(mos, (long)MetaSlabArray.Value, (int)MetaSlabShift.Value, (int)AShift.Value);
27 | }
28 |
29 | public void ReadBytes(Span dest, long offset)
30 | {
31 | if (mMetaSlabs != null && !mMetaSlabs.ContainsRange(offset, dest.Length))
32 | {
33 | throw new Exception("Reading unallocated data.");
34 | }
35 | ReadBytesCore(dest, offset);
36 | }
37 |
38 | protected abstract void ReadBytesCore(Span dest, long offset);
39 |
40 | public ulong Guid
41 | {
42 | get;
43 | private set;
44 | }
45 | public ulong ID { get; private set; }
46 |
47 | //These are optional, as children of raidz and mirror don't have them.
48 | public ulong? MetaSlabArray { get; private set; }
49 | public ulong? MetaSlabShift { get; private set; }
50 | public ulong? AShift { get; private set; }
51 | public ulong? ASize { get; private set; }
52 |
53 | public static Vdev Create(NvList config, Dictionary leafs)
54 | {
55 | var type = config.Get("type");
56 | switch (type)
57 | {
58 | case "mirror":
59 | return new MirrorVdev(config, leafs);
60 | case "disk":
61 | case "file":
62 | return new HddVdev(config, leafs[config.Get("guid")]);
63 | case "raidz":
64 | return new RaidzVdev(config, leafs);
65 | default:
66 | throw new NotSupportedException("Unknown type: " + type);
67 | }
68 | throw new NotImplementedException();
69 | }
70 |
71 | public static Vdev[] CreateVdevTree(List hdds)
72 | {
73 | var hddMap = new Dictionary();
74 | var innerVdevConfigs = new Dictionary();
75 | foreach (var hdd in hdds)
76 | {
77 | hddMap.Add(hdd.Config.Get("guid"), hdd);
78 | var vdevTree = hdd.Config.Get("vdev_tree");
79 | innerVdevConfigs[vdevTree.Get("guid")] = vdevTree;
80 | }
81 |
82 | var innerVdevs = new List();
83 | foreach (var kvp in innerVdevConfigs)
84 | {
85 | innerVdevs.Add(Vdev.Create(kvp.Value, hddMap));
86 | }
87 |
88 | ulong calculatedTopGuid = 0;
89 | for (int i = 0; i < innerVdevs.Count; i++)
90 | {
91 | calculatedTopGuid += innerVdevs[i].Guid;
92 | }
93 |
94 | var ret = innerVdevs.OrderBy(v => v.ID).ToArray();
95 | for (uint i = 0; i < ret.Length; i++)
96 | {
97 | if (ret[i].ID != i)
98 | throw new Exception("Missing vdev.");
99 | }
100 | return ret;
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/ZfsSharpLib/Zfs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 | using System.Text;
6 | using ZfsSharpLib.VirtualDevices;
7 |
8 | namespace ZfsSharpLib
9 | {
10 | public class Zfs : IDisposable
11 | {
12 | const string ROOT_DATASET = "root_dataset";
13 | const string CONFIG = "config";
14 | const string DDT_STATISTICS = "DDT-statistics";
15 | const ulong SUPPORTED_VERSION = 5000;
16 |
17 | readonly static ReadOnlyCollection sSupportReadFeatures = new ReadOnlyCollection(new string[]{
18 | "com.delphix:hole_birth", //as far as I can tell this just means that hole block pointers will have their birth fields filled in
19 | "com.delphix:extensible_dataset", //this means a DSL_DATASET DN contains ZAP entries
20 | "org.illumos:lz4_compress",
21 | "com.delphix:embedded_data",
22 | });
23 |
24 | List mHdds;
25 | Zio mZio;
26 | Dictionary mObjDir;
27 | NvList mConfig;
28 | ObjectSet mMos;
29 |
30 | static void assertStructSize(int size)
31 | {
32 | int messuredSize = Program.SizeOf();
33 | System.Diagnostics.Debug.Assert(messuredSize == size);
34 | }
35 |
36 | ///
37 | /// Either the path to a virutal hard disk image file or a directory containing multiple images.
38 | public Zfs(string directoryOrFile)
39 | {
40 | //make sure we correctly set the size of structs
41 | assertStructSize(zio_gbh_phys_t.SPA_GANGBLOCKSIZE);
42 |
43 | mHdds = LeafVdevInfo.GetLeafVdevs(directoryOrFile);
44 |
45 | try
46 | {
47 | Load();
48 | }
49 | catch
50 | {
51 | foreach (var hdd in mHdds)
52 | {
53 | hdd.Dispose();
54 | }
55 | throw;
56 | }
57 | }
58 |
59 | private void Load()
60 | {
61 | if (mHdds.Count == 0)
62 | throw new Exception("Did not find any hard drives.");
63 |
64 | //make sure we support reading the pool
65 | foreach (var hdd in mHdds)
66 | {
67 | CheckVersion(hdd.Config);
68 | }
69 |
70 | //ensure all HDDs are part of the same pool
71 | var poolGuid = mHdds.Select(h => h.Config.Get("pool_guid")).Distinct().ToArray();
72 | if (poolGuid.Length != 1)
73 | throw new Exception("Hard drives are part of different pools: " + string.Join(", ", poolGuid.Select(p => p.ToString())));
74 |
75 | //ensure all HDDs agree on the transaction group id
76 | var txg = mHdds.Select(hdd => hdd.Uberblock.Txg).Distinct().ToArray();
77 | if (txg.Length != 1)
78 | throw new Exception("Uberblocks do not all have the same transaction group id: " + string.Join(", ", txg.Select(p => p.ToString())));
79 |
80 | var ub = mHdds[0].Uberblock;
81 | if (ub.Txg == 0)
82 | throw new Exception("Root block pointer's transaction group is zero!");
83 |
84 | var vdevs = Vdev.CreateVdevTree(mHdds);
85 |
86 | mZio = new Zio(vdevs);
87 |
88 | mMos = new ObjectSet(mZio, mZio.Get(ub.rootbp));
89 | if (mMos.Type != dmu_objset_type_t.DMU_OST_META)
90 | throw new Exception("Given block pointer did not point to the MOS.");
91 |
92 | mZio.InitMetaSlabs(mMos);
93 | //the second time we will make sure that space maps contain themselves
94 | mZio.InitMetaSlabs(mMos);
95 |
96 | var objectDirectory = mMos.ReadEntry(1);
97 | //The MOS's directory sometimes has things that don't like like directory entries.
98 | //For example, the "scan" entry has scrub status stuffed into as an array of longs.
99 | mObjDir = Zap.GetDirectoryEntries(objectDirectory, true);
100 |
101 | if (mObjDir.ContainsKey(DDT_STATISTICS))
102 | {
103 | var ddtStats = Zap.Parse(mMos.ReadEntry(mObjDir[DDT_STATISTICS]));
104 | //TODO: maybe do something interesting with the stats
105 | }
106 |
107 | {
108 | var configDn = mMos.ReadEntry(mObjDir[CONFIG]);
109 | var configBytes = Program.RentBytes(checked((int)configDn.AvailableDataSize));
110 | configDn.Read(configBytes, 0);
111 | mConfig = new NvList(configBytes);
112 | Program.ReturnBytes(configBytes);
113 | }
114 |
115 | CheckVersion(mConfig);
116 | CheckFeatures();
117 | }
118 |
119 | private void CheckFeatures()
120 | {
121 | var fr = Zap.GetDirectoryEntries(mMos, mObjDir["features_for_read"]);
122 | var fw = Zap.GetDirectoryEntries(mMos, mObjDir["features_for_write"]);
123 | var ff = Zap.Parse(mMos.ReadEntry(mObjDir["feature_descriptions"])).ToDictionary(kvp => kvp.Key, kvp => Encoding.ASCII.GetString((byte[])kvp.Value));
124 | if (fw.ContainsKey("com.delphix:enabled_txg") && fw["com.delphix:enabled_txg"] > 0)
125 | {
126 | var fe = Zap.GetDirectoryEntries(mMos, mObjDir["feature_enabled_txg"]);
127 | }
128 |
129 | foreach (var feature in fr)
130 | {
131 | if (feature.Value != 0 && !sSupportReadFeatures.Contains(feature.Key))
132 | {
133 | throw new Exception("Unsupported feature: " + feature.Key);
134 | }
135 | }
136 | }
137 |
138 | private static void CheckVersion(NvList cfg)
139 | {
140 | if (cfg.Get("version") != SUPPORTED_VERSION)
141 | throw new Exception("Unsupported version.");
142 |
143 | var state = (pool_state)cfg.Get("state");
144 | if (state != pool_state.ACTIVE && state != pool_state.EXPORTED)
145 | throw new Exception("Unknown state: " + state);
146 |
147 | var features = cfg.Get("features_for_read");
148 | foreach (var kvp in features)
149 | {
150 | if (!sSupportReadFeatures.Contains(kvp.Key))
151 | throw new Exception("Unsupported feature: " + kvp.Key);
152 | }
153 | }
154 |
155 | public DatasetDirectory GetRootDataset()
156 | {
157 | var dsd = new DatasetDirectory(mMos, mObjDir[ROOT_DATASET], mConfig.Get("name"), mZio);
158 | return dsd;
159 | }
160 |
161 | public List GetAllDataSets()
162 | {
163 | var ret = new List();
164 | listDataSetName(mObjDir[ROOT_DATASET], mConfig.Get("name"), ret);
165 | return ret;
166 | }
167 |
168 | void listDataSetName(long objectId, string nameBase, List ret)
169 | {
170 | var dsd = new DatasetDirectory(mMos, objectId, nameBase, mZio);
171 | ret.Add(dsd);
172 |
173 | foreach (var kvp in dsd.GetChildIds())
174 | {
175 | listDataSetName(kvp.Value, nameBase + "/" + kvp.Key, ret);
176 | }
177 | }
178 |
179 | public void Dispose()
180 | {
181 | foreach (var hdd in mHdds)
182 | {
183 | hdd.Dispose();
184 | }
185 | }
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/ZfsSharpLib/ZfsSharpLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 | 7.3
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ZfsSharpLib/lz4net/LZ4.portable/LZ4Codec.portable.cs:
--------------------------------------------------------------------------------
1 | #region license
2 |
3 | /*
4 | Copyright (c) 2013, Milosz Krajewski
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided
8 | that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice, this list of conditions
11 | and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
14 | and the following disclaimer in the documentation and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | #endregion
27 |
28 | using LZ4.Services;
29 | using System.Runtime.CompilerServices;
30 |
31 | namespace LZ4
32 | {
33 | public static partial class LZ4Codec
34 | {
35 | /// Determines whether VS2015 runtime is installed.
36 | /// Note, on Mono the Registry class is not available at all,
37 | /// so access to it have to be isolated.
38 | /// true it VS2015 runtime is installed, false otherwise.
39 | private static bool Has2015Runtime() { return false; }
40 |
41 | // ReSharper disable InconsistentNaming
42 |
43 | /// Initializes codecs from LZ4s.
44 | [MethodImpl(MethodImplOptions.NoInlining)]
45 | private static void InitializeLZ4s()
46 | {
47 | _service_S32 = new Safe32LZ4Service();
48 | _service_S64 = new Safe64LZ4Service();
49 | }
50 |
51 | // ReSharper restore InconsistentNaming
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ZfsSharpLib/lz4net/LZ4/ILZ4Service.cs:
--------------------------------------------------------------------------------
1 | #region license
2 |
3 | /*
4 | Copyright (c) 2013, Milosz Krajewski
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided
8 | that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice, this list of conditions
11 | and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
14 | and the following disclaimer in the documentation and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | #endregion
27 |
28 | namespace LZ4
29 | {
30 | internal interface ILZ4Service
31 | {
32 | string CodecName { get; }
33 | int Decode(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength, bool knownOutputLength);
34 | }
35 | }
--------------------------------------------------------------------------------
/ZfsSharpLib/lz4net/LZ4/LZ4Codec.cs:
--------------------------------------------------------------------------------
1 | #region license
2 |
3 | /*
4 | Copyright (c) 2013-2017, Milosz Krajewski
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided
8 | that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice, this list of conditions
11 | and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
14 | and the following disclaimer in the documentation and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | #endregion
27 |
28 | using System;
29 | using System.Diagnostics;
30 | using System.Runtime.CompilerServices;
31 | using System.Text;
32 |
33 | namespace LZ4
34 | {
35 | ///
36 | /// LZ4 codec selecting best implementation depending on platform.
37 | ///
38 | public static partial class LZ4Codec
39 | {
40 | #region fields
41 |
42 | /// Encoding service.
43 | private static readonly ILZ4Service Encoder;
44 |
45 | /// Encoding service for HC algorithm.
46 | private static readonly ILZ4Service EncoderHC;
47 |
48 | /// Decoding service.
49 | private static readonly ILZ4Service Decoder;
50 |
51 | // ReSharper disable InconsistentNaming
52 |
53 | // safe c#
54 | private static ILZ4Service _service_S32;
55 | private static ILZ4Service _service_S64;
56 |
57 | // ReSharper restore InconsistentNaming
58 |
59 | #endregion
60 |
61 | #region initialization
62 |
63 | /// Initializes the class.
64 | static LZ4Codec()
65 | {
66 | InitializeLZ4s();
67 |
68 | ILZ4Service encoder, decoder, encoderHC;
69 | SelectCodec(out encoder, out decoder, out encoderHC);
70 |
71 | Encoder = encoder;
72 | Decoder = decoder;
73 | EncoderHC = encoderHC;
74 |
75 | if (Encoder == null || Decoder == null)
76 | {
77 | throw new NotSupportedException("No LZ4 compression service found");
78 | }
79 | }
80 |
81 | private static void SelectCodec(out ILZ4Service encoder, out ILZ4Service decoder, out ILZ4Service encoderHC)
82 | {
83 | // refer to: http://lz4net.codeplex.com/wikipage?title=Performance%20Testing for explanation about this order
84 | // feel free to change preferred order, just don't do it willy-nilly back it up with some evidence
85 | // it has been tested for Intel on Microsoft .NET only but looks reasonable for Mono as well
86 | if (IntPtr.Size == 4)
87 | {
88 | encoder =
89 | _service_S32 ??
90 | _service_S64;
91 | decoder =
92 | _service_S64 ??
93 | _service_S32;
94 | encoderHC =
95 | _service_S32 ??
96 | _service_S64;
97 | }
98 | else
99 | {
100 | encoder =
101 | _service_S32 ??
102 | _service_S64;
103 | decoder =
104 | _service_S64 ??
105 | _service_S32;
106 | encoderHC =
107 | _service_S32 ??
108 | _service_S64;
109 | }
110 | }
111 |
112 | /// Tries to execute specified action. Ignores exception if it failed.
113 | /// The method.
114 | [MethodImpl(MethodImplOptions.NoInlining)]
115 | private static void Try(Action method)
116 | {
117 | try
118 | {
119 | method();
120 | }
121 | catch
122 | {
123 | // ignore exception
124 | }
125 | }
126 |
127 | /// Tries to execute specified action. Ignores exception if it failed.
128 | /// Type of result.
129 | /// The method.
130 | /// The default value, returned when action fails.
131 | /// Result of given method, or default value.
132 | [MethodImpl(MethodImplOptions.NoInlining)]
133 | private static T Try(Func method, T defaultValue)
134 | {
135 | try
136 | {
137 | return method();
138 | }
139 | catch
140 | {
141 | return defaultValue;
142 | }
143 | }
144 |
145 | #endregion
146 |
147 | #region public interface
148 |
149 | /// Gets the name of selected codec(s).
150 | /// The name of the codec.
151 | public static string CodecName
152 | {
153 | get
154 | {
155 | return string.Format(
156 | "{0}/{1}/{2}HC",
157 | Encoder == null ? "" : Encoder.CodecName,
158 | Decoder == null ? "" : Decoder.CodecName,
159 | EncoderHC == null ? "" : EncoderHC.CodecName);
160 | }
161 | }
162 |
163 | /// Get maximum output length.
164 | /// Input length.
165 | /// Output length.
166 | public static int MaximumOutputLength(int inputLength)
167 | {
168 | return inputLength + (inputLength / 255) + 16;
169 | }
170 |
171 | #region Decode
172 |
173 | /// Decodes the specified input.
174 | /// The input.
175 | /// The input offset.
176 | /// Length of the input.
177 | /// The output.
178 | /// The output offset.
179 | /// Length of the output.
180 | /// Set it to true if output length is known.
181 | /// Number of bytes written.
182 | public static int Decode(
183 | byte[] input,
184 | int inputOffset,
185 | int inputLength,
186 | byte[] output,
187 | int outputOffset,
188 | int outputLength = 0,
189 | bool knownOutputLength = false)
190 | {
191 | return Decoder.Decode(input, inputOffset, inputLength, output, outputOffset, outputLength, knownOutputLength);
192 | }
193 |
194 | /// Decodes the specified input.
195 | /// The input.
196 | /// The input offset.
197 | /// Length of the input.
198 | /// Length of the output.
199 | /// Decompressed buffer.
200 | public static byte[] Decode(byte[] input, int inputOffset, int inputLength, int outputLength)
201 | {
202 | if (inputLength < 0)
203 | inputLength = input.Length - inputOffset;
204 |
205 | if (input == null)
206 | throw new ArgumentNullException("input");
207 | if (inputOffset < 0 || inputOffset + inputLength > input.Length)
208 | throw new ArgumentException("inputOffset and inputLength are invalid for given input");
209 |
210 | var result = new byte[outputLength];
211 | var length = Decode(input, inputOffset, inputLength, result, 0, outputLength, true);
212 | if (length != outputLength)
213 | throw new ArgumentException("outputLength is not valid");
214 | return result;
215 | }
216 |
217 | #endregion
218 |
219 | #endregion
220 | }
221 | }
--------------------------------------------------------------------------------
/ZfsSharpLib/lz4net/LZ4/LZ4StreamFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LZ4
4 | {
5 | ///
6 | /// Additional flags for LZ4Stream.
7 | ///
8 | [Flags]
9 | public enum LZ4StreamFlags
10 | {
11 | /// Empty settings. No special behavior.
12 | None = 0x00,
13 |
14 | /// Allows to use interactive mode, possibly returning partial blocks.
15 | InteractiveRead = 0x01,
16 |
17 | /// Uses high compression version of algorithm.
18 | HighCompression = 0x02,
19 |
20 | /// Isolates inner stream so it does not get
21 | /// closed when LZ4 stream is closed.
22 | IsolateInnerStream = 0x04,
23 |
24 | /// Default settings.
25 | Default = None,
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ZfsSharpLib/lz4net/LZ4/LZ4StreamMode.cs:
--------------------------------------------------------------------------------
1 | #region license
2 |
3 | /*
4 | Copyright (c) 2013, Milosz Krajewski
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided
8 | that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice, this list of conditions
11 | and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
14 | and the following disclaimer in the documentation and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | #endregion
27 |
28 | namespace LZ4
29 | {
30 | ///
31 | /// Originally this type comes from System.IO.Compression,
32 | /// but it is not present in portable assemblies.
33 | ///
34 | public enum LZ4StreamMode
35 | {
36 | /// Compress
37 | Compress,
38 |
39 | /// Decompress
40 | Decompress,
41 | }
42 | }
--------------------------------------------------------------------------------
/ZfsSharpLib/lz4net/LZ4/Services/Safe32LZ4Service.cs:
--------------------------------------------------------------------------------
1 | #region license
2 |
3 | /*
4 | Copyright (c) 2013, Milosz Krajewski
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided
8 | that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice, this list of conditions
11 | and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
14 | and the following disclaimer in the documentation and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | #endregion
27 |
28 | namespace LZ4.Services
29 | {
30 | internal class Safe32LZ4Service: ILZ4Service
31 | {
32 | #region ILZ4Service Members
33 |
34 | public string CodecName
35 | {
36 | get { return "Safe 32"; }
37 | }
38 |
39 | public int Encode(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength)
40 | {
41 | return LZ4ps.LZ4Codec.Encode32(input, inputOffset, inputLength, output, outputOffset, outputLength);
42 | }
43 |
44 | public int Decode(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength, bool knownOutputLength)
45 | {
46 | return LZ4ps.LZ4Codec.Decode32(input, inputOffset, inputLength, output, outputOffset, outputLength, knownOutputLength);
47 | }
48 |
49 | public int EncodeHC(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength)
50 | {
51 | return LZ4ps.LZ4Codec.Encode32HC(input, inputOffset, inputLength, output, outputOffset, outputLength);
52 | }
53 |
54 | #endregion
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/ZfsSharpLib/lz4net/LZ4/Services/Safe64LZ4Service.cs:
--------------------------------------------------------------------------------
1 | #region license
2 |
3 | /*
4 | Copyright (c) 2013, Milosz Krajewski
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided
8 | that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice, this list of conditions
11 | and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
14 | and the following disclaimer in the documentation and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | #endregion
27 |
28 | namespace LZ4.Services
29 | {
30 | internal class Safe64LZ4Service: ILZ4Service
31 | {
32 | #region ILZ4Service Members
33 |
34 | public string CodecName
35 | {
36 | get { return "Safe 64"; }
37 | }
38 |
39 | public int Encode(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength)
40 | {
41 | return LZ4ps.LZ4Codec.Encode64(input, inputOffset, inputLength, output, outputOffset, outputLength);
42 | }
43 |
44 | public int Decode(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength, bool knownOutputLength)
45 | {
46 | return LZ4ps.LZ4Codec.Decode64(input, inputOffset, inputLength, output, outputOffset, outputLength, knownOutputLength);
47 | }
48 |
49 | public int EncodeHC(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength)
50 | {
51 | return LZ4ps.LZ4Codec.Encode64HC(input, inputOffset, inputLength, output, outputOffset, outputLength);
52 | }
53 |
54 | #endregion
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/ZfsSharpLib/lz4net/LZ4ps/LZ4Codec.cs:
--------------------------------------------------------------------------------
1 | #region license
2 |
3 | /*
4 | Copyright (c) 2013, Milosz Krajewski
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided
8 | that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice, this list of conditions
11 | and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
14 | and the following disclaimer in the documentation and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | #endregion
27 |
28 | using System;
29 |
30 | /*
31 | NOTE:
32 | This file is shared between LZ4pn and LZ4ps.
33 | If you would like to modify this file please keep in mind that your changes will
34 | affect both projects.
35 | Use 'UNSAFE' conditional define to differentiate
36 | */
37 |
38 | // ReSharper disable InconsistentNaming
39 |
40 | #if UNSAFE
41 | namespace LZ4pn
42 | #else
43 | namespace LZ4ps
44 | #endif
45 | {
46 | public static partial class LZ4Codec
47 | {
48 | #region configuration
49 |
50 | ///
51 | /// Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
52 | /// Increasing memory usage improves compression ratio
53 | /// Reduced memory usage can improve speed, due to cache effect
54 | /// Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
55 | ///
56 | private const int MEMORY_USAGE = 14;
57 |
58 | ///
59 | /// Decreasing this value will make the algorithm skip faster data segments considered "incompressible"
60 | /// This may decrease compression ratio dramatically, but will be faster on incompressible data
61 | /// Increasing this value will make the algorithm search more before declaring a segment "incompressible"
62 | /// This could improve compression a bit, but will be slower on incompressible data
63 | /// The default value (6) is recommended
64 | ///
65 | private const int NOTCOMPRESSIBLE_DETECTIONLEVEL = 6;
66 |
67 | #if !UNSAFE
68 |
69 | /// Buffer length when Buffer.BlockCopy becomes faster than straight loop.
70 | /// Please note that safe implementation REQUIRES it to be greater (not even equal) than 8.
71 | private const int BLOCK_COPY_LIMIT = 16;
72 |
73 | #endif
74 |
75 | #endregion
76 |
77 | #region consts
78 |
79 | private const int MINMATCH = 4;
80 | #pragma warning disable 162
81 | // ReSharper disable once UnreachableCode
82 | private const int SKIPSTRENGTH =
83 | NOTCOMPRESSIBLE_DETECTIONLEVEL > 2
84 | ? NOTCOMPRESSIBLE_DETECTIONLEVEL
85 | : 2;
86 | #pragma warning restore 162
87 | private const int COPYLENGTH = 8;
88 | private const int LASTLITERALS = 5;
89 | private const int MFLIMIT = COPYLENGTH + MINMATCH;
90 | private const int MINLENGTH = MFLIMIT + 1;
91 | private const int MAXD_LOG = 16;
92 | private const int MAXD = 1 << MAXD_LOG;
93 | private const int MAXD_MASK = MAXD - 1;
94 | private const int MAX_DISTANCE = (1 << MAXD_LOG) - 1;
95 | private const int ML_BITS = 4;
96 | private const int ML_MASK = (1 << ML_BITS) - 1;
97 | private const int RUN_BITS = 8 - ML_BITS;
98 | private const int RUN_MASK = (1 << RUN_BITS) - 1;
99 | private const int STEPSIZE_64 = 8;
100 | private const int STEPSIZE_32 = 4;
101 |
102 | private const int LZ4_64KLIMIT = (1 << 16) + (MFLIMIT - 1);
103 |
104 | private const int HASH_LOG = MEMORY_USAGE - 2;
105 | private const int HASH_TABLESIZE = 1 << HASH_LOG;
106 | private const int HASH_ADJUST = (MINMATCH * 8) - HASH_LOG;
107 |
108 | private const int HASH64K_LOG = HASH_LOG + 1;
109 | private const int HASH64K_TABLESIZE = 1 << HASH64K_LOG;
110 | private const int HASH64K_ADJUST = (MINMATCH * 8) - HASH64K_LOG;
111 |
112 | private const int HASHHC_LOG = MAXD_LOG - 1;
113 | private const int HASHHC_TABLESIZE = 1 << HASHHC_LOG;
114 | private const int HASHHC_ADJUST = (MINMATCH * 8) - HASHHC_LOG;
115 | //private const int HASHHC_MASK = HASHHC_TABLESIZE - 1;
116 |
117 | private static readonly int[] DECODER_TABLE_32 = { 0, 3, 2, 3, 0, 0, 0, 0 };
118 | private static readonly int[] DECODER_TABLE_64 = { 0, 0, 0, -1, 0, 1, 2, 3 };
119 |
120 | private static readonly int[] DEBRUIJN_TABLE_32 = {
121 | 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1,
122 | 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1
123 | };
124 |
125 | private static readonly int[] DEBRUIJN_TABLE_64 = {
126 | 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7,
127 | 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7,
128 | 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6,
129 | 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7
130 | };
131 |
132 | private const int MAX_NB_ATTEMPTS = 256;
133 | private const int OPTIMAL_ML = (ML_MASK - 1) + MINMATCH;
134 |
135 | #endregion
136 |
137 | #region public interface (common)
138 |
139 | /// Gets maximum the length of the output.
140 | /// Length of the input.
141 | /// Maximum number of bytes needed for compressed buffer.
142 | public static int MaximumOutputLength(int inputLength)
143 | {
144 | return inputLength + (inputLength / 255) + 16;
145 | }
146 |
147 | #endregion
148 |
149 | #region internal interface (common)
150 |
151 | internal static void CheckArguments(
152 | Span input, int inputOffset, ref int inputLength,
153 | Span output, int outputOffset, ref int outputLength)
154 | {
155 | if (inputLength < 0) inputLength = input.Length - inputOffset;
156 | if (inputLength == 0)
157 | {
158 | outputLength = 0;
159 | return;
160 | }
161 |
162 | if (input == null) throw new ArgumentNullException("input");
163 | if (inputOffset < 0 || inputOffset + inputLength > input.Length)
164 | throw new ArgumentException("inputOffset and inputLength are invalid for given input");
165 |
166 | if (outputLength < 0) outputLength = output.Length - outputOffset;
167 | if (output == null) throw new ArgumentNullException("output");
168 | if (outputOffset < 0 || outputOffset + outputLength > output.Length)
169 | throw new ArgumentException("outputOffset and outputLength are invalid for given output");
170 | }
171 |
172 | #endregion
173 | }
174 | }
175 |
176 | // ReSharper restore InconsistentNaming
--------------------------------------------------------------------------------