Skip to content

Commit 2c59c79

Browse files
committed
NTFSish system improvements to symlink/reparse points
IAbstractRecord Filename standardization for NTFSish as well
1 parent b349e92 commit 2c59c79

File tree

6 files changed

+35
-28
lines changed

6 files changed

+35
-28
lines changed

Library/DiscUtils.Core/NativeFileSystem.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -806,15 +806,11 @@ public IEnumerable<IAbstractRecord> AllEntries {
806806

807807
}
808808
internal class NativeAbstractRecord : IAbstractRecord {
809-
public NativeAbstractRecord(NativeFileSystem fs, String path){
810-
info = new FileInfo(Path.Combine(fs.BasePath,path));
811-
IsDirectory = false;
812-
FileName = fs.CleanItems(info.FullName);
813-
this.fs = fs;
809+
public NativeAbstractRecord(NativeFileSystem fs, String path) : this (fs, new FileInfo(Path.Combine(fs.BasePath, path)), false) {
814810
}
815811
internal NativeAbstractRecord(NativeFileSystem fs, System.IO.FileSystemInfo info, bool isDirectory){
816812
this.info = info;
817-
IsDirectory = true;
813+
IsDirectory = isDirectory;
818814
FileName = fs.CleanItems(info.FullName);
819815
this.fs = fs;
820816
}

Library/DiscUtils.Core/ReparsePoint.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public sealed class ReparsePoint
3636
/// </summary>
3737
/// <param name="tag">The defined reparse point tag.</param>
3838
/// <param name="content">The reparse point's content.</param>
39-
public ReparsePoint(int tag, byte[] content)
39+
public ReparsePoint(uint tag, byte[] content)
4040
{
4141
Tag = tag;
4242
Content = content;
@@ -50,22 +50,25 @@ public ReparsePoint(int tag, byte[] content)
5050
/// <summary>
5151
/// Gets or sets the defined reparse point tag.
5252
/// </summary>
53-
public int Tag { get; set; }
53+
public uint Tag { get; set; }
5454
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/c8e77b37-3909-4fe6-a4ea-2b9d423b1ee4
55-
private const int IO_REPARSE_TAG_MOUNT_POINT = unchecked((int)0xA0000003);
56-
private const int IO_REPARSE_TAG_SYMLINK = unchecked((int)0xA000000C);
55+
private const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003;
56+
private const uint IO_REPARSE_TAG_SYMLINK = 0xA000000C;
5757
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/b41f1cbf-10df-4a47-98d4-1c52a833d913
5858
private enum SymlinkFlags : int {
5959
FullpathName = 0,
6060
SYMLINK_FLAG_RELATIVE = 1
6161
}
62+
public static bool IsValidSymlinkTag(uint tag) {
63+
return tag == IO_REPARSE_TAG_SYMLINK || tag == IO_REPARSE_TAG_MOUNT_POINT;
64+
}
6265
internal string ParseSymlink(String originalPath) {
6366

6467
var reparsePoint = this;
6568

6669
using var stream = new MemoryStream(reparsePoint.Content);
6770
using var reader = new BinaryReader(stream);
68-
if (reparsePoint.Tag != IO_REPARSE_TAG_SYMLINK && reparsePoint.Tag != IO_REPARSE_TAG_MOUNT_POINT)
71+
if (! IsValidSymlinkTag(reparsePoint.Tag) )
6972
throw new IOException($"Reparse point on {originalPath} is not a symlink or mount point (tag: 0x{reparsePoint.Tag:X8})");
7073

7174
var substNameOffset = reader.ReadUInt16();

Library/DiscUtils.Core/Vfs/IAbstractRecord.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public interface IAbstractRecord {
99
DateTime CreationTimeUtc { get; }
1010
FileAttributes FileAttributes { get; }
1111
/// <summary>
12-
/// the SubPath relative to the IAbstractDirectory that returned it
12+
/// the SubPath relative to the parent IAbstractDirectory
1313
/// </summary>
1414
string FileName { get; }
1515
bool IsDirectory { get; }

Library/DiscUtils.Ntfs/NtfsAbstractRecord.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ internal class NtfsAbstractRecord(NtfsFileSystem fileSystem, FileNameRecord Reco
1414

1515
public DateTime CreationTimeUtc => Record.CreationTime;
1616
public FileAttributes FileAttributes => Record.FileAttributes;
17-
public string FileName => FilePath;
17+
public string FileName => Record.FileName;
1818
public bool IsDirectory => Record.Flags.HasFlag(NtfsFileAttributes.Directory);
19-
public bool IsSymlink => Record.Flags.HasFlag(NtfsFileAttributes.ReparsePoint);
19+
public bool IsSymlink => Record.Flags.HasFlag(NtfsFileAttributes.ReparsePoint) && ReparsePoint.IsValidSymlinkTag(Record.EASizeOrReparsePointTag);
20+
2021
public DateTime LastAccessTimeUtc => Record.LastAccessTime;
2122
public DateTime LastWriteTimeUtc => Record.ModificationTime;
2223
public long FileId => (long)FileIndex.Value;
@@ -26,6 +27,7 @@ internal class NtfsAbstractRecord(NtfsFileSystem fileSystem, FileNameRecord Reco
2627

2728
protected NtfsFileSystem FileSystem { get; } = fileSystem;
2829
protected FileNameRecord Record { get; } = Record;
30+
public String FullPath => FilePath;
2931
protected FileRecordReference FileIndex { get; } = FileIndex;
3032
DateTime IVfsFile.CreationTimeUtc { get; set; }
3133
FileAttributes IVfsFile.FileAttributes { get; set; }
@@ -35,7 +37,7 @@ internal class NtfsAbstractRecord(NtfsFileSystem fileSystem, FileNameRecord Reco
3537
DateTime IVfsFile.LastWriteTimeUtc { get; set; }
3638

3739
public IAbstractDirectory GetAsAbstractDirectory() {
38-
40+
3941
if (!IsDirectory)
4042
throw new InvalidOperationException("Not a directory");
4143
var file = AsFile();

Library/DiscUtils.Ntfs/NtfsFileSystem.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,7 +1426,7 @@ public ReparsePoint GetReparsePoint(string path)
14261426

14271427
using var contentStream = stream.Value.Open(FileAccess.Read);
14281428
var rp = contentStream.ReadStruct<ReparsePointRecord>((int)contentStream.Length);
1429-
return new ReparsePoint((int)rp.Tag, rp.Content);
1429+
return new ReparsePoint(rp.Tag, rp.Content);
14301430
}
14311431
}
14321432

@@ -2674,12 +2674,15 @@ public override IAbstractRecord GetAbstractRecord(string path) {
26742674
public override string GetSymlinkTarget(IAbstractRecord dirEntry) {
26752675
if (!dirEntry.IsSymlink)
26762676
throw new ArgumentException($"dirEntry is not a symlink");
2677+
if (dirEntry is not NtfsAbstractRecord ntfsDirEntry)
2678+
throw new ArgumentException($"dirEntry is not an NtfsAbstractRecord");
2679+
26772680

2678-
var reparsePoint = GetReparsePoint(dirEntry.FileName);
2681+
var reparsePoint = GetReparsePoint(ntfsDirEntry.FullPath);
26792682
if (reparsePoint == null)
2680-
throw new IOException($"Unable to read reparse point for {dirEntry.FileName}");
2683+
throw new IOException($"Unable to read reparse point for {ntfsDirEntry.FullPath}");
26812684

2682-
return reparsePoint.ParseSymlink(dirEntry.FileName);
2685+
return reparsePoint.ParseSymlink(ntfsDirEntry.FullPath);
26832686
}
26842687
/// <summary>
26852688
/// A plugin system for handling reparse points. Handlers for specific tags can register here

Library/DiscUtils.Wim/WimFileSystem.cs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public ReparsePoint GetReparsePoint(string path)
119119
using var s = _file.OpenResourceStream(hdr);
120120
var buffer = new byte[s.Length];
121121
s.ReadExactly(buffer, 0, buffer.Length);
122-
return new ReparsePoint((int)dirEntry.ReparseTag, buffer);
122+
return new ReparsePoint(dirEntry.ReparseTag, buffer);
123123
}
124124

125125
/// <summary>
@@ -751,14 +751,15 @@ private IEnumerable<string> DoSearch(string path, Func<string, bool> filter, boo
751751
internal class WimAbstractRecord (WimFileSystem FileSystem, DirectoryEntry Record, String Path) : IAbstractRecord{
752752
public DateTime CreationTimeUtc => DateTime.FromFileTimeUtc(Record.CreationTime); // Entry has creationTime
753753
public FileAttributes FileAttributes => Record.Attributes;
754-
public string FileName => Path;
754+
public string FileName => Record.FileName;
755+
public string FullPath => Path;
755756
public bool IsDirectory => Record.Attributes.HasFlag(FileAttributes.Directory);
756-
public bool IsSymlink => Record.Attributes.HasFlag(FileAttributes.ReparsePoint);
757+
public bool IsSymlink => Record.Attributes.HasFlag(FileAttributes.ReparsePoint) && ReparsePoint.IsValidSymlinkTag(Record.ReparseTag);
757758
public DateTime LastAccessTimeUtc => DateTime.FromFileTimeUtc(Record.LastAccessTime);
758759
public DateTime LastWriteTimeUtc => DateTime.FromFileTimeUtc(Record.LastWriteTime);
759760
public long FileId => BitConverter.ToInt64(Record.Hash, 0) ^ BitConverter.ToInt64(Record.Hash, 8) ^ BitConverter.ToInt32(Record.Hash, 16);
760761
public long FileSize => FileSystem._file.LocateResource(Record.GetStreamHash(default))?.OriginalSize ?? 0;
761-
public SparseStream FileContent => FileSystem.OpenFile(FileName, FileMode.Open, FileAccess.Read);
762+
public SparseStream FileContent => FileSystem.OpenFile(Path, FileMode.Open, FileAccess.Read);
762763
protected WimFileSystem FileSystem { get; } = FileSystem;
763764
protected DirectoryEntry Record{ get; } = Record;
764765

@@ -772,17 +773,19 @@ internal class WimAbstractDirectory(WimFileSystem FileSystem, DirectoryEntry Rec
772773

773774
}
774775
private IAbstractRecord GetEntryAsAbstractRecord(DirectoryEntry record, String Path) => (record.Attributes.HasFlag(FileAttributes.Directory) && record.Attributes.HasFlag(FileAttributes.ReparsePoint) == false) ?
775-
new WimAbstractDirectory(this,record,Path) :
776-
new WimAbstractRecord(this,record,Path);
776+
new WimAbstractDirectory(this,record, Path) :
777+
new WimAbstractRecord(this,record, Path);
777778
public override IAbstractRecord GetAbstractRecord(string path)=> GetEntryAsAbstractRecord(GetEntry(path),String.IsNullOrWhiteSpace(path) ? "/" : "");
778779
public override string GetSymlinkTarget(IAbstractRecord dirEntry) {
779780
if (!dirEntry.IsSymlink)
780781
throw new ArgumentException($"dirEntry is not a symlink");
782+
if (dirEntry is not WimAbstractRecord dirEntryWim)
783+
throw new ArgumentException($"dirEntry is not a WimAbstractRecord");
781784

782-
var reparsePoint = GetReparsePoint(dirEntry.FileName);
785+
var reparsePoint = GetReparsePoint(dirEntryWim.FullPath);
783786
if (reparsePoint == null)
784-
throw new IOException($"Unable to read reparse point for {dirEntry.FileName}");
787+
throw new IOException($"Unable to read reparse point for {dirEntryWim.FullPath}");
785788

786-
return reparsePoint.ParseSymlink(dirEntry.FileName);
789+
return reparsePoint.ParseSymlink(dirEntryWim.FullPath);
787790
}
788791
}

0 commit comments

Comments
 (0)