/// <summary> /// Some validations and operations require knowledge of whether a mount is nested under another mount. /// </summary> /// <returns>true if the operations were performed successfully</returns> private bool AddAndPerformNestingOperations() { bool success = true; // flip alternative roots -> mount map for easier lookup var alternativeRoots = m_alternativeRoots.ToMultiValueDictionary(kvp => kvp.Value, kvp => kvp.Key); // Compute the depth of each mount root so parent mounts can be visited first List <KeyValuePair <int, IMount> > mountsByRootDepth = new List <KeyValuePair <int, IMount> >(); foreach (IMount mount in m_mountMapBuilder.Values) { int depth = 0; AbsolutePath path = mount.Path; while (path.IsValid) { path = path.GetParent(m_context.PathTable); depth++; } mountsByRootDepth.Add(new KeyValuePair <int, IMount>(depth, mount)); } mountsByRootDepth.Sort((depthAndMount1, depthAndMount2) => depthAndMount1.Key.CompareTo(depthAndMount2.Key)); foreach (var mountEntry in mountsByRootDepth) { var childMount = mountEntry.Value; var parentMountInfo = MountPathExpander.GetSemanticPathInfo(childMount.Path); if (parentMountInfo.IsValid) { IMount parentMount; if (!m_mountMapBuilder.TryGetValue(parentMountInfo.Root, out parentMount) && m_parent != null) { parentMount = m_parent.m_mountMapBuilder[parentMountInfo.Root]; } if (parentMount.IsScrubbable && !childMount.IsScrubbable) { LogRelatedMountError( Logger.Log.ScrubbableMountsMayOnlyContainScrubbableMounts, childMount: childMount, parentMount: parentMount); success = false; } } // TODO: Does not adding on success == false change behavior if (success) { MountPathExpander.Add(m_context.PathTable, childMount); if (alternativeRoots.TryGetValue(childMount, out var additionalRoots)) { foreach (var root in additionalRoots) { MountPathExpander.AddWithExistingName( m_context.PathTable, new SemanticPathInfo(childMount.Name, root, childMount.TrackSourceFileChanges, childMount.IsReadable, childMount.IsWritable, childMount.IsSystem, childMount.IsScrubbable, childMount.AllowCreateDirectory)); } } } } return(success); }
/// <summary> /// Some validations and operations require knowledge of whether a mount is nested under another mount. /// </summary> /// <returns>true if the operations were performed successfully</returns> private bool AddAndPerformNestingOperations() { bool success = true; // flip alternative roots -> mount map for easier lookup var alternativeRoots = m_alternativeRoots.ToMultiValueDictionary(kvp => kvp.Value, kvp => kvp.Key); // Collect here the mount roots of tokenizable mounts. The logic is: // * System mounts are tokenizable. // * Mounts that are descendants of a system mount are tokenizable // Rationale: We don't tokenize all mounts because that can lead to incorrect fingerprinting. E.g. let's say a tool writes // a path P in an output file, P is a descendant of a tokenized mount root M and the corresponding pip gets cached. // If the pip is looked up on a machine where M is a different root M', then we can get a cache hit, whereas it should have been // a miss because the tool would have produced an output file with a written path P' // As a compromise solution, system mounts are deemed safe enough to tokenize because they tend to be stable enough and it is unlikely // that tools embed system-related paths in produced files. Transitively, any non-system mount that has a // system mount above should be equally safe. var tokenizableMountRoots = new HashSet <AbsolutePath>(); // All system mount paths var systemMounts = m_mountPathIdentifiersByMount.Keys.Where(mount => mount.IsSystem && mount.Path.IsValid).Select(mount => mount.Path).ToHashSet(); // Compute the depth of each mount root so parent mounts can be visited first // As we go upwards, also verify if there is any parent mount that is a system one List <KeyValuePair <int, IMount> > mountsByRootDepth = new List <KeyValuePair <int, IMount> >(); foreach (IMount mount in m_mountMapBuilder.Values) { int depth = 0; AbsolutePath path = mount.Path; while (path.IsValid) { // if a mount is defined in the same location of a system mount, or has a system mount somewhere above, then it is also tokenizable if (systemMounts.Contains(path)) { tokenizableMountRoots.Add(mount.Path); } path = path.GetParent(m_context.PathTable); depth++; } mountsByRootDepth.Add(new KeyValuePair <int, IMount>(depth, mount)); } mountsByRootDepth.Sort((depthAndMount1, depthAndMount2) => depthAndMount1.Key.CompareTo(depthAndMount2.Key)); foreach (var mountEntry in mountsByRootDepth) { var childMount = mountEntry.Value; var parentMountInfo = MountPathExpander.GetSemanticPathInfo(childMount.Path); if (parentMountInfo.IsValid) { IMount parentMount; if (!m_mountMapBuilder.TryGetValue(parentMountInfo.Root, out parentMount) && m_parent != null) { parentMount = m_parent.m_mountMapBuilder[parentMountInfo.Root]; } if (parentMount.IsScrubbable && !childMount.IsScrubbable) { LogRelatedMountError( Logger.Log.ScrubbableMountsMayOnlyContainScrubbableMounts, childMount: childMount, parentMount: parentMount); success = false; } } // TODO: Does not adding on success == false change behavior if (success) { MountPathExpander.Add(m_context.PathTable, childMount, isTokenizable: tokenizableMountRoots.Contains(childMount.Path)); if (alternativeRoots.TryGetValue(childMount, out var additionalRoots)) { foreach (var root in additionalRoots) { MountPathExpander.AddWithExistingName( m_context.PathTable, new SemanticPathInfo( childMount.Name, root, childMount.TrackSourceFileChanges, childMount.IsReadable, childMount.IsWritable, childMount.IsSystem, childMount.IsStatic, childMount.IsScrubbable, childMount.AllowCreateDirectory, tokenizable: tokenizableMountRoots.Contains(root))); } } } } return(success); }