Пример #1
0
        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;
            }
        }
Пример #2
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));
        }
Пример #3
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);
        }
Пример #4
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));
        }
Пример #5
0
        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
                       ));
        }
Пример #6
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);
        }
Пример #7
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);
        }
Пример #8
0
        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));
        }
Пример #9
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);
        }
Пример #10
0
        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);
                    }
                }
            }
        }
Пример #11
0
        public override async Task <IOFileAttributes> GetAttributesAsync(CancellationToken cancellationToken = default)
        {
            var folder = await FsHelper.GetFolderAsync(_fullPath, cancellationToken).ConfigureAwait(false);

            return(folder.Attributes.ToIOFileAttributes());
        }
Пример #12
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));
        }
Пример #13
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);
                }
            }
        }