internal static Task <PipRuntimeTimeTable> LoadAsync(Stream fileStream) { return(ExceptionUtilities.HandleRecoverableIOExceptionAsync( async() => { int size = await ReadFileFormatMarkerAsync(fileStream); var table = new PipRuntimeTimeTable(LoggingContext); using (BuildXLReader reader = new BuildXLReader(false, fileStream, true)) { for (int i = 0; i < size; ++i) { long semiStableHash = reader.ReadInt64(); PipHistoricPerfData historicData; if (PipHistoricPerfData.Deserialize(reader, out historicData)) { if (!table.m_runtimeData.TryAdd(semiStableHash, historicData)) { throw new BuildXLException("Corrupted file has duplicate records"); } } } } if (fileStream.Position != fileStream.Length) { throw new BuildXLException("Corrupted file has excess bytes"); } return table; }, ex => { throw new BuildXLException("Reading of file failed", ex); })); }
/// <inheritdoc /> public Task <Possible <Unit, Failure> > TryMaterializeAsync( FileRealizationMode fileRealizationMode, ExpandedAbsolutePath path, ContentHash contentHash) { return(Task.Run( () => { lock (m_lock) { CacheEntry entry; if (m_content.TryGetValue(contentHash, out entry)) { if ((entry.Sites & CacheSites.Local) == 0) { return new Failure <string>("Content is available in 'remote' cache but is not local. Load it locally first with TryLoadAvailableContentAsync."); } string expandedPath = path.ExpandedPath; var result = ExceptionUtilities.HandleRecoverableIOExceptionAsync( async() => await PlaceFileInternalAsync(expandedPath, contentHash, fileRealizationMode), ex => { throw new BuildXLException("Failed to materialize content (content found, but couldn't write it)", ex); }); m_pathRealizationModes[expandedPath] = fileRealizationMode; return result.Result; } else { return new Failure <string>("Content not found (locally or remotely). Store it first with TryStoreAsync."); } } })); }
/// <inheritdoc /> public Task <bool> CopyFileAsync( string source, string destination, Func <SafeFileHandle, SafeFileHandle, bool> predicate = null, Action <SafeFileHandle, SafeFileHandle> onCompletion = null) { Contract.Requires(!string.IsNullOrEmpty(source)); Contract.Requires(!string.IsNullOrEmpty(destination)); return(ExceptionUtilities.HandleRecoverableIOExceptionAsync( async() => { using (FileStream sourceStream = CreateAsyncFileStream( source, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) { if (predicate != null) { SafeFileHandle destinationHandle; OpenFileResult predicateQueryOpenResult = s_fileSystem.TryCreateOrOpenFile( destination, FileDesiredAccess.GenericRead, FileShare.Read | FileShare.Delete, FileMode.OpenOrCreate, FileFlagsAndAttributes.None, out destinationHandle); using (destinationHandle) { if (!predicateQueryOpenResult.Succeeded) { throw new BuildXLException( I($"Failed to open a copy destination '{destination}' to check its version"), predicateQueryOpenResult.CreateExceptionForError()); } if (!predicate(sourceStream.SafeFileHandle, predicateQueryOpenResult.OpenedOrTruncatedExistingFile ? destinationHandle : null)) { return false; } } } using (FileStream destinationStream = CreateReplacementFile(destination, FileShare.Delete, openAsync: true)) { await sourceStream.CopyToAsync(destinationStream); onCompletion?.Invoke(sourceStream.SafeFileHandle, destinationStream.SafeFileHandle); } } return true; }, ex => { throw new BuildXLException("File copy failed", ex); })); }
/// <inheritdoc /> public Task <bool> WriteAllBytesAsync( string filePath, byte[] bytes, Func <SafeFileHandle, bool> predicate = null, Action <SafeFileHandle> onCompletion = null) { Contract.Requires(!string.IsNullOrEmpty(filePath)); Contract.Requires(bytes != null); return(ExceptionUtilities.HandleRecoverableIOExceptionAsync( async() => { if (predicate != null) { SafeFileHandle destinationHandle; OpenFileResult predicateQueryOpenResult = m_fileSystem.TryCreateOrOpenFile( filePath, FileDesiredAccess.GenericRead, FileShare.Read | FileShare.Delete, FileMode.OpenOrCreate, FileFlagsAndAttributes.None, out destinationHandle); using (destinationHandle) { if (!predicateQueryOpenResult.Succeeded) { throw new BuildXLException( I($"Failed to open file '{filePath}' to check its version"), predicateQueryOpenResult.CreateExceptionForError()); } if (!predicate(predicateQueryOpenResult.OpenedOrTruncatedExistingFile ? destinationHandle : null)) { return false; } } } using (FileStream stream = CreateReplacementFile(filePath, FileShare.Delete, openAsync: true)) { await stream.WriteAsync(bytes, 0, bytes.Length); } if (onCompletion != null) { using (var file = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Delete)) { onCompletion(file.SafeFileHandle); } } return true; }, ex => { throw new BuildXLException("File write failed", ex); })); }
/// <summary> /// Tries to duplicate a file. /// </summary> public static Task <FileDuplicationResult> TryDuplicateOneFileAsync(string sourcePath, string destinationPath) { Contract.Requires(!string.IsNullOrWhiteSpace(sourcePath)); Contract.Requires(!string.IsNullOrWhiteSpace(destinationPath)); if (string.Compare(sourcePath, destinationPath, StringComparison.OrdinalIgnoreCase) == 0) { return(Task.FromResult(FileDuplicationResult.Existed)); // Nothing to do. } return(ExceptionUtilities.HandleRecoverableIOExceptionAsync( async() => { var destinationDirectory = Path.GetDirectoryName(destinationPath); if (DirectoryExistsNoFollow(destinationDirectory)) { if (FileExistsNoFollow(destinationPath)) { DeleteFile(destinationPath); } } else { CreateDirectory(destinationDirectory); } if (!OperatingSystemHelper.IsUnixOS) { var hardlinkResult = s_fileSystem.TryCreateHardLinkViaSetInformationFile(destinationPath, sourcePath); if (hardlinkResult == CreateHardLinkStatus.Success) { return FileDuplicationResult.Hardlinked; } } else { if (IsCopyOnWriteSupportedByEnlistmentVolume) { var possiblyCreateCopyOnWrite = TryCreateCopyOnWrite(sourcePath, destinationPath, followSymlink: false); if (possiblyCreateCopyOnWrite.Succeeded) { return FileDuplicationResult.Copied; } } } await CopyFileAsync(sourcePath, destinationPath); return FileDuplicationResult.Copied; }, ex => { throw new BuildXLException(ex.Message); })); }
/// <summary> /// Tries to duplicate a file. /// </summary> public static Task <FileDuplicationResult> TryDuplicateOneFileAsync(string sourcePath, string destinationPath) { Contract.Requires(!string.IsNullOrWhiteSpace(sourcePath)); Contract.Requires(!string.IsNullOrWhiteSpace(destinationPath)); if (string.Compare(sourcePath, destinationPath, StringComparison.OrdinalIgnoreCase) == 0) { return(Task.FromResult(FileDuplicationResult.Existed)); // Nothing to do. } return(ExceptionUtilities.HandleRecoverableIOExceptionAsync( async() => { var destinationDirectory = Path.GetDirectoryName(destinationPath); if (DirectoryExistsNoFollow(destinationDirectory)) { if (FileExistsNoFollow(destinationPath)) { DeleteFile(destinationPath); } } else { CreateDirectory(destinationDirectory); } // Our implementation on macOS requires APFS which has copy on write. There's no advantage to using // a hardlink over a file copy. VSTS 1333283 if (!OperatingSystemHelper.IsUnixOS) { var hardlinkResult = s_fileSystem.TryCreateHardLinkViaSetInformationFile(destinationPath, sourcePath); if (hardlinkResult == CreateHardLinkStatus.Success) { return FileDuplicationResult.Hardlinked; } } await CopyFileAsync(sourcePath, destinationPath); return FileDuplicationResult.Copied; }, ex => { throw new BuildXLException(ex.Message); })); }
internal Task SaveAsync(Stream fileStream) { return(ExceptionUtilities.HandleRecoverableIOExceptionAsync( async() => { await WriteFileFormatMarkerAsync(fileStream, m_runtimeData.Count); using (BuildXLWriter writer = new BuildXLWriter(false, fileStream, true, false)) { foreach (KeyValuePair <long, PipHistoricPerfData> kvp in m_runtimeData) { writer.Write(kvp.Key); kvp.Value.Serialize(writer); } } // Truncate, just in case file already existed but was bigger fileStream.SetLength(fileStream.Position); return (object)null; }, ex => { throw new BuildXLException("Writing of file failed", ex); })); }
/// <inheritdoc /> public Task <bool> CopyFileAsync( string source, string destination, Func <SafeFileHandle, SafeFileHandle, bool> predicate = null, Action <SafeFileHandle, SafeFileHandle> onCompletion = null) { Contract.Requires(!string.IsNullOrEmpty(source)); Contract.Requires(!string.IsNullOrEmpty(destination)); return(ExceptionUtilities.HandleRecoverableIOExceptionAsync( async() => { using (FileStream sourceStream = CreateAsyncFileStream( source, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) { if (predicate != null) { SafeFileHandle destinationHandle; OpenFileResult predicateQueryOpenResult = m_fileSystem.TryCreateOrOpenFile( destination, FileDesiredAccess.GenericRead, FileShare.Read | FileShare.Delete, FileMode.OpenOrCreate, FileFlagsAndAttributes.None, out destinationHandle); using (destinationHandle) { if (!predicateQueryOpenResult.Succeeded) { throw new BuildXLException( I($"Failed to open a copy destination '{destination}' to check its version"), predicateQueryOpenResult.CreateExceptionForError()); } if (!predicate(sourceStream.SafeFileHandle, predicateQueryOpenResult.OpenedOrTruncatedExistingFile ? destinationHandle : null)) { return false; } } } using (var destinationStream = CreateReplacementFile(destination, FileShare.Delete, openAsync: true)) { await sourceStream.CopyToAsync(destinationStream); var mode = GetFilePermissionsForFilePath(source, followSymlink: false); var result = SetFilePermissionsForFilePath(destination, checked ((FilePermissions)mode), followSymlink: false); if (result < 0) { throw new BuildXLException($"Failed to set permissions for file copy at '{destination}' - error: {Marshal.GetLastWin32Error()}"); } } if (onCompletion != null) { using (var dest = File.Open(destination, FileMode.Open, FileAccess.Read, FileShare.Delete)) { onCompletion(sourceStream.SafeFileHandle, dest.SafeFileHandle); } } } return true; }, ex => { throw new BuildXLException(I($"File copy from '{source}' to '{destination}' failed"), ex); })); }