|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.Diagnostics; |
1 | 4 | using System.IO; |
| 5 | +using System.Text; |
2 | 6 |
|
3 | 7 | namespace ApplicationUtility; |
4 | 8 |
|
5 | 9 | class Format_V3 : FormatBase |
6 | 10 | { |
| 11 | + protected override string LogTag => "AssemblyStore/Format_V3"; |
| 12 | + |
7 | 13 | public const uint HeaderSize = 5 * sizeof(uint); |
8 | 14 | public const uint IndexEntrySize32 = sizeof(uint) + sizeof(uint) + sizeof(byte); |
9 | 15 | public const uint IndexEntrySize64 = sizeof(ulong) + sizeof(uint) + sizeof(byte); |
10 | 16 | public const uint AssemblyDescriptorSize = 7 * sizeof(uint); |
11 | 17 |
|
| 18 | + ulong assemblyNamesOffset; |
| 19 | + |
12 | 20 | public Format_V3 (Stream storeStream, string? description) |
13 | 21 | : base (storeStream, description) |
14 | 22 | {} |
15 | 23 |
|
16 | | - protected override IAspectState ValidateInner () |
| 24 | + protected bool EnsureValidState (string where, out IAspectState? retval) |
17 | 25 | { |
18 | | - Log.Debug ("AssemblyStore/Format_V3: validating store format."); |
| 26 | + retval = null; |
19 | 27 | if (Header == null || Header.EntryCount == null || Header.IndexEntryCount == null || Header.IndexSize == null) { |
20 | | - return ValidationFailed ($"AssemblyStore/Format_V3: invalid header data."); |
| 28 | + retval = ValidationFailed ($"{LogTag}: invalid header data in {where}."); |
| 29 | + return false; |
21 | 30 | } |
22 | 31 |
|
23 | 32 | if (Descriptors == null || Descriptors.Count == 0) { |
24 | | - return ValidationFailed ($"AssemblyStore/Format_V3: no descriptors read."); |
| 33 | + retval = ValidationFailed ($"{LogTag}: no descriptors read in {where}."); |
| 34 | + return false; |
25 | 35 | } |
26 | 36 |
|
27 | | - // TODO: validate stream size |
28 | | - // TODO: populate |
29 | | - return new AssemblyStoreAspectState (true); |
| 37 | + return true; |
| 38 | + } |
30 | 39 |
|
31 | | - BasicAspectState ValidationFailed (string message) |
32 | | - { |
33 | | - Log.Debug (message); |
34 | | - return new BasicAspectState (false); |
| 40 | + protected override IAspectState ValidateInner () |
| 41 | + { |
| 42 | + Log.Debug ($"{LogTag}: validating store format."); |
| 43 | + if (!EnsureValidState (nameof (ValidateInner), out IAspectState? retval)) { |
| 44 | + return retval!; |
35 | 45 | } |
| 46 | + |
| 47 | + // Repetitive to `EnsureValidState`, but it's better than using `!` all over the place below... |
| 48 | + Debug.Assert (Header != null); |
| 49 | + Debug.Assert (Header.EntryCount != null); |
| 50 | + Debug.Assert (Header.IndexEntryCount != null); |
| 51 | + Debug.Assert (Descriptors != null); |
| 52 | + |
| 53 | + ulong indexEntrySize = Header.Version.Is64Bit ? IndexEntrySize64 : IndexEntrySize32; |
| 54 | + ulong indexSize = (indexEntrySize * (ulong)Header.IndexEntryCount!); |
| 55 | + ulong descriptorsSize = AssemblyDescriptorSize * (ulong)Header.EntryCount!; |
| 56 | + ulong requiredStreamSize = HeaderSize + indexSize + descriptorsSize; |
| 57 | + |
| 58 | + // It points to the start of the assembly names block |
| 59 | + assemblyNamesOffset = requiredStreamSize; |
| 60 | + |
| 61 | + // This is a trick to avoid having to read all the assembly names, but if the stream is valid, it won't be a |
| 62 | + // problem and otherwise, well, we're validating after all. First descriptor's data offset points to the next |
| 63 | + // byte after assembly names block. |
| 64 | + ulong assemblyNamesSize = ((AssemblyStoreAssemblyDescriptorV3)Descriptors[0]).DataOffset - requiredStreamSize; |
| 65 | + requiredStreamSize += assemblyNamesSize; |
| 66 | + |
| 67 | + foreach (var d in Descriptors) { |
| 68 | + var desc = (AssemblyStoreAssemblyDescriptorV3)d; |
| 69 | + |
| 70 | + requiredStreamSize += desc.DataSize + desc.DebugDataSize + desc.ConfigDataSize; |
| 71 | + } |
| 72 | + Log.Debug ($"{LogTag}: calculated the required stream size to be {requiredStreamSize}"); |
| 73 | + |
| 74 | + if (requiredStreamSize > Int64.MaxValue) { |
| 75 | + return ValidationFailed ($"{LogTag}: required stream size is too long for the stream API to handle."); |
| 76 | + } |
| 77 | + |
| 78 | + if ((long)requiredStreamSize != StoreStream.Length) { |
| 79 | + return ValidationFailed ($"{LogTag}: stream has invalid size, expected {requiredStreamSize} bytes, found {StoreStream.Length} instead."); |
| 80 | + } else { |
| 81 | + Log.Debug ($"{LogTag}: stream size is valid."); |
| 82 | + } |
| 83 | + |
| 84 | + return new AssemblyStoreAspectState (this); |
| 85 | + } |
| 86 | + |
| 87 | + protected override IList<string> ReadAssemblyNames (BinaryReader reader) |
| 88 | + { |
| 89 | + Debug.Assert (Header != null); |
| 90 | + Debug.Assert (Header.EntryCount != null); |
| 91 | + |
| 92 | + reader.BaseStream.Seek ((long)assemblyNamesOffset, SeekOrigin.Begin); |
| 93 | + var ret = new List<string> (); |
| 94 | + |
| 95 | + for (ulong i = 0; i < Header.EntryCount; i++) { |
| 96 | + uint length = reader.ReadUInt32 (); |
| 97 | + if (length == 0) { |
| 98 | + continue; |
| 99 | + } |
| 100 | + |
| 101 | + byte[] nameBytes = reader.ReadBytes ((int)length); |
| 102 | + ret.Add (Encoding.UTF8.GetString (nameBytes)); |
| 103 | + } |
| 104 | + |
| 105 | + return ret.AsReadOnly (); |
| 106 | + } |
| 107 | + |
| 108 | + protected override bool ReadAssemblies (BinaryReader reader, out IList<ApplicationAssembly>? assemblies) |
| 109 | + { |
| 110 | + Debug.Assert (Descriptors != null); |
| 111 | + |
| 112 | + assemblies = null; |
| 113 | + if (!EnsureValidState (nameof (ReadAssemblies), out _)) { |
| 114 | + return false; |
| 115 | + } |
| 116 | + |
| 117 | + IList<string> assemblyNames = ReadAssemblyNames (reader); |
| 118 | + if (assemblyNames.Count != Descriptors.Count) { |
| 119 | + Log.Debug ($"{LogTag}: assembly name count ({assemblyNames.Count}) is different to descriptor count ({Descriptors.Count})"); |
| 120 | + return false; |
| 121 | + } |
| 122 | + |
| 123 | + var ret = new List<ApplicationAssembly> (); |
| 124 | + for (int i = 0; i < Descriptors.Count; i++) { |
| 125 | + var desc = (AssemblyStoreAssemblyDescriptorV3)Descriptors[i]; |
| 126 | + string name = assemblyNames[i]; |
| 127 | + var assemblyStream = new SubStream (reader.BaseStream, (long)desc.DataOffset, (long)desc.DataSize); |
| 128 | + IAspectState assemblyState = ApplicationAssembly.ProbeAspect (assemblyStream, name); |
| 129 | + if (!assemblyState.Success) { |
| 130 | + assemblyStream.Dispose (); |
| 131 | + continue; |
| 132 | + } |
| 133 | + |
| 134 | + var assembly = (ApplicationAssembly)ApplicationAssembly.LoadAspect (assemblyStream, assemblyState, name); |
| 135 | + ret.Add (assembly); |
| 136 | + } |
| 137 | + |
| 138 | + assemblies = ret.AsReadOnly (); |
| 139 | + return true; |
36 | 140 | } |
37 | 141 | } |
0 commit comments