public override async Task <bool> ExistsAsync(CancellationToken cancellationToken = default) { try { await FsHelper.GetFolderAsync(_fullPath, cancellationToken).ConfigureAwait(false); return(true); } catch (DirectoryNotFoundException) { return(false); } catch (IOException) { // IOException might be thrown if a conflicting file exists. // In such cases the specification requires us to return false. try { await FsHelper.GetFileAsync(_fullPath, cancellationToken).ConfigureAwait(false); return(false); } catch { // No conflicting file exists. Rethrow the original IOException. } throw; } }
public override Task <StorageFolder> CopyAsync( StoragePath destinationPath, NameCollisionOption options, CancellationToken cancellationToken = default ) { _ = destinationPath ?? throw new ArgumentNullException(nameof(destinationPath)); if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } if (!(destinationPath is PhysicalStoragePath)) { throw new ArgumentException( ExceptionStrings.FsCompatibility.StoragePathTypeNotSupported(), nameof(destinationPath) ); } return(Task.Run(() => { var fullDestinationPath = destinationPath.FullPath; var destination = FileSystem.GetFolder(destinationPath); var overwrite = options.ToOverwriteBool(); // Specification requires DirectoryNotFoundException if the destination parent folder // does not exist. if (destination.Parent is PhysicalStorageFolder destinationParent) { destinationParent.EnsureExists(); } if (overwrite) { DeleteConflictingDestinationFolderWithoutDeletingThisFolder(fullDestinationPath.ToString()); // At this point the conflicting destination folder should be deleted. // If that is not the case, we can assume that we are essentially copying // the folder to the same location (because otherwise, the method above would // have deleted the folder at the destination path). if (Directory.Exists(fullDestinationPath.ToString())) { throw new IOException(ExceptionStrings.StorageFolder.CannotCopyToSameLocation()); } } else { // The CopyDirectory helper cannot easily verify that no conflicting folder exists // at the destination. Specification requires an IOException on conflicts for the Fail option. if (Directory.Exists(fullDestinationPath.ToString())) { throw new IOException(ExceptionStrings.StorageFolder.CopyConflictingFolderExistsAtDestination()); } } FsHelper.CopyDirectory(_fullPath.ToString(), fullDestinationPath.ToString(), cancellationToken); return destination; }, cancellationToken)); }
public override async Task CreateAsync( bool recursive, CreationCollisionOption options, CancellationToken cancellationToken = default ) { if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } await EnsureNoConflictingFolderExistsAsync(cancellationToken).ConfigureAwait(false); WinStorageFolder parentFolder; if (recursive) { parentFolder = await FsHelper .GetOrCreateFolderAsync(_fullParentPath, cancellationToken) .ConfigureAwait(false); } else { parentFolder = await FsHelper .GetFolderAsync(_fullParentPath, cancellationToken) .ConfigureAwait(false); } await parentFolder .CreateFileAsync(_fullPath.Name, options.ToWinOptions()) .AsTask(cancellationToken) .WithConvertedException() .ConfigureAwait(false); }
public override Task SetAttributesAsync(IOFileAttributes attributes, CancellationToken cancellationToken = default) { // There's no "native" API for setting file/folder attributes. // We can at least try to use System.IO's API - it should at least work in certain locations // like the application data. if (!EnumInfo.IsDefined(attributes)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(attributes), nameof(attributes)); } return(Task.Run(async() => { try { // Get the folder to ensure that it exists and to throw the appropriate exception. await FsHelper.GetFolderAsync(_fullPath, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); File.SetAttributes(_fullPath.ToString(), attributes); } catch (FileNotFoundException ex) { // Since we're using a File API, we must manually convert the FileNotFoundException. throw new DirectoryNotFoundException(message: null, ex); } }, cancellationToken)); }
public override async Task <StorageFolderProperties> GetPropertiesAsync(CancellationToken cancellationToken = default) { var folder = await FsHelper.GetFolderAsync(_fullPath, cancellationToken).ConfigureAwait(false); var props = await folder.GetBasicPropertiesAsync().AsAwaitable(cancellationToken); var lastModified = props.DateModified == default ? (DateTimeOffset?)null : props.DateModified; return(new StorageFolderProperties( folder.Name, IOPath.GetFileNameWithoutExtension(folder.Name), PhysicalPathHelper.GetExtensionWithoutTrailingExtensionSeparator(folder.Name)?.ToNullIfEmpty(), folder.DateCreated, lastModified )); }
public override async Task CreateAsync( bool recursive, CreationCollisionOption options, CancellationToken cancellationToken = default ) { if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } // We cannot reasonably create a root directory with the Windows Storage API. // If someone tries to do so, we'll simply deny the call. In most cases, the root // folder will exist anyway. if (_fullParentPath is null) { throw new UnauthorizedAccessException(); } await EnsureNoConflictingFileExistsAsync(cancellationToken).ConfigureAwait(false); WinStorageFolder parentFolder; if (recursive) { parentFolder = await FsHelper .GetOrCreateFolderAsync(_fullParentPath, cancellationToken) .ConfigureAwait(false); } else { parentFolder = await FsHelper .GetFolderAsync(_fullParentPath, cancellationToken) .ConfigureAwait(false); } await parentFolder .CreateFolderAsync(_fullPath.Name, options.ToWinOptions()) .AsTask(cancellationToken) .WithConvertedException() .ConfigureAwait(false); }
public override async Task <StorageFile> MoveAsync( StoragePath destinationPath, NameCollisionOption options, CancellationToken cancellationToken = default ) { _ = destinationPath ?? throw new ArgumentNullException(nameof(destinationPath)); if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } if (!(destinationPath is PhysicalStoragePath)) { throw new ArgumentException( ExceptionStrings.FsCompatibility.StoragePathTypeNotSupported(), nameof(destinationPath) ); } if (destinationPath.FullPath.Parent is null) { throw new IOException(ExceptionStrings.StorageFile.CannotMoveToRootLocation()); } var fullDestinationPath = destinationPath.FullPath; var destinationFile = FileSystem.GetFile(fullDestinationPath); var file = await FsHelper.GetFileAsync(_fullPath, cancellationToken).ConfigureAwait(false); var destFolder = await FsHelper .GetFolderAsync(fullDestinationPath.Parent, cancellationToken) .ConfigureAwait(false); await file .MoveAsync(destFolder, fullDestinationPath.Name, options.ToWinOptions()) .AsTask(cancellationToken) .WithConvertedException() .ConfigureAwait(false); return(destinationFile); }
public override Task <StorageFolderProperties> GetPropertiesAsync(CancellationToken cancellationToken = default) { return(Task.Run(() => { EnsureExists(); // Attempting to get the real folder name can fail, e.g. the folder might have been deleted in between. // In such a case, simply return the last fetched name. It will happen rarely and is good enough // for such cases. cancellationToken.ThrowIfCancellationRequested(); var realFolderName = FsHelper.GetRealDirectoryName(_fullPath.ToString()) ?? Path.Name; var lastWriteTime = Directory.GetLastWriteTimeUtc(_fullPath.ToString()); return new StorageFolderProperties( realFolderName, IOPath.GetFileNameWithoutExtension(realFolderName), PhysicalPathHelper.GetExtensionWithoutTrailingExtensionSeparator(realFolderName)?.ToNullIfEmpty(), Directory.GetCreationTimeUtc(_fullPath.ToString()), lastWriteTime ); }, cancellationToken)); }
public override async Task <StorageFile> RenameAsync( string newName, NameCollisionOption options, CancellationToken cancellationToken = default ) { _ = newName ?? throw new ArgumentNullException(nameof(newName)); if (newName.Length == 0) { throw new ArgumentException(ExceptionStrings.String.CannotBeEmpty(), nameof(newName)); } if (newName.Contains(PhysicalPathHelper.InvalidNewNameCharacters)) { throw new ArgumentException( ExceptionStrings.StorageFile.NewNameContainsInvalidChar(FileSystem.PathInformation), nameof(newName) ); } if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } var destinationPath = _fullParentPath.Join(newName).FullPath; var destinationFile = FileSystem.GetFile(destinationPath); var file = await FsHelper.GetFileAsync(_fullPath, cancellationToken).ConfigureAwait(false); await file .RenameAsync(newName, options.ToWinOptions()) .AsTask(cancellationToken) .WithConvertedException() .ConfigureAwait(false); return(destinationFile); }
public bool ReadFsChangeBody(string path, FsChange fsChange) { var shebangPosition = 0; var makeExecutable = false; string tempPath = null; FileStream fs = null; bool bodyReadSuccess = false; try { long written = 0; int chunkSize; do { chunkSize = ReadInt(); if (chunkSize < 0) { // error occurred in sender return(false); } var remain = chunkSize; while (remain > 0) { var read = BinaryReader.Read(_buffer, 0, Math.Min(BUFFER_LENGTH, remain)); if (read <= 0) { throw new EndOfStreamException($"Premature end of stream {remain}, {chunkSize}, {read})"); } if (written == 0) { var directoryName = Path.GetDirectoryName(path); Directory.CreateDirectory(directoryName); tempPath = Path.Combine(directoryName, "." + Path.GetFileName(path) + "." + Path.GetRandomFileName()); fs = new FileStream(tempPath, FileMode.CreateNew, FileAccess.Write, FileShare.Read); } if (PlatformHasChmod && shebangPosition < ShebangLength) { for (var i = 0; i < read;) { if (_buffer[i++] != ShebangBytes[shebangPosition++]) { // no shebang shebangPosition = int.MaxValue; break; } if (shebangPosition == ShebangLength) { makeExecutable = true; break; } } } fs?.Write(_buffer, 0, read); written += read; remain -= read; } } while (chunkSize > 0); bodyReadSuccess = written == fsChange.Length; if (bodyReadSuccess && makeExecutable) { // 0755, rwxr-xr-x fs.ChangeMode(0b111_101_101); } // create empty file if (bodyReadSuccess && fsChange.Length == 0) { var directoryName = Path.GetDirectoryName(path); Directory.CreateDirectory(directoryName); using (new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) { } File.SetLastWriteTime(path, fsChange.LastWriteTime); } return(bodyReadSuccess); } catch (Exception) { SkipFsChangeBody(); throw; } finally { if (fs != null) { fs.Dispose(); if (bodyReadSuccess) { try { File.SetLastWriteTime(tempPath, fsChange.LastWriteTime); File.Move(tempPath, path, true); } catch (Exception) { FsHelper.TryDeleteFile(tempPath); throw; } } else { FsHelper.TryDeleteFile(tempPath); } } } }
public override async Task <IOFileAttributes> GetAttributesAsync(CancellationToken cancellationToken = default) { var folder = await FsHelper.GetFolderAsync(_fullPath, cancellationToken).ConfigureAwait(false); return(folder.Attributes.ToIOFileAttributes()); }
public override async Task <StorageFolder> RenameAsync( string newName, NameCollisionOption options, CancellationToken cancellationToken = default ) { _ = newName ?? throw new ArgumentNullException(nameof(newName)); if (newName.Length == 0) { throw new ArgumentException(ExceptionStrings.String.CannotBeEmpty(), nameof(newName)); } if (newName.Contains(PhysicalPathHelper.InvalidNewNameCharacters)) { throw new ArgumentException( ExceptionStrings.StorageFolder.NewNameContainsInvalidChar(FileSystem.PathInformation), nameof(newName) ); } if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } var srcFolder = await FsHelper.GetFolderAsync(_fullPath, cancellationToken).ConfigureAwait(false); var fullDestinationPath = _fullParentPath is null ? FileSystem.GetPath(newName).FullPath : _fullParentPath.Join(newName).FullPath; // The Windows Storage API doesn't do a hard replace with the ReplaceExisting option. // For example, if we had this structure: // |_ src // | |_ foo.ext // |_ dst // |_ bar.ext // // and renamed `src` to `dst`, we'd get this result: // |_ dst // |_ foo.ext // |_ bar.ext // // What we (and the spec) want is this: // |_ dst // |_ foo.ext // // We can manually delete the dst folder if it exists to fulfill the specification. // We're *only* doing it if we can be sure that we're not doing an in-place rename though, // i.e. rename `src` to `src`. // Otherwise we'd run into the problem that `src` is deleted and that the rename operation // will fail (DirectoryNotFound). Afterwards the entire folder is gone permanently. // That must be avoided at all cost. if (options == NameCollisionOption.ReplaceExisting && !fullDestinationPath.Name.Equals(_fullPath.Name, FileSystem.PathInformation.DefaultStringComparison)) { try { var dstFolder = await FsHelper.GetFolderAsync(fullDestinationPath, cancellationToken).ConfigureAwait(false); await dstFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).AsAwaitable(cancellationToken); } catch { // If deleting the conflicting folder fails, it's okay, since the whole process // is just there for fulfilling the specification. // The Windows Storage API will still replace conflicting elements. // It's just that certain files may be left over (as described above). // Not fulfilling the spec is the best thing we can do without taking higher risks // of lost data. } } await srcFolder .RenameAsync(newName, options.ToWinOptions()) .AsTask(cancellationToken) .WithConvertedException() .ConfigureAwait(false); return(FileSystem.GetFolder(fullDestinationPath)); }
public override async Task <StorageFolder> CopyAsync( StoragePath destinationPath, NameCollisionOption options, CancellationToken cancellationToken = default ) { _ = destinationPath ?? throw new ArgumentNullException(nameof(destinationPath)); if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } if (!(destinationPath is PhysicalStoragePath)) { throw new ArgumentException( ExceptionStrings.FsCompatibility.StoragePathTypeNotSupported(), nameof(destinationPath) ); } if (destinationPath.FullPath.Parent is null) { throw new IOException(ExceptionStrings.StorageFolder.CannotMoveToRootLocation()); } if (destinationPath.FullPath == _fullPath) { throw new IOException(ExceptionStrings.StorageFolder.CannotCopyToSameLocation()); } var destinationParentFolder = await FsHelper .GetFolderAsync(destinationPath.FullPath.Parent, cancellationToken) .ConfigureAwait(false); var sourceFolder = await FsHelper.GetFolderAsync(_fullPath, cancellationToken).ConfigureAwait(false); await Impl(sourceFolder, destinationParentFolder, destinationPath.FullPath.Name).ConfigureAwait(false); return(FileSystem.GetFolder(destinationPath.FullPath)); async Task Impl(WinStorageFolder src, WinStorageFolder dstFolderParent, string dstFolderName) { var dstFolder = await dstFolderParent .CreateFolderAsync(dstFolderName, ((CreationCollisionOption)options).ToWinOptions()) .AsTask(cancellationToken) .WithConvertedException() .ConfigureAwait(false); foreach (var file in await src.GetFilesAsync().AsAwaitable(cancellationToken)) { await file .CopyAsync(dstFolder, file.Name) .AsTask(cancellationToken) .WithConvertedException() .ConfigureAwait(false); } foreach (var folder in await src.GetFoldersAsync().AsAwaitable(cancellationToken)) { await Impl(folder, dstFolder, folder.Name).ConfigureAwait(false); } } }