Beispiel #1
0
        /// <inheritdoc />
        /// <remarks>
        ///     Interestingly, despite not requiring a handle as a parameter,
        ///     this is safe to call while holding a Stream to a file.
        /// </remarks>
        public void AllowAttributeWrites(AbsolutePath path)
        {
            path.ThrowIfPathTooLong();

            WithErrorHandling(
                path,
                absolutePath => SetAttributeWrites(absolutePath, AccessControlType.Allow),
                absolutePath => $"Failed to remove a Deny Write Attributes ACL on {absolutePath}");
        }
Beispiel #2
0
        /// <inheritdoc />
        /// <remarks>
        ///     Interestingly, despite not requiring a handle as a parameter,
        ///     this is safe to call while holding a Stream to a file.
        /// </remarks>
        public void DenyFileWrites(AbsolutePath path)
        {
            path.ThrowIfPathTooLong();

            WithErrorHandling(
                path,
                absolutePath => SetFileWrites(absolutePath, AccessControlType.Deny),
                absolutePath => $"Failed to set a Deny Writes ACL on {absolutePath}");
        }
Beispiel #3
0
 /// <inheritdoc />
 public IEnumerable <AbsolutePath> EnumerateDirectories(AbsolutePath path, EnumerateOptions options)
 {
     path.ThrowIfPathTooLong();
     return(Directory.EnumerateDirectories(
                path.Path,
                "*",
                (options & EnumerateOptions.Recurse) != 0 ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
            .Select(pathString => new AbsolutePath(pathString)));
 }
        /// <inheritdoc />
        public void SetFileAttributes(AbsolutePath path, FileAttributes attributes)
        {
            path.ThrowIfPathTooLong();
            if ((attributes & FileSystemConstants.UnsupportedFileAttributes) != 0)
            {
                throw new NotImplementedException();
            }

            FileUtilities.SetFileAttributes(path.Path, attributes);
        }
        /// <inheritdoc />
        public Task <StreamWithLength?> OpenAsync(AbsolutePath path, FileAccess fileAccess, FileMode fileMode, FileShare share, FileOptions options, int bufferSize)
        {
            path.ThrowIfPathTooLong();

            if (FileSystemConstants.UnsupportedFileModes.Contains(fileMode))
            {
                throw new NotImplementedException($"The mode '{fileMode}' is not supported by the {nameof(PassThroughFileSystem)}.");
            }

            return(Task.FromResult(TryOpenFile(path, fileAccess, fileMode, share, options, bufferSize)));
        }
        /// <inheritdoc />
        public async Task <Stream> OpenAsync(AbsolutePath path, FileAccess fileAccess, FileMode fileMode, FileShare share, FileOptions options, int bufferSize)
        {
            path.ThrowIfPathTooLong();

            if (FileSystemConstants.UnsupportedFileModes.Contains(fileMode))
            {
                throw new NotImplementedException($"The mode '{fileMode}' is not supported by the {nameof(PassThroughFileSystem)}.");
            }

            using (await ConcurrentAccess.WaitToken())
            {
                return(await OpenInsideSemaphoreAsync(path, fileAccess, fileMode, share, options, bufferSize));
            }
        }
        /// <inheritdoc />
        public void DeleteDirectory(AbsolutePath path, DeleteOptions deleteOptions)
        {
            path.ThrowIfPathTooLong();

            try
            {
                Directory.Delete(path.Path, (deleteOptions & DeleteOptions.Recurse) != 0);
            }
            catch (UnauthorizedAccessException accessException)
            {
                if ((deleteOptions & DeleteOptions.ReadOnly) != 0 &&
                    accessException.HResult > 0 &&
                    accessException.HResult == Hresult.AccessDenied)
                {
                    bool foundReadonly = false;

                    foreach (FileInfo fileInfo in EnumerateFiles(path, EnumerateOptions.Recurse))
                    {
                        if ((GetFileAttributes(fileInfo.FullPath) & FileAttributes.ReadOnly) != 0)
                        {
                            SetFileAttributes(fileInfo.FullPath, FileAttributes.Normal);
                            foundReadonly = true;
                        }
                    }

                    foreach (AbsolutePath directoryPath in EnumerateDirectories(path, EnumerateOptions.Recurse))
                    {
                        if ((GetFileAttributes(directoryPath) & FileAttributes.ReadOnly) != 0)
                        {
                            SetFileAttributes(directoryPath, FileAttributes.Normal);
                            foundReadonly = true;
                        }
                    }

                    if (!foundReadonly)
                    {
                        throw;
                    }

                    Directory.Delete(path.Path, (deleteOptions & DeleteOptions.Recurse) != 0);
                }
                else
                {
                    throw;
                }
            }
        }
Beispiel #8
0
        /// <inheritdoc />
        /// <remarks>
        ///     This was once an extension method, but had to be brought in as a member so that is could
        ///     use the ConcurrentAccess throttling mechanism to limit the number of calls into the
        ///     write stream dispose. That dispose call ends up in the framework calling its blocking
        ///     flush method. A large number of tasks in that call path end up causing the creation of
        ///     nearly a single dedicated thread per task.
        /// </remarks>
        public async Task CopyFileAsync(AbsolutePath sourcePath, AbsolutePath destinationPath, bool replaceExisting)
        {
            sourcePath.ThrowIfPathTooLong();
            destinationPath.ThrowIfPathTooLong();

            if (FileUtilities.IsCopyOnWriteSupportedByEnlistmentVolume)
            {
                if (await TryCopyOnWriteFileInsideSemaphoreAsync(
                        sourcePath,
                        destinationPath,
                        replaceExisting))
                {
                    return;
                }
            }

            await CopyFileInsideSemaphoreAsync(sourcePath, destinationPath, replaceExisting);
        }
Beispiel #9
0
        /// <inheritdoc />
        public void DeleteFile(AbsolutePath path)
        {
            path.ThrowIfPathTooLong();

            try
            {
                FileUtilities.DeleteFile(path.Path);
            }
            catch (BuildXLException e)
            {
                // Preserving backward compatibility and throwing 'UnauthorizedAccessException' due to shared violation.
                // 0x20 is shared violation.
                if (e.InnerException is NativeWin32Exception win32 && win32.ErrorCode == 0x20)
                {
                    throw new UnauthorizedAccessException(e.Message, e);
                }
            }
        }
        /// <inheritdoc />
        public ulong GetFileId(AbsolutePath path)
        {
            path.ThrowIfPathTooLong();

            if (BuildXL.Utilities.OperatingSystemHelper.IsUnixOS)
            {
                var createOrOpenResult = FileUtilities.TryCreateOrOpenFile(path.ToString(), FileDesiredAccess.FileReadAttributes,
                                                                           FileShare.ReadWrite | FileShare.Delete, FileMode.Open, FileFlagsAndAttributes.FileFlagOverlapped, out SafeFileHandle handle);

                if (!createOrOpenResult.Succeeded)
                {
                    throw ThrowLastWin32Error(path.ToString(), $"Failed to create or open file {path} to get its ID. Status: {createOrOpenResult.Status}");
                }

                return((ulong)handle.DangerousGetHandle().ToInt64());
            }
            else
            {
                using (SafeFileHandle sourceFileHandle = NativeMethods.CreateFile(
                           path.Path,
                           NativeMethods.FILE_READ_ATTRIBUTES,
                           FileShare.ReadWrite | FileShare.Delete,
                           IntPtr.Zero,
                           FileMode.Open,
                           0, /* Allow symbolic links to redirect us */
                           IntPtr.Zero))
                {
                    string errorMessage = string.Format(CultureInfo.InvariantCulture, "Could not get file id of {0}.", path.Path);

                    if (sourceFileHandle.IsInvalid)
                    {
                        throw new FileNotFoundException(errorMessage, path.FileName);
                    }

                    if (!NativeMethods.GetFileInformationByHandle(sourceFileHandle, out var handleInfo))
                    {
                        throw ThrowLastWin32Error(path.Path, errorMessage);
                    }

                    return((((ulong)handleInfo.FileIndexHigh) << 32) | handleInfo.FileIndexLow);
                }
            }
        }
        /// <summary>
        ///     Copy a file from one path to another synchronously.
        /// </summary>
        public void CopyFile(AbsolutePath sourcePath, AbsolutePath destinationPath, bool replaceExisting)
        {
            sourcePath.ThrowIfPathTooLong();
            destinationPath.ThrowIfPathTooLong();

            if (!FileUtilities.FileExistsNoFollow(sourcePath.Path))
            {
                var message = string.Format(CultureInfo.InvariantCulture, "missing source file=[{0}]", sourcePath);
                throw new FileNotFoundException(message, sourcePath.Path);
            }

            if (replaceExisting)
            {
                var possiblyDeleteExistingDestination = FileUtilities.TryDeletePathIfExists(destinationPath.Path);
                if (!possiblyDeleteExistingDestination.Succeeded)
                {
                    throw possiblyDeleteExistingDestination.Failure.CreateException();
                }
            }

            CreateDirectory(destinationPath.GetParent());

            if (FileUtilities.IsCopyOnWriteSupportedByEnlistmentVolume)
            {
                var possiblyCreateCopyOnWrite = FileUtilities.TryCreateCopyOnWrite(sourcePath.Path, destinationPath.Path, followSymlink: false);
                if (possiblyCreateCopyOnWrite.Succeeded)
                {
                    return;
                }
            }

            if (FileUtilities.IsInKernelCopyingSupportedByHostSystem)
            {
                var possibleInKernelFileCopy = FileUtilities.TryInKernelFileCopy(sourcePath.Path, destinationPath.Path, followSymlink: false);
                if (possibleInKernelFileCopy.Succeeded)
                {
                    return;
                }
            }

            File.Copy(sourcePath.Path, destinationPath.Path, replaceExisting);
        }
        /// <inheritdoc />
        public unsafe void SetLastAccessTimeUtc(AbsolutePath absPath, DateTime lastAccessTimeUtc)
        {
            absPath.ThrowIfPathTooLong();

            var path = absPath.Path;

            if (OperatingSystemHelper.IsUnixOS)
            {
                throw new PlatformNotSupportedException(".NETStandard APIs don't support creation time on MacOS.");
            }
            else
            {
                // System.IO.File.SetLastAccessTimeUtc over-requests the access it needs to set the timestamps on a file.
                // Specifically, it opens a handle to the file with GENERIC_WRITE which will fail on files marked read-only.
                var time = new NativeMethods.FILE_TIME(lastAccessTimeUtc);

                using (SafeFileHandle handle = NativeMethods.CreateFile(path, NativeMethods.FILE_WRITE_ATTRIBUTES, FileShare.ReadWrite | FileShare.Delete, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero))
                {
                    if (handle.IsInvalid)
                    {
                        throw ThrowLastWin32Error(
                                  path,
                                  string.Format(
                                      CultureInfo.InvariantCulture,
                                      "Unable to open a handle for SetFileTime at '{0}' to '{1}'.",
                                      path,
                                      lastAccessTimeUtc));
                    }

                    if (!NativeMethods.SetFileTime(handle, null, &time, null))
                    {
                        throw ThrowLastWin32Error(
                                  path,
                                  string.Format(
                                      CultureInfo.InvariantCulture,
                                      "Unable to SetFileTime at '{0}' to '{1}'.",
                                      path,
                                      lastAccessTimeUtc));
                    }
                }
            }
        }
        /// <inheritdoc />
        public void DeleteFile(AbsolutePath path)
        {
            path.ThrowIfPathTooLong();

            try
            {
                FileUtilities.DeleteFile(path.Path);
            }
            catch (BuildXLException e)
            {
                // Preserving backward compatibility and throwing 'UnauthorizedAccessException' due to shared violation.
                // 0x20 is shared violation.
                if (e.InnerException is NativeWin32Exception win32 && win32.ErrorCode == 0x20)
                {
                    var extraMessage = FileUtilities.TryFindOpenHandlesToFile(path.ToString(), out var info, printCurrentFilePath: false)
                        ? info
                        : "Attempt to find processes with open handles to the file failed.";

                    throw new UnauthorizedAccessException($"{e.Message} {extraMessage}", e);
                }
            }
        }
        /// <inheritdoc />
        public void EnumerateFiles(AbsolutePath path, string pattern, bool recursive, Action <FileInfo> fileHandler)
        {
            path.ThrowIfPathTooLong();
            var result = FileUtilities.EnumerateFiles(
                path.Path,
                recursive,
                pattern ?? "*",
                (directory, fileName, attributes, length) =>
            {
                fileHandler(
                    new FileInfo()
                {
                    FullPath = new AbsolutePath(directory) / fileName,
                    Length   = length
                });
            });

            if (!result.Succeeded)
            {
                throw new IOException($"File enumeration failed for '{path}'.", new Win32Exception(result.NativeErrorCode));
            }
        }
 /// <inheritdoc />
 public byte[] ReadAllBytes(AbsolutePath path)
 {
     path.ThrowIfPathTooLong();
     return(File.ReadAllBytes(path.Path));
 }
 /// <inheritdoc />
 public bool FileExists(AbsolutePath path)
 {
     path.ThrowIfPathTooLong();
     return(File.Exists(path.Path));
 }
 /// <inheritdoc />
 public bool DirectoryExists(AbsolutePath path)
 {
     path.ThrowIfPathTooLong();
     return(Directory.Exists(path.Path));
 }
 /// <inheritdoc />
 public long GetFileSize(AbsolutePath path)
 {
     path.ThrowIfPathTooLong();
     return(new System.IO.FileInfo(path.Path).Length);
 }
 private System.IO.FileInfo GetFileInfo(AbsolutePath path)
 {
     path.ThrowIfPathTooLong();
     return(new System.IO.FileInfo(path.Path));
 }
 /// <inheritdoc />
 public bool FileAttributesAreSubset(AbsolutePath path, FileAttributes attributes)
 {
     path.ThrowIfPathTooLong();
     return((attributes & File.GetAttributes(path.Path)) != 0);
 }
 /// <inheritdoc />
 public DateTime GetLastAccessTimeUtc(AbsolutePath path)
 {
     path.ThrowIfPathTooLong();
     return(new System.IO.FileInfo(path.Path).LastAccessTimeUtc);
 }
 /// <inheritdoc />
 public FileAttributes GetFileAttributes(AbsolutePath path)
 {
     path.ThrowIfPathTooLong();
     return(File.GetAttributes(path.Path) & ~FileSystemConstants.UnsupportedFileAttributes);
 }
 /// <inheritdoc />
 public void CreateDirectory(AbsolutePath path)
 {
     path.ThrowIfPathTooLong();
     Directory.CreateDirectory(path.Path);
 }
        /// <inheritdoc />
        public void WriteAllBytes(AbsolutePath path, byte[] content)
        {
            path.ThrowIfPathTooLong();

            File.WriteAllBytes(path.Path, content);
        }