Пример #1
0
        /// <summary>
        /// Adds synthetic read accesses for all intermediate directory symlinks for the given access path
        /// </summary>
        /// <remarks>
        /// TODO: This function is only adding accesses for symlinks in the given path, so if those suymlinks point to other symlinks,
        /// those are not added. So the result is not completely sound, consider doing multi-hop resolution.
        /// </remarks>
        public void AddReadsForIntermediateSymlinks(FileAccessManifest manifest, ReportedFileAccess access, AbsolutePath accessPath, Dictionary <AbsolutePath, CompactSet <ReportedFileAccess> > accessesByPath)
        {
            Contract.Requires(accessPath.IsValid);
            AbsolutePath currentPath = accessPath.GetParent(m_context.PathTable);

            while (currentPath.IsValid)
            {
                // If we the current path is resolved and its resolved path is the same, then we know there are no more symlinks
                // in the path. Shorcut the search.
                if (m_resolvedPathCache.TryGetValue(currentPath, out var resolvedPath) && currentPath == resolvedPath)
                {
                    return;
                }

                bool isDirSymlink;
                var  result = m_symlinkCache.TryGet(currentPath);
                if (!result.IsFound)
                {
                    isDirSymlink = FileUtilities.IsDirectorySymlinkOrJunction(currentPath.ToString(m_context.PathTable));
                    m_symlinkCache.TryAdd(currentPath, isDirSymlink);
                }
                else
                {
                    isDirSymlink = result.Item.Value;
                }

                if (isDirSymlink)
                {
                    accessesByPath.TryGetValue(currentPath, out CompactSet <ReportedFileAccess> existingAccessesToPath);
                    accessesByPath[currentPath] = existingAccessesToPath.Add(GenerateReadAccessForPath(manifest, currentPath, access));
                }

                currentPath = currentPath.GetParent(m_context.PathTable);
            }
        }
Пример #2
0
        private bool IsDirectorySymlinkOrJunctionWithCache(ExpandedAbsolutePath path)
        {
            if (m_symlinkCache.TryGet(path.Path) is var cachedResult && cachedResult.IsFound)
            {
                return(cachedResult.Item.Value);
            }

            var result = FileUtilities.IsDirectorySymlinkOrJunction(path.ExpandedPath);

            m_symlinkCache.TryAdd(path.Path, result);

            return(result);
        }
Пример #3
0
        private int EnsurePath(AbsolutePath path)
        {
            if (!path.IsValid)
            {
                return(0);
            }

            var getResult = m_pathToParentIndexMap.TryGet(path);

            if (getResult.IsFound)
            {
                return(getResult.Index);
            }

            var parentIndex = EnsurePath(path.GetParent(m_pathTable));
            var addResult   = m_pathToParentIndexMap.GetOrAdd(path, parentIndex);

            Contract.Assert(!addResult.IsFound);

            return(addResult.Index);
        }
Пример #4
0
        /// <summary>
        /// Return a path where all intermediate reparse point directories are resolved to their final destinations.
        /// </summary>
        /// <remarks>
        /// The final segment of the path is never resolved
        /// </remarks>
        public AbsolutePath ResolveIntermediateDirectoryReparsePoints(AbsolutePath path)
        {
            Contract.Requires(path.IsValid);

            // This function only takes care of intermediate directories, so let's fully resolve the parent path
            var parentPath = path.GetParent(m_context.PathTable);

            // If no parent, there is nothing to resolve
            if (!parentPath.IsValid)
            {
                return(path);
            }

            PathAtom filename = path.GetName(m_context.PathTable);

            // Check the cache
            var cachedResult = m_resolvedPathCache.TryGet(parentPath);

            if (cachedResult.IsFound)
            {
                return(cachedResult.Item.Value.Combine(m_context.PathTable, filename));
            }

            // The cache didn't have it, so let's resolve it
            if (!TryResolvePath(parentPath.ToString(m_context.PathTable), out ExpandedAbsolutePath resolvedExpandedPath))
            {
                // If we cannot get the final path (e.g. file not found causes this), then we assume the path
                // is already canonicalized

                // Observe we cannot update the cache since the path was not resolved.
                return(path);
            }

            // Update the cache
            m_resolvedPathCache.TryAdd(parentPath, resolvedExpandedPath.Path);

            return(resolvedExpandedPath.Path.Combine(m_context.PathTable, filename));
        }
Пример #5
0
        private AbsolutePath ResolvePathWithCache(AbsolutePath accessPath, [CanBeNull] string accessPathAsString, [CanBeNull] out string resolvedPathAsString)
        {
            // BuildXL already handles the case of paths whose final fragment is a symlink. So we make sure to resolve everything else.

            var parentPath = accessPath.GetParent(m_context.PathTable);

            if (!parentPath.IsValid)
            {
                // The path doesn't have a root, so even if it is a symlink, BuildXL should be good with it
                resolvedPathAsString = accessPathAsString;
                return(accessPath);
            }

            var lastFragment = accessPath.GetName(m_context.PathTable);

            // In addition to buildxl already handling final fragment symlinks, by caching parents we enhance the chance of a hit, where
            // many files shared the same parent. Query the cache with it.
            var cachedResult = m_resolvedPathCache.TryGet(parentPath);

            if (cachedResult.IsFound)
            {
                // Observe we don't cache expanded paths since that might cause the majority of all the paths of a build to live
                // in memory for the whole execution time. So in case of a cache hit, we just return the absolute path. The expanded
                // path needs to be reconstructed as needed.
                resolvedPathAsString = null;
                var resolvedParentPath = cachedResult.Item.Value;

                // The cached resolved path could be invalid, meaning the original parent pointed to a drive's root
                return(resolvedParentPath.IsValid ?
                       resolvedParentPath.Combine(m_context.PathTable, lastFragment) :
                       AbsolutePath.Create(m_context.PathTable, lastFragment.ToString(m_context.StringTable)));
            }

            // The cache didn't have it, so let's resolve it
            accessPathAsString ??= accessPath.ToString(m_context.PathTable);
            if (!TryResolveSymlinkedPath(accessPathAsString, out ExpandedAbsolutePath resolvedExpandedPath))
            {
                // If we cannot get the final path (e.g. file not found causes this), then we assume the path
                // is already canonicalized

                // Observe we cannot update the cache since the path was not resolved.
                // TODO: we could consider always trying to resolve the parent

                resolvedPathAsString = accessPathAsString;

                return(accessPath);
            }

            // Update the cache. Observe we may be storing an invalid path if the resolved path does not have a parent.
            var resolvedPathParent = resolvedExpandedPath.Path.GetParent(m_context.PathTable);

            m_resolvedPathCache.TryAdd(parentPath, resolvedPathParent);

            // If the resolved path is the same as the access path, then we know its last fragment is not a symlink.
            // So we might just update the symlink cache as well and hopefully speed up subsequent requests to generate read accesses
            // for intermediate symlink dirs
            if (parentPath == resolvedExpandedPath.Path)
            {
                m_symlinkCache.TryAdd(parentPath, false);
            }

            resolvedPathAsString = resolvedExpandedPath.ExpandedPath;
            return(resolvedExpandedPath.Path);
        }
Пример #6
0
 /// <summary>
 /// Gets the symlink target or <see cref="AbsolutePath.Invalid"/> if the path is not a registered symlink
 /// </summary>
 public AbsolutePath TryGetSymlinkTarget(AbsolutePath symlink) => m_symlinkDefinitionMap.TryGet(symlink).Item.Value;
Пример #7
0
        private AbsolutePath ResolvePathWithCache(
            AbsolutePath accessPath,
            [CanBeNull] string accessPathAsString,
            ReportedFileOperation operation,
            FlagsAndAttributes flagsAndAttributes,
            [CanBeNull] out string resolvedPathAsString,
            out bool isDirectoryReparsePoint)
        {
            accessPathAsString ??= accessPath.ToString(m_context.PathTable);
            // If the final segment is a directory reparse point and the operation acts on it, don't resolve it
            if (ShouldNotResolveLastSegment(ExpandedAbsolutePath.CreateUnsafe(accessPath, accessPathAsString), operation, flagsAndAttributes, out isDirectoryReparsePoint))
            {
                AbsolutePath resolvedParentPath = ResolvePathWithCache(accessPath.GetParent(m_context.PathTable), accessPathAsString: null, operation, flagsAndAttributes, out string resolvedParentPathAsString, out _);
                PathAtom     name = accessPath.GetName(m_context.PathTable);
                resolvedPathAsString = resolvedParentPathAsString != null?Path.Combine(resolvedParentPathAsString, name.ToString(m_context.StringTable)) : null;

                return(resolvedParentPath.Combine(m_context.PathTable, name));
            }

            // Check the cache
            var cachedResult = m_resolvedPathCache.TryGet(accessPath);

            if (cachedResult.IsFound)
            {
                // Observe we don't cache expanded paths since that might cause the majority of all the paths of a build to live
                // in memory for the whole execution time. So in case of a cache hit, we just return the absolute path. The expanded
                // path needs to be reconstructed as needed.
                resolvedPathAsString = null;
                return(cachedResult.Item.Value);
            }

            // If not found and the path is not a reparse point, check the cache for its parent.
            // Many files may have the same parent, and since the path is not a reparse point we know that
            // resolve(parent\segment) == resolve(parent)\segment
            var parentPath = accessPath.GetParent(m_context.PathTable);

            if (parentPath.IsValid && !isDirectoryReparsePoint && m_resolvedPathCache.TryGet(parentPath) is var cachedParent && cachedParent.IsFound)
            {
                var lastFragment = accessPath.GetName(m_context.PathTable);
                resolvedPathAsString = null;
                AbsolutePath cachedParentPath = cachedParent.Item.Value;
                return(cachedParentPath.IsValid ?
                       cachedParentPath.Combine(m_context.PathTable, lastFragment) :
                       AbsolutePath.Create(m_context.PathTable, lastFragment.ToString(m_context.StringTable)));
            }

            // The cache didn't have it, so let's resolve it
            if (!TryResolveSymlinkedPath(accessPathAsString, out ExpandedAbsolutePath resolvedExpandedPath))
            {
                // If we cannot get the final path (e.g. file not found causes this), then we assume the path
                // is already canonicalized

                // Observe we cannot update the cache since the path was not resolved.
                // TODO: we could consider always trying to resolve the parent

                resolvedPathAsString = accessPathAsString;

                return(accessPath);
            }

            // Update the cache
            m_resolvedPathCache.TryAdd(accessPath, resolvedExpandedPath.Path);

            // If the path is not a reparse point, update the parent as well
            if (!isDirectoryReparsePoint && parentPath.IsValid)
            {
                // Observe we may be storing an invalid path if the resolved path does not have a parent.
                m_resolvedPathCache.TryAdd(parentPath, resolvedExpandedPath.Path.GetParent(m_context.PathTable));
            }

            // If the resolved path is the same as the access path, then we know its last fragment is not a reparse point.
            // So we might just update the symlink cache as well and hopefully speed up subsequent requests to generate read accesses
            // for intermediate symlink dirs
            if (accessPath == resolvedExpandedPath.Path)
            {
                while (parentPath.IsValid && m_symlinkCache.TryAdd(parentPath, false))
                {
                    parentPath = parentPath.GetParent(m_context.PathTable);
                }
            }

            resolvedPathAsString = resolvedExpandedPath.ExpandedPath;
            return(resolvedExpandedPath.Path);
        }