public async Task Upload(string objectName, Stream readOnlyInput, CancellationToken cancellationToken) { var path = FilesystemUtils.PreparePath(objectName, _context); cancellationToken.ThrowIfCancellationRequested(); if (Path.GetDirectoryName(path) is { } directory) { Directory.CreateDirectory(directory); } using var file = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete); await readOnlyInput.CopyToAsync(file, cancellationToken); }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously -- deleting file is synchronous public async Task Clear(CancellationToken cancellationToken) #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { _isLocked = false; var name = FilesystemUtils.PreparePath(FilesystemConstants.LockPath + _lockName + LockExtension, _context); cancellationToken.ThrowIfCancellationRequested(); try { File.Delete(name); } catch (DirectoryNotFoundException) { } }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously -- opening local file is synchronous public async Task <Stream?> Read(string objectName, CancellationToken cancellationToken) #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { var path = FilesystemUtils.PreparePath(objectName, _context); cancellationToken.ThrowIfCancellationRequested(); try { return(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)); } catch (DirectoryNotFoundException) { return(null); } catch (FileNotFoundException) { return(null); } }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously -- creating file is synchronous public async Task Lock(CancellationToken cancellationToken) #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { var now = DateTime.UtcNow; var lockLifetime = TimeSpan.FromMinutes(3); var threshold = now - lockLifetime; var path = FilesystemUtils.PreparePath(FilesystemConstants.LockPath, _context); cancellationToken.ThrowIfCancellationRequested(); Directory.CreateDirectory(path); foreach (var key in Directory.GetFiles(path, "*" + LockExtension)) { cancellationToken.ThrowIfCancellationRequested(); var lockTime = File.GetLastWriteTime(key).ToUniversalTime(); if (lockTime < threshold) { File.Delete(key); } else { var lockName = Path.GetFileNameWithoutExtension(key); if (lockName == _lockName) { break; } var message = _isLocked ? FormattableString.Invariant($"The filesystem lock \"{_lockName}\" was overriden by \"{key}\" (time: {lockTime:o}, threshold: {threshold:o}).") : FormattableString.Invariant($"The filesystem lock \"{_lockName}\" is prevented by \"{key}\" (time: {lockTime:o}, threshold: {threshold:o})."); _isLocked = false; throw new OperationCanceledException(message); } } cancellationToken.ThrowIfCancellationRequested(); File.Create(path + _lockName + LockExtension).Dispose(); _isLocked = true; }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously -- deleting local file is synchronous public async Task Delete(string objectName, CancellationToken cancellationToken) #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { var path = FilesystemUtils.PreparePath(objectName, _context); var recyclePath = _recycleContext is object?FilesystemUtils.PreparePath(objectName, _recycleContext) : null; cancellationToken.ThrowIfCancellationRequested(); if (recyclePath is object) { if (Path.GetDirectoryName(path) is { } directory) { Directory.CreateDirectory(directory); } try { File.Move(path, recyclePath, true); } catch (FileNotFoundException) { return; } catch (DirectoryNotFoundException) { return; } } else { try { File.Delete(path); } catch (DirectoryNotFoundException) { return; } } var root = Path.GetDirectoryName(FilesystemUtils.PreparePath("", _context)); if (root is null) { return; } var rootLength = root.Length; while (true) { if (Path.GetDirectoryName(path) is not { } directory) { break; } cancellationToken.ThrowIfCancellationRequested(); if (Directory.EnumerateFileSystemEntries(directory).Any()) { break; } var dirLength = directory.Length; if (dirLength == rootLength) { break; } if (dirLength < rootLength) { throw new InvalidProgramException(FormattableString.Invariant($"Recursive deletion of directory \"{directory}\" attempted to escape root \"{root}\".")); } Directory.Delete(directory); path = directory; } }