예제 #1
0
        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));
                }
            }
        }
예제 #2
0
        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));
            }
        }
예제 #3
0
        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));
        }
예제 #4
0
        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));
        }
예제 #5
0
        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));
        }
예제 #6
0
        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));
        }
예제 #7
0
        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));
        }
예제 #8
0
        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);
        }
예제 #9
0
        //
        // 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));
        }
예제 #10
0
        /// <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);
        }
예제 #11
0
        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)),
            });
예제 #12
0
        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);
        }
예제 #13
0
        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;
            }
        }
예제 #14
0
        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));
        }
예제 #15
0
        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));
        }
예제 #16
0
        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));
        }
예제 #17
0
        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));
        }
예제 #18
0
        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));
            }
        }
예제 #19
0
        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);
        }
예제 #20
0
        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);
        }
예제 #21
0
        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);
        }
예제 #22
0
        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));
        }
예제 #23
0
        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);
        }
예제 #24
0
        /// <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));
예제 #25
0
        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));
        }
예제 #26
0
        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));
            }
        }
예제 #27
0
        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));
        }
예제 #28
0
        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);
                }
            }
        }