AbstractCompoundFileDetailDetector.cs 65 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Text;
  7. using Masuit.Tools.Mime;
  8. namespace Masuit.Tools.Files.FileDetector;
  9. public abstract class AbstractCompoundFileDetailDetector : AbstractSignatureDetector
  10. {
  11. private static readonly SignatureInformation[] CfSignatureInfo = {
  12. new() { Position = 0, Signature = new byte [] { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 } },
  13. };
  14. /// <inheritdoc />
  15. protected override SignatureInformation[] SignatureInformations => CfSignatureInfo;
  16. public abstract IEnumerable<string> Chunks { get; }
  17. public override string MimeType => new MimeMapper().GetMimeFromExtension("." + Extension);
  18. public override List<FormatCategory> FormatCategories => GetType().GetCustomAttributes<FormatCategoryAttribute>().Select(a => a.Category).ToList();
  19. protected abstract bool IsValidChunk(string chunkName, byte[] chunkData);
  20. public override bool Detect(Stream stream)
  21. {
  22. if (!base.Detect(stream))
  23. {
  24. return false;
  25. }
  26. stream.Position = 0;
  27. try
  28. {
  29. using var cf = new CompoundFile(stream, CFSConfiguration.LeaveOpen | CFSConfiguration.Default);
  30. return !(from chunk in Chunks
  31. let compoundFileStream = cf.RootStorage.GetStream(chunk)
  32. where compoundFileStream == null || !IsValidChunk(chunk, compoundFileStream.GetData())
  33. select chunk).Any();
  34. }
  35. catch
  36. {
  37. return false;
  38. }
  39. }
  40. #region Modified OpenMCDF
  41. // -------------------------------------------------------------
  42. // This is a porting from java code, under MIT license of |
  43. // the beautiful Red-Black Tree implementation you can find at |
  44. // http://en.literateprograms.org/Red-black_tree_(Java)#chunk |
  45. // Many Thanks to original Implementors. |
  46. // -------------------------------------------------------------
  47. internal enum Color
  48. { RED = 0, BLACK = 1 }
  49. internal enum NodeOp
  50. {
  51. LAssigned, RAssigned, CAssigned, PAssigned, VAssigned
  52. }
  53. internal interface IRBNode : IComparable
  54. {
  55. IRBNode Left { get; set; }
  56. IRBNode Right { get; set; }
  57. Color Color { get; set; }
  58. IRBNode Parent { get; set; }
  59. IRBNode Grandparent();
  60. IRBNode Sibling();
  61. IRBNode Uncle();
  62. }
  63. internal sealed class RBTree
  64. {
  65. public IRBNode Root { get; set; }
  66. private static Color NodeColor(IRBNode n) => n == null ? Color.BLACK : n.Color;
  67. public RBTree()
  68. { }
  69. public RBTree(IRBNode root)
  70. { Root = root; }
  71. private IRBNode Lookup(IRBNode template)
  72. {
  73. IRBNode n = Root;
  74. while (n != null)
  75. {
  76. int compResult = template.CompareTo(n);
  77. if (compResult == 0) return n;
  78. n = compResult < 0 ? n.Left : n.Right;
  79. }
  80. return n;
  81. }
  82. public bool TryLookup(IRBNode template, out IRBNode val)
  83. {
  84. val = Lookup(template);
  85. return val != null;
  86. }
  87. private void Replace(IRBNode oldn, IRBNode newn)
  88. {
  89. if (oldn.Parent == null) Root = newn;
  90. else
  91. {
  92. if (oldn == oldn.Parent.Left)
  93. {
  94. oldn.Parent.Left = newn;
  95. }
  96. else
  97. {
  98. oldn.Parent.Right = newn;
  99. }
  100. }
  101. if (newn != null) newn.Parent = oldn.Parent;
  102. }
  103. private void RotateL(IRBNode n)
  104. {
  105. IRBNode r = n.Right;
  106. Replace(n, r);
  107. n.Right = r.Left;
  108. if (r.Left != null) r.Left.Parent = n;
  109. r.Left = n;
  110. n.Parent = r;
  111. }
  112. private void RotateR(IRBNode n)
  113. {
  114. IRBNode l = n.Left;
  115. Replace(n, l);
  116. n.Left = l.Right;
  117. if (l.Right != null) l.Right.Parent = n;
  118. l.Right = n;
  119. n.Parent = l;
  120. }
  121. public void Insert(IRBNode newNode)
  122. {
  123. newNode.Color = Color.RED;
  124. if (Root == null) Root = newNode;
  125. else
  126. {
  127. var n = Root;
  128. while (true)
  129. {
  130. int compResult = newNode.CompareTo(n);
  131. if (compResult == 0) throw new Exception($"RBNode {newNode} already present in tree");
  132. if (compResult < 0)
  133. {
  134. if (n.Left == null)
  135. {
  136. n.Left = newNode;
  137. break;
  138. }
  139. n = n.Left;
  140. }
  141. else
  142. {
  143. if (n.Right == null)
  144. {
  145. n.Right = newNode;
  146. break;
  147. }
  148. n = n.Right;
  149. }
  150. }
  151. newNode.Parent = n;
  152. }
  153. Insert1(newNode);
  154. NodeInserted?.Invoke(newNode);
  155. }
  156. private void Insert1(IRBNode n)
  157. {
  158. if (n.Parent == null) n.Color = Color.BLACK;
  159. else Insert2(n);
  160. }
  161. private void Insert2(IRBNode n)
  162. {
  163. if (NodeColor(n.Parent) == Color.BLACK) return;
  164. Insert3(n);
  165. }
  166. private void Insert3(IRBNode n)
  167. {
  168. if (NodeColor(n.Uncle()) == Color.RED)
  169. {
  170. n.Parent.Color = Color.BLACK;
  171. n.Uncle().Color = Color.BLACK;
  172. n.Grandparent().Color = Color.RED;
  173. Insert1(n.Grandparent());
  174. }
  175. else Insert4(n);
  176. }
  177. private void Insert4(IRBNode n)
  178. {
  179. if (n == n.Parent.Right && n.Parent == n.Grandparent().Left)
  180. {
  181. RotateL(n.Parent);
  182. n = n.Left;
  183. }
  184. else if (n == n.Parent.Left && n.Parent == n.Grandparent().Right)
  185. {
  186. RotateR(n.Parent);
  187. n = n.Right;
  188. }
  189. Insert5(n);
  190. }
  191. private void Insert5(IRBNode n)
  192. {
  193. n.Parent.Color = Color.BLACK;
  194. n.Grandparent().Color = Color.RED;
  195. if (n == n.Parent.Left && n.Parent == n.Grandparent().Left)
  196. RotateR(n.Grandparent());
  197. else RotateL(n.Grandparent());
  198. }
  199. private static IRBNode MaximumNode(IRBNode n)
  200. {
  201. while (n.Right != null)
  202. n = n.Right;
  203. return n;
  204. }
  205. public void VisitTree(Action<IRBNode> action)
  206. {
  207. IRBNode walker = Root;
  208. if (walker != null)
  209. DoVisitTree(action, walker);
  210. }
  211. private static void DoVisitTree(Action<IRBNode> action, IRBNode walker)
  212. {
  213. while (true)
  214. {
  215. if (walker.Left != null)
  216. {
  217. DoVisitTree(action, walker.Left);
  218. }
  219. action?.Invoke(walker);
  220. if (walker.Right != null)
  221. {
  222. walker = walker.Right;
  223. continue;
  224. }
  225. break;
  226. }
  227. }
  228. internal void VisitTreeNodes(Action<IRBNode> action)
  229. {
  230. IRBNode walker = Root;
  231. if (walker != null)
  232. {
  233. DoVisitTreeNodes(action, walker);
  234. }
  235. }
  236. private void DoVisitTreeNodes(Action<IRBNode> action, IRBNode walker)
  237. {
  238. while (true)
  239. {
  240. if (walker.Left != null)
  241. {
  242. DoVisitTreeNodes(action, walker.Left);
  243. }
  244. action?.Invoke(walker);
  245. if (walker.Right != null)
  246. {
  247. walker = walker.Right;
  248. continue;
  249. }
  250. break;
  251. }
  252. }
  253. public class RBTreeEnumerator : IEnumerator<IRBNode>
  254. {
  255. private int position = -1;
  256. private Queue<IRBNode> heap = new();
  257. internal RBTreeEnumerator(RBTree tree)
  258. {
  259. tree.VisitTreeNodes(heap.Enqueue);
  260. }
  261. public IRBNode Current => heap.ElementAt(position);
  262. public void Dispose()
  263. { }
  264. object System.Collections.IEnumerator.Current => heap.ElementAt(position);
  265. public bool MoveNext() => ++position < heap.Count;
  266. public void Reset()
  267. { position = -1; }
  268. }
  269. public RBTreeEnumerator GetEnumerator() => new(this);
  270. internal void FireNodeOperation(IRBNode node, NodeOp operation)
  271. {
  272. NodeOperation?.Invoke(node, operation);
  273. }
  274. internal event Action<IRBNode> NodeInserted;
  275. internal event Action<IRBNode, NodeOp> NodeOperation;
  276. }
  277. /* This Source Code Form is subject to the terms of the Mozilla Public
  278. * License, v. 2.0. If a copy of the MPL was not distributed with this
  279. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  280. *
  281. * The Original Code is OpenMCDF - Compound Document Format library.
  282. *
  283. * The Initial Developer of the Original Code is Federico Blaseotto.*/
  284. internal enum SectorType
  285. {
  286. Normal, Mini, FAT, DIFAT, RangeLockSector, Directory
  287. }
  288. internal sealed class Sector : IDisposable
  289. {
  290. public static int MinisectorSize = 64;
  291. public const int Freesect = unchecked((int)0xFFFFFFFF);
  292. public const int Endofchain = unchecked((int)0xFFFFFFFE);
  293. public const int Fatsect = unchecked((int)0xFFFFFFFD);
  294. public const int Difsect = unchecked((int)0xFFFFFFFC);
  295. public bool DirtyFlag { get; set; }
  296. public bool IsStreamed => _stream != null && Size != MinisectorSize && Id * Size + Size < _stream.Length;
  297. private readonly Stream _stream;
  298. public Sector(int size, Stream stream)
  299. {
  300. Size = size;
  301. _stream = stream;
  302. }
  303. public Sector(int size, byte[] data)
  304. {
  305. Size = size;
  306. _data = data;
  307. _stream = null;
  308. }
  309. public Sector(int size)
  310. {
  311. Size = size;
  312. _data = null;
  313. _stream = null;
  314. }
  315. internal SectorType Type { get; set; }
  316. public int Id { get; set; } = -1;
  317. public int Size { get; private set; } = 0;
  318. private byte[] _data;
  319. public byte[] GetData()
  320. {
  321. if (_data == null)
  322. {
  323. _data = new byte[Size];
  324. if (IsStreamed)
  325. {
  326. _stream.Seek(Size + Id * Size, SeekOrigin.Begin);
  327. _stream.Read(_data, 0, Size);
  328. }
  329. }
  330. return _data;
  331. }
  332. public void ZeroData()
  333. {
  334. _data = new byte[Size];
  335. DirtyFlag = true;
  336. }
  337. public void InitFATData()
  338. {
  339. _data = new byte[Size];
  340. for (int i = 0; i < Size; i++)
  341. _data[i] = 0xFF;
  342. DirtyFlag = true;
  343. }
  344. internal void ReleaseData() => _data = null;
  345. private readonly object _lockObject = new();
  346. #region IDisposable Members
  347. private bool _disposed;
  348. void IDisposable.Dispose()
  349. {
  350. try
  351. {
  352. if (!_disposed)
  353. {
  354. lock (_lockObject)
  355. {
  356. _data = null;
  357. DirtyFlag = false;
  358. Id = Endofchain;
  359. Size = 0;
  360. }
  361. }
  362. }
  363. finally { _disposed = true; }
  364. GC.SuppressFinalize(this);
  365. }
  366. #endregion IDisposable Members
  367. }
  368. internal enum StgType : int
  369. {
  370. StgInvalid = 0,
  371. StgStorage = 1,
  372. StgStream = 2,
  373. StgLockbytes = 3,
  374. StgProperty = 4,
  375. StgRoot = 5
  376. }
  377. internal sealed class DirectoryEntry : IRBNode
  378. {
  379. internal const int ThisIsGreater = 1;
  380. internal const int OtherIsGreater = -1;
  381. private readonly IList<DirectoryEntry> _dirRepository;
  382. public int Sid { get; set; } = -1;
  383. internal const int Nostream = unchecked((int)0xFFFFFFFF);
  384. private DirectoryEntry(string name, StgType stgType, IList<DirectoryEntry> dirRepository)
  385. {
  386. _dirRepository = dirRepository;
  387. StgType = stgType;
  388. switch (stgType)
  389. {
  390. case StgType.StgStream:
  391. StorageClsid = new Guid(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  392. CreationDate = new byte[8];
  393. ModifyDate = new byte[8];
  394. break;
  395. case StgType.StgStorage:
  396. CreationDate = BitConverter.GetBytes((DateTime.Now.ToFileTime()));
  397. break;
  398. case StgType.StgRoot:
  399. CreationDate = new byte[8];
  400. ModifyDate = new byte[8];
  401. break;
  402. }
  403. SetEntryName(name);
  404. }
  405. public byte[] EntryName { get; private set; } = new byte[64];
  406. public string GetEntryName()
  407. {
  408. return EntryName is { Length: > 0 } ? Encoding.Unicode.GetString(EntryName).Remove((NameLength - 1) / 2) : string.Empty;
  409. }
  410. public void SetEntryName(string entryName)
  411. {
  412. if (entryName.Contains(@"\") || entryName.Contains(@"/") || entryName.Contains(@":") || entryName.Contains(@"!"))
  413. throw new Exception("Invalid character in entry: the characters '\\', '/', ':','!' cannot be used in entry name");
  414. if (entryName.Length > 31)
  415. throw new Exception("Entry name MUST be smaller than 31 characters");
  416. byte[] temp = Encoding.Unicode.GetBytes(entryName);
  417. var newName = new byte[64];
  418. Buffer.BlockCopy(temp, 0, newName, 0, temp.Length);
  419. newName[temp.Length] = 0x00;
  420. newName[temp.Length + 1] = 0x00;
  421. EntryName = newName;
  422. NameLength = (ushort)(temp.Length + 2);
  423. }
  424. public ushort NameLength { get; private set; }
  425. public StgType StgType { get; set; } = StgType.StgInvalid;
  426. public Color Color { get; set; } = Color.BLACK;
  427. public int LeftSibling { get; set; } = Nostream;
  428. public int RightSibling { get; set; } = Nostream;
  429. public int Child { get; set; } = Nostream;
  430. public Guid StorageClsid { get; set; } = Guid.NewGuid();
  431. public int StateBits { get; set; }
  432. public byte[] CreationDate { get; set; } = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  433. public byte[] ModifyDate { get; set; } = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  434. public int StartSetc { get; set; } = Sector.Endofchain;
  435. public long Size { get; set; }
  436. public int CompareTo(object obj)
  437. {
  438. if (obj is not DirectoryEntry otherDir)
  439. throw new Exception("Invalid casting: compared object does not implement IDirectorEntry interface");
  440. if (NameLength > otherDir.NameLength)
  441. return ThisIsGreater;
  442. if (NameLength < otherDir.NameLength)
  443. return OtherIsGreater;
  444. string thisName = Encoding.Unicode.GetString(EntryName, 0, NameLength);
  445. string otherName = Encoding.Unicode.GetString(otherDir.EntryName, 0, otherDir.NameLength);
  446. for (int z = 0; z < thisName.Length; z++)
  447. {
  448. char thisChar = char.ToUpperInvariant(thisName[z]);
  449. char otherChar = char.ToUpperInvariant(otherName[z]);
  450. if (thisChar > otherChar)
  451. return ThisIsGreater;
  452. if (thisChar < otherChar)
  453. return OtherIsGreater;
  454. }
  455. return 0;
  456. }
  457. public override bool Equals(object obj)
  458. {
  459. return CompareTo(obj) == 0;
  460. }
  461. private static ulong Fnv_hash(byte[] buffer)
  462. {
  463. ulong h = 2166136261;
  464. int i;
  465. for (i = 0; i < buffer.Length; i++)
  466. h = (h * 16777619) ^ buffer[i];
  467. return h;
  468. }
  469. public override int GetHashCode()
  470. {
  471. return (int)Fnv_hash(EntryName);
  472. }
  473. public void Read(Stream stream, int ver = 3)
  474. {
  475. using (BinaryReader rw = new BinaryReader(stream, Encoding.UTF8, true))
  476. {
  477. EntryName = rw.ReadBytes(64);
  478. NameLength = rw.ReadUInt16();
  479. StgType = (StgType)rw.ReadByte();
  480. Color = (Color)rw.ReadByte();
  481. LeftSibling = rw.ReadInt32();
  482. RightSibling = rw.ReadInt32();
  483. Child = rw.ReadInt32();
  484. if (StgType == StgType.StgInvalid)
  485. {
  486. LeftSibling = Nostream;
  487. RightSibling = Nostream;
  488. Child = Nostream;
  489. }
  490. StorageClsid = new Guid(rw.ReadBytes(16));
  491. StateBits = rw.ReadInt32();
  492. CreationDate = rw.ReadBytes(8);
  493. ModifyDate = rw.ReadBytes(8);
  494. StartSetc = rw.ReadInt32();
  495. if (ver == 3)
  496. {
  497. Size = rw.ReadInt32();
  498. rw.ReadBytes(4);
  499. }
  500. else
  501. Size = rw.ReadInt64();
  502. }
  503. }
  504. public string Name => GetEntryName();
  505. public IRBNode Left
  506. {
  507. get
  508. {
  509. if (LeftSibling == Nostream)
  510. return null;
  511. return _dirRepository[LeftSibling];
  512. }
  513. set
  514. {
  515. LeftSibling = (value as DirectoryEntry)?.Sid ?? Nostream;
  516. if (LeftSibling != Nostream)
  517. _dirRepository[LeftSibling].Parent = this;
  518. }
  519. }
  520. public IRBNode Right
  521. {
  522. get => RightSibling == Nostream ? null : _dirRepository[RightSibling];
  523. set
  524. {
  525. RightSibling = ((DirectoryEntry)value)?.Sid ?? Nostream;
  526. if (RightSibling != Nostream)
  527. _dirRepository[RightSibling].Parent = this;
  528. }
  529. }
  530. public IRBNode Parent { get; set; }
  531. public IRBNode Grandparent() => Parent?.Parent;
  532. public IRBNode Sibling() => (this == Parent.Left) ? Parent.Right : Parent.Left;
  533. public IRBNode Uncle() => Parent?.Sibling();
  534. internal static DirectoryEntry New(String name, StgType stgType, IList<DirectoryEntry> dirRepository)
  535. {
  536. DirectoryEntry de;
  537. if (dirRepository != null)
  538. {
  539. de = new DirectoryEntry(name, stgType, dirRepository);
  540. dirRepository.Add(de);
  541. de.Sid = dirRepository.Count - 1;
  542. }
  543. else
  544. throw new ArgumentNullException(nameof(dirRepository), "Directory repository cannot be null in New() method");
  545. return de;
  546. }
  547. internal static DirectoryEntry Mock(string name, StgType stgType) => new(name, stgType, null);
  548. internal static DirectoryEntry TryNew(string name, StgType stgType, IList<DirectoryEntry> dirRepository)
  549. {
  550. var de = new DirectoryEntry(name, stgType, dirRepository);
  551. if (de != null)
  552. {
  553. for (int i = 0; i < dirRepository.Count; i++)
  554. {
  555. if (dirRepository[i].StgType == StgType.StgInvalid)
  556. {
  557. dirRepository[i] = de;
  558. de.Sid = i;
  559. return de;
  560. }
  561. }
  562. }
  563. dirRepository.Add(de);
  564. de.Sid = dirRepository.Count - 1;
  565. return de;
  566. }
  567. public override string ToString() => $"{Name} [{Sid}]{(StgType == StgType.StgStream ? "Stream" : "Storage")}";
  568. public void AssignValueTo(IRBNode other)
  569. {
  570. var d = other as DirectoryEntry;
  571. d.SetEntryName(GetEntryName());
  572. d.CreationDate = new byte[CreationDate.Length];
  573. CreationDate.CopyTo(d.CreationDate, 0);
  574. d.ModifyDate = new byte[ModifyDate.Length];
  575. ModifyDate.CopyTo(d.ModifyDate, 0);
  576. d.Size = Size;
  577. d.StartSetc = StartSetc;
  578. d.StateBits = StateBits;
  579. d.StgType = StgType;
  580. d.StorageClsid = new Guid(StorageClsid.ToByteArray());
  581. d.Child = Child;
  582. }
  583. }
  584. internal abstract class CFItem : IComparable<CFItem>
  585. {
  586. private CompoundFile compoundFile;
  587. protected CompoundFile CompoundFile => compoundFile;
  588. protected void CheckDisposed()
  589. {
  590. if (compoundFile.IsClosed)
  591. throw new ObjectDisposedException("Owner Compound file has been closed and owned items have been invalidated");
  592. }
  593. protected CFItem()
  594. { }
  595. protected CFItem(CompoundFile compoundFile)
  596. { compoundFile = compoundFile; }
  597. internal DirectoryEntry DirEntry { get; set; }
  598. internal int CompareTo(CFItem other) => DirEntry.CompareTo(other.DirEntry);
  599. public int CompareTo(object obj) => DirEntry.CompareTo((obj as CFItem).DirEntry);
  600. public static bool operator ==(CFItem leftItem, CFItem rightItem)
  601. {
  602. if (ReferenceEquals(leftItem, rightItem))
  603. return true;
  604. if (((object)leftItem == null) || ((object)rightItem == null))
  605. return false;
  606. return leftItem.CompareTo(rightItem) == 0;
  607. }
  608. public static bool operator !=(CFItem leftItem, CFItem rightItem) => !(leftItem == rightItem);
  609. public override bool Equals(object obj) => CompareTo(obj) == 0;
  610. public override int GetHashCode() => DirEntry.GetEntryName().GetHashCode();
  611. public string Name
  612. {
  613. get
  614. {
  615. var n = DirEntry.GetEntryName();
  616. return (n != null && n.Length > 0) ? n.TrimEnd('\0') : string.Empty;
  617. }
  618. }
  619. public long Size => DirEntry.Size;
  620. public bool IsStorage => DirEntry.StgType == StgType.StgStorage;
  621. public bool IsStream => DirEntry.StgType == StgType.StgStream;
  622. public bool IsRoot => DirEntry.StgType == StgType.StgRoot;
  623. public DateTime CreationDate
  624. {
  625. get => DateTime.FromFileTime(BitConverter.ToInt64(DirEntry.CreationDate, 0));
  626. set
  627. {
  628. if (DirEntry.StgType != StgType.StgStream && DirEntry.StgType != StgType.StgRoot)
  629. DirEntry.CreationDate = BitConverter.GetBytes((value.ToFileTime()));
  630. else
  631. throw new Exception("Creation Date can only be set on storage entries");
  632. }
  633. }
  634. public DateTime ModifyDate
  635. {
  636. get => DateTime.FromFileTime(BitConverter.ToInt64(DirEntry.ModifyDate, 0));
  637. set
  638. {
  639. if (DirEntry.StgType != StgType.StgStream && DirEntry.StgType != StgType.StgRoot)
  640. DirEntry.ModifyDate = BitConverter.GetBytes((value.ToFileTime()));
  641. else
  642. throw new Exception("Modify Date can only be set on storage entries");
  643. }
  644. }
  645. public Guid CLSID
  646. {
  647. get => DirEntry.StorageClsid;
  648. set
  649. {
  650. if (DirEntry.StgType != StgType.StgStream)
  651. DirEntry.StorageClsid = value;
  652. else
  653. throw new Exception("Object class GUID can only be set on Root and Storage entries");
  654. }
  655. }
  656. int IComparable<CFItem>.CompareTo(CFItem other) => DirEntry.CompareTo(other.DirEntry);
  657. public override string ToString()
  658. {
  659. return DirEntry != null ? $"[{DirEntry.LeftSibling},{DirEntry.Sid},{DirEntry.RightSibling}] {DirEntry.GetEntryName()}" : string.Empty;
  660. }
  661. }
  662. internal sealed class CFStream : CFItem
  663. {
  664. internal CFStream(CompoundFile compoundFile, DirectoryEntry dirEntry) : base(compoundFile)
  665. {
  666. if (dirEntry == null || dirEntry.Sid < 0)
  667. throw new Exception("Attempting to add a CFStream using an unitialized directory");
  668. DirEntry = dirEntry;
  669. }
  670. public byte[] GetData()
  671. {
  672. CheckDisposed();
  673. return CompoundFile.GetData(this);
  674. }
  675. public int Read(byte[] buffer, long position, int count)
  676. {
  677. CheckDisposed();
  678. return CompoundFile.ReadData(this, position, buffer, 0, count);
  679. }
  680. internal int Read(byte[] buffer, long position, int offset, int count)
  681. {
  682. CheckDisposed();
  683. return CompoundFile.ReadData(this, position, buffer, offset, count);
  684. }
  685. }
  686. internal sealed class CFStorage : CFItem
  687. {
  688. private RBTree children;
  689. internal RBTree Children => children ??= LoadChildren(DirEntry.Sid) ?? CompoundFile.CreateNewTree();
  690. internal CFStorage(CompoundFile compFile, DirectoryEntry dirEntry) : base(compFile)
  691. {
  692. if (dirEntry == null || dirEntry.Sid < 0)
  693. throw new Exception("Attempting to create a CFStorage using an unitialized directory");
  694. DirEntry = dirEntry;
  695. }
  696. private RBTree LoadChildren(int SID)
  697. {
  698. var childrenTree = CompoundFile.GetChildrenTree(SID);
  699. DirEntry.Child = (childrenTree.Root as DirectoryEntry)?.Sid ?? DirectoryEntry.Nostream;
  700. return childrenTree;
  701. }
  702. public CFStream GetStream(String streamName)
  703. {
  704. CheckDisposed();
  705. var tmp = DirectoryEntry.Mock(streamName, StgType.StgStream);
  706. if (Children.TryLookup(tmp, out IRBNode outDe) && (((DirectoryEntry)outDe).StgType == StgType.StgStream))
  707. return new CFStream(CompoundFile, (DirectoryEntry)outDe);
  708. throw new KeyNotFoundException("Cannot find item [" + streamName + "] within the current storage");
  709. }
  710. public CFStream TryGetStream(String streamName)
  711. {
  712. CheckDisposed();
  713. var tmp = DirectoryEntry.Mock(streamName, StgType.StgStream);
  714. if (Children.TryLookup(tmp, out IRBNode outDe) && ((outDe as DirectoryEntry).StgType == StgType.StgStream))
  715. return new CFStream(CompoundFile, (DirectoryEntry)outDe);
  716. return null;
  717. }
  718. public CFStorage GetStorage(String storageName)
  719. {
  720. CheckDisposed();
  721. var template = DirectoryEntry.Mock(storageName, StgType.StgInvalid);
  722. if (Children.TryLookup(template, out var outDe) && (outDe as DirectoryEntry).StgType == StgType.StgStorage)
  723. return new CFStorage(CompoundFile, outDe as DirectoryEntry);
  724. else
  725. throw new KeyNotFoundException("Cannot find item [" + storageName + "] within the current storage");
  726. }
  727. public CFStorage TryGetStorage(string storageName)
  728. {
  729. CheckDisposed();
  730. var template = DirectoryEntry.Mock(storageName, StgType.StgInvalid);
  731. if (Children.TryLookup(template, out var outDe) && ((DirectoryEntry)outDe).StgType == StgType.StgStorage)
  732. return new CFStorage(CompoundFile, outDe as DirectoryEntry);
  733. return null;
  734. }
  735. public void VisitEntries(Action<CFItem> action, bool recursive)
  736. {
  737. CheckDisposed();
  738. if (action != null)
  739. {
  740. var subStorages = new List<IRBNode>();
  741. void internalAction(IRBNode targetNode)
  742. {
  743. DirectoryEntry d = targetNode as DirectoryEntry;
  744. if (d.StgType == StgType.StgStream)
  745. action(new CFStream(CompoundFile, d));
  746. else
  747. action(new CFStorage(CompoundFile, d));
  748. if (d.Child != DirectoryEntry.Nostream)
  749. subStorages.Add(targetNode);
  750. }
  751. Children.VisitTreeNodes(internalAction);
  752. if (recursive && subStorages.Count > 0)
  753. foreach (var n in subStorages)
  754. new CFStorage(CompoundFile, n as DirectoryEntry).VisitEntries(action, recursive);
  755. }
  756. }
  757. }
  758. internal class CFItemComparer : IComparer<CFItem>
  759. {
  760. public int Compare(CFItem x, CFItem y) => (x.DirEntry.CompareTo(y.DirEntry));
  761. }
  762. internal sealed class Header
  763. {
  764. public byte[] HeaderSignature { get; private set; } = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
  765. public byte[] CLSID { get; set; } = new byte[16];
  766. public ushort MinorVersion { get; private set; } = 0x003E;
  767. public ushort MajorVersion { get; private set; } = 0x0003;
  768. public ushort ByteOrder { get; private set; } = 0xFFFE;
  769. public ushort SectorShift { get; private set; } = 9;
  770. public ushort MiniSectorShift { get; private set; } = 6;
  771. public int DirectorySectorsNumber { get; set; }
  772. public int FATSectorsNumber { get; set; }
  773. public int FirstDirectorySectorID { get; set; } = Sector.Endofchain;
  774. public uint MinSizeStandardStream { get; set; } = 4096;
  775. public int FirstMiniFATSectorID { get; set; } = unchecked((int)0xFFFFFFFE);
  776. public uint MiniFATSectorsNumber { get; set; }
  777. public int FirstDIFATSectorID { get; set; } = Sector.Endofchain;
  778. public uint DIFATSectorsNumber { get; set; }
  779. public int[] DIFAT { get; } = new int[109];
  780. public Header() : this(3)
  781. {
  782. }
  783. public Header(ushort version)
  784. {
  785. switch (version)
  786. {
  787. case 3:
  788. MajorVersion = 3;
  789. SectorShift = 0x0009;
  790. break;
  791. case 4:
  792. MajorVersion = 4;
  793. SectorShift = 0x000C;
  794. break;
  795. default:
  796. throw new Exception("Invalid Compound File Format version");
  797. }
  798. for (int i = 0; i < 109; i++)
  799. DIFAT[i] = Sector.Freesect;
  800. }
  801. public void Read(Stream stream)
  802. {
  803. var rw = new BinaryReader(stream, Encoding.UTF8, true);
  804. HeaderSignature = rw.ReadBytes(8);
  805. CheckSignature();
  806. CLSID = rw.ReadBytes(16);
  807. MinorVersion = rw.ReadUInt16();
  808. MajorVersion = rw.ReadUInt16();
  809. CheckVersion();
  810. ByteOrder = rw.ReadUInt16();
  811. SectorShift = rw.ReadUInt16();
  812. MiniSectorShift = rw.ReadUInt16();
  813. rw.ReadBytes(6);
  814. DirectorySectorsNumber = rw.ReadInt32();
  815. FATSectorsNumber = rw.ReadInt32();
  816. FirstDirectorySectorID = rw.ReadInt32();
  817. rw.ReadUInt32();
  818. MinSizeStandardStream = rw.ReadUInt32();
  819. FirstMiniFATSectorID = rw.ReadInt32();
  820. MiniFATSectorsNumber = rw.ReadUInt32();
  821. FirstDIFATSectorID = rw.ReadInt32();
  822. DIFATSectorsNumber = rw.ReadUInt32();
  823. for (int i = 0; i < 109; i++)
  824. DIFAT[i] = rw.ReadInt32();
  825. }
  826. private void CheckVersion()
  827. {
  828. if (MajorVersion != 3 && MajorVersion != 4)
  829. throw new InvalidDataException("Unsupported Binary File Format version: OpenMcdf only supports Compound Files with major version equal to 3 or 4 ");
  830. }
  831. private byte[] OLE_CFS_SIGNATURE = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
  832. private void CheckSignature()
  833. {
  834. for (int i = 0; i < HeaderSignature.Length; i++)
  835. if (HeaderSignature[i] != OLE_CFS_SIGNATURE[i])
  836. throw new InvalidDataException("Invalid OLE structured storage file");
  837. }
  838. }
  839. internal sealed class StreamView : Stream
  840. {
  841. private readonly int _sectorSize;
  842. private long _position;
  843. private readonly List<Sector> _sectorChain;
  844. private readonly Stream _stream;
  845. private readonly bool _isFatStream = false;
  846. private readonly List<Sector> _freeSectors = [];
  847. public IEnumerable<Sector> FreeSectors => _freeSectors;
  848. public StreamView(List<Sector> sectorChain, int sectorSize, Stream stream)
  849. {
  850. if (sectorSize <= 0)
  851. throw new Exception("Sector size must be greater than zero");
  852. _sectorChain = sectorChain ?? throw new Exception("Sector Chain cannot be null");
  853. _sectorSize = sectorSize;
  854. _stream = stream;
  855. }
  856. public StreamView(List<Sector> sectorChain, int sectorSize, long length, Queue<Sector> availableSectors, Stream stream, bool isFatStream = false) : this(sectorChain, sectorSize, stream)
  857. {
  858. _isFatStream = isFatStream;
  859. AdjustLength(length, availableSectors);
  860. }
  861. public List<Sector> BaseSectorChain => _sectorChain;
  862. public override bool CanRead => true;
  863. public override bool CanSeek => true;
  864. public override bool CanWrite => true;
  865. public override void Flush()
  866. { }
  867. private long _length;
  868. public override long Length => _length;
  869. public override long Position
  870. {
  871. get => _position;
  872. set
  873. {
  874. if (_position > _length - 1)
  875. throw new ArgumentOutOfRangeException("value");
  876. _position = value;
  877. }
  878. }
  879. private byte[] buf = new byte[4];
  880. public int ReadInt32()
  881. {
  882. Read(buf, 0, 4);
  883. return (((buf[0] | (buf[1] << 8)) | (buf[2] << 16)) | (buf[3] << 24));
  884. }
  885. public override int Read(byte[] buffer, int offset, int count)
  886. {
  887. int nRead = 0;
  888. if (_sectorChain is { Count: > 0 })
  889. {
  890. // First sector
  891. int secIndex = (int)(_position / _sectorSize);
  892. var nToRead = Math.Min(_sectorChain[0].Size - ((int)_position % _sectorSize), count);
  893. if (secIndex < _sectorChain.Count)
  894. {
  895. Buffer.BlockCopy(_sectorChain[secIndex].GetData(), (int)(_position % _sectorSize), buffer, offset, nToRead);
  896. }
  897. nRead += nToRead;
  898. ++secIndex;
  899. // Central sectors
  900. while (nRead < (count - _sectorSize))
  901. {
  902. nToRead = _sectorSize;
  903. Buffer.BlockCopy(_sectorChain[secIndex].GetData(), 0, buffer, offset + nRead, nToRead);
  904. nRead += nToRead;
  905. ++secIndex;
  906. }
  907. // Last sector
  908. nToRead = count - nRead;
  909. if (nToRead != 0)
  910. {
  911. Buffer.BlockCopy(_sectorChain[secIndex].GetData(), 0, buffer, offset + nRead, nToRead);
  912. nRead += nToRead;
  913. }
  914. _position += nRead;
  915. return nRead;
  916. }
  917. return 0;
  918. }
  919. public override long Seek(long offset, SeekOrigin origin)
  920. {
  921. switch (origin)
  922. {
  923. case SeekOrigin.Begin: _position = offset; break;
  924. case SeekOrigin.Current: _position += offset; break;
  925. case SeekOrigin.End: _position = Length - offset; break;
  926. }
  927. AdjustLength(_position);
  928. return _position;
  929. }
  930. private void AdjustLength(long value, Queue<Sector> availableSectors = null)
  931. {
  932. _length = value;
  933. long delta = value - _sectorChain.Count * (long)_sectorSize;
  934. if (delta > 0)
  935. {
  936. int nSec = (int)Math.Ceiling(((double)delta / _sectorSize));
  937. while (nSec > 0)
  938. {
  939. Sector t;
  940. if (availableSectors == null || availableSectors.Count == 0)
  941. {
  942. t = new Sector(_sectorSize, _stream);
  943. if (_sectorSize == Sector.MinisectorSize)
  944. t.Type = SectorType.Mini;
  945. }
  946. else
  947. t = availableSectors.Dequeue();
  948. if (_isFatStream)
  949. t.InitFATData();
  950. _sectorChain.Add(t);
  951. nSec--;
  952. }
  953. }
  954. }
  955. public override void SetLength(long value) => AdjustLength(value);
  956. public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
  957. }
  958. [Flags]
  959. internal enum CFSConfiguration
  960. {
  961. Default = 1,
  962. LeaveOpen = 16,
  963. }
  964. internal sealed class CompoundFile : IDisposable
  965. {
  966. public CFSConfiguration Configuration { get; private set; } = CFSConfiguration.Default;
  967. internal int GetSectorSize() => 2 << (_header.SectorShift - 1);
  968. private const int HeaderDifatEntriesCount = 109;
  969. private readonly int _difatSectorFatEntriesCount = 127;
  970. private readonly int _fatSectorEntriesCount = 128;
  971. private const int SizeOfSid = 4;
  972. private const int FlushingQueueSize = 6000;
  973. private const int FlushingBufferMaxSize = 1024 * 1024 * 16;
  974. private List<Sector> _sectors = [];
  975. private Header _header;
  976. internal Stream SourceStream;
  977. public CompoundFile(Stream stream, CFSConfiguration configParameters)
  978. {
  979. closeStream = !configParameters.HasFlag(CFSConfiguration.LeaveOpen);
  980. LoadStream(stream);
  981. _difatSectorFatEntriesCount = (GetSectorSize() / 4) - 1;
  982. _fatSectorEntriesCount = (GetSectorSize() / 4);
  983. }
  984. private void Load(Stream stream)
  985. {
  986. try
  987. {
  988. _header = new Header();
  989. directoryEntries = new List<DirectoryEntry>();
  990. SourceStream = stream;
  991. _header.Read(stream);
  992. int nSector = Ceiling((stream.Length - GetSectorSize()) / (double)GetSectorSize());
  993. if (stream.Length > 0x7FFFFF0)
  994. _transactionLockAllocated = true;
  995. _sectors = [];
  996. for (int i = 0; i < nSector; i++)
  997. _sectors.Add(null);
  998. LoadDirectories();
  999. RootStorage = new CFStorage(this, directoryEntries[0]);
  1000. }
  1001. catch (Exception)
  1002. {
  1003. if (stream != null && closeStream)
  1004. stream.Dispose();
  1005. throw;
  1006. }
  1007. }
  1008. private void LoadStream(Stream stream)
  1009. {
  1010. if (stream == null)
  1011. throw new Exception("Stream parameter cannot be null");
  1012. if (!stream.CanSeek)
  1013. throw new Exception("Cannot load a non-seekable Stream");
  1014. stream.Seek(0, SeekOrigin.Begin);
  1015. Load(stream);
  1016. }
  1017. private void AllocateMiniSectorChain(List<Sector> sectorChain)
  1018. {
  1019. List<Sector> miniFAT = GetSectorChain(_header.FirstMiniFATSectorID, SectorType.Normal);
  1020. List<Sector> miniStream = GetSectorChain(RootEntry.StartSetc, SectorType.Normal);
  1021. StreamView miniFATView = new StreamView(miniFAT, GetSectorSize(),
  1022. _header.MiniFATSectorsNumber * Sector.MinisectorSize,
  1023. null, SourceStream, true);
  1024. StreamView miniStreamView = new StreamView(miniStream, GetSectorSize(),
  1025. RootStorage.Size, null, SourceStream);
  1026. for (int i = 0; i < sectorChain.Count; i++)
  1027. {
  1028. Sector s = sectorChain[i];
  1029. if (s.Id == -1)
  1030. {
  1031. miniStreamView.Seek(RootStorage.Size + Sector.MinisectorSize, SeekOrigin.Begin);
  1032. s.Id = (int)(miniStreamView.Position - Sector.MinisectorSize) / Sector.MinisectorSize;
  1033. RootStorage.DirEntry.Size = miniStreamView.Length;
  1034. }
  1035. }
  1036. for (int i = 0; i < sectorChain.Count - 1; i++)
  1037. {
  1038. int currentId = sectorChain[i].Id;
  1039. int nextId = sectorChain[i + 1].Id;
  1040. miniFATView.Seek(currentId * 4, SeekOrigin.Begin);
  1041. miniFATView.Write(BitConverter.GetBytes(nextId), 0, 4);
  1042. }
  1043. miniFATView.Seek(sectorChain[sectorChain.Count - 1].Id * SizeOfSid, SeekOrigin.Begin);
  1044. miniFATView.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
  1045. AllocateSectorChain(miniStreamView.BaseSectorChain);
  1046. AllocateSectorChain(miniFATView.BaseSectorChain);
  1047. if (miniFAT.Count > 0)
  1048. {
  1049. RootStorage.DirEntry.StartSetc = miniStream[0].Id;
  1050. _header.MiniFATSectorsNumber = (uint)miniFAT.Count;
  1051. _header.FirstMiniFATSectorID = miniFAT[0].Id;
  1052. }
  1053. }
  1054. private void SetSectorChain(List<Sector> sectorChain)
  1055. {
  1056. if (sectorChain == null || sectorChain.Count == 0)
  1057. return;
  1058. SectorType _st = sectorChain[0].Type;
  1059. if (_st == SectorType.Normal)
  1060. AllocateSectorChain(sectorChain);
  1061. else if (_st == SectorType.Mini)
  1062. AllocateMiniSectorChain(sectorChain);
  1063. }
  1064. private void AllocateSectorChain(List<Sector> sectorChain)
  1065. {
  1066. foreach (Sector s in sectorChain)
  1067. {
  1068. if (s.Id == -1)
  1069. {
  1070. _sectors.Add(s);
  1071. s.Id = _sectors.Count - 1;
  1072. }
  1073. }
  1074. AllocateFATSectorChain(sectorChain);
  1075. }
  1076. internal bool _transactionLockAdded = false;
  1077. internal int _lockSectorId = -1;
  1078. internal bool _transactionLockAllocated = false;
  1079. private void CheckForLockSector()
  1080. {
  1081. if (_transactionLockAdded && !_transactionLockAllocated)
  1082. {
  1083. StreamView fatStream = new StreamView(GetFatSectorChain(), GetSectorSize(), SourceStream);
  1084. fatStream.Seek(_lockSectorId * 4, SeekOrigin.Begin);
  1085. fatStream.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
  1086. _transactionLockAllocated = true;
  1087. }
  1088. }
  1089. private void AllocateFATSectorChain(List<Sector> sectorChain)
  1090. {
  1091. List<Sector> fatSectors = GetSectorChain(-1, SectorType.FAT);
  1092. StreamView fatStream = new StreamView(fatSectors, GetSectorSize(),
  1093. _header.FATSectorsNumber * GetSectorSize(), null,
  1094. SourceStream, true);
  1095. for (int i = 0; i < sectorChain.Count - 1; i++)
  1096. {
  1097. Sector sN = sectorChain[i + 1];
  1098. Sector sC = sectorChain[i];
  1099. fatStream.Seek(sC.Id * 4, SeekOrigin.Begin);
  1100. fatStream.Write(BitConverter.GetBytes(sN.Id), 0, 4);
  1101. }
  1102. fatStream.Seek(sectorChain[sectorChain.Count - 1].Id * 4, SeekOrigin.Begin);
  1103. fatStream.Write(BitConverter.GetBytes(Sector.Endofchain), 0, 4);
  1104. AllocateDIFATSectorChain(fatStream.BaseSectorChain);
  1105. }
  1106. private void AllocateDIFATSectorChain(List<Sector> FATsectorChain)
  1107. {
  1108. _header.FATSectorsNumber = FATsectorChain.Count;
  1109. foreach (Sector s in FATsectorChain)
  1110. {
  1111. if (s.Id == -1)
  1112. {
  1113. _sectors.Add(s);
  1114. s.Id = _sectors.Count - 1;
  1115. s.Type = SectorType.FAT;
  1116. }
  1117. }
  1118. int nCurrentSectors = _sectors.Count;
  1119. int nDIFATSectors = (int)_header.DIFATSectorsNumber;
  1120. if (FATsectorChain.Count > HeaderDifatEntriesCount)
  1121. {
  1122. nDIFATSectors = Ceiling((double)(FATsectorChain.Count - HeaderDifatEntriesCount) / _difatSectorFatEntriesCount);
  1123. nDIFATSectors = LowSaturation(nDIFATSectors - (int)_header.DIFATSectorsNumber); //required DIFAT
  1124. }
  1125. nCurrentSectors += nDIFATSectors;
  1126. while (_header.FATSectorsNumber * _fatSectorEntriesCount < nCurrentSectors)
  1127. {
  1128. Sector extraFATSector = new Sector(GetSectorSize(), SourceStream);
  1129. _sectors.Add(extraFATSector);
  1130. extraFATSector.Id = _sectors.Count - 1;
  1131. extraFATSector.Type = SectorType.FAT;
  1132. FATsectorChain.Add(extraFATSector);
  1133. _header.FATSectorsNumber++;
  1134. nCurrentSectors++;
  1135. if (nDIFATSectors * _difatSectorFatEntriesCount < (_header.FATSectorsNumber > HeaderDifatEntriesCount ? _header.FATSectorsNumber - HeaderDifatEntriesCount : 0))
  1136. {
  1137. nDIFATSectors++;
  1138. nCurrentSectors++;
  1139. }
  1140. }
  1141. var difatSectors = GetSectorChain(-1, SectorType.DIFAT);
  1142. var difatStream = new StreamView(difatSectors, GetSectorSize(), SourceStream);
  1143. for (int i = 0; i < FATsectorChain.Count; i++)
  1144. {
  1145. if (i < HeaderDifatEntriesCount)
  1146. _header.DIFAT[i] = FATsectorChain[i].Id;
  1147. else
  1148. {
  1149. if (i != HeaderDifatEntriesCount && (i - HeaderDifatEntriesCount) % _difatSectorFatEntriesCount == 0)
  1150. {
  1151. difatStream.Write(new byte[sizeof(int)], 0, sizeof(int));
  1152. }
  1153. difatStream.Write(BitConverter.GetBytes(FATsectorChain[i].Id), 0, sizeof(int));
  1154. }
  1155. }
  1156. for (int i = 0; i < difatStream.BaseSectorChain.Count; i++)
  1157. {
  1158. if (difatStream.BaseSectorChain[i].Id == -1)
  1159. {
  1160. _sectors.Add(difatStream.BaseSectorChain[i]);
  1161. difatStream.BaseSectorChain[i].Id = _sectors.Count - 1;
  1162. difatStream.BaseSectorChain[i].Type = SectorType.DIFAT;
  1163. }
  1164. }
  1165. _header.DIFATSectorsNumber = (uint)nDIFATSectors;
  1166. if (difatStream.BaseSectorChain != null && difatStream.BaseSectorChain.Count > 0)
  1167. {
  1168. _header.FirstDIFATSectorID = difatStream.BaseSectorChain[0].Id;
  1169. _header.DIFATSectorsNumber = (uint)difatStream.BaseSectorChain.Count;
  1170. for (int i = 0; i < difatStream.BaseSectorChain.Count - 1; i++)
  1171. Buffer.BlockCopy(BitConverter.GetBytes(difatStream.BaseSectorChain[i + 1].Id), 0, difatStream.BaseSectorChain[i].GetData(), GetSectorSize() - sizeof(int), 4);
  1172. Buffer.BlockCopy(BitConverter.GetBytes(Sector.Endofchain), 0, difatStream.BaseSectorChain[difatStream.BaseSectorChain.Count - 1].GetData(), GetSectorSize() - sizeof(int), sizeof(int));
  1173. }
  1174. else _header.FirstDIFATSectorID = Sector.Endofchain;
  1175. StreamView fatSv = new StreamView(FATsectorChain, GetSectorSize(), _header.FATSectorsNumber * GetSectorSize(), null, SourceStream);
  1176. for (int i = 0; i < _header.DIFATSectorsNumber; i++)
  1177. {
  1178. fatSv.Seek(difatStream.BaseSectorChain[i].Id * 4, SeekOrigin.Begin);
  1179. fatSv.Write(BitConverter.GetBytes(Sector.Difsect), 0, 4);
  1180. }
  1181. for (int i = 0; i < _header.FATSectorsNumber; i++)
  1182. {
  1183. fatSv.Seek(fatSv.BaseSectorChain[i].Id * 4, SeekOrigin.Begin);
  1184. fatSv.Write(BitConverter.GetBytes(Sector.Fatsect), 0, 4);
  1185. }
  1186. _header.FATSectorsNumber = fatSv.BaseSectorChain.Count;
  1187. }
  1188. private List<Sector> GetDifatSectorChain()
  1189. {
  1190. List<Sector> result = new List<Sector>();
  1191. int nextSecID;
  1192. if (_header.DIFATSectorsNumber != 0)
  1193. {
  1194. var validationCount = (int)_header.DIFATSectorsNumber;
  1195. Sector s = _sectors[_header.FirstDIFATSectorID];
  1196. if (s == null)
  1197. {
  1198. _sectors[_header.FirstDIFATSectorID] = s = new Sector(GetSectorSize(), SourceStream)
  1199. {
  1200. Type = SectorType.DIFAT,
  1201. Id = _header.FirstDIFATSectorID
  1202. };
  1203. }
  1204. result.Add(s);
  1205. while (validationCount >= 0)
  1206. {
  1207. nextSecID = BitConverter.ToInt32(s.GetData(), GetSectorSize() - 4);
  1208. if (nextSecID == Sector.Freesect || nextSecID == Sector.Endofchain) break;
  1209. validationCount--;
  1210. if (validationCount < 0)
  1211. {
  1212. Dispose();
  1213. throw new InvalidDataException("DIFAT sectors count mismatched. Corrupted compound file");
  1214. }
  1215. s = _sectors[nextSecID];
  1216. if (s == null)
  1217. _sectors[nextSecID] = s = new Sector(GetSectorSize(), SourceStream) { Id = nextSecID };
  1218. result.Add(s);
  1219. }
  1220. }
  1221. return result;
  1222. }
  1223. private List<Sector> GetFatSectorChain()
  1224. {
  1225. const int nHeaderFatEntry = 109;
  1226. List<Sector> result = [];
  1227. int nextSecId;
  1228. List<Sector> difatSectors = GetDifatSectorChain();
  1229. int idx = 0;
  1230. while (idx < _header.FATSectorsNumber && idx < nHeaderFatEntry)
  1231. {
  1232. nextSecId = _header.DIFAT[idx];
  1233. Sector s = _sectors[nextSecId];
  1234. if (s == null)
  1235. {
  1236. _sectors[nextSecId] = s = new Sector(GetSectorSize(), SourceStream)
  1237. {
  1238. Id = nextSecId,
  1239. Type = SectorType.FAT
  1240. };
  1241. }
  1242. result.Add(s);
  1243. ++idx;
  1244. }
  1245. if (difatSectors.Count > 0)
  1246. {
  1247. var difatStream = new StreamView(difatSectors, GetSectorSize(), _header.FATSectorsNumber > nHeaderFatEntry ? (_header.FATSectorsNumber - nHeaderFatEntry) * 4 : 0, null, SourceStream);
  1248. byte[] nextDifatSectorBuffer = new byte[4];
  1249. difatStream.Read(nextDifatSectorBuffer, 0, 4);
  1250. nextSecId = BitConverter.ToInt32(nextDifatSectorBuffer, 0);
  1251. int i = 0;
  1252. int nFat = nHeaderFatEntry;
  1253. while (nFat < _header.FATSectorsNumber)
  1254. {
  1255. if (difatStream.Position == ((GetSectorSize() - 4) + i * GetSectorSize()))
  1256. {
  1257. difatStream.Seek(4, SeekOrigin.Current);
  1258. ++i;
  1259. continue;
  1260. }
  1261. Sector s = _sectors[nextSecId];
  1262. if (s == null)
  1263. {
  1264. _sectors[nextSecId] = s = new Sector(GetSectorSize(), SourceStream)
  1265. {
  1266. Type = SectorType.FAT,
  1267. Id = nextSecId
  1268. };
  1269. }
  1270. result.Add(s);
  1271. difatStream.Read(nextDifatSectorBuffer, 0, 4);
  1272. nextSecId = BitConverter.ToInt32(nextDifatSectorBuffer, 0);
  1273. nFat++;
  1274. }
  1275. }
  1276. return result;
  1277. }
  1278. private List<Sector> GetNormalSectorChain(int secID)
  1279. {
  1280. var result = new List<Sector>();
  1281. int nextSecId = secID;
  1282. var fatSectors = GetFatSectorChain();
  1283. var fatStream = new StreamView(fatSectors, GetSectorSize(), fatSectors.Count * GetSectorSize(), null, SourceStream);
  1284. while (true)
  1285. {
  1286. if (nextSecId == Sector.Endofchain) break;
  1287. if (nextSecId < 0)
  1288. throw new InvalidDataException($"Next Sector ID reference is below zero. NextID : {nextSecId}");
  1289. if (nextSecId >= _sectors.Count)
  1290. throw new InvalidDataException($"Next Sector ID reference an out of range sector. NextID : {nextSecId} while sector count {_sectors.Count}");
  1291. Sector s = _sectors[nextSecId];
  1292. if (s == null)
  1293. {
  1294. _sectors[nextSecId] = s = new Sector(GetSectorSize(), SourceStream)
  1295. {
  1296. Id = nextSecId,
  1297. Type = SectorType.Normal
  1298. };
  1299. }
  1300. result.Add(s);
  1301. fatStream.Seek(nextSecId * 4, SeekOrigin.Begin);
  1302. int next = fatStream.ReadInt32();
  1303. if (next != nextSecId)
  1304. nextSecId = next;
  1305. else
  1306. throw new InvalidDataException("Cyclic sector chain found. File is corrupted");
  1307. }
  1308. return result;
  1309. }
  1310. private List<Sector> GetMiniSectorChain(int secID)
  1311. {
  1312. List<Sector> result = [];
  1313. if (secID != Sector.Endofchain)
  1314. {
  1315. var miniFat = GetNormalSectorChain(_header.FirstMiniFATSectorID);
  1316. var miniStream = GetNormalSectorChain(RootEntry.StartSetc);
  1317. var miniFatView = new StreamView(miniFat, GetSectorSize(), _header.MiniFATSectorsNumber * Sector.MinisectorSize, null, SourceStream);
  1318. var miniStreamView = new StreamView(miniStream, GetSectorSize(), RootStorage.Size, null, SourceStream);
  1319. var miniFatReader = new BinaryReader(miniFatView);
  1320. var nextSecId = secID;
  1321. while (true)
  1322. {
  1323. if (nextSecId == Sector.Endofchain)
  1324. break;
  1325. var ms = new Sector(Sector.MinisectorSize, SourceStream);
  1326. ms.Id = nextSecId;
  1327. ms.Type = SectorType.Mini;
  1328. miniStreamView.Seek(nextSecId * Sector.MinisectorSize, SeekOrigin.Begin);
  1329. miniStreamView.Read(ms.GetData(), 0, Sector.MinisectorSize);
  1330. result.Add(ms);
  1331. miniFatView.Seek(nextSecId * 4, SeekOrigin.Begin);
  1332. nextSecId = miniFatReader.ReadInt32();
  1333. }
  1334. }
  1335. return result;
  1336. }
  1337. internal List<Sector> GetSectorChain(int secID, SectorType chainType)
  1338. {
  1339. switch (chainType)
  1340. {
  1341. case SectorType.DIFAT:
  1342. return GetDifatSectorChain();
  1343. case SectorType.FAT:
  1344. return GetFatSectorChain();
  1345. case SectorType.Normal:
  1346. return GetNormalSectorChain(secID);
  1347. case SectorType.Mini:
  1348. return GetMiniSectorChain(secID);
  1349. default:
  1350. throw new Exception("Unsupproted chain type");
  1351. }
  1352. }
  1353. public CFStorage RootStorage { get; private set; }
  1354. public int Version => _header.MajorVersion;
  1355. internal RBTree CreateNewTree()
  1356. {
  1357. return new RBTree();
  1358. }
  1359. internal RBTree GetChildrenTree(int sid)
  1360. {
  1361. RBTree bst = new RBTree();
  1362. DoLoadChildren(bst, directoryEntries[sid]);
  1363. return bst;
  1364. }
  1365. private RBTree DoLoadChildrenTrusted(DirectoryEntry de)
  1366. {
  1367. RBTree bst = null;
  1368. if (de.Child != DirectoryEntry.Nostream)
  1369. bst = new RBTree(directoryEntries[de.Child]);
  1370. return bst;
  1371. }
  1372. private void DoLoadChildren(RBTree bst, DirectoryEntry de)
  1373. {
  1374. if (de.Child != DirectoryEntry.Nostream)
  1375. {
  1376. if (directoryEntries[de.Child].StgType == StgType.StgInvalid) return;
  1377. LoadSiblings(bst, directoryEntries[de.Child]);
  1378. NullifyChildNodes(directoryEntries[de.Child]);
  1379. bst.Insert(directoryEntries[de.Child]);
  1380. }
  1381. }
  1382. private void NullifyChildNodes(DirectoryEntry de)
  1383. {
  1384. de.Parent = null;
  1385. de.Left = null;
  1386. de.Right = null;
  1387. }
  1388. private readonly List<int> _levelSiDs = [];
  1389. private void LoadSiblings(RBTree bst, DirectoryEntry de)
  1390. {
  1391. _levelSiDs.Clear();
  1392. if (de.LeftSibling != DirectoryEntry.Nostream)
  1393. DoLoadSiblings(bst, directoryEntries[de.LeftSibling]);
  1394. if (de.RightSibling != DirectoryEntry.Nostream)
  1395. {
  1396. _levelSiDs.Add(de.RightSibling);
  1397. DoLoadSiblings(bst, directoryEntries[de.RightSibling]);
  1398. }
  1399. }
  1400. private void DoLoadSiblings(RBTree bst, DirectoryEntry de)
  1401. {
  1402. if (ValidateSibling(de.LeftSibling))
  1403. {
  1404. _levelSiDs.Add(de.LeftSibling);
  1405. DoLoadSiblings(bst, directoryEntries[de.LeftSibling]);
  1406. }
  1407. if (ValidateSibling(de.RightSibling))
  1408. {
  1409. _levelSiDs.Add(de.RightSibling);
  1410. DoLoadSiblings(bst, directoryEntries[de.RightSibling]);
  1411. }
  1412. NullifyChildNodes(de);
  1413. bst.Insert(de);
  1414. }
  1415. private bool ValidateSibling(int sid)
  1416. {
  1417. if (sid != DirectoryEntry.Nostream)
  1418. {
  1419. if (sid >= directoryEntries.Count)
  1420. return false;
  1421. if (directoryEntries[sid].StgType == StgType.StgInvalid)
  1422. return false;
  1423. if (!Enum.IsDefined(typeof(StgType), directoryEntries[sid].StgType))
  1424. return false;
  1425. if (_levelSiDs.Contains(sid))
  1426. throw new InvalidDataException("Cyclic reference of directory item");
  1427. return true;
  1428. }
  1429. return false;
  1430. }
  1431. private void LoadDirectories()
  1432. {
  1433. List<Sector> directoryChain = GetSectorChain(_header.FirstDirectorySectorID, SectorType.Normal);
  1434. if (_header.FirstDirectorySectorID == Sector.Endofchain)
  1435. _header.FirstDirectorySectorID = directoryChain[0].Id;
  1436. var dirReader = new StreamView(directoryChain, GetSectorSize(), directoryChain.Count * GetSectorSize(), null, SourceStream);
  1437. while (dirReader.Position < directoryChain.Count * GetSectorSize())
  1438. {
  1439. DirectoryEntry de = DirectoryEntry.New(String.Empty, StgType.StgInvalid, directoryEntries);
  1440. de.Read(dirReader, Version);
  1441. }
  1442. }
  1443. internal int ReadData(CFStream cFStream, long position, byte[] buffer, int offset, int count)
  1444. {
  1445. var de = cFStream.DirEntry;
  1446. count = (int)Math.Min(de.Size - offset, count);
  1447. var sView = de.Size < _header.MinSizeStandardStream ? new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, SourceStream) : new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, SourceStream);
  1448. sView.Seek(position, SeekOrigin.Begin);
  1449. return sView.Read(buffer, offset, count);
  1450. }
  1451. internal byte[] GetData(CFStream cFStream)
  1452. {
  1453. AssertDisposed();
  1454. byte[] result;
  1455. DirectoryEntry de = cFStream.DirEntry;
  1456. if (de.Size < _header.MinSizeStandardStream)
  1457. {
  1458. var miniView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, SourceStream);
  1459. using var br = new BinaryReader(miniView);
  1460. result = br.ReadBytes((int)de.Size);
  1461. }
  1462. else
  1463. {
  1464. var sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, SourceStream);
  1465. result = new byte[(int)de.Size];
  1466. sView.Read(result, 0, result.Length);
  1467. }
  1468. return result;
  1469. }
  1470. public byte[] GetDataBySID(int sid)
  1471. {
  1472. AssertDisposed();
  1473. if (sid < 0)
  1474. return null;
  1475. byte[] result;
  1476. try
  1477. {
  1478. DirectoryEntry de = directoryEntries[sid];
  1479. if (de.Size < _header.MinSizeStandardStream)
  1480. {
  1481. var miniView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MinisectorSize, de.Size, null, SourceStream);
  1482. var br = new BinaryReader(miniView);
  1483. result = br.ReadBytes((int)de.Size);
  1484. br.Dispose();
  1485. }
  1486. else
  1487. {
  1488. var sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, SourceStream);
  1489. result = new byte[(int)de.Size];
  1490. sView.Read(result, 0, result.Length);
  1491. }
  1492. }
  1493. catch
  1494. {
  1495. throw new Exception("Cannot get data for SID");
  1496. }
  1497. return result;
  1498. }
  1499. public Guid GetGuidBySID(int sid)
  1500. {
  1501. AssertDisposed();
  1502. if (sid < 0)
  1503. throw new Exception("Invalid SID");
  1504. var de = directoryEntries[sid];
  1505. return de.StorageClsid;
  1506. }
  1507. public Guid GetGuidForStream(int sid)
  1508. {
  1509. AssertDisposed();
  1510. if (sid < 0)
  1511. throw new Exception("Invalid SID");
  1512. var g = new Guid("00000000000000000000000000000000");
  1513. for (int i = sid - 1; i >= 0; i--)
  1514. {
  1515. if (directoryEntries[i].StorageClsid != g && directoryEntries[i].StgType == StgType.StgStorage)
  1516. return directoryEntries[i].StorageClsid;
  1517. }
  1518. return g;
  1519. }
  1520. private static int Ceiling(double d) => (int)Math.Ceiling(d);
  1521. private static int LowSaturation(int i) => i > 0 ? i : 0;
  1522. private bool closeStream = true;
  1523. internal bool IsClosed => _disposed;
  1524. #region IDisposable Members
  1525. private bool _disposed;//false
  1526. private object lockObject = new();
  1527. public void Dispose()
  1528. {
  1529. try
  1530. {
  1531. if (!_disposed)
  1532. {
  1533. lock (lockObject)
  1534. {
  1535. if (_sectors != null)
  1536. {
  1537. _sectors.Clear();
  1538. _sectors = null;
  1539. }
  1540. RootStorage = null;
  1541. _header = null;
  1542. directoryEntries.Clear();
  1543. directoryEntries = null;
  1544. }
  1545. if (SourceStream != null && closeStream && !Configuration.HasFlag(CFSConfiguration.LeaveOpen))
  1546. SourceStream.Dispose();
  1547. }
  1548. }
  1549. finally
  1550. {
  1551. _disposed = true;
  1552. }
  1553. GC.SuppressFinalize(this);
  1554. }
  1555. #endregion IDisposable Members
  1556. private List<DirectoryEntry> directoryEntries = [];
  1557. internal IList<DirectoryEntry> GetDirectories() => directoryEntries;
  1558. internal DirectoryEntry RootEntry => directoryEntries[0];
  1559. private IEnumerable<DirectoryEntry> FindDirectoryEntries(string entryName)
  1560. {
  1561. return directoryEntries.Where(d => d.GetEntryName() == entryName && d.StgType != StgType.StgInvalid).ToList();
  1562. }
  1563. public IList<CFItem> GetAllNamedEntries(string entryName)
  1564. {
  1565. var r = FindDirectoryEntries(entryName);
  1566. return (from id in r
  1567. where id.GetEntryName() == entryName && id.StgType != StgType.StgInvalid
  1568. select (CFItem)(id.StgType == StgType.StgStorage ? new CFStorage(this, id) : new CFStream(this, id))).ToList();
  1569. }
  1570. public int GetNumDirectories()
  1571. {
  1572. AssertDisposed();
  1573. return directoryEntries.Count;
  1574. }
  1575. public string GetNameDirEntry(int id)
  1576. {
  1577. AssertDisposed();
  1578. if (id < 0)
  1579. throw new Exception("Invalid Storage ID");
  1580. return directoryEntries[id].Name;
  1581. }
  1582. public StgType GetStorageType(int id)
  1583. {
  1584. AssertDisposed();
  1585. if (id < 0)
  1586. throw new Exception("Invalid Storage ID");
  1587. return directoryEntries[id].StgType;
  1588. }
  1589. private void AssertDisposed()
  1590. {
  1591. if (_disposed)
  1592. throw new ObjectDisposedException("Compound File closed: cannot access data");
  1593. }
  1594. }
  1595. #endregion Modified OpenMCDF
  1596. }