Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added file open helper which fixes opening files with too strict FileShare configuration #496

Merged
merged 5 commits into from Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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))
{
devedse marked this conversation as resolved.
Show resolved Hide resolved
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