Client LuaCsForBarotrauma
SafeIO.cs
1 #nullable enable
2 
3 using System;
4 using System.Collections.Generic;
5 using System.Collections.Immutable;
6 using System.IO;
7 using System.Linq;
8 #if CLIENT
10 using Barotrauma.Steam;
11 #endif
12 
13 namespace Barotrauma.IO
14 {
15  static class Validation
16  {
17  private static readonly ImmutableArray<Identifier> unwritableDirs = new[] { "Content".ToIdentifier() }.ToImmutableArray();
18  private static readonly ImmutableArray<Identifier> unwritableExtensions = new[]
19  {
20  ".exe", ".dll", ".json", ".pdb", ".com", ".scr", ".dylib", ".so", ".a", ".app", //executables and libraries
21  ".bat", ".sh", //shell scripts
22  }.ToIdentifiers().ToImmutableArray();
23 
24  public ref struct Skipper
25  {
26  public void Dispose()
27  {
28  SkipValidationInDebugBuilds = false;
29  }
30  }
31 
35  public static Skipper SkipInDebugBuilds()
36  {
37  SkipValidationInDebugBuilds = true;
38  return new Skipper();
39  }
40 
44  public static bool SkipValidationInDebugBuilds;
45 
46  public static bool CanWrite(string path, bool isDirectory)
47  {
48  string getFullPath(string p)
49  => System.IO.Path.GetFullPath(p).CleanUpPath();
50 
51  path = getFullPath(path);
52  string localModsDir = getFullPath(ContentPackage.LocalModsDir);
53  string workshopModsDir = getFullPath(ContentPackage.WorkshopModsDir);
54 #if CLIENT
55  string workshopStagingDir = getFullPath(SteamManager.Workshop.PublishStagingDir);
56  string tempDownloadDir = getFullPath(ModReceiver.DownloadFolder);
57 #endif
58 
59  if (!isDirectory)
60  {
61  Identifier extension = System.IO.Path.GetExtension(path).Replace(" ", "").ToIdentifier();
62 
63  bool pathStartsWith(string prefix)
64  => path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
65 
66  if (!pathStartsWith(workshopModsDir)
67  && !pathStartsWith(localModsDir)
68 #if CLIENT
69  && !pathStartsWith(tempDownloadDir)
70  && !pathStartsWith(workshopStagingDir)
71 #endif
72  && unwritableExtensions.Any(e => e == extension))
73  {
74  return false;
75  }
76  }
77 
78  foreach (var unwritableDir in unwritableDirs)
79  {
80  string dir = System.IO.Path.GetFullPath(unwritableDir.Value).CleanUpPath();
81 
82  if (path.StartsWith(dir, StringComparison.InvariantCultureIgnoreCase))
83  {
84 #if DEBUG
85  return SkipValidationInDebugBuilds;
86 #else
87  return false;
88 #endif
89  }
90  }
91 
92  return true;
93  }
94  }
95 
96  public static class SafeXML
97  {
98  public static void SaveSafe(
99  this System.Xml.Linq.XDocument doc,
100  string path,
101  System.Xml.Linq.SaveOptions saveOptions = System.Xml.Linq.SaveOptions.None,
102  bool throwExceptions = false)
103  {
104  if (!Validation.CanWrite(path, false))
105  {
106  string errorMsg = $"Cannot save XML document to \"{path}\": modifying the files in this folder/with this extension is not allowed.";
107  if (throwExceptions)
108  {
109  throw new InvalidOperationException(errorMsg);
110  }
111  else
112  {
113  DebugConsole.ThrowError(errorMsg);
114  }
115  return;
116  }
117  doc.Save(path, saveOptions);
118  }
119 
120  public static void SaveSafe(this System.Xml.Linq.XElement element, string path, bool throwExceptions = false)
121  {
122  if (!Validation.CanWrite(path, false))
123  {
124  string errorMsg = $"Cannot save XML element to \"{path}\": modifying the files in this folder/with this extension is not allowed.";
125  if (throwExceptions)
126  {
127  throw new InvalidOperationException(errorMsg);
128  }
129  else
130  {
131  DebugConsole.ThrowError(errorMsg);
132  }
133  return;
134  }
135  element.Save(path);
136  }
137 
138  public static void SaveSafe(this System.Xml.Linq.XDocument doc, XmlWriter writer)
139  {
140  doc.WriteTo(writer);
141  }
142 
143  public static void WriteTo(this System.Xml.Linq.XDocument doc, XmlWriter writer)
144  {
145  writer.Write(doc);
146  }
147  }
148 
149  public class XmlWriter : IDisposable
150  {
151  public readonly System.Xml.XmlWriter? Writer;
152 
153  public XmlWriter(string path, System.Xml.XmlWriterSettings settings)
154  {
155  if (!Validation.CanWrite(path, false))
156  {
157  DebugConsole.ThrowError($"Cannot write XML document to \"{path}\": modifying the files in this folder/with this extension is not allowed.");
158  Writer = null;
159  return;
160  }
161  Writer = System.Xml.XmlWriter.Create(path, settings);
162  }
163 
164  public static XmlWriter Create(string path, System.Xml.XmlWriterSettings settings)
165  {
166  return new XmlWriter(path, settings);
167  }
168 
169  public void Write(System.Xml.Linq.XDocument doc)
170  {
171  if (Writer == null)
172  {
173  DebugConsole.ThrowError("Cannot write to invalid XmlWriter");
174  return;
175  }
176  doc.WriteTo(Writer);
177  }
178 
179  public void Flush()
180  {
181  if (Writer == null)
182  {
183  DebugConsole.ThrowError("Cannot flush invalid XmlWriter");
184  return;
185  }
186  Writer.Flush();
187  }
188 
189  public void Dispose()
190  {
191  if (Writer == null)
192  {
193  DebugConsole.ThrowError("Cannot dispose invalid XmlWriter");
194  return;
195  }
196  Writer.Dispose();
197  }
198  }
199 
200  public static class XmlWriterExtensions
201  {
202  public static void Save(this System.Xml.Linq.XDocument doc, XmlWriter writer)
203  {
204  doc.Save(writer.Writer ?? throw new NullReferenceException("Unable to save XML document: XML writer is null."));
205  }
206  }
207 
208  public static class Path
209  {
210  public static readonly char DirectorySeparatorChar = System.IO.Path.DirectorySeparatorChar;
211  public static readonly char AltDirectorySeparatorChar = System.IO.Path.AltDirectorySeparatorChar;
212 
213  public static string GetExtension(string path) => System.IO.Path.GetExtension(path);
214 
215  public static string GetFileNameWithoutExtension(string path) => System.IO.Path.GetFileNameWithoutExtension(path);
216 
217  public static string? GetPathRoot(string? path) => System.IO.Path.GetPathRoot(path);
218 
219  public static string GetRelativePath(string relativeTo, string path) => System.IO.Path.GetRelativePath(relativeTo, path);
220 
221  public static string GetDirectoryName(ContentPath path) => GetDirectoryName(path.Value)!;
222 
223  public static string? GetDirectoryName(string path) => System.IO.Path.GetDirectoryName(path);
224 
225  public static string GetFileName(string path) => System.IO.Path.GetFileName(path);
226 
227  public static string GetFullPath(string path) => System.IO.Path.GetFullPath(path);
228 
229  public static string Combine(params string[] s) => System.IO.Path.Combine(s);
230 
231  public static string GetTempFileName() => System.IO.Path.GetTempFileName();
232 
233  public static bool IsPathRooted(string path) => System.IO.Path.IsPathRooted(path);
234 
235  private static readonly ImmutableHashSet<char> invalidFileNameChars = ImmutableHashSet.Create
236  (
237  '\"', '<', '>', '|', '\0',
238  (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
239  (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
240  (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
241  (char)31, ':', '*', '?', '\\', '/'
242  );
243 
247  public static ImmutableHashSet<char> GetInvalidFileNameCharsCrossPlatform() => invalidFileNameChars;
248  }
249 
250  public static class Directory
251  {
252  public static string GetCurrentDirectory()
253  {
254  // Intentionally crash with all exceptions, if this fails.
255  return System.IO.Directory.GetCurrentDirectory();
256  }
257 
258  public static void SetCurrentDirectory(string path)
259  {
260  // Intentionally crash with all exceptions, if this fails.
261  System.IO.Directory.SetCurrentDirectory(path);
262  }
263 
264  private static readonly EnumerationOptions IgnoreInaccessibleSystemAndHidden = new EnumerationOptions
265  {
266  MatchType = MatchType.Win32,
267  AttributesToSkip = FileAttributes.System | FileAttributes.Hidden,
268  IgnoreInaccessible = true
269  };
270 
271  private static EnumerationOptions GetEnumerationOptions(bool ignoreInaccessible, bool recursive)
272  {
273  return new EnumerationOptions
274  {
275  MatchType = MatchType.Win32,
276  AttributesToSkip = FileAttributes.System | FileAttributes.Hidden,
277  IgnoreInaccessible = ignoreInaccessible,
278  RecurseSubdirectories = recursive
279  };
280  }
281 
282  public static string[] GetFiles(string path)
283  {
284  return System.IO.Directory.GetFiles(path, "*", IgnoreInaccessibleSystemAndHidden);
285  }
286 
287  public static string[] GetFiles(string path, string pattern, SearchOption option = SearchOption.AllDirectories)
288  {
289  EnumerationOptions enumerationOptions = GetEnumerationOptions(ignoreInaccessible: true, option == SearchOption.AllDirectories);
290  return System.IO.Directory.GetFiles(path, pattern, enumerationOptions);
291  }
292 
293  public static string[] GetDirectories(string path, string searchPattern = "*")
294  {
295  return System.IO.Directory.GetDirectories(path, searchPattern, IgnoreInaccessibleSystemAndHidden);
296  }
297 
298  public static string[] GetFileSystemEntries(string path)
299  {
300  return System.IO.Directory.GetFileSystemEntries(path, "*", IgnoreInaccessibleSystemAndHidden);
301  }
302 
303  public static IEnumerable<string> EnumerateDirectories(string path, string pattern)
304  {
305  return System.IO.Directory.EnumerateDirectories(path, pattern, IgnoreInaccessibleSystemAndHidden);
306  }
307 
308  public static IEnumerable<string> EnumerateFiles(string path, string pattern)
309  {
310  return System.IO.Directory.EnumerateFiles(path, pattern, IgnoreInaccessibleSystemAndHidden);
311  }
312 
313  public static bool Exists(string path)
314  {
315  return System.IO.Directory.Exists(path);
316  }
317 
318  public static System.IO.DirectoryInfo? CreateDirectory(string path, bool catchUnauthorizedAccessExceptions = false)
319  {
320  if (!Validation.CanWrite(path, true))
321  {
322  DebugConsole.ThrowError($"Cannot create directory \"{path}\": modifying the contents of this folder/using this extension is not allowed.");
323  Validation.CanWrite(path, true);
324  return null;
325  }
326  try
327  {
328  return System.IO.Directory.CreateDirectory(path);
329  }
330  catch (UnauthorizedAccessException e)
331  {
332  DebugConsole.ThrowError($"Cannot create directory at \"{path}\": unauthorized access. The file/folder might be read-only!", e);
333  if (!catchUnauthorizedAccessExceptions) { throw; }
334  return null;
335  }
336  }
337 
338  public static void Delete(string path, bool recursive = true, bool catchUnauthorizedAccessExceptions = true)
339  {
340  if (!Validation.CanWrite(path, true))
341  {
342  DebugConsole.ThrowError($"Cannot delete directory \"{path}\": modifying the contents of this folder/using this extension is not allowed.");
343  return;
344  }
345  //TODO: validate recursion?
346  try
347  {
348  System.IO.Directory.Delete(path, recursive);
349  }
350  catch (UnauthorizedAccessException e)
351  {
352  DebugConsole.ThrowError($"Cannot delete \"{path}\": unauthorized access. The file/folder might be read-only!", e);
353  if (!catchUnauthorizedAccessExceptions) { throw; }
354  }
355  }
356 
357  public static bool TryDelete(string path, bool recursive = true)
358  {
359  try
360  {
361  Delete(path, recursive, catchUnauthorizedAccessExceptions: false);
362  return true;
363  }
364  catch
365  {
366  return false;
367  }
368  }
369 
370  public static DateTime GetLastWriteTime(string path, bool catchUnauthorizedAccessExceptions = true)
371  {
372  try
373  {
374  return System.IO.Directory.GetLastWriteTime(path);
375  }
376  catch (UnauthorizedAccessException e)
377  {
378  DebugConsole.ThrowError($"Cannot get last write time at \"{path}\": unauthorized access. The file/folder might be read-only!", e);
379  if (!catchUnauthorizedAccessExceptions) { throw; }
380  return new DateTime();
381  }
382  }
383 
384  public static void Copy(string src, string dest, bool overwrite = false)
385  {
386  if (!Validation.CanWrite(dest, true))
387  {
388  DebugConsole.ThrowError($"Cannot copy \"{src}\" to \"{dest}\": modifying the contents of the destination folder is not allowed.");
389  return;
390  }
391 
392  CreateDirectory(dest);
393 
394  foreach (string path in GetFiles(src))
395  {
396  File.Copy(path, Path.Combine(dest, Path.GetRelativePath(src, path)), overwrite);
397  }
398 
399  foreach (string path in GetDirectories(src))
400  {
401  Copy(path, Path.Combine(dest, Path.GetRelativePath(src, path)), overwrite);
402  }
403  }
404 
405  public static void Move(string src, string dest, bool overwrite = false)
406  {
407  if (!overwrite && Exists(dest))
408  {
409  DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": destination folder already exists.");
410  return;
411  }
412 
413  if (!Validation.CanWrite(src, true))
414  {
415  DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": modifying the contents of the source folder is not allowed.");
416  return;
417  }
418 
419  if (!Validation.CanWrite(dest, true))
420  {
421  DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": modifying the contents of the destination folder is not allowed.");
422  return;
423  }
424 
425  if (!overwrite || !Exists(dest) || TryDelete(dest))
426  {
427  System.IO.Directory.Move(src, dest);
428  }
429  }
430  }
431 
432  public static class File
433  {
434  public static bool Exists(ContentPath path) => Exists(path.Value);
435 
436  public static bool Exists(string path) => System.IO.File.Exists(path);
437 
438  public static void Copy(string src, string dest, bool overwrite = false, bool catchUnauthorizedAccessExceptions = true)
439  {
440  if (!Validation.CanWrite(dest, false))
441  {
442  DebugConsole.ThrowError($"Cannot copy \"{src}\" to \"{dest}\": modifying the contents of this folder/using this extension is not allowed.");
443  return;
444  }
445  try
446  {
447  System.IO.File.Copy(src, dest, overwrite);
448  }
449  catch (UnauthorizedAccessException e)
450  {
451  DebugConsole.ThrowError($"Cannot copy \"{src}\" to \"{dest}\": unauthorized access. The file/folder might be read-only!", e);
452  if (!catchUnauthorizedAccessExceptions) { throw; }
453  }
454  }
455 
456  public static void Move(string src, string dest, bool catchUnauthorizedAccessExceptions = true)
457  {
458  if (!Validation.CanWrite(src, false))
459  {
460  DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": modifying the contents of the source folder is not allowed.");
461  return;
462  }
463  if (!Validation.CanWrite(dest, false))
464  {
465  DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": modifying the contents of the destination folder is not allowed");
466  return;
467  }
468  try
469  {
470  System.IO.File.Move(src, dest);
471  }
472  catch (UnauthorizedAccessException e)
473  {
474  DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": unauthorized access. The file/folder might be read-only!", e);
475  if (!catchUnauthorizedAccessExceptions) { throw; }
476  }
477  }
478 
479  public static void Delete(ContentPath path, bool catchUnauthorizedAccessExceptions = true) => Delete(path.Value, catchUnauthorizedAccessExceptions);
480 
481  public static void Delete(string path, bool catchUnauthorizedAccessExceptions = true)
482  {
483  if (!Validation.CanWrite(path, false))
484  {
485  DebugConsole.ThrowError($"Cannot delete file \"{path}\": modifying the contents of this folder/using this extension is not allowed.");
486  return;
487  }
488  try
489  {
490  System.IO.File.Delete(path);
491  }
492  catch (UnauthorizedAccessException e)
493  {
494  DebugConsole.ThrowError($"Cannot delete {path}: unauthorized access. The file/folder might be read-only!", e);
495  if (!catchUnauthorizedAccessExceptions) { throw; }
496  }
497  }
498 
499  public static DateTime GetLastWriteTime(string path)
500  {
501  return System.IO.File.GetLastWriteTime(path);
502  }
503 
504  public static FileStream? Open(
505  string path,
506  System.IO.FileMode mode,
507  System.IO.FileAccess access = System.IO.FileAccess.ReadWrite,
508  System.IO.FileShare? share = null,
509  bool catchUnauthorizedAccessExceptions = true)
510  {
511  switch (mode)
512  {
513  case System.IO.FileMode.Create:
514  case System.IO.FileMode.CreateNew:
515  case System.IO.FileMode.OpenOrCreate:
516  case System.IO.FileMode.Append:
517  case System.IO.FileMode.Truncate:
518  if (!Validation.CanWrite(path, false))
519  {
520  DebugConsole.ThrowError($"Cannot open \"{path}\" in {mode} mode: modifying the contents of this folder/using this extension is not allowed.");
521  return null;
522  }
523  break;
524  }
525  access =
526  !Validation.CanWrite(path, false) ?
527  System.IO.FileAccess.Read :
528  access;
529  var shareVal = share ?? (access == System.IO.FileAccess.Read ? System.IO.FileShare.Read : System.IO.FileShare.None);
530  try
531  {
532  return new FileStream(path, System.IO.File.Open(path, mode, access, shareVal));
533  }
534  catch (UnauthorizedAccessException e)
535  {
536  DebugConsole.ThrowError($"Cannot open {path} (stream): unauthorized access. The file/folder might be read-only!", e);
537  if (!catchUnauthorizedAccessExceptions) { throw; }
538  return null;
539  }
540  }
541 
542  public static FileStream? OpenRead(string path, bool catchUnauthorizedAccessExceptions = true)
543  {
544  return Open(path, System.IO.FileMode.Open, System.IO.FileAccess.Read, catchUnauthorizedAccessExceptions: catchUnauthorizedAccessExceptions);
545  }
546 
547  public static FileStream? OpenWrite(string path, bool catchUnauthorizedAccessExceptions = true)
548  {
549  return Open(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, catchUnauthorizedAccessExceptions: catchUnauthorizedAccessExceptions);
550  }
551 
552  public static FileStream? Create(string path, bool catchUnauthorizedAccessExceptions = true)
553  {
554  return Open(path, System.IO.FileMode.Create, System.IO.FileAccess.Write, catchUnauthorizedAccessExceptions: catchUnauthorizedAccessExceptions);
555  }
556 
557  public static void WriteAllBytes(string path, byte[] contents, bool catchUnauthorizedAccessExceptions = true)
558  {
559  if (!Validation.CanWrite(path, false))
560  {
561  DebugConsole.ThrowError($"Cannot write all bytes to \"{path}\": modifying the files in this folder/with this extension is not allowed.");
562  return;
563  }
564  try
565  {
566  System.IO.File.WriteAllBytes(path, contents);
567  }
568  catch (UnauthorizedAccessException e)
569  {
570  DebugConsole.ThrowError($"Cannot write at {path}: unauthorized access. The file/folder might be read-only!", e);
571  if (!catchUnauthorizedAccessExceptions) { throw; }
572  }
573  }
574 
575  public static void WriteAllText(string path, string contents, System.Text.Encoding? encoding = null, bool catchUnauthorizedAccessExceptions = true)
576  {
577  if (!Validation.CanWrite(path, false))
578  {
579  DebugConsole.ThrowError($"Cannot write all text to \"{path}\": modifying the files in this folder/with this extension is not allowed.");
580  return;
581  }
582  try
583  {
584  System.IO.File.WriteAllText(path, contents, encoding ?? System.Text.Encoding.UTF8);
585  }
586  catch (UnauthorizedAccessException e)
587  {
588  DebugConsole.ThrowError($"Cannot write at {path}: unauthorized access. The file/folder might be read-only!", e);
589  if (!catchUnauthorizedAccessExceptions) { throw; }
590  }
591  }
592 
593  public static void WriteAllLines(string path, IEnumerable<string> contents, System.Text.Encoding? encoding = null, bool catchUnauthorizedAccessExceptions = true)
594  {
595  if (!Validation.CanWrite(path, false))
596  {
597  DebugConsole.ThrowError($"Cannot write all lines to \"{path}\": modifying the files in this folder/with this extension is not allowed.");
598  return;
599  }
600  try
601  {
602  System.IO.File.WriteAllLines(path, contents, encoding ?? System.Text.Encoding.UTF8);
603  }
604  catch (UnauthorizedAccessException e)
605  {
606  DebugConsole.ThrowError($"Cannot write at {path}: unauthorized access. The file/folder might be read-only!", e);
607  if (!catchUnauthorizedAccessExceptions) { throw; }
608  }
609  }
610 
611  public static byte[] ReadAllBytes(string path, bool catchUnauthorizedAccessExceptions = true)
612  {
613  try
614  {
615  return System.IO.File.ReadAllBytes(path);
616  }
617  catch (UnauthorizedAccessException e)
618  {
619  DebugConsole.ThrowError($"Cannot read {path}: unauthorized access. The file/folder might be read-only!", e);
620  if (!catchUnauthorizedAccessExceptions) { throw; }
621  return Array.Empty<byte>();
622  }
623  }
624 
625  public static string ReadAllText(string path, System.Text.Encoding? encoding = null, bool catchUnauthorizedAccessExceptions = true)
626  {
627  try
628  {
629  return System.IO.File.ReadAllText(path, encoding ?? System.Text.Encoding.UTF8);
630  }
631  catch (UnauthorizedAccessException e)
632  {
633  DebugConsole.ThrowError($"Cannot read {path}: unauthorized access. The file/folder might be read-only!", e);
634  if (!catchUnauthorizedAccessExceptions) { throw; }
635  return string.Empty;
636  }
637  }
638 
639  public static string[] ReadAllLines(string path, System.Text.Encoding? encoding = null, bool catchUnauthorizedAccessExceptions = true)
640  {
641  try
642  {
643  return System.IO.File.ReadAllLines(path, encoding ?? System.Text.Encoding.UTF8);
644  }
645  catch (UnauthorizedAccessException e)
646  {
647  DebugConsole.ThrowError($"Cannot read {path}: unauthorized access. The file/folder might be read-only!", e);
648  if (!catchUnauthorizedAccessExceptions) { throw; }
649  return Array.Empty<string>();
650  }
651  }
652  public static string SanitizeName(string str)
653  {
654  string sanitized = "";
655  foreach (char c in str)
656  {
657  char newChar = Path.GetInvalidFileNameCharsCrossPlatform().Contains(c) ? '-' : c;
658  sanitized += newChar;
659  }
660  return sanitized;
661  }
662  }
663 
664  public class FileStream : System.IO.Stream
665  {
666  private readonly System.IO.FileStream innerStream;
667  private readonly string fileName;
668 
669  public FileStream(string fn, System.IO.FileStream stream)
670  {
671  innerStream = stream;
672  fileName = fn;
673  }
674 
675  public override bool CanRead => innerStream.CanRead;
676  public override bool CanSeek => innerStream.CanSeek;
677  public override bool CanTimeout => innerStream.CanTimeout;
678  public override bool CanWrite
679  {
680  get
681  {
682  if (!Validation.CanWrite(fileName, false)) { return false; }
683  return innerStream.CanWrite;
684  }
685  }
686 
687  public override long Length => innerStream.Length;
688 
689  public override long Position
690  {
691  get
692  {
693  return innerStream.Position;
694  }
695  set
696  {
697  innerStream.Position = value;
698  }
699  }
700 
701  public override int Read(byte[] buffer, int offset, int count)
702  {
703  return innerStream.Read(buffer, offset, count);
704  }
705 
706  public override void Write(byte[] buffer, int offset, int count)
707  {
708  if (Validation.CanWrite(fileName, false))
709  {
710  innerStream.Write(buffer, offset, count);
711  }
712  else
713  {
714  DebugConsole.ThrowError($"Cannot write to file \"{fileName}\": modifying the files in this folder/with this extension is not allowed.");
715  }
716  }
717 
718  public override long Seek(long offset, System.IO.SeekOrigin origin)
719  {
720  return innerStream.Seek(offset, origin);
721  }
722 
723  public override void SetLength(long value)
724  {
725  innerStream.SetLength(value);
726  }
727 
728  public override void Flush()
729  {
730  innerStream.Flush();
731  }
732 
733  protected override void Dispose(bool notCalledByFinalizer)
734  {
735  if (notCalledByFinalizer) { innerStream.Dispose(); }
736  }
737  }
738 
739  public class DirectoryInfo
740  {
741  private System.IO.DirectoryInfo innerInfo;
742 
743  public DirectoryInfo(string path)
744  {
745  innerInfo = new System.IO.DirectoryInfo(path);
746  }
747 
748  private DirectoryInfo(System.IO.DirectoryInfo info)
749  {
750  innerInfo = info;
751  }
752 
753  public bool Exists => innerInfo.Exists;
754  public string Name => innerInfo.Name;
755  public string FullName => innerInfo.FullName;
756 
757  public System.IO.FileAttributes Attributes => innerInfo.Attributes;
758 
759  public IEnumerable<DirectoryInfo> GetDirectories()
760  {
761  var dirs = innerInfo.GetDirectories();
762  foreach (var dir in dirs)
763  {
764  yield return new DirectoryInfo(dir);
765  }
766  }
767 
768  public IEnumerable<FileInfo> GetFiles()
769  {
770  var files = innerInfo.GetFiles();
771  foreach (var file in files)
772  {
773  yield return new FileInfo(file);
774  }
775  }
776 
777  public void Delete()
778  {
779  if (!Validation.CanWrite(innerInfo.FullName, false))
780  {
781  DebugConsole.ThrowError($"Cannot delete directory \"{Name}\": modifying the contents of this folder/using this extension is not allowed.");
782  return;
783  }
784  innerInfo.Delete();
785  }
786  }
787 
788  public class FileInfo
789  {
790  private System.IO.FileInfo innerInfo;
791 
792  public FileInfo(string path)
793  {
794  innerInfo = new System.IO.FileInfo(path);
795  }
796 
797  public FileInfo(System.IO.FileInfo info)
798  {
799  innerInfo = info;
800  }
801 
802  public bool Exists => innerInfo.Exists;
803  public string Name => innerInfo.Name;
804  public string FullName => innerInfo.FullName;
805  public long Length => innerInfo.Length;
806 
807  public bool IsReadOnly
808  {
809  get
810  {
811  return innerInfo.IsReadOnly;
812  }
813  set
814  {
815  if (!Validation.CanWrite(innerInfo.FullName, false))
816  {
817  DebugConsole.ThrowError($"Cannot set read-only to {value} for \"{Name}\": modifying the files in this folder/with this extension is not allowed.");
818  return;
819  }
820  innerInfo.IsReadOnly = value;
821  }
822  }
823 
824  public void CopyTo(string dest, bool overwriteExisting = false)
825  {
826  if (!Validation.CanWrite(dest, false))
827  {
828  DebugConsole.ThrowError($"Cannot copy \"{Name}\" to \"{dest}\": modifying the contents of the destination folder is not allowed.");
829  return;
830  }
831  innerInfo.CopyTo(dest, overwriteExisting);
832  }
833 
834  public void Delete()
835  {
836  if (!Validation.CanWrite(innerInfo.FullName, false))
837  {
838  DebugConsole.ThrowError($"Cannot delete file \"{Name}\": modifying the files in this folder/with this extension is not allowed.");
839  return;
840  }
841  innerInfo.Delete();
842  }
843  }
844 }
static readonly string WorkshopModsDir
DirectoryInfo(string path)
Definition: SafeIO.cs:743
System.IO.FileAttributes Attributes
Definition: SafeIO.cs:757
IEnumerable< FileInfo > GetFiles()
Definition: SafeIO.cs:768
IEnumerable< DirectoryInfo > GetDirectories()
Definition: SafeIO.cs:759
FileInfo(string path)
Definition: SafeIO.cs:792
FileInfo(System.IO.FileInfo info)
Definition: SafeIO.cs:797
void CopyTo(string dest, bool overwriteExisting=false)
Definition: SafeIO.cs:824
override long Length
Definition: SafeIO.cs:687
override bool CanSeek
Definition: SafeIO.cs:676
FileStream(string fn, System.IO.FileStream stream)
Definition: SafeIO.cs:669
override long Seek(long offset, System.IO.SeekOrigin origin)
Definition: SafeIO.cs:718
override void Write(byte[] buffer, int offset, int count)
Definition: SafeIO.cs:706
override bool CanTimeout
Definition: SafeIO.cs:677
override bool CanRead
Definition: SafeIO.cs:675
override void SetLength(long value)
Definition: SafeIO.cs:723
override void Flush()
Definition: SafeIO.cs:728
override void Dispose(bool notCalledByFinalizer)
Definition: SafeIO.cs:733
override bool CanWrite
Definition: SafeIO.cs:679
override long Position
Definition: SafeIO.cs:690
override int Read(byte[] buffer, int offset, int count)
Definition: SafeIO.cs:701
XmlWriter(string path, System.Xml.XmlWriterSettings settings)
Definition: SafeIO.cs:153
static XmlWriter Create(string path, System.Xml.XmlWriterSettings settings)
Definition: SafeIO.cs:164
readonly System.Xml.? XmlWriter Writer
Definition: SafeIO.cs:151
void Write(System.Xml.Linq.XDocument doc)
Definition: SafeIO.cs:169