Skip to content

Commit 8454f31

Browse files
committed
Implemented logging errors to file.
1 parent c0996be commit 8454f31

File tree

8 files changed

+268
-45
lines changed

8 files changed

+268
-45
lines changed

AsyncToSyncConverter.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright (c) Roland Pihlakas 2019 - 2020
2+
// Copyright (c) Roland Pihlakas 2019 - 2022
33
44
//
55
// Roland Pihlakas licenses this file to you under the GNU Lesser General Public License, ver 2.1.
@@ -125,7 +125,17 @@ public static async Task AsyncFileUpdated(Context context)
125125
{
126126
//using (await Global.FileOperationAsyncLock.LockAsync())
127127
{
128-
var fileData = await FileExtensions.ReadAllTextAsync(Extensions.GetLongPath(context.Event.FullName), context.Token);
128+
string fileData;
129+
try
130+
{
131+
fileData = await FileExtensions.ReadAllTextAsync(Extensions.GetLongPath(context.Event.FullName), context.Token);
132+
}
133+
catch (FileNotFoundException) //file was removed by the time queue processing got to it
134+
{
135+
return;
136+
}
137+
138+
129139
var originalData = fileData;
130140

131141

BinaryFileExtensions.cs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright (c) Roland Pihlakas 2019 - 2020
2+
// Copyright (c) Roland Pihlakas 2019 - 2022
33
44
//
55
// Roland Pihlakas licenses this file to you under the GNU Lesser General Public License, ver 2.1.
@@ -27,6 +27,11 @@ public static bool BinaryEqual(Binary a, Binary b)
2727

2828
public static async Task<Tuple<byte[], long>> ReadAllBytesAsync(string path, CancellationToken cancellationToken = default(CancellationToken), long maxFileSize = 0)
2929
{
30+
if (path == null)
31+
throw new ArgumentNullException(nameof(path));
32+
if (path.Length == 0)
33+
throw new ArgumentException("Argument_EmptyPath: {0}", nameof(path));
34+
3035
while (true)
3136
{
3237
if (cancellationToken.IsCancellationRequested)
@@ -76,16 +81,25 @@ public static bool BinaryEqual(Binary a, Binary b)
7681
}
7782
}
7883

79-
public static async Task WriteAllBytesAsync(string path, byte[] contents, CancellationToken cancellationToken = default(CancellationToken), int writeBufferKB = 0, int bufferWriteDelayMs = 0)
84+
public static async Task WriteAllBytesAsync(string path, byte[] contents, bool createTempFileFirst, CancellationToken cancellationToken = default(CancellationToken), int writeBufferKB = 0, int bufferWriteDelayMs = 0)
8085
{
86+
if (path == null)
87+
throw new ArgumentNullException(nameof(path));
88+
if (path.Length == 0)
89+
throw new ArgumentException("Argument_EmptyPath: {0}", nameof(path));
90+
91+
var tempPath = path;
92+
if (createTempFileFirst)
93+
tempPath += ".tmp";
94+
8195
while (true)
8296
{
8397
cancellationToken.ThrowIfCancellationRequested();
8498

8599
try
86100
{
87101
using (var stream = new FileStream(
88-
path,
102+
tempPath,
89103
FileMode.OpenOrCreate,
90104
FileAccess.Write,
91105
FileShare.Read,
@@ -110,9 +124,22 @@ public static bool BinaryEqual(Binary a, Binary b)
110124

111125
await stream.WriteAsync(contents, i, writeBufferLength, cancellationToken);
112126
}
127+
}
113128

114-
return;
129+
if (createTempFileFirst)
130+
{
131+
if (await Extensions.FSOperation(() => File.Exists(path), cancellationToken))
132+
{
133+
#pragma warning disable SEC0116 //Warning SEC0116 Unvalidated file paths are passed to a file delete API, which can allow unauthorized file system operations (e.g. read, write, delete) to be performed on unintended server files.
134+
await Extensions.FSOperation(() => File.Delete(path), cancellationToken);
135+
#pragma warning restore SEC0116
136+
}
137+
138+
await Extensions.FSOperation(() => File.Move(tempPath, path), cancellationToken);
115139
}
140+
141+
142+
return; //exit while loop
116143
}
117144
catch (IOException)
118145
{

Extensions.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright (c) Roland Pihlakas 2019 - 2020
2+
// Copyright (c) Roland Pihlakas 2019 - 2022
33
44
//
55
// Roland Pihlakas licenses this file to you under the GNU Lesser General Public License, ver 2.1.
@@ -75,6 +75,10 @@ public static Dictionary<TValue, TKey> Inverse<TKey, TValue>(this IDictionary<TK
7575

7676
public static string GetLongPath(string path)
7777
{
78+
if (!ConfigParser.IsWindows)
79+
return Path.GetFullPath(path); //GetFullPath: convert relative path to full path
80+
81+
7882
//@"\\?\" prefix is needed for reading from long paths: https://stackoverflow.com/questions/44888844/directorynotfoundexception-when-using-long-paths-in-net-4-7 and https://superuser.com/questions/1617012/support-of-the-unc-server-share-syntax-in-windows
7983

8084
if (path.Substring(0, 2) == @"\\") //network path or path already starting with \\?\
@@ -83,10 +87,20 @@ public static string GetLongPath(string path)
8387
}
8488
else
8589
{
86-
return @"\\?\" + path;
90+
return @"\\?\" + Path.GetFullPath(path); //GetFullPath: convert relative path to full path
8791
}
8892
}
8993

94+
public static string GetDirPathWithTrailingSlash(string dirPath)
95+
{
96+
if (string.IsNullOrWhiteSpace(dirPath))
97+
return dirPath;
98+
99+
dirPath = Path.Combine(dirPath, "."); //NB! add "." in order to ensure that slash is appended to the end of the path
100+
dirPath = dirPath.Substring(0, dirPath.Length - 1); //drop the "." again
101+
return dirPath;
102+
}
103+
90104
public static async Task FSOperation(Action func, CancellationToken token)
91105
{
92106
//await Task.Run(func).WaitAsync(token);

FileExtensions.cs

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ private static StreamReader AsyncStreamReader(string path, Encoding encoding)
5151
if (encoding == null)
5252
throw new ArgumentNullException(nameof(encoding));
5353
if (path.Length == 0)
54-
throw new ArgumentException("SR.Argument_EmptyPath: {0}", nameof(path));
54+
throw new ArgumentException("Argument_EmptyPath: {0}", nameof(path));
5555

5656
while (true) //roland
5757
{
@@ -133,17 +133,21 @@ private static StreamWriter AsyncStreamWriter(string path, Encoding encoding, bo
133133
return new StreamWriter(stream, encoding);
134134
}
135135

136-
public static Task WriteAllTextAsync(string path, string contents, CancellationToken cancellationToken = default(CancellationToken))
137-
=> WriteAllTextAsync(path, contents, UTF8NoBOM, cancellationToken);
136+
public static Task WriteAllTextAsync(string path, string contents, bool createTempFileFirst, CancellationToken cancellationToken = default(CancellationToken))
137+
=> WriteAllTextAsync(path, contents, UTF8NoBOM, createTempFileFirst, cancellationToken);
138138

139-
public static async Task WriteAllTextAsync(string path, string contents, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken))
139+
public static async Task WriteAllTextAsync(string path, string contents, Encoding encoding, bool createTempFileFirst, CancellationToken cancellationToken = default(CancellationToken))
140140
{
141141
if (path == null)
142142
throw new ArgumentNullException(nameof(path));
143143
if (encoding == null)
144144
throw new ArgumentNullException(nameof(encoding));
145145
if (path.Length == 0)
146-
throw new ArgumentException("SR.Argument_EmptyPath: {0}", nameof(path));
146+
throw new ArgumentException("Argument_EmptyPath: {0}", nameof(path));
147+
148+
var tempPath = path;
149+
if (createTempFileFirst)
150+
tempPath += ".tmp";
147151

148152
while (true) //roland
149153
{
@@ -157,12 +161,28 @@ private static StreamWriter AsyncStreamWriter(string path, Encoding encoding, bo
157161

158162
if (string.IsNullOrEmpty(contents))
159163
{
160-
new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read).Dispose();
161-
return; // await Task.CompletedTask;
164+
new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.Read).Dispose();
165+
}
166+
else
167+
{
168+
await InternalWriteAllTextAsync(AsyncStreamWriter(tempPath, encoding, append: false), contents, cancellationToken);
162169
}
163170

164-
await InternalWriteAllTextAsync(AsyncStreamWriter(path, encoding, append: false), contents, cancellationToken);
165-
return;
171+
172+
if (createTempFileFirst)
173+
{
174+
if (await Extensions.FSOperation(() => File.Exists(path), cancellationToken))
175+
{
176+
#pragma warning disable SEC0116 //Warning SEC0116 Unvalidated file paths are passed to a file delete API, which can allow unauthorized file system operations (e.g. read, write, delete) to be performed on unintended server files.
177+
await Extensions.FSOperation(() => File.Delete(path), cancellationToken);
178+
#pragma warning restore SEC0116
179+
}
180+
181+
await Extensions.FSOperation(() => File.Move(tempPath, path), cancellationToken);
182+
}
183+
184+
185+
return; //exit while loop
166186
}
167187
catch (IOException) //roland
168188
{
@@ -214,5 +234,52 @@ private static async Task InternalWriteAllTextAsync(StreamWriter sw, string cont
214234
#endif
215235
}
216236
}
237+
238+
public static Task AppendAllTextAsync(string path, string contents, CancellationToken cancellationToken = default(CancellationToken))
239+
=> AppendAllTextAsync(path, contents, UTF8NoBOM, cancellationToken);
240+
241+
public static async Task AppendAllTextAsync(string path, string contents, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken))
242+
{
243+
if (path == null)
244+
throw new ArgumentNullException(nameof(path));
245+
if (encoding == null)
246+
throw new ArgumentNullException(nameof(encoding));
247+
if (path.Length == 0)
248+
throw new ArgumentException("Argument_EmptyPath", nameof(path));
249+
250+
251+
252+
while (true) //roland
253+
{
254+
try //roland
255+
{
256+
cancellationToken.ThrowIfCancellationRequested();
257+
//if (cancellationToken.IsCancellationRequested)
258+
//{
259+
// return Task.FromCanceled(cancellationToken);
260+
//}
261+
262+
if (string.IsNullOrEmpty(contents))
263+
{
264+
// Just to throw exception if there is a problem opening the file.
265+
new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read).Dispose();
266+
return; // Task.CompletedTask;
267+
}
268+
269+
await InternalWriteAllTextAsync(AsyncStreamWriter(path, encoding, append: true), contents, cancellationToken);
270+
271+
return;
272+
}
273+
catch (IOException) //roland
274+
{
275+
//retry after delay
276+
#if !NOASYNC
277+
await Task.Delay(1000, cancellationToken); //TODO: config file?
278+
#else
279+
cancellationToken.WaitHandle.WaitOne(1000);
280+
#endif
281+
}
282+
}
283+
}
217284
}
218285
}

0 commit comments

Comments
 (0)