/// <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.IsMacOS ? XattrBased.IsSharedOpaqueOutput(expandedPath) : TimestampBased.IsSharedOpaqueOutput(expandedPath)); }
/// <summary> /// Marks a given path as "shared opaque output" /// </summary> /// <remarks> /// Retries are needed because: (Win|Unix).SetPathAsSharedOpaqueOutput does something like /// - check if file has write access rights /// - if it doesn't, give it those rights /// - mark the file as shared opaque output /// - ... /// There is a race between checking and setting access rights, so it is possible that /// here we check its rights, we see that it has the correct rights, then someone else /// (e.g., the cache) revokes those rights, and so we fail to mark the file as shared opaque output. /// </remarks> /// <exception cref="BuildXLException">When unsuccessful</exception> public static void SetPathAsSharedOpaqueOutput(string expandedPath) { int attempt = 0; while (true) { attempt += 1; // wait a bit between attempts if (attempt > 1) { System.Threading.Thread.Sleep(Multiply(SleepDurationBetweenMarkingAttempts, attempt - 1)); } try { if (OperatingSystemHelper.IsMacOS) { XattrBased.SetPathAsSharedOpaqueOutput(expandedPath); } else { TimestampBased.SetPathAsSharedOpaqueOutput(expandedPath); } return; } catch (BuildXLException e) { if (attempt >= MaxNumberOfAttemptsForMarkingSharedOpaqueOutputs) { throw new BuildXLException($"Exceeded max number of attempts ({MaxNumberOfAttemptsForMarkingSharedOpaqueOutputs}) to mark '{expandedPath}' as shared opaque output", e); } } } }