Skip to content

Commit 7da3cf3

Browse files
authored
fix: Simplified IFileSystem (#3641)
1 parent e85e6f6 commit 7da3cf3

14 files changed

+154
-147
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
### Features
1616

17-
- Added a flag to options `DisableFileWrite` to allow users to opt-out of all file writing operations. Note that toggling this will affect features such as offline caching and auto-session tracking and release health as these rely on some file persistency ([#3614](https://github.com/getsentry/sentry-dotnet/pull/3614))
17+
- Added a flag to options `DisableFileWrite` to allow users to opt-out of all file writing operations. Note that toggling this will affect features such as offline caching and auto-session tracking and release health as these rely on some file persistency ([#3614](https://github.com/getsentry/sentry-dotnet/pull/3614), [#3641](https://github.com/getsentry/sentry-dotnet/pull/3641))
1818

1919
### Dependencies
2020

src/Sentry/GlobalSessionManager.cs

+36-13
Original file line numberDiff line numberDiff line change
@@ -51,29 +51,42 @@ private void PersistSession(SessionUpdate update, DateTimeOffset? pauseTimestamp
5151
return;
5252
}
5353

54+
if (_options.DisableFileWrite)
55+
{
56+
_options.LogInfo("File write has been disabled via the options. Skipping persisting session.");
57+
return;
58+
}
59+
5460
try
5561
{
5662
_options.LogDebug("Creating persistence directory for session file at '{0}'.", _persistenceDirectoryPath);
5763

58-
var result = _options.FileSystem.CreateDirectory(_persistenceDirectoryPath);
59-
if (result is not FileOperationResult.Success)
64+
if (!_options.FileSystem.CreateDirectory(_persistenceDirectoryPath))
6065
{
61-
if (result is FileOperationResult.Disabled)
62-
{
63-
_options.LogInfo("Persistent directory for session file has not been created. File-write has been disabled via the options.");
64-
}
65-
else
66-
{
67-
_options.LogError("Failed to create persistent directory for session file.");
68-
}
69-
66+
_options.LogError("Failed to create persistent directory for session file.");
7067
return;
7168
}
7269

7370
var filePath = Path.Combine(_persistenceDirectoryPath, PersistedSessionFileName);
7471

7572
var persistedSessionUpdate = new PersistedSessionUpdate(update, pauseTimestamp);
76-
persistedSessionUpdate.WriteToFile(_options.FileSystem, filePath, _options.DiagnosticLogger);
73+
if (!_options.FileSystem.CreateFileForWriting(filePath, out var file))
74+
{
75+
_options.LogError("Failed to persist session file.");
76+
return;
77+
}
78+
79+
using var writer = new Utf8JsonWriter(file);
80+
81+
try
82+
{
83+
persistedSessionUpdate.WriteTo(writer, _options.DiagnosticLogger);
84+
writer.Flush();
85+
}
86+
finally
87+
{
88+
file.Dispose();
89+
}
7790

7891
_options.LogDebug("Persisted session to a file '{0}'.", filePath);
7992
}
@@ -91,6 +104,12 @@ private void DeletePersistedSession()
91104
return;
92105
}
93106

107+
if (_options.DisableFileWrite)
108+
{
109+
_options.LogInfo("File write has been disabled via the options. Skipping deletion of persisted session files.");
110+
return;
111+
}
112+
94113
var filePath = Path.Combine(_persistenceDirectoryPath, PersistedSessionFileName);
95114
try
96115
{
@@ -108,7 +127,11 @@ private void DeletePersistedSession()
108127
}
109128
}
110129

111-
_options.FileSystem.DeleteFile(filePath);
130+
if (!_options.FileSystem.DeleteFile(filePath))
131+
{
132+
_options.LogError("Failed to delete persisted session file.");
133+
return;
134+
}
112135

113136
_options.LogInfo("Deleted persisted session file '{0}'.", filePath);
114137
}

src/Sentry/Http/HttpTransportBase.cs

+20-12
Original file line numberDiff line numberDiff line change
@@ -383,20 +383,24 @@ private void HandleFailure(HttpResponseMessage response, Envelope envelope)
383383
persistLargeEnvelopePathEnvVar,
384384
destinationDirectory);
385385

386+
if (_options.DisableFileWrite)
387+
{
388+
_options.LogInfo("File write has been disabled via the options. Skipping persisting envelope.");
389+
return;
390+
}
391+
386392
var destination = Path.Combine(destinationDirectory, "envelope_too_large",
387393
(eventId ?? SentryId.Create()).ToString());
388394

389-
var createDirectoryResult = _options.FileSystem.CreateDirectory(Path.GetDirectoryName(destination)!);
390-
if (createDirectoryResult is not FileOperationResult.Success)
395+
if (!_options.FileSystem.CreateDirectory(Path.GetDirectoryName(destination)!))
391396
{
392-
_options.LogError("Failed to create directory to store the envelope: {0}", createDirectoryResult);
397+
_options.LogError("Failed to create directory to store the envelope.");
393398
return;
394399
}
395400

396-
var result = _options.FileSystem.CreateFileForWriting(destination, out var envelopeFile);
397-
if (result is not FileOperationResult.Success)
401+
if (!_options.FileSystem.CreateFileForWriting(destination, out var envelopeFile))
398402
{
399-
_options.LogError("Failed to create envelope file: {0}", result);
403+
_options.LogError("Failed to create envelope file.");
400404
return;
401405
}
402406

@@ -449,20 +453,24 @@ private async Task HandleFailureAsync(HttpResponseMessage response, Envelope env
449453
persistLargeEnvelopePathEnvVar,
450454
destinationDirectory);
451455

456+
if (_options.DisableFileWrite)
457+
{
458+
_options.LogInfo("File write has been disabled via the options. Skipping persisting envelope.");
459+
return;
460+
}
461+
452462
var destination = Path.Combine(destinationDirectory, "envelope_too_large",
453463
(eventId ?? SentryId.Create()).ToString());
454464

455-
var createDirectoryResult = _options.FileSystem.CreateDirectory(Path.GetDirectoryName(destination)!);
456-
if (createDirectoryResult is not FileOperationResult.Success)
465+
if (!_options.FileSystem.CreateDirectory(Path.GetDirectoryName(destination)!))
457466
{
458-
_options.LogError("Failed to create directory to store the envelope: {0}", createDirectoryResult);
467+
_options.LogError("Failed to create directory to store the envelope.");
459468
return;
460469
}
461470

462-
var result = _options.FileSystem.CreateFileForWriting(destination, out var envelopeFile);
463-
if (result is not FileOperationResult.Success)
471+
if (!_options.FileSystem.CreateFileForWriting(destination, out var envelopeFile))
464472
{
465-
_options.LogError("Failed to create envelope file: {0}", result);
473+
_options.LogError("Failed to create envelope file.");
466474
return;
467475
}
468476

src/Sentry/ISentryJsonSerializable.cs

-18
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,3 @@ public interface ISentryJsonSerializable
1717
/// </remarks>
1818
void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger);
1919
}
20-
21-
internal static class JsonSerializableExtensions
22-
{
23-
public static void WriteToFile(this ISentryJsonSerializable serializable, IFileSystem fileSystem, string filePath, IDiagnosticLogger? logger)
24-
{
25-
var result = fileSystem.CreateFileForWriting(filePath, out var file);
26-
if (result is not FileOperationResult.Success)
27-
{
28-
return;
29-
}
30-
31-
using var writer = new Utf8JsonWriter(file);
32-
33-
serializable.WriteTo(writer, logger);
34-
writer.Flush();
35-
file.Dispose();
36-
}
37-
}

src/Sentry/Internal/FileSystemBase.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ public IEnumerable<string> EnumerateFiles(string path, string searchPattern, Sea
2020

2121
public Stream OpenFileForReading(string path) => File.OpenRead(path);
2222

23-
public abstract FileOperationResult CreateDirectory(string path);
24-
public abstract FileOperationResult DeleteDirectory(string path, bool recursive = false);
25-
public abstract FileOperationResult CreateFileForWriting(string path, out Stream fileStream);
26-
public abstract FileOperationResult WriteAllTextToFile(string path, string contents);
27-
public abstract FileOperationResult MoveFile(string sourceFileName, string destFileName, bool overwrite = false);
28-
public abstract FileOperationResult DeleteFile(string path);
23+
public abstract bool CreateDirectory(string path);
24+
public abstract bool DeleteDirectory(string path, bool recursive = false);
25+
public abstract bool CreateFileForWriting(string path, out Stream fileStream);
26+
public abstract bool WriteAllTextToFile(string path, string contents);
27+
public abstract bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false);
28+
public abstract bool DeleteFile(string path);
2929
}

src/Sentry/Internal/Http/CachingTransport.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ private CachingTransport(ITransport innerTransport, SentryOptions options, bool
7878
options.TryGetProcessSpecificCacheDirectoryPath() ??
7979
throw new InvalidOperationException("Cache directory or DSN is not set.");
8080

81+
// Sanity check: This should never happen in the first place.
82+
// We check for `DisableFileWrite` before creating the CachingTransport.
83+
Debug.Assert(!_options.DisableFileWrite);
84+
8185
_processingDirectoryPath = Path.Combine(_isolatedCacheDirectoryPath, ProcessingFolder);
8286
}
8387

@@ -451,8 +455,7 @@ private async Task StoreToCacheAsync(
451455

452456
EnsureFreeSpaceInCache();
453457

454-
var result = _options.FileSystem.CreateFileForWriting(envelopeFilePath, out var stream);
455-
if (result is not FileOperationResult.Success)
458+
if (!_options.FileSystem.CreateFileForWriting(envelopeFilePath, out var stream))
456459
{
457460
_options.LogDebug("Failed to store to cache.");
458461
return;

src/Sentry/Internal/IFileSystem.cs

+11-13
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
namespace Sentry.Internal;
22

3-
internal enum FileOperationResult
4-
{
5-
Success,
6-
Failure,
7-
Disabled
8-
}
9-
103
internal interface IFileSystem
114
{
5+
// Note: You are responsible for handling success/failure when attempting to write to disk.
6+
// You are required to check for `Options.FileWriteDisabled` whether you are allowed to call any writing operations.
7+
// The options will automatically pick between `ReadOnly` and `ReadAndWrite` to prevent accidental file writing that
8+
// could cause crashes on restricted platforms like the Nintendo Switch.
9+
1210
// Note: This is not comprehensive. If you need other filesystem methods, add to this interface,
1311
// then implement in both Sentry.Internal.FileSystem and Sentry.Testing.FakeFileSystem.
1412

@@ -21,10 +19,10 @@ internal interface IFileSystem
2119
string? ReadAllTextFromFile(string file);
2220
Stream OpenFileForReading(string path);
2321

24-
FileOperationResult CreateDirectory(string path);
25-
FileOperationResult DeleteDirectory(string path, bool recursive = false);
26-
FileOperationResult CreateFileForWriting(string path, out Stream fileStream);
27-
FileOperationResult WriteAllTextToFile(string path, string contents);
28-
FileOperationResult MoveFile(string sourceFileName, string destFileName, bool overwrite = false);
29-
FileOperationResult DeleteFile(string path);
22+
bool CreateDirectory(string path);
23+
bool DeleteDirectory(string path, bool recursive = false);
24+
bool CreateFileForWriting(string path, out Stream fileStream);
25+
bool WriteAllTextToFile(string path, string contents);
26+
bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false);
27+
bool DeleteFile(string path);
3028
}

src/Sentry/Internal/InstallationIdHelper.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ internal class InstallationIdHelper(SentryOptions options)
6060
var directoryPath = Path.Combine(rootPath, "Sentry", options.Dsn!.GetHashString());
6161
var fileSystem = options.FileSystem;
6262

63-
if (fileSystem.CreateDirectory(directoryPath) is not FileOperationResult.Success)
63+
if (!fileSystem.CreateDirectory(directoryPath))
6464
{
6565
options.LogDebug("Failed to create a directory for installation ID file ({0}).", directoryPath);
6666
return null;
@@ -79,7 +79,7 @@ internal class InstallationIdHelper(SentryOptions options)
7979

8080
// Generate new installation ID and store it in a file
8181
var id = Guid.NewGuid().ToString();
82-
if (fileSystem.WriteAllTextToFile(filePath, id) is not FileOperationResult.Success)
82+
if (!fileSystem.WriteAllTextToFile(filePath, id))
8383
{
8484
options.LogDebug("Failed to write Installation ID to file ({0}).", filePath);
8585
return null;

src/Sentry/Internal/ReadOnlyFilesystem.cs

+12-8
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,24 @@ namespace Sentry.Internal;
22

33
internal class ReadOnlyFileSystem : FileSystemBase
44
{
5-
public override FileOperationResult CreateDirectory(string path) => FileOperationResult.Disabled;
5+
// Note: You are responsible for handling success/failure when attempting to write to disk.
6+
// You are required to check for `Options.FileWriteDisabled` whether you are allowed to call any writing operations.
7+
// The options will automatically pick between `ReadOnly` and `ReadAndWrite` to prevent accidental file writing that
8+
// could cause crashes on restricted platforms like the Nintendo Switch.
69

7-
public override FileOperationResult DeleteDirectory(string path, bool recursive = false) => FileOperationResult.Disabled;
10+
public override bool CreateDirectory(string path) => false;
811

9-
public override FileOperationResult CreateFileForWriting(string path, out Stream fileStream)
12+
public override bool DeleteDirectory(string path, bool recursive = false) => false;
13+
14+
public override bool CreateFileForWriting(string path, out Stream fileStream)
1015
{
1116
fileStream = Stream.Null;
12-
return FileOperationResult.Disabled;
17+
return false;
1318
}
1419

15-
public override FileOperationResult WriteAllTextToFile(string path, string contents) => FileOperationResult.Disabled;
20+
public override bool WriteAllTextToFile(string path, string contents) => false;
1621

17-
public override FileOperationResult MoveFile(string sourceFileName, string destFileName, bool overwrite = false) =>
18-
FileOperationResult.Disabled;
22+
public override bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false) => false;
1923

20-
public override FileOperationResult DeleteFile(string path) => FileOperationResult.Disabled;
24+
public override bool DeleteFile(string path) => false;
2125
}

src/Sentry/Internal/ReadWriteFileSystem.cs

+18-13
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,36 @@ namespace Sentry.Internal;
22

33
internal class ReadWriteFileSystem : FileSystemBase
44
{
5-
public override FileOperationResult CreateDirectory(string path)
5+
// Note: You are responsible for handling success/failure when attempting to write to disk.
6+
// You are required to check for `Options.FileWriteDisabled` whether you are allowed to call any writing operations.
7+
// The options will automatically pick between `ReadOnly` and `ReadAndWrite` to prevent accidental file writing that
8+
// could cause crashes on restricted platforms like the Nintendo Switch.
9+
10+
public override bool CreateDirectory(string path)
611
{
712
Directory.CreateDirectory(path);
8-
return DirectoryExists(path) ? FileOperationResult.Success : FileOperationResult.Failure;
13+
return DirectoryExists(path);
914
}
1015

11-
public override FileOperationResult DeleteDirectory(string path, bool recursive = false)
16+
public override bool DeleteDirectory(string path, bool recursive = false)
1217
{
1318
Directory.Delete(path, recursive);
14-
return Directory.Exists(path) ? FileOperationResult.Failure : FileOperationResult.Success;
19+
return !Directory.Exists(path);
1520
}
1621

17-
public override FileOperationResult CreateFileForWriting(string path, out Stream fileStream)
22+
public override bool CreateFileForWriting(string path, out Stream fileStream)
1823
{
1924
fileStream = File.Create(path);
20-
return FileOperationResult.Success;
25+
return true;
2126
}
2227

23-
public override FileOperationResult WriteAllTextToFile(string path, string contents)
28+
public override bool WriteAllTextToFile(string path, string contents)
2429
{
2530
File.WriteAllText(path, contents);
26-
return File.Exists(path) ? FileOperationResult.Success : FileOperationResult.Failure;
31+
return File.Exists(path);
2732
}
2833

29-
public override FileOperationResult MoveFile(string sourceFileName, string destFileName, bool overwrite = false)
34+
public override bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false)
3035
{
3136
#if NETCOREAPP3_0_OR_GREATER
3237
File.Move(sourceFileName, destFileName, overwrite);
@@ -44,15 +49,15 @@ public override FileOperationResult MoveFile(string sourceFileName, string destF
4449

4550
if (File.Exists(sourceFileName) || !File.Exists(destFileName))
4651
{
47-
return FileOperationResult.Failure;
52+
return false;
4853
}
4954

50-
return FileOperationResult.Success;
55+
return true;
5156
}
5257

53-
public override FileOperationResult DeleteFile(string path)
58+
public override bool DeleteFile(string path)
5459
{
5560
File.Delete(path);
56-
return File.Exists(path) ? FileOperationResult.Failure : FileOperationResult.Success;
61+
return !File.Exists(path);
5762
}
5863
}

src/Sentry/Internal/SdkComposer.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ private ITransport CreateTransport()
3232

3333
if (_options.DisableFileWrite)
3434
{
35-
_options.LogInfo("File writing is disabled, Skipping caching transport creation.");
35+
_options.LogInfo("File write has been disabled via the options. Skipping caching transport creation.");
3636
}
3737
else
3838
{

0 commit comments

Comments
 (0)