Repository navigation
Expand file tree
/
Copy pathVolumeInfoCreationTimeTests.cs
More file actions
165 lines (138 loc) · 5.72 KB
/
Copy pathVolumeInfoCreationTimeTests.cs
File metadata and controls
165 lines (138 loc) · 5.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
using XISOSharp.Models;
namespace XISOSharp.Tests;
/// <summary>
/// Volume descriptor FILETIME surfacing (2.3) and descriptor-sector reporting
/// (3.2): <see cref="XisoReader.GetVolumeInfo(string)"/> populates
/// <c>CreationTime</c>/<c>FileTimeRaw</c>/<c>DescriptorSector</c> from the same
/// probe used for validity, agreeing with the dedicated
/// <see cref="XisoReader.GetFileTime(string, int?)"/> read.
/// </summary>
[Collection("Sequential")]
public sealed class VolumeInfoCreationTimeTests : IDisposable
{
private readonly List<string> _tempDirs = [];
public void Dispose()
{
foreach (string dir in _tempDirs)
{
try
{
if (Directory.Exists(dir)) Directory.Delete(dir, true);
}
catch
{
// ignored
}
}
}
private string CreateTempDir(string prefix)
{
string dir = Path.Combine(Path.GetTempPath(), $"{prefix}_{Guid.NewGuid():N}");
Directory.CreateDirectory(dir);
_tempDirs.Add(dir);
return dir;
}
private string CreateIso(ulong? fileTime = null)
{
string src = CreateTempDir("xiso_vol_src");
File.WriteAllText(Path.Combine(src, "hello.txt"), "hello world");
File.WriteAllText(Path.Combine(src, "data.bin"), "12345");
string outDir = CreateTempDir("xiso_vol_iso");
int rc = XisoWriter.CreateXiso(src, outDir, null, null, out string? isoPath, null, null,
fileTime: fileTime);
Assert.Equal(0, rc);
Assert.NotNull(isoPath);
return isoPath;
}
[Fact]
public void GetVolumeInfo_AgreesWithGetFileTime()
{
string iso = CreateIso();
VolumeInfo info = XisoReader.GetVolumeInfo(iso);
Assert.True(info.IsValid);
Assert.Equal(XisoReader.GetFileTimeRaw(iso), info.FileTimeRaw);
Assert.Equal(XisoReader.GetFileTime(iso), info.CreationTime);
}
[Fact]
public void GetVolumeInfo_DeterministicZero_Is1601()
{
string iso = CreateIso(fileTime: 0);
VolumeInfo info = XisoReader.GetVolumeInfo(iso);
Assert.Equal(0UL, info.FileTimeRaw);
Assert.Equal(new DateTimeOffset(1601, 1, 1, 0, 0, 0, TimeSpan.Zero), info.CreationTime);
}
[Fact]
public void GetVolumeInfo_KnownTime_MatchesRoundTrip()
{
DateTimeOffset target = new(2021, 12, 31, 23, 59, 59, TimeSpan.Zero);
string iso = CreateIso(fileTime: FileTimeHelper.ToFileTimeRaw(target));
VolumeInfo info = XisoReader.GetVolumeInfo(iso);
Assert.Equal(FileTimeHelper.ToFileTimeRaw(target), info.FileTimeRaw);
Assert.Equal(target, info.CreationTime);
}
[Fact]
public void GetVolumeInfo_InvalidImage_TimeNullAndSectorMinusOne()
{
string work = CreateTempDir("xiso_vol_bad");
string garbage = Path.Combine(work, "garbage.iso");
File.WriteAllBytes(garbage, new byte[64 * 1024]);
VolumeInfo info = XisoReader.GetVolumeInfo(garbage);
Assert.False(info.IsValid);
Assert.Null(info.CreationTime);
Assert.Equal(0UL, info.FileTimeRaw);
Assert.Equal(-1, info.DescriptorSector);
}
[Fact]
public void GetVolumeInfo_GarbageFileTime_ClampsWithoutThrowing()
{
string iso = CreateIso(fileTime: 0);
const long offset = Constants.HeaderOffset + Constants.HeaderDataLength + 4 + 4;
using (FileStream fs = new(iso, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
Span<byte> garbage = stackalloc byte[8];
System.Buffers.Binary.BinaryPrimitives.WriteUInt64LittleEndian(garbage, ulong.MaxValue);
fs.Seek(offset, SeekOrigin.Begin);
fs.Write(garbage);
}
VolumeInfo info = XisoReader.GetVolumeInfo(iso);
Assert.True(info.IsValid); // the descriptor itself is still intact
Assert.Equal(ulong.MaxValue, info.FileTimeRaw);
Assert.NotNull(info.CreationTime);
Assert.Equal(DateTimeOffset.MaxValue.Year, info.CreationTime!.Value.Year);
}
[Fact]
public void DescriptorSector_StandardImage_Is32()
{
string iso = CreateIso();
VolumeInfo info = XisoReader.GetVolumeInfo(iso);
VolumeInfo explorerVolume = new XisoExplorer(iso).Volume;
Assert.Equal(32, info.DescriptorSector);
Assert.Equal(32, explorerVolume.DescriptorSector);
}
[Fact]
public void DescriptorSector_PartitionRelative_MatchesKnownOffsets()
{
// Every known layout keeps the descriptor at partition-relative sector
// 32 (HeaderOffset / 2048), regardless of the absolute disc lseek.
string iso = CreateIso();
Assert.Equal(Constants.HeaderOffset / Constants.SectorSize,
XisoReader.GetVolumeInfo(iso).DescriptorSector);
// An XGD3-style image shifts the partition by Xgd3LseekOffset; the
// descriptor must still report partition sector 32, with the shift
// surfaced through DiscLseek instead.
string work = CreateTempDir("xiso_vol_xgd3");
string shifted = Path.Combine(work, "shifted.iso");
byte[] isoBytes = File.ReadAllBytes(iso);
using (FileStream fs = new(shifted, FileMode.Create, FileAccess.Write, FileShare.None, 65536))
{
fs.SetLength(Constants.Xgd3LseekOffset);
fs.Seek(Constants.Xgd3LseekOffset, SeekOrigin.Begin);
fs.Write(isoBytes);
}
VolumeInfo partitioned = XisoReader.GetVolumeInfo(shifted);
Assert.True(partitioned.IsValid);
Assert.Equal(Constants.Xgd3LseekOffset, partitioned.DiscLseek);
Assert.Equal("XGD3", partitioned.DiscFormat);
Assert.Equal(Constants.HeaderOffset / Constants.SectorSize, partitioned.DescriptorSector);
}
}