93 ArgumentNullException.ThrowIfNull(path);
94 ArgumentNullException.ThrowIfNull(data);
96 cancellationToken.ThrowIfCancellationRequested();
97 var directory = Path.GetDirectoryName(path) ??
throw new ArgumentException(
"path cannot be rooted!", nameof(path));
98 Directory.CreateDirectory(directory);
100 var newFile = !File.Exists(path);
102 cancellationToken.ThrowIfCancellationRequested();
104 logger.LogTrace(
"Starting checked write to {path} ({fileType} file)", path, newFile ?
"New" :
"Pre-existing");
106 using (var file = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
108 cancellationToken.ThrowIfCancellationRequested();
111 if (file.Length != 0 && sha1InOut ==
null)
113 logger.LogDebug(
"Aborting checked write due to missing SHA!");
118 using (var sha1 = SHA1.Create())
120 string? GetSha1(
Stream dataToHash)
122 if (dataToHash ==
null)
125 byte[] sha1Computed = dataToHash.Length != 0
126 ? sha1.ComputeHash(dataToHash)
127 : sha1.ComputeHash(Array.Empty<
byte>());
129 return String.Join(String.Empty, sha1Computed.Select(b => b.ToString(
"x2", CultureInfo.InvariantCulture)));
132 var originalSha1 = GetSha1(file);
135 logger.LogTrace(
"Existing SHA calculated to be {sha}", originalSha1);
137 if (originalSha1 != sha1InOut && !(newFile && sha1InOut ==
null))
139 sha1InOut = originalSha1;
143 sha1InOut = GetSha1(data);
146 cancellationToken.ThrowIfCancellationRequested();
148 if (data.Length != 0)
150 logger.LogDebug(
"Writing file of length {size}", data.Length);
151 file.Seek(0, SeekOrigin.Begin);
152 data.Seek(0, SeekOrigin.Begin);
154 cancellationToken.ThrowIfCancellationRequested();
155 file.SetLength(data.Length);
160 if (data.Length == 0)
162 logger.LogDebug(
"Stream is empty, deleting file");