Beispiel #1
0
        public async Task ProcessInContainerGeneratingNestedOutputsPassesAllSandboxValidations()
        {
            var context = BuildXLContext.CreateInstanceForTesting();

            using (var tempFiles = new TempFileStorage(canGetFileNames: true))
            {
                var pathTable = context.PathTable;

                var outputFile       = tempFiles.GetUniqueFileName(@"outputs\");
                var outputFileNested = tempFiles.GetUniqueFileName(@"outputs\nested\");

                var outputFilePath       = AbsolutePath.Create(pathTable, outputFile);
                var outputFileNestedPath = AbsolutePath.Create(pathTable, outputFileNested);

                FileUtilities.CreateDirectory(outputFileNestedPath.GetParent(pathTable).ToString(pathTable));

                // Arguments to create two output files, one nested under the other
                var arguments = $"echo hi > {outputFile} && echo bye > {outputFileNested}";
                var outputs   = ReadOnlyArray <FileArtifactWithAttributes> .FromWithoutCopy(
                    new[]
                {
                    FileArtifactWithAttributes.Create(FileArtifact.CreateOutputFile(outputFilePath), FileExistence.Required),
                    FileArtifactWithAttributes.Create(FileArtifact.CreateOutputFile(outputFileNestedPath), FileExistence.Required)
                });

                var pip = CreateConsoleProcessInContainer(context, tempFiles, pathTable, arguments, outputs, CollectionUtilities.EmptyArray <DirectoryArtifact>().ToReadOnlyArray());
                var pipExecutionResult = await RunProcess(context, pip);

                // Observe that the fact the execution result succeeds means that the expected outputs are in their expected place
                XAssert.AreEqual(SandboxedProcessPipExecutionStatus.Succeeded, pipExecutionResult.Status);
            }
        }
Beispiel #2
0
        public async Task TombstoneFileDoesNotRepresentARequiredOutput()
        {
            var context = BuildXLContext.CreateInstanceForTesting();

            using (var tempFiles = new TempFileStorage(canGetFileNames: true))
            {
                var pathTable = context.PathTable;

                var outputFile     = tempFiles.GetUniqueFileName();
                var outputFilePath = AbsolutePath.Create(pathTable, outputFile);

                var renamedFile     = $"{outputFile}.renamed";
                var renamedFilePath = AbsolutePath.Create(pathTable, renamedFile);

                // Arguments to create an output file that gets immediately renamed.
                // However, both files are marked as required, even though the original one
                // is not there after the rename happens
                var arguments = $"echo hi > {outputFile} && move {outputFile} {renamedFile}";
                var outputs   = ReadOnlyArray <FileArtifactWithAttributes> .FromWithoutCopy(
                    new[]
                {
                    FileArtifactWithAttributes.Create(FileArtifact.CreateOutputFile(outputFilePath), FileExistence.Required),
                    FileArtifactWithAttributes.Create(FileArtifact.CreateOutputFile(renamedFilePath), FileExistence.Required)
                });

                var pip = CreateConsoleProcessInContainer(context, tempFiles, pathTable, arguments, outputs, CollectionUtilities.EmptyArray <DirectoryArtifact>().ToReadOnlyArray());
                // the move command under the console seems to have some issue with the detours policy
                // This is orthogonal to the test, and we don't care about detours at this point
                var pipExecutionResult = await RunProcess(context, pip, failUnexpectedFileAccesses : false);

                // The redirected output is created as a tombstone file, but the sandboxed pip executor should report it as an absent file
                AssertErrorEventLogged(ProcessesLogEventId.PipProcessMissingExpectedOutputOnCleanExit);
                AssertErrorEventLogged(ProcessesLogEventId.PipProcessExpectedMissingOutputs);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Creates an output directory.
        /// </summary>
        protected ValueTuple <DirectoryArtifact, ReadOnlyArray <FileArtifactWithAttributes> > CreateOutputDirectory(
            AbsolutePath rootPath = default(AbsolutePath),
            RelativePath relativePathToDirectory = default(RelativePath),
            RelativePath[] relativePathToMembers = null)
        {
            var directory = CreateDirectory(rootPath, relativePathToDirectory);
            ReadOnlyArray <FileArtifactWithAttributes> members;

            if (relativePathToMembers == null || relativePathToMembers.Length == 0)
            {
                members = ReadOnlyArray <FileArtifactWithAttributes> .Empty;
            }
            else
            {
                var fullMemberNames = new FileArtifactWithAttributes[relativePathToMembers.Length];
                for (int i = 0; i < fullMemberNames.Length; ++i)
                {
                    fullMemberNames[i] = FileArtifactWithAttributes.Create(FileArtifact.CreateOutputFile(directory.Path.Combine(Context.PathTable, relativePathToMembers[i])), FileExistence.Required);
                }

                members = ReadOnlyArray <FileArtifactWithAttributes> .FromWithoutCopy(fullMemberNames);
            }

            return(directory, members);
        }
        public async Task CorrelateMoveFileAsync()
        {
            var context   = BuildXLContext.CreateInstanceForTesting();
            var pathTable = context.PathTable;

            using (var tempFiles = new TempFileStorage(canGetFileNames: true, rootPath: TemporaryDirectory))
            {
                AbsolutePath sourceDirectory = tempFiles.GetDirectory(pathTable, "Source");
                AbsolutePath sourceFile      = sourceDirectory.Combine(pathTable, "SourceFile.txt");
                var          destinationFile = tempFiles.GetFileName(pathTable, "DestinationFile.txt");
                WriteFile(pathTable, sourceFile, "content");

                var process = CreateDetourProcess(
                    context,
                    pathTable,
                    tempFiles,
                    argumentStr: "CorrelateMoveFile",
                    inputFiles: ReadOnlyArray <FileArtifact> .Empty,
                    inputDirectories: ReadOnlyArray <DirectoryArtifact> .Empty,
                    outputFiles: ReadOnlyArray <FileArtifactWithAttributes> .FromWithoutCopy(
                        FileArtifactWithAttributes.Create(
                            FileArtifact.CreateOutputFile(destinationFile), FileExistence.Required)),
                    outputDirectories: ReadOnlyArray <DirectoryArtifact> .Empty,
                    untrackedScopes: ReadOnlyArray <AbsolutePath> .FromWithoutCopy(sourceDirectory));

                var correlator = new Correlator(pathTable);
                SandboxedProcessPipExecutionResult result = await RunProcessAsync(
                    pathTable : pathTable,
                    ignoreSetFileInformationByHandle : false,
                    ignoreZwRenameFileInformation : false,
                    monitorNtCreate : true,
                    ignoreReparsePoints : false,
                    ignoreNonCreateFileReparsePoints : false,
                    monitorZwCreateOpenQueryFile : false,
                    context : context,
                    pip : process,
                    detoursListener : correlator,
                    errorString : out _);

                VerifyNormalSuccess(context, result);

                XAssert.IsTrue(File.Exists(destinationFile.ToString(pathTable)));

                var toVerify = new List <(AbsolutePath, RequestedAccess, FileAccessStatus)>
                {
                    (sourceFile, RequestedAccess.ReadWrite, FileAccessStatus.Allowed),
                    (destinationFile, RequestedAccess.Write, FileAccessStatus.Allowed)
                };

                VerifyFileAccesses(context, result.AllReportedFileAccesses, toVerify.ToArray());
                correlator.VerifyCorrelation(new Correlator.VerifiedCorrelation(
                                                 destinationFile.ToString(pathTable),
                                                 ReportedFileOperation.MoveFileWithProgressDest,
                                                 sourceFile.ToString(pathTable),
                                                 ReportedFileOperation.MoveFileWithProgressSource));
            }
        }
        public void FileExistenceStaysTheSameWithCallToCreateNextWrittenVersion(FileExistence fileExistence)
        {
            var          pathTable       = new PathTable();
            AbsolutePath filePath        = AbsolutePath.Create(pathTable, A("t", "file1.txt"));
            var          artifact        = FileArtifactWithAttributes.Create(FileArtifact.CreateSourceFile(filePath), fileExistence);
            var          anotherArtifact = artifact.CreateNextWrittenVersion();

            XAssert.AreEqual(artifact.FileExistence, anotherArtifact.FileExistence,
                             "FileExistence should be the same after call to CreateNextWrittenVersion");
        }
Beispiel #6
0
        private static Process CreateOutputFileProcess(BuildXLContext context, TempFileStorage tempFiles, AbsolutePath outputFilePath)
        {
            // Arguments to create an output file
            var arguments = $"echo hi > {outputFilePath.ToString(context.PathTable)}";
            var outputs   = ReadOnlyArray <FileArtifactWithAttributes> .FromWithoutCopy(
                new[] { FileArtifactWithAttributes.Create(FileArtifact.CreateOutputFile(outputFilePath), FileExistence.Required) });

            var pip = CreateConsoleProcessInContainer(context, tempFiles, context.PathTable, arguments, outputs, CollectionUtilities.EmptyArray <DirectoryArtifact>().ToReadOnlyArray());

            return(pip);
        }
        public void TestSerialization(FileExistence fileExistence)
        {
            var pathTable    = new PathTable();
            var fileArtifact = FileArtifactWithAttributes.Create(FileArtifact.CreateSourceFile(AbsolutePath.Create(pathTable,
                                                                                                                   A("c", "foo.txt"))), fileExistence);

            HasTheSamePathAndExistence(fileArtifact, CloneViaSerialization(fileArtifact));

            // Write count is not affected by serialization/deserialization
            HasTheSamePathAndExistence(fileArtifact, CloneViaSerialization(fileArtifact.CreateNextWrittenVersion()));
        }
Beispiel #8
0
        public async Task IsolationLevelControlsWriteRedirection(ContainerIsolationLevel containerIsolationLevel)
        {
            var context = BuildXLContext.CreateInstanceForTesting();

            using (var tempFiles = new TempFileStorage(canGetFileNames: true))
            {
                var pathTable = context.PathTable;

                var outputFile       = tempFiles.GetUniqueFileName(@"fileOutputs\");
                var opaqueOutputFile = tempFiles.GetUniqueFileName(@"directoryOutputs\");

                var outputFilePath       = AbsolutePath.Create(pathTable, outputFile);
                var opaqueOutputFilePath = AbsolutePath.Create(pathTable, opaqueOutputFile);

                var arguments = $"echo hi > {outputFile} && echo bye > {opaqueOutputFile}";
                var outputs   = ReadOnlyArray <FileArtifactWithAttributes> .FromWithoutCopy(
                    new[]
                {
                    FileArtifactWithAttributes.Create(FileArtifact.CreateOutputFile(outputFilePath), FileExistence.Required),
                });

                var opaqueOutputs = ReadOnlyArray <DirectoryArtifact> .FromWithoutCopy(
                    new[]
                {
                    new DirectoryArtifact(opaqueOutputFilePath.GetParent(pathTable), 1, isSharedOpaque: containerIsolationLevel == ContainerIsolationLevel.IsolateSharedOpaqueOutputDirectories),
                }
                    );

                var pip = CreateConsoleProcessInContainer(context, tempFiles, pathTable, arguments, outputs, opaqueOutputs, containerIsolationLevel);
                var pipExecutionResult = await RunProcess(context, pip);

                XAssert.AreEqual(SandboxedProcessPipExecutionStatus.Succeeded, pipExecutionResult.Status);

                var redirectedDirForFiles = pip.UniqueRedirectedDirectoryRoot.Combine(pathTable, "fileOutputs");
                var redirectedFile        = redirectedDirForFiles.Combine(pathTable, outputFilePath.GetName(pathTable)).ToString(pathTable);

                var redirectedDirForDirectories = pip.UniqueRedirectedDirectoryRoot.Combine(pathTable, "directoryOutputs");
                var redirectedOpaqueFile        = redirectedDirForDirectories.Combine(pathTable, opaqueOutputFilePath.GetName(pathTable)).ToString(pathTable);

                // Make sure outputs got redirected based on the configured isolation level
                switch (containerIsolationLevel)
                {
                case ContainerIsolationLevel.IsolateOutputFiles:
                    XAssert.IsTrue(File.Exists(redirectedFile));
                    break;

                case ContainerIsolationLevel.IsolateExclusiveOpaqueOutputDirectories:
                case ContainerIsolationLevel.IsolateSharedOpaqueOutputDirectories:
                    XAssert.IsTrue(File.Exists(redirectedOpaqueFile));
                    break;
                }
            }
        }
        public void TestTemporaryAndRequiredOutputArtifacts()
        {
            var          pathTable = new PathTable();
            AbsolutePath filePath  = AbsolutePath.Create(pathTable, A("t", "file1.txt"));

            var temporaryArtifact = FileArtifactWithAttributes.Create(FileArtifact.CreateSourceFile(filePath), FileExistence.Temporary).CreateNextWrittenVersion();
            var requiredArtifact  = FileArtifactWithAttributes.Create(FileArtifact.CreateSourceFile(filePath), FileExistence.Required).CreateNextWrittenVersion();
            var optionalArtifact  = FileArtifactWithAttributes.Create(FileArtifact.CreateSourceFile(filePath), FileExistence.Optional).CreateNextWrittenVersion();

            XAssert.IsTrue(temporaryArtifact.IsOutputFile, "Instance should be output");
            XAssert.IsTrue(requiredArtifact.IsOutputFile, "Instance should be output");
            XAssert.IsTrue(optionalArtifact.IsOutputFile, "Instance should be output");

            XAssert.IsTrue(temporaryArtifact.IsTemporaryOutputFile, "Temporary artifact should be temporary");
            XAssert.IsFalse(requiredArtifact.IsTemporaryOutputFile, "Required artifact is not temporary");
            XAssert.IsFalse(optionalArtifact.IsTemporaryOutputFile, "Optional artifact is not temporary");

            XAssert.IsFalse(temporaryArtifact.IsRequiredOutputFile, "Temporary artifact is not required");
            XAssert.IsTrue(requiredArtifact.IsRequiredOutputFile, "Required artifact is required");
            XAssert.IsFalse(optionalArtifact.IsRequiredOutputFile, "Optional artifact is not required");
        }
        public void FileArtifactEquality()
        {
            var          pathTable = new PathTable();
            FileArtifact file1     = FileArtifact.CreateSourceFile(AbsolutePath.Create(pathTable, A("t", "file1.txt")));
            FileArtifact file2     = FileArtifact.CreateSourceFile(AbsolutePath.Create(pathTable, A("t", "file2.txt")));

            StructTester.TestEquality(
                baseValue: FileArtifactWithAttributes.Create(file1, FileExistence.Required),
                equalValue: FileArtifactWithAttributes.Create(file1, FileExistence.Required),
                notEqualValues: new[]
            {
                FileArtifactWithAttributes.Create(file1, FileExistence.Optional),
                FileArtifactWithAttributes.Create(file1, FileExistence.Optional).CreateNextWrittenVersion(),
                FileArtifactWithAttributes.Create(file2, FileExistence.Temporary),
                FileArtifactWithAttributes.Create(file2, FileExistence.Temporary).CreateNextWrittenVersion(),
                FileArtifactWithAttributes.Create(file1, FileExistence.Required).CreateNextWrittenVersion(),
                FileArtifactWithAttributes.Create(file1, FileExistence.Required).CreateNextWrittenVersion().CreateNextWrittenVersion(),
                FileArtifactWithAttributes.Create(file1.CreateNextWrittenVersion(), FileExistence.Required)
            },
                eq: (left, right) => left == right,
                neq: (left, right) => left != right,
                skipHashCodeForNotEqualValues: true);
        }
        public async Task DynamicDirectoryMaterializationDoesNotDeleteDeclaredFiles()
        {
            var harness = CreateDefaultHarness();

            harness.Seal();

            var pathTable = harness.Environment.Context.PathTable;

            DirectoryArtifact dynamicOutputDirectory = CreateDirectory();

            FileArtifact explicitDeclaredOutputFile = CreateOutputFile(rootPath: dynamicOutputDirectory.Path, fileName: "expOut.txt");
            string       explicitNestedDeclaredOutputFileContents = "This is a nested declared file dependency";

            var          nestedDirectory = dynamicOutputDirectory.Path.Combine(pathTable, "nested");
            FileArtifact explicitNestedDeclaredOutputFile   = CreateOutputFile(rootPath: nestedDirectory, fileName: "expNestOut.txt");
            string       explicitDeclaredOutputFileContents = "This is a declared file dependency";

            FileArtifact dynamicOutputFile         = CreateOutputFile(rootPath: dynamicOutputDirectory.Path, fileName: "dynout.txt");
            string       dynamicOutputFileContents = "This is a dynamic file dependency";

            await harness.StoreAndReportStringContent(explicitDeclaredOutputFileContents, explicitDeclaredOutputFile);

            await harness.StoreAndReportStringContent(explicitNestedDeclaredOutputFileContents, explicitNestedDeclaredOutputFile);

            await harness.StoreAndReportStringContent(dynamicOutputFileContents, dynamicOutputFile);

            const string PriorOutputText = "Prior output";

            harness.WriteText(dynamicOutputFile, PriorOutputText);
            harness.WriteText(explicitDeclaredOutputFile, PriorOutputText);
            harness.WriteText(explicitNestedDeclaredOutputFile, PriorOutputText);

            // Add some extraneous files/directories to be removed
            const string RemovedFileText        = "Removed file text";
            var          removedNestedDirectory = dynamicOutputDirectory.Path.Combine(pathTable, "removedNested");

            harness.WriteText(CreateOutputFile(rootPath: dynamicOutputDirectory.Path, fileName: "removedRootFile.txt"), RemovedFileText);
            harness.WriteText(CreateOutputFile(rootPath: dynamicOutputDirectory.Path, fileName: "removedRootFile2.txt"), RemovedFileText);
            harness.WriteText(CreateOutputFile(rootPath: nestedDirectory, fileName: "nestedremoved.txt"), RemovedFileText);
            harness.WriteText(CreateOutputFile(rootPath: nestedDirectory, fileName: "nestedremoved1.txt"), RemovedFileText);
            harness.WriteText(CreateOutputFile(rootPath: removedNestedDirectory, fileName: "removedNestedFile.txt"), RemovedFileText);
            Directory.CreateDirectory(dynamicOutputDirectory.Path.Combine(pathTable, "removedEmptyNested").ToString(pathTable));
            Directory.CreateDirectory(dynamicOutputDirectory.Path.Combine(pathTable, "removedEmptyNested3").ToString(pathTable));

            harness.Environment.RegisterDynamicOutputDirectory(dynamicOutputDirectory);

            var dynamicDirectoryContents = new[] { explicitDeclaredOutputFile, explicitNestedDeclaredOutputFile, dynamicOutputFile };

            // Report files for directory
            harness.FileContentManager.ReportDynamicDirectoryContents(
                dynamicOutputDirectory,
                dynamicDirectoryContents.Select(fa => FileArtifactWithAttributes.Create(fa, FileExistence.Required)),
                PipOutputOrigin.NotMaterialized);

            var producer = CreateCmdProcess(
                dependencies: new FileArtifact[0],
                outputs: new[] { explicitDeclaredOutputFile, explicitNestedDeclaredOutputFile },
                directoryOutputs: new[] { dynamicOutputDirectory });

            var fileConsumer = CreateCmdProcess(
                dependencies: new[] { explicitDeclaredOutputFile, explicitNestedDeclaredOutputFile },
                outputs: new[] { CreateOutputFile() });

            var directoryConsumer = CreateCmdProcess(
                dependencies: new FileArtifact[0],
                directoryDependencies: new[] { dynamicOutputDirectory },
                outputs: new[] { CreateOutputFile() });

            // First materialize the files by materializing inputs for the pip which consumes the files directly
            var fileMaterializationResult = await harness.FileContentManager.TryMaterializeDependenciesAsync(fileConsumer, harness.UntrackedOpContext);

            Assert.True(fileMaterializationResult);

            // Verify that only the explicit declared dependencies have their contents materialized
            harness.VerifyContent(explicitDeclaredOutputFile, explicitDeclaredOutputFileContents);
            harness.VerifyContent(explicitNestedDeclaredOutputFile, explicitNestedDeclaredOutputFileContents);

            // The dynamic file shouldn't be materialized at this point
            harness.VerifyContent(dynamicOutputFile, PriorOutputText);

            // Modify the explicit outputs so that we can check later that materialization did not
            // delete and re-materialize these files
            const string ModifiedExplicitOutputText = "Modified explicit output";

            harness.WriteText(explicitDeclaredOutputFile, ModifiedExplicitOutputText);
            harness.WriteText(explicitNestedDeclaredOutputFile, ModifiedExplicitOutputText);

            // Now materialize the directory
            var directoryMaterializationResult = await harness.FileContentManager.TryMaterializeDependenciesAsync(directoryConsumer, harness.UntrackedOpContext);

            Assert.True(directoryMaterializationResult);

            var filesAfterMaterialization = new HashSet <string>(Directory.GetFiles(dynamicOutputDirectory.Path.ToString(pathTable), "*.*", SearchOption.AllDirectories), OperatingSystemHelper.PathComparer);

            HashSet <string> dynamicDirectoryContentPaths = new HashSet <string>(OperatingSystemHelper.PathComparer);

            dynamicDirectoryContentPaths.UnionWith(dynamicDirectoryContents.Select(f => f.Path.ToString(pathTable)));

            // Check that the dynamic directory contents are the only remaining files
            Assert.Subset(dynamicDirectoryContentPaths, filesAfterMaterialization);
            Assert.Superset(dynamicDirectoryContentPaths, filesAfterMaterialization);

            var directoriesAfterMaterialization = new HashSet <string>(Directory.GetDirectories(dynamicOutputDirectory.Path.ToString(pathTable), "*.*", SearchOption.AllDirectories), OperatingSystemHelper.PathComparer);

            HashSet <string> dynamicDirectorySubDirectoryPaths = new HashSet <string>(OperatingSystemHelper.PathComparer);

            dynamicDirectorySubDirectoryPaths.UnionWith(dynamicDirectoryContents.Select(f => f.Path.GetParent(pathTable).ToString(pathTable)));

            // Don't count the root directory
            dynamicDirectorySubDirectoryPaths.Remove(dynamicOutputDirectory.Path.ToString(pathTable));

            // Check that the dynamic directory contents parent directories are the only remaining directories
            Assert.Subset(dynamicDirectorySubDirectoryPaths, directoriesAfterMaterialization);
            Assert.Superset(dynamicDirectorySubDirectoryPaths, directoriesAfterMaterialization);

            XAssert.IsTrue(fileMaterializationResult);

            // Verify all files have the expected contents
            // Dynamic file should have the content for the cache
            // Explicit dependencies should have the modified explicit content because
            // the file content manager skips content which has been materialized already so
            // the later modifications should go unnoticed
            harness.VerifyContent(explicitDeclaredOutputFile, ModifiedExplicitOutputText);
            harness.VerifyContent(explicitNestedDeclaredOutputFile, ModifiedExplicitOutputText);

            harness.VerifyContent(dynamicOutputFile, dynamicOutputFileContents);
        }