/// <summary> /// Checks if the given path is an output under a shared opaque by verifying whether <see cref="WellKnownTimestamps.OutputInSharedOpaqueTimestamp"/> is the creation time of the file /// </summary> /// <remarks> /// If the given path is a directory, it is always considered part of a shared opaque /// </remarks> public static bool IsSharedOpaqueOutput(string expandedPath) { // On Windows: FOLLOW symlinks // - directory symlinks are not fully supported (e.g., producing directory symlinks) // - other reparse points are at play (e.g., junctions, Helium containers) which necessitate // that we transparently follow those reparse points // On non-Windows: NO_FOLLOW symlinks // - directory symlinks are supported // - all symlinks are treated uniformly as files // - if 'expandedPath' is a symlink pointing to a non-existent file, 'expandedPath' should hence // still be treated as an existent file // - similarly, if 'expandedPath' is a symlink pointing to a directory, it should still be treated as a file bool followSymlink = !OperatingSystemHelper.IsUnixOS; // If the file is not there (the check may be happening against a reported write that later got deleted) // then there is nothing to do var maybeResult = FileUtilities.TryProbePathExistence(expandedPath, followSymlink); if (maybeResult.Succeeded && maybeResult.Result == PathExistence.Nonexistent) { return(true); } // We don't really track directories as part of shared opaques. // So we consider them all potential members and return true. // It is important to track directory symlinks, because they are considered files for sake of shared opaque scrubbing. if (maybeResult.Succeeded && maybeResult.Result == PathExistence.ExistsAsDirectory) { return(true); } return(OperatingSystemHelper.IsUnixOS ? Unix.IsSharedOpaqueOutput(expandedPath) : Win.IsSharedOpaqueOutput(expandedPath)); }