Skip to content

Commit

Permalink
Merge pull request #496 from devedse/develop
Browse files Browse the repository at this point in the history
Added file open helper which fixes opening files with too strict FileShare configuration
  • Loading branch information
revenz committed Mar 20, 2024
2 parents 434ca33 + a28e726 commit c090929
Show file tree
Hide file tree
Showing 11 changed files with 51 additions and 18 deletions.
2 changes: 1 addition & 1 deletion Server/Controllers/DownloadController.cs
Expand Up @@ -20,7 +20,7 @@ public IActionResult Download()
if (System.IO.File.Exists(file) == false)
return NotFound();

return new FileStreamResult(System.IO.File.OpenRead(file), "application/octet-stream")
return new FileStreamResult(FileOpenHelper.OpenRead_NoLocks(file), "application/octet-stream")
{
FileDownloadName = zipName
};
Expand Down
2 changes: 1 addition & 1 deletion Server/Controllers/PluginController.cs
Expand Up @@ -218,7 +218,7 @@ public FileStreamResult DownloadPackage([FromRoute] string package)

try
{
return File(System.IO.File.OpenRead(file), "application/octet-stream");
return File(FileOpenHelper.OpenRead_NoLocks(file), "application/octet-stream");
}
catch(Exception ex)
{
Expand Down
6 changes: 3 additions & 3 deletions Server/Helpers/Gzipper.cs
Expand Up @@ -22,7 +22,7 @@ public static bool CompressFile(string inputFile, string outputFile, bool delete
if (File.Exists(outputFile))
File.Delete(outputFile);

using (FileStream originalFileStream = File.Open(inputFile, FileMode.Open))
using (FileStream originalFileStream = FileOpenHelper.OpenRead_NoLocks(inputFile))
{
using FileStream compressedFileStream = File.Create(outputFile);
using var compressor = new GZipStream(compressedFileStream, CompressionMode.Compress);
Expand Down Expand Up @@ -55,7 +55,7 @@ public static bool DecompressFile(string inputFile, string outputFile, bool dele
if (File.Exists(outputFile))
File.Delete(outputFile);

using FileStream compressedFileStream = File.Open(inputFile, FileMode.Open);
using FileStream compressedFileStream = FileOpenHelper.OpenRead_NoLocks(inputFile);
using FileStream outputFileStream = File.Create(outputFile);
using var decompressor = new GZipStream(compressedFileStream, CompressionMode.Decompress);
decompressor.CopyTo(outputFileStream);
Expand Down Expand Up @@ -96,7 +96,7 @@ public static void CompressToFile(string file, string contents)
/// <returns>the decompresses file contents</returns>
public static string DecompressFileToString(string inputFile, int lines = 0, int bytes = 0)
{
using FileStream compressedFileStream = File.Open(inputFile, FileMode.Open);
using FileStream compressedFileStream = FileOpenHelper.OpenRead_NoLocks(inputFile);
using MemoryStream outputStream = new MemoryStream();
using var decompressor = new GZipStream(compressedFileStream, CompressionMode.Decompress);

Expand Down
Binary file modified Server/Server.csproj
Binary file not shown.
4 changes: 2 additions & 2 deletions Server/Workers/WatchedLibrary.cs
Expand Up @@ -791,7 +791,7 @@ private async Task<bool> CanAccess(FileInfo file, int fileSizeDetectionInterval)

checkedAccess = true;

using (var fs = File.Open(file.FullName, FileMode.Open))
using (var fs = FileOpenHelper.OpenForCheckingReadWriteAccess(file.FullName))
{
if(fs.CanRead == false)
{
Expand Down Expand Up @@ -924,4 +924,4 @@ private bool QueueContains(string item)
return QueuedFiles.Contains(item);
}
}
}
}
2 changes: 1 addition & 1 deletion ServerShared/FileServices/FileDownloader.cs
Expand Up @@ -113,7 +113,7 @@ public async Task<Result<bool>> DownloadFile(string path, string destinationPath
logger?.ILog("Content-Length header is not present in the response.");
}

using FileStream fileStream = File.OpenWrite(destinationPath);
using FileStream fileStream = FileOpenHelper.OpenWrite_NoReadLock(destinationPath, FileMode.Create);

int bufferSize;

Expand Down
10 changes: 5 additions & 5 deletions ServerShared/FileServices/FileHasher.cs
Expand Up @@ -19,21 +19,21 @@ public static async Task<string> Hash(string filePath)
var fileInfo = new FileInfo(filePath);
if (!fileInfo.Exists)
return string.Empty;
using(var stream = new BufferedStream(File.OpenRead(filePath), 1200000))

using (var stream = new BufferedStream(FileOpenHelper.OpenRead_NoLocks(filePath), 1200000))
{
// The rest remains the same
var sha = SHA256.Create();
byte[] checksum = await sha.ComputeHashAsync(stream);
return BitConverter.ToString(checksum).Replace("-", string.Empty);
}

using var hasher = SHA256.Create();
byte[]? hash;

if (fileInfo.Length > 100_000_000)
{
using var stream = File.OpenRead(filePath);
using var stream = FileOpenHelper.OpenRead_NoLocks(filePath);
const int chunkSize = 100_000_000; // 100MB chunks
var buffer = new byte[chunkSize];

Expand All @@ -51,7 +51,7 @@ public static async Task<string> Hash(string filePath)
}
else
{
await using var stream = File.OpenRead(filePath);
await using var stream = FileOpenHelper.OpenRead_NoLocks(filePath);
hash = await hasher.ComputeHashAsync(stream);
}

Expand Down
4 changes: 2 additions & 2 deletions ServerShared/FileServices/FileUploader.cs
Expand Up @@ -87,7 +87,7 @@ public async Task<(bool Success, string Error)> UploadFile(string filePath, stri
url += "?path=" + HttpUtility.UrlEncode(serverPath);
logger.ILog("Uploading file to URL: " + url);

await using var fileStream = File.OpenRead(filePath);
await using var fileStream = FileOpenHelper.OpenRead_NoLocks(filePath);
var content = new StreamContent(fileStream);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Add("x-executor", executorUid.ToString());
Expand Down Expand Up @@ -116,7 +116,7 @@ public async Task<(bool Success, string Error)> UploadFile(string filePath, stri
/// <returns>The computed SHA256 hash of the file as a byte array.</returns>
private static async Task<byte[]> CalculateFileHash(string filePath)
{
await using var stream = File.OpenRead(filePath);
await using var stream = FileOpenHelper.OpenRead_NoLocks(filePath);
return await SHA256.Create().ComputeHashAsync(stream);
}

Expand Down
3 changes: 1 addition & 2 deletions ServerShared/Helpers/FileHelper.cs
Expand Up @@ -7,7 +7,6 @@ namespace FileFlows.ServerShared.Helpers;
/// </summary>
public class FileHelper
{

/// <summary>
/// Removes illegal file/path characters from a string
/// </summary>
Expand Down Expand Up @@ -46,7 +45,7 @@ public static string CalculateFingerprint(string file)
}
else
{
using var stream = File.OpenRead(file);
using var stream = FileOpenHelper.OpenRead_NoLocks(file);
hash = hasher.ComputeHash(stream);
}

Expand Down
34 changes: 34 additions & 0 deletions Shared/Helpers/FileOpenHelper.cs
@@ -0,0 +1,34 @@
using System.IO;

/// <summary>
/// Helper class for file open operations
/// </summary>
public static class FileOpenHelper
{
/// <summary>
/// Opens a file for reading without a read/write lock
/// This allows other applications to read/write to the file while it is being read
/// </summary>
/// <param name="file">the file to open</param>
/// <returns>the file stream</returns>
public static FileStream OpenRead_NoLocks(string file)
=> File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

/// <summary>
/// Opens a file for reading and writing without a read/write lock
/// This allows other applications to keep reading/writing the file while it's being opened
/// This is useful to check if CanRead/CanWrite is true on a file
/// Actually writing to the file with this stream might have undesired effects
/// </summary>
/// <param name="file">the file to open</param>
/// <returns>the file stream</returns>
public static FileStream OpenForCheckingReadWriteAccess(string file)
=> File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);

/// <summary>
/// Opens a file for writing without a write lock
/// This allows other applications to read the file while it is being written to
/// </summary>
public static FileStream OpenWrite_NoReadLock(string file, FileMode fileMode)
=> File.Open(file, fileMode, FileAccess.Write, FileShare.Read);
}
2 changes: 1 addition & 1 deletion Shared/Helpers/HttpHelper.cs
Expand Up @@ -143,7 +143,7 @@ public static async Task DownloadFile(string url, string destination)

using HttpResponseMessage response = await Client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
await using var streamToReadFrom = await response.Content.ReadAsStreamAsync();
await using var streamToWriteTo = File.Open(destination, FileMode.Create);
await using var streamToWriteTo = FileOpenHelper.OpenWrite_NoReadLock(destination, FileMode.Create);
await streamToReadFrom.CopyToAsync(streamToWriteTo);
}
catch (Exception ex)
Expand Down

0 comments on commit c090929

Please sign in to comment.