public override async Task DeleteAsync(DeletionOption options, CancellationToken cancellationToken = default) { if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } cancellationToken.ThrowIfCancellationRequested(); lock (_inMemoryFileSystem.Storage) { switch (options) { case DeletionOption.Fail: _storage.GetFolderNode(Path).Delete(); break; case DeletionOption.IgnoreMissing: _storage.TryGetFolderNodeAndThrowOnConflictingFile(Path)?.Delete(); break; default: throw new NotSupportedException(ExceptionStrings.Enum.UnsupportedValue(options)); } } }
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(_inMemoryFileSystem.InvalidNewNameCharacters)) { throw new ArgumentException( ExceptionStrings.StorageFolder.NewNameContainsInvalidChar(FileSystem.PathInformation), nameof(newName) ); } if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } cancellationToken.ThrowIfCancellationRequested(); var destinationPath = Path.FullPath.Parent?.Join(newName) ?? FileSystem.GetPath(newName); lock (_inMemoryFileSystem.Storage) { return(MoveInternal(destinationPath, options, cancellationToken)); } }
public override Task <Stream> CreateAndOpenAsync( bool recursive, CreationCollisionOption options, CancellationToken cancellationToken = default ) { if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } return(Task.Run <Stream>(() => { if (recursive) { Directory.CreateDirectory(_fullParentPath.ToString()); } try { cancellationToken.ThrowIfCancellationRequested(); return new FileStream(_fullPath.ToString(), options.ToFileMode()); } catch (UnauthorizedAccessException ex) { EnsureNoConflictingFolderExists(_fullPath.ToString(), ex); throw; } }, cancellationToken)); }
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 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 destinationPath = _fullParentPath?.Join(newName) ?? FileSystem.GetPath(newName); return(MoveAsync(destinationPath, options, cancellationToken)); }
public override Task <StorageFile> 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 fullDstPath = destinationPath.FullPath; var overwrite = options.ToOverwriteBool(); #if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 // File.Copy on Unix systems AND .NET Core 2.0 - 2.2 has this weird behavior where // no exception is thrown if the destination is a folder. When copying a file to // a conflicting folder, the API, instead of throwing, simply moves the file *into* // the folder. For example, assume that we copy "src/srcFile.ext" to "dst": // |_ src // | |_ srcFile.ext // |_ dst // // We'd assume that this throws, but instead, this happens: // |_ src // | |_ srcFile.ext // |_ dst // |_ srcFile.ext // // This can be fixed by preemptively verifying that there is no conflicting folder. // This has the disadvantage that we lose the inner exception which would normally // be thrown (the UnauthorizedAccessException below). To not lose it with other TFMs, // only include the check in the failing .NET Core versions. EnsureNoConflictingFolderExists(fullDstPath.ToString()); #endif try { File.Copy(_fullPath.ToString(), fullDstPath.ToString(), overwrite); return FileSystem.GetFile(destinationPath); } catch (UnauthorizedAccessException ex) { EnsureNoConflictingFolderExists(_fullPath.ToString(), ex); EnsureNoConflictingFolderExists(fullDstPath.ToString(), ex); throw; } }, cancellationToken)); }
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 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); }
// // The Open.../Read.../Write... methods below do not require explicit locking. // Locking only happens when acquiring the FileContentStream from the node (via OpenFileContentStream(...)). // Once the stream exists, the underlying FileNode automatically protects itself against // modification since the file is "in use". // See FileContentStream/FileNode for details. // public override async Task <Stream> OpenAsync(FileAccess fileAccess, CancellationToken cancellationToken = default) { if (!EnumInfo.IsDefined(fileAccess)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(fileAccess), nameof(fileAccess)); } return(OpenFileContentStream(fileAccess, replaceExistingContent: false, cancellationToken)); }
/// <summary> /// Converts the <paramref name="knownFolder"/> to a string and then calls the /// <see cref="InMemoryFileSystem.GetPath(string)"/> method with it. /// Then returns the full path of the result. /// </summary> /// <param name="fileSystem"> /// An <see cref="InMemoryFileSystem"/> instance for which the <see cref="KnownFolder"/> /// should be located. /// </param> /// <param name="knownFolder"> /// The <see cref="KnownFolder"/> value to be located for the specified <paramref name="fileSystem"/>. /// </param> /// <returns> /// A <see cref="StoragePath"/> which locates the specified <paramref name="knownFolder"/> /// within the provided <paramref name="fileSystem"/>. /// </returns> /// <exception cref="ArgumentException"> /// <paramref name="knownFolder"/> is an invalid <see cref="KnownFolder"/> enumeration value. /// </exception> public StoragePath GetPath(InMemoryFileSystem fileSystem, KnownFolder knownFolder) { _ = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); if (!EnumInfo.IsDefined(knownFolder)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(knownFolder), nameof(knownFolder)); } return(fileSystem.GetPath(knownFolder.ToString()).FullPath); }
public override Task DeleteAsync(DeletionOption options, CancellationToken cancellationToken = default) { if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } return(options switch { DeletionOption.Fail => FailImpl(), DeletionOption.IgnoreMissing => IgnoreMissingImpl(), _ => throw new NotSupportedException(ExceptionStrings.Enum.UnsupportedValue(options)), });
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)); } CreateInternal(recursive, options, cancellationToken); }
public override async Task SetAttributesAsync(FileAttributes attributes, CancellationToken cancellationToken = default) { if (!EnumInfo.IsDefined(attributes)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(attributes), nameof(attributes)); } cancellationToken.ThrowIfCancellationRequested(); lock (_inMemoryFileSystem.Storage) { _storage.GetFolderNode(Path).Attributes = attributes; } }
public override Task SetAttributesAsync(FileAttributes attributes, CancellationToken cancellationToken = default) { if (!EnumInfo.IsDefined(attributes)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(attributes), nameof(attributes)); } return(Task.Run(() => { EnsureNoConflictingFolderExists(_fullPath.ToString()); cancellationToken.ThrowIfCancellationRequested(); File.SetAttributes(_fullPath.ToString(), attributes); }, cancellationToken)); }
public override Task <StorageFolder> 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) ); } return(Task.Run(() => { // For whatever reason, Directory.Move moves files instead of throwing an exception. // We've got to manually verify that the current location actually is a directory and not a file. EnsureNoConflictingFileExists(); var fullDestinationPath = destinationPath.FullPath; var destination = FileSystem.GetFolder(destinationPath); var overwrite = options.ToOverwriteBool(); 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 moving // 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.CannotMoveToSameLocation()); } } cancellationToken.ThrowIfCancellationRequested(); Directory.Move(_fullPath.ToString(), fullDestinationPath.ToString()); return destination; }, cancellationToken)); }
public override 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) ); } return(Task.Run(() => { EnsureExists(cancellationToken); var fullDestinationPath = destinationPath.FullPath; var overwrite = options.ToOverwriteBool(); // System.IO doesn't throw when moving files to the same location. // Detecting this via paths will not always work, but it fulfills the spec most of the time. if (_fullPath == fullDestinationPath) { throw new IOException(ExceptionStrings.StorageFile.CannotMoveToSameLocation()); } try { FilePolyfills.Move(_fullPath.ToString(), fullDestinationPath.ToString(), overwrite); return FileSystem.GetFile(destinationPath); } catch (UnauthorizedAccessException ex) { EnsureNoConflictingFolderExists(_fullPath.ToString(), ex); EnsureNoConflictingFolderExists(fullDestinationPath.ToString(), ex); throw; } }, cancellationToken)); }
public override Task CreateAsync( bool recursive, CreationCollisionOption options, CancellationToken cancellationToken = default ) { if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } return(Task.Run(async() => { // Directory.CreateDirectory is recursive by default. // If recursive is false, we must manually ensure that the parent directory exists (if this is not // a root directory). if (!recursive && _fullParentPath is not null && !Directory.Exists(_fullParentPath.ToString())) { throw new DirectoryNotFoundException(); } // Directory.CreateDirectory is very user friendly in the sense that it rarely throws exceptions. // This means that we have to manually implement support for collision options. // This can lead to race conditions, but it's the best we can do. if (await ExistsAsync(cancellationToken).ConfigureAwait(false)) { switch (options) { case CreationCollisionOption.Fail: throw new IOException(ExceptionStrings.StorageFolder.CreateFailFolderAlreadyExists()); case CreationCollisionOption.ReplaceExisting: await DeleteAsync(DeletionOption.IgnoreMissing, cancellationToken).ConfigureAwait(false); break; case CreationCollisionOption.UseExisting: return; default: throw new NotSupportedException(ExceptionStrings.Enum.UnsupportedValue(options)); } } cancellationToken.ThrowIfCancellationRequested(); Directory.CreateDirectory(_fullPath.ToString()); }, cancellationToken)); }
public override async Task <Stream> CreateAndOpenAsync( bool recursive, CreationCollisionOption options, CancellationToken cancellationToken = default ) { if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } lock (_inMemoryFileSystem.Storage) { CreateInternal(recursive, options, cancellationToken); return(OpenFileContentStream(FileAccess.ReadWrite, replaceExistingContent: false, 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)); } // 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 async Task <StorageFolder> 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) ); } var fullDestinationPath = destinationPath.FullPath; var destinationFolder = FileSystem.GetFolder(fullDestinationPath); // There is no native Move API. The current "best practice" (haha) is to simply copy // a folder instead of moving. // We might be able to improve performance if we're moving into the same directory, i.e. // if we're effectively doing a rename. // Since this is path based, we have to be careful, of course. if (fullDestinationPath.Parent == _fullParentPath) { await RenameAsync(destinationPath.FullPath.Name, options, cancellationToken).ConfigureAwait(false); } else { await CopyAsync(destinationPath, options, cancellationToken).ConfigureAwait(false); } await DeleteAsync(DeletionOption.IgnoreMissing, cancellationToken).ConfigureAwait(false); return(destinationFolder); }
public override Task SetAttributesAsync(FileAttributes attributes, CancellationToken cancellationToken = default) { if (!EnumInfo.IsDefined(attributes)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(attributes), nameof(attributes)); } return(Task.Run(() => { try { EnsureNoConflictingFileExists(); 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 <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); }
/// <inheritdoc/> public override StoragePath GetPath(KnownFolder knownFolder) { if (!EnumInfo.IsDefined(knownFolder)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(knownFolder), nameof(knownFolder)); } var path = knownFolder switch { KnownFolder.TemporaryData => Path.GetTempPath(), KnownFolder.RoamingApplicationData => GetSpecialFolder(ApplicationData), KnownFolder.LocalApplicationData => GetSpecialFolder(LocalApplicationData), KnownFolder.ProgramData => GetSpecialFolder(CommonApplicationData), KnownFolder.UserProfile => GetSpecialFolder(UserProfile), KnownFolder.Desktop => GetSpecialFolder(Desktop), KnownFolder.DocumentsLibrary => GetSpecialFolder(MyDocuments), KnownFolder.PicturesLibrary => GetSpecialFolder(MyPictures), KnownFolder.VideosLibrary => GetSpecialFolder(MyVideos), KnownFolder.MusicLibrary => GetSpecialFolder(MyMusic), _ => throw new NotSupportedException(ExceptionStrings.FileSystem.KnownFolderNotSupported(knownFolder)), }; return(GetPath(path));
public override async Task <StorageFolder> MoveAsync( StoragePath destinationPath, NameCollisionOption options, CancellationToken cancellationToken = default ) { _ = destinationPath ?? throw new ArgumentNullException(nameof(destinationPath)); if (!ReferenceEquals(destinationPath.FileSystem, FileSystem)) { throw new ArgumentException( ExceptionStrings.InMemoryFileSystem.MemberIncompatibleWithInstance(), nameof(destinationPath) ); } if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } return(MoveInternal(destinationPath, options, cancellationToken)); }
public override async Task <StorageFolder> CopyAsync( StoragePath destinationPath, NameCollisionOption options, CancellationToken cancellationToken = default ) { _ = destinationPath ?? throw new ArgumentNullException(nameof(destinationPath)); if (!ReferenceEquals(destinationPath.FileSystem, FileSystem)) { throw new ArgumentException( ExceptionStrings.InMemoryFileSystem.MemberIncompatibleWithInstance(), nameof(destinationPath) ); } if (!EnumInfo.IsDefined(options)) { throw new ArgumentException(ExceptionStrings.Enum.UndefinedValue(options), nameof(options)); } cancellationToken.ThrowIfCancellationRequested(); var replaceExisting = options switch { NameCollisionOption.Fail => false, NameCollisionOption.ReplaceExisting => true, _ => throw new NotSupportedException(ExceptionStrings.Enum.UnsupportedValue(options)), }; lock (_inMemoryFileSystem.Storage) { var folderNode = _storage.GetFolderNode(Path); folderNode.Copy(destinationPath, replaceExisting); return(FileSystem.GetFolder(destinationPath.FullPath)); } }
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); } } }