Skip to content

Commit ad9b7f4

Browse files
authored
Made PlaintextStream more compatible (#92)
1 parent ddd071d commit ad9b7f4

68 files changed

Lines changed: 995 additions & 293 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/Core/SecureFolderFS.Core.Dokany/Callbacks/BaseDokanyCallbacks.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -372,17 +372,15 @@ protected static ulong GetContextValue(IDokanFileInfo info)
372372

373373
protected static int AlignSizeForPagingIo(int bufferLength, long offset, long streamLength, IDokanFileInfo info)
374374
{
375-
if (info.PagingIo)
376-
{
377-
var longDistanceToEnd = streamLength - offset;
378-
if (longDistanceToEnd > int.MaxValue)
379-
return bufferLength;
380-
381-
if (longDistanceToEnd < bufferLength)
382-
return (int)longDistanceToEnd;
375+
if (!info.PagingIo)
376+
return bufferLength;
383377

378+
var longDistanceToEnd = streamLength - offset;
379+
if (longDistanceToEnd > int.MaxValue)
384380
return bufferLength;
385-
}
381+
382+
if (longDistanceToEnd < bufferLength)
383+
return (int)longDistanceToEnd;
386384

387385
return bufferLength;
388386
}

src/Core/SecureFolderFS.Core.FileSystem/Buffers/HeaderBuffer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace SecureFolderFS.Core.FileSystem.Buffers
44
{
55
/// <inheritdoc cref="BufferHolder"/>
6-
internal sealed class HeaderBuffer : BufferHolder
6+
public sealed class HeaderBuffer : BufferHolder
77
{
88
/// <summary>
99
/// Gets or sets the value that determines whether the header buffer is initialized or not.

src/Core/SecureFolderFS.Core.FileSystem/Chunks/CachingChunkAccess.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public override bool FlushAvailable
2626
public CachingChunkAccess(ChunkReader chunkReader, ChunkWriter chunkWriter, IContentCrypt contentCrypt, IFileSystemStatistics fileSystemStatistics)
2727
: base(chunkReader, chunkWriter, contentCrypt, fileSystemStatistics)
2828
{
29-
_chunkCache = new(FileSystem.Constants.Caching.RECOMMENDED_SIZE_CHUNK);
29+
_chunkCache = new(FileSystem.Constants.Caching.RECOMMENDED_SIZE_CHUNKS);
3030
}
3131

3232
/// <inheritdoc/>
@@ -151,7 +151,7 @@ private void SetChunk(long chunkNumber, ChunkBuffer plaintextChunk)
151151
{
152152
lock (_chunkCache)
153153
{
154-
if (_chunkCache.Count >= FileSystem.Constants.Caching.RECOMMENDED_SIZE_CHUNK)
154+
if (_chunkCache.Count >= FileSystem.Constants.Caching.RECOMMENDED_SIZE_CHUNKS)
155155
{
156156
// Get chunk number to remove
157157
var chunkNumberToRemove = _chunkCache.Keys.First();

src/Core/SecureFolderFS.Core.FileSystem/Chunks/ChunkReader.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public int ReadChunk(long chunkNumber, Span<byte> plaintextChunk)
4545
var ciphertextChunk = ArrayPool<byte>.Shared.Rent(ciphertextSize);
4646
try
4747
{
48-
// ArrayPool may return larger array than requested
48+
// ArrayPool may return a larger array than requested
4949
var realCiphertextChunk = ciphertextChunk.AsSpan(0, ciphertextSize);
5050

5151
// Get available read stream or throw
@@ -56,8 +56,15 @@ public int ReadChunk(long chunkNumber, Span<byte> plaintextChunk)
5656
if (ciphertextPosition > ciphertextStream.Length)
5757
return 0;
5858

59+
// Set the correct stream position
60+
if (!ciphertextStream.TrySetPositionOrAdvance(ciphertextPosition))
61+
return 0;
62+
63+
// Return early if the stream is at the EOF position
64+
if (ciphertextStream.IsEndOfStream())
65+
return 0;
66+
5967
// Read from stream at correct chunk
60-
ciphertextStream.Position = ciphertextPosition;
6168
var read = ciphertextStream.Read(realCiphertextChunk);
6269

6370
// Check for end of file
@@ -70,7 +77,7 @@ public int ReadChunk(long chunkNumber, Span<byte> plaintextChunk)
7077
var chunkReservedSize = Math.Min(read, _security.ContentCrypt.ChunkFirstReservedSize);
7178
var chunkReserved = realCiphertextChunk.Slice(0, chunkReservedSize);
7279

73-
// Check if the reserved part is all zeros in which case the decryption will be skipped (the chunk was extended)
80+
// Check if the reserved part is all zeros, in which case the decryption will be skipped (the chunk was extended)
7481
if (chunkReservedSize > 0 && SpanExtensions.IsAllZeros(chunkReserved))
7582
{
7683
plaintextChunk.Clear();

src/Core/SecureFolderFS.Core.FileSystem/Chunks/ChunkWriter.cs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using SecureFolderFS.Core.FileSystem.Buffers;
33
using SecureFolderFS.Core.FileSystem.Exceptions;
44
using SecureFolderFS.Core.FileSystem.Streams;
5+
using SecureFolderFS.Shared.Extensions;
56
using SecureFolderFS.Storage.VirtualFileSystem;
67
using System;
78
using System.Buffers;
@@ -39,35 +40,44 @@ public void WriteChunk(long chunkNumber, ReadOnlySpan<byte> plaintextChunk)
3940
// Calculate position in ciphertext stream
4041
var streamPosition = _security.HeaderCrypt.HeaderCiphertextSize + chunkNumber * _security.ContentCrypt.ChunkCiphertextSize;
4142

42-
// Rent array for ciphertext chunk
43+
// Rent buffer
4344
var ciphertextChunk = ArrayPool<byte>.Shared.Rent(ciphertextSize);
44-
var realCiphertextChunk = ciphertextChunk.AsSpan(0, ciphertextSize);
45+
try
46+
{
47+
// ArrayPool may return a larger array than requested
48+
var realCiphertextChunk = ciphertextChunk.AsSpan(0, ciphertextSize);
4549

46-
// Encrypt
47-
_security.ContentCrypt.EncryptChunk(
48-
plaintextChunk,
49-
chunkNumber,
50-
_fileHeader,
51-
realCiphertextChunk);
50+
// Encrypt
51+
_security.ContentCrypt.EncryptChunk(
52+
plaintextChunk,
53+
chunkNumber,
54+
_fileHeader,
55+
realCiphertextChunk);
5256

53-
_fileSystemStatistics.BytesEncrypted?.Report(plaintextChunk.Length);
57+
_fileSystemStatistics.BytesEncrypted?.Report(plaintextChunk.Length);
5458

55-
// Get available read-write stream or throw
56-
var ciphertextStream = _streamsManager.GetReadWriteStream();
57-
_ = ciphertextStream ?? throw new UnavailableStreamException();
59+
// Get available read-write stream or throw
60+
var ciphertextStream = _streamsManager.GetReadWriteStream();
61+
_ = ciphertextStream ?? throw new UnavailableStreamException();
5862

59-
// Check position bounds
60-
if (streamPosition > ciphertextStream.Length)
61-
return;
63+
// Check position bounds
64+
if (streamPosition > ciphertextStream.Length)
65+
return;
6266

63-
// Write to stream at correct chunk
64-
ciphertextStream.Position = streamPosition;
65-
ciphertextStream.Write(realCiphertextChunk);
67+
// Set the correct stream position
68+
if (!ciphertextStream.TrySetPositionOrAdvance(streamPosition))
69+
return;
6670

67-
_fileSystemStatistics.BytesWritten?.Report(plaintextChunk.Length);
71+
// Write to stream at the correct chunk
72+
ciphertextStream.Write(realCiphertextChunk);
6873

69-
// Return array
70-
ArrayPool<byte>.Shared.Return(ciphertextChunk);
74+
_fileSystemStatistics.BytesWritten?.Report(realCiphertextChunk.Length);
75+
}
76+
finally
77+
{
78+
// Return buffer
79+
ArrayPool<byte>.Shared.Return(ciphertextChunk);
80+
}
7181
}
7282

7383
/// <inheritdoc/>

src/Core/SecureFolderFS.Core.FileSystem/Constants.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
public static class Constants
44
{
55
public const string LOCAL_FILE_SYSTEM_NAME = "AbstractStorage";
6-
76
public const string UNC_NAME = "securefolderfs";
87
public const int FILE_EOF = 0;
98
public const int DIRECTORY_ID_SIZE = 16;
@@ -26,7 +25,7 @@ public static class Names
2625

2726
public static class Caching
2827
{
29-
public const int RECOMMENDED_SIZE_CHUNK = 6;
28+
public const int RECOMMENDED_SIZE_CHUNKS = 6;
3029
public const int RECOMMENDED_SIZE_DIRECTORY_ID = 1000;
3130
public const int RECOMMENDED_SIZE_CIPHERTEXT_FILENAMES = 2000;
3231
public const int RECOMMENDED_SIZE_PLAINTEXT_FILENAMES = 2000;

src/Core/SecureFolderFS.Core.FileSystem/CryptFiles/OpenCryptFile.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,21 @@ namespace SecureFolderFS.Core.FileSystem.CryptFiles
1515
internal sealed class OpenCryptFile : IDisposable
1616
{
1717
private readonly Security _security;
18-
private readonly HeaderBuffer _headerBuffer;
1918
private readonly ChunkAccess _chunkAccess;
20-
private readonly StreamsManager _streamsManager;
2119
private readonly Action<string> _notifyClosed;
20+
private readonly StreamsManager _streamsManager;
2221
private readonly Dictionary<Stream, long> _openedStreams;
2322

2423
/// <summary>
2524
/// Gets the unique ID of the file.
2625
/// </summary>
2726
public string Id { get; }
2827

28+
/// <summary>
29+
/// Gets the buffer that holds the header information for the cryptographic file.
30+
/// </summary>
31+
public HeaderBuffer HeaderBuffer { get; }
32+
2933
public OpenCryptFile(
3034
string id,
3135
Security security,
@@ -35,8 +39,8 @@ public OpenCryptFile(
3539
Action<string> notifyClosed)
3640
{
3741
Id = id;
42+
HeaderBuffer = headerBuffer;
3843
_security = security;
39-
_headerBuffer = headerBuffer;
4044
_chunkAccess = chunkAccess;
4145
_streamsManager = streamsManager;
4246
_notifyClosed = notifyClosed;
@@ -50,7 +54,7 @@ public OpenCryptFile(
5054
/// <returns>A new instance of <see cref="PlaintextStream"/>.</returns>
5155
public PlaintextStream OpenStream(Stream ciphertextStream)
5256
{
53-
// Register ciphertext stream
57+
// Register the ciphertext stream
5458
if (_openedStreams.TryGetValue(ciphertextStream, out var value))
5559
_openedStreams[ciphertextStream] = ++value;
5660
else
@@ -60,16 +64,16 @@ public PlaintextStream OpenStream(Stream ciphertextStream)
6064
_streamsManager.AddStream(ciphertextStream);
6165

6266
// Open the plaintext stream
63-
return new PlaintextStream(ciphertextStream, _security, _chunkAccess, _headerBuffer, NotifyClosed);
67+
return new PlaintextStream(ciphertextStream, _security, _chunkAccess, HeaderBuffer, NotifyClosed);
6468
}
6569

6670
private void NotifyClosed(Stream ciphertextStream)
6771
{
68-
// Make sure to remove it and update references count
72+
// Make sure to remove it and update the reference count
6973
if (_openedStreams.ContainsKey(ciphertextStream) && --_openedStreams[ciphertextStream] <= 0)
7074
_openedStreams.Remove(ciphertextStream);
7175

72-
// Dispose the stream
76+
// Dispose of the stream
7377
_streamsManager.RemoveStream(ciphertextStream);
7478
ciphertextStream.Dispose();
7579

src/Core/SecureFolderFS.Core.FileSystem/CryptFiles/OpenCryptFileManager.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ public OpenCryptFileManager(Security security, bool enableChunkCache, IFileSyste
4848
public OpenCryptFile NewCryptFile(string id, BufferHolder headerBuffer)
4949
{
5050
var cryptFile = GetCryptFile(id, headerBuffer);
51-
5251
lock (_openCryptFiles)
5352
_openCryptFiles[id] = cryptFile;
5453

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.IO;
3+
using System.Runtime.CompilerServices;
4+
using SecureFolderFS.Core.Cryptography;
5+
using SecureFolderFS.Core.FileSystem.Buffers;
6+
using SecureFolderFS.Storage.VirtualFileSystem;
7+
8+
namespace SecureFolderFS.Core.FileSystem.Extensions
9+
{
10+
internal static class FileHeaderExtensions
11+
{
12+
[SkipLocalsInit]
13+
public static bool ReadHeader(this HeaderBuffer headerBuffer, Stream ciphertextStream, Security security)
14+
{
15+
if (headerBuffer.IsHeaderReady)
16+
return true;
17+
18+
if (!ciphertextStream.CanRead)
19+
throw FileSystemExceptions.StreamNotReadable;
20+
21+
var ciphertextPosition = ciphertextStream.Position;
22+
if (ciphertextPosition != 0 && !ciphertextStream.CanSeek)
23+
throw FileSystemExceptions.StreamNotSeekable;
24+
25+
// Allocate ciphertext header
26+
Span<byte> ciphertextHeader = stackalloc byte[security.HeaderCrypt.HeaderCiphertextSize];
27+
28+
// Read header
29+
int read;
30+
if (ciphertextPosition != 0)
31+
{
32+
ciphertextStream.Position = 0L;
33+
read = ciphertextStream.Read(ciphertextHeader);
34+
ciphertextStream.Position = ciphertextPosition;
35+
}
36+
else
37+
read = ciphertextStream.Read(ciphertextHeader);
38+
39+
// Check if the read amount is correct
40+
if (read < ciphertextHeader.Length)
41+
return false;
42+
43+
// Decrypt header
44+
headerBuffer.IsHeaderReady = security.HeaderCrypt.DecryptHeader(ciphertextHeader, headerBuffer);
45+
46+
return headerBuffer.IsHeaderReady;
47+
}
48+
}
49+
}

src/Core/SecureFolderFS.Core.FileSystem/Extensions/StreamingExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace SecureFolderFS.Core.FileSystem.Extensions
66
{
77
public static class StreamingExtensions
88
{
9-
/// <inheritdoc cref="StreamsAccess.OpenPlaintextStream"/>
9+
/// <inheritdoc cref="StreamsAccess.OpenPlaintextStream(string, Stream, bool)"/>
1010
public static Stream? TryOpenPlaintextStream(this StreamsAccess streamsAccess, string id, Stream ciphertextStream, bool takeFailureOwnership = true)
1111
{
1212
try

0 commit comments

Comments
 (0)