tgstation-server 6.12.0
The /tg/station 13 server suite
Loading...
Searching...
No Matches
SynchronousIOManager.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Globalization;
4using System.IO;
5using System.Linq;
6using System.Security.Cryptography;
7using System.Threading;
8
9using Microsoft.Extensions.Logging;
10
12{
15 {
19 readonly ILogger<SynchronousIOManager> logger;
20
25 public SynchronousIOManager(ILogger<SynchronousIOManager> logger)
26 {
27 this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
28 }
29
31 public bool CreateDirectory(string path, CancellationToken cancellationToken)
32 {
33 if (IsDirectory(path))
34 return true;
35 cancellationToken.ThrowIfCancellationRequested();
36 Directory.CreateDirectory(path);
37 return false;
38 }
39
41 public bool DeleteDirectory(string path)
42 {
43 if (File.Exists(path))
44 return false;
45
46 if (!Directory.Exists(path))
47 return true;
48
49 if (Directory.EnumerateFileSystemEntries(path).Any())
50 return false;
51
52 Directory.Delete(path);
53 return true;
54 }
55
57 public IEnumerable<string> GetDirectories(string path, CancellationToken cancellationToken)
58 {
59 foreach (var directoryName in Directory.EnumerateDirectories(path))
60 {
61 yield return Path.GetFileName(directoryName);
62 cancellationToken.ThrowIfCancellationRequested();
63 }
64 }
65
67 public IEnumerable<string> GetFiles(string path, CancellationToken cancellationToken)
68 {
69 foreach (var fileName in Directory.EnumerateFiles(path))
70 {
71 yield return Path.GetFileName(fileName);
72 cancellationToken.ThrowIfCancellationRequested();
73 }
74 }
75
77 public bool IsDirectory(string path)
78 {
79 ArgumentNullException.ThrowIfNull(path);
80 return Directory.Exists(path);
81 }
82
84 public byte[] ReadFile(string path)
85 {
86 ArgumentNullException.ThrowIfNull(path);
87 return File.ReadAllBytes(path);
88 }
89
91 public bool WriteFileChecked(string path, Stream data, ref string? sha1InOut, CancellationToken cancellationToken)
92 {
93 ArgumentNullException.ThrowIfNull(path);
94 ArgumentNullException.ThrowIfNull(data);
95
96 cancellationToken.ThrowIfCancellationRequested();
97 var directory = Path.GetDirectoryName(path) ?? throw new ArgumentException("path cannot be rooted!", nameof(path));
98 Directory.CreateDirectory(directory);
99
100 var newFile = !File.Exists(path);
101
102 cancellationToken.ThrowIfCancellationRequested();
103
104 logger.LogTrace("Starting checked write to {path} ({fileType} file)", path, newFile ? "New" : "Pre-existing");
105
106 using (var file = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
107 {
108 cancellationToken.ThrowIfCancellationRequested();
109
110 // no sha1? no write
111 if (file.Length != 0 && sha1InOut == null)
112 {
113 logger.LogDebug("Aborting checked write due to missing SHA!");
114 return false;
115 }
116
117 // suppressed due to only using for consistency checks
118 using (var sha1 = SHA1.Create())
119 {
120 string? GetSha1(Stream dataToHash)
121 {
122 if (dataToHash == null)
123 return null;
124
125 byte[] sha1Computed = dataToHash.Length != 0
126 ? sha1.ComputeHash(dataToHash)
127 : sha1.ComputeHash(Array.Empty<byte>());
128
129 return String.Join(String.Empty, sha1Computed.Select(b => b.ToString("x2", CultureInfo.InvariantCulture)));
130 }
131
132 var originalSha1 = GetSha1(file);
133
134 if (!newFile)
135 logger.LogTrace("Existing SHA calculated to be {sha}", originalSha1);
136
137 if (originalSha1 != sha1InOut && !(newFile && sha1InOut == null))
138 {
139 sha1InOut = originalSha1;
140 return false;
141 }
142
143 sha1InOut = GetSha1(data);
144 }
145
146 cancellationToken.ThrowIfCancellationRequested();
147
148 if (data.Length != 0)
149 {
150 logger.LogDebug("Writing file of length {size}", data.Length);
151 file.Seek(0, SeekOrigin.Begin);
152 data.Seek(0, SeekOrigin.Begin);
153
154 cancellationToken.ThrowIfCancellationRequested();
155 file.SetLength(data.Length);
156 data.CopyTo(file);
157 }
158 }
159
160 if (data.Length == 0)
161 {
162 logger.LogDebug("Stream is empty, deleting file");
163 File.Delete(path);
164 }
165
166 return true;
167 }
168 }
169}
SynchronousIOManager(ILogger< SynchronousIOManager > logger)
Initializes a new instance of the SynchronousIOManager class.
bool DeleteDirectory(string path)
Deletes a directory at path if it's empty.true if the directory does not exist or is empty and was d...
IEnumerable< string > GetDirectories(string path, CancellationToken cancellationToken)
Enumerate directories in a given path .A IEnumerable<T> of directory names in path .
byte[] ReadFile(string path)
Read the bytes of a file at a given path .A byte array representing the contents of the file at path ...
bool CreateDirectory(string path, CancellationToken cancellationToken)
Create an empty directory at path .true if the directory already existed, false otherwise.
bool WriteFileChecked(string path, Stream data, ref string? sha1InOut, CancellationToken cancellationToken)
Write data to a file at a given path .true on success, false if the operation failed due to sha1InOu...
readonly ILogger< SynchronousIOManager > logger
The ILogger for the SynchronousIOManager.
bool IsDirectory(string path)
Checks if a given path is a directory.true if path is a directory, false otherwise.
IEnumerable< string > GetFiles(string path, CancellationToken cancellationToken)
Enumerate files in a given path .A IEnumerable<T> of file names in path .
For accessing the disk in a synchronous manner.