Пример #1
0
        /// <inheritdoc />
        public Task <bool> CopyFileAsync(
            string source,
            string destination,
            Func <SafeFileHandle, SafeFileHandle, bool> predicate = null,
            Action <SafeFileHandle, SafeFileHandle> onCompletion  = null)
        {
            Contract.Requires(!string.IsNullOrEmpty(source));
            Contract.Requires(!string.IsNullOrEmpty(destination));

            return(ExceptionUtilities.HandleRecoverableIOExceptionAsync(
                       async() =>
            {
                using (FileStream sourceStream = CreateAsyncFileStream(
                           source,
                           FileMode.Open,
                           FileAccess.Read,
                           FileShare.Read | FileShare.Delete))
                {
                    if (predicate != null)
                    {
                        SafeFileHandle destinationHandle;
                        OpenFileResult predicateQueryOpenResult = s_fileSystem.TryCreateOrOpenFile(
                            destination,
                            FileDesiredAccess.GenericRead,
                            FileShare.Read | FileShare.Delete,
                            FileMode.OpenOrCreate,
                            FileFlagsAndAttributes.None,
                            out destinationHandle);
                        using (destinationHandle)
                        {
                            if (!predicateQueryOpenResult.Succeeded)
                            {
                                throw new BuildXLException(
                                    I($"Failed to open a copy destination '{destination}' to check its version"),
                                    predicateQueryOpenResult.CreateExceptionForError());
                            }

                            if (!predicate(sourceStream.SafeFileHandle, predicateQueryOpenResult.OpenedOrTruncatedExistingFile ? destinationHandle : null))
                            {
                                return false;
                            }
                        }
                    }

                    using (FileStream destinationStream = CreateReplacementFile(destination, FileShare.Delete, openAsync: true))
                    {
                        await sourceStream.CopyToAsync(destinationStream);
                        onCompletion?.Invoke(sourceStream.SafeFileHandle, destinationStream.SafeFileHandle);
                    }
                }

                return true;
            },
                       ex => { throw new BuildXLException("File copy failed", ex); }));
        }
Пример #2
0
        /// <inheritdoc />
        public Task <bool> WriteAllBytesAsync(
            string filePath,
            byte[] bytes,
            Func <SafeFileHandle, bool> predicate = null,
            Action <SafeFileHandle> onCompletion  = null)
        {
            Contract.Requires(!string.IsNullOrEmpty(filePath));
            Contract.Requires(bytes != null);

            return(ExceptionUtilities.HandleRecoverableIOExceptionAsync(
                       async() =>
            {
                if (predicate != null)
                {
                    SafeFileHandle destinationHandle;
                    OpenFileResult predicateQueryOpenResult = m_fileSystem.TryCreateOrOpenFile(
                        filePath,
                        FileDesiredAccess.GenericRead,
                        FileShare.Read | FileShare.Delete,
                        FileMode.OpenOrCreate,
                        FileFlagsAndAttributes.None,
                        out destinationHandle);
                    using (destinationHandle)
                    {
                        if (!predicateQueryOpenResult.Succeeded)
                        {
                            throw new BuildXLException(
                                I($"Failed to open file '{filePath}' to check its version"),
                                predicateQueryOpenResult.CreateExceptionForError());
                        }

                        if (!predicate(predicateQueryOpenResult.OpenedOrTruncatedExistingFile ? destinationHandle : null))
                        {
                            return false;
                        }
                    }
                }

                using (FileStream stream = CreateReplacementFile(filePath, FileShare.Delete, openAsync: true))
                {
                    await stream.WriteAsync(bytes, 0, bytes.Length);
                }

                if (onCompletion != null)
                {
                    using (var file = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Delete))
                    {
                        onCompletion(file.SafeFileHandle);
                    }
                }

                return true;
            },
                       ex => { throw new BuildXLException("File write failed", ex); }));
        }
Пример #3
0
        /// <summary>
        /// Creates an instance of <see cref="NamedPipeServerStream"/> and immediately connects a client to it.
        /// </summary>
        /// <param name="serverDirection">Server direction.</param>
        /// <param name="serverOptions">Server options.</param>
        /// <param name="clientOptions">Client options.</param>
        /// <param name="clientHandle">Output client handle.</param>
        /// <returns>An instance of <see cref="NamedPipeServerStream"/>.</returns>
        public static NamedPipeServerStream CreateNamedPipeServerStream(
            PipeDirection serverDirection,
            PipeOptions serverOptions,
            PipeOptions clientOptions,
            out SafeFileHandle clientHandle)
        {
            string pipeName         = @"BuildXL-" + Guid.NewGuid().ToString("N");
            var    pipeServerStream = new NamedPipeServerStream(
                pipeName,
                serverDirection,
                maxNumberOfServerInstances: 1,
                transmissionMode: PipeTransmissionMode.Byte,
                options: serverOptions,
                inBufferSize: PipeBufferSize,
                outBufferSize: PipeBufferSize);

            FileDesiredAccess clientDesiredAccess = FileDesiredAccess.None;

            if ((PipeDirection.In & serverDirection) != 0)
            {
                clientDesiredAccess |= FileDesiredAccess.GenericWrite;
            }

            if ((PipeDirection.Out & serverDirection) != 0)
            {
                clientDesiredAccess |= FileDesiredAccess.GenericRead;
            }

            FileFlagsAndAttributes clientFlags = clientOptions == PipeOptions.Asynchronous
                ? FileFlagsAndAttributes.FileFlagOverlapped
                : 0;

            OpenFileResult openClientHandle = FileUtilities.TryCreateOrOpenFile(
                @"\\.\pipe\" + pipeName,
                clientDesiredAccess,
                FileShare.None,
                FileMode.Open,
                clientFlags | FileFlagsAndAttributes.SecurityAnonymous,
                out clientHandle);

            if (!openClientHandle.Succeeded)
            {
                throw openClientHandle.CreateExceptionForError();
            }

            // Client should be made inheritable.
            SetInheritable(clientHandle);

            pipeServerStream.WaitForConnection();

            return(pipeServerStream);
        }
Пример #4
0
        /// <summary>
        /// Creates a pipe in which the specified ends are inheritable by child processes.
        /// In order for the pipe to be closable by the child, the inherited end should be closed after process creation.
        /// </summary>
        /// <remarks>
        /// This implementation creates a named pipe instance and immediately connects it. This is effectively the same implementation
        /// as <c>CreatePipe</c> (see %SDXROOT%\minkernel\kernelbase\pipe.c), but we compute a random pipe named rather than using the special
        /// 'anonymous' naming case of opening \\.\pipe or \Device\NamedPipe\ (without a subsequent path).
        /// We choose to not use <c>CreatePipe</c> since it opens both sides for synchronous I/O. Instead, for running build tools we want the
        /// BuildXL side opened for overlapped I/O and the build tool side opened for synchronous I/O (if the build tool side acts as a redirected
        /// console handle; recall that synchronous read and write attempts will fail on an async handle, which motivates the <c>CreatePipe</c> defaults
        /// in the first place). This is significant since many build tool console pipes are forever silent, and with sync handles we'd need N * 3 threads
        /// (stdin, stdout, stderr for N concurrent processes) to pessimistically drain them.
        /// </remarks>
        public static void CreateInheritablePipe(PipeInheritance inheritance, PipeFlags flags, out SafeFileHandle readHandle, out SafeFileHandle writeHandle)
        {
            string pipeName = @"\\.\pipe\BuildXL-" + Guid.NewGuid().ToString("N");

            writeHandle = ProcessUtilities.CreateNamedPipe(
                pipeName,
                PipeOpenMode.PipeAccessOutbound | (((flags & PipeFlags.WriteSideAsync) != 0) ? PipeOpenMode.FileFlagOverlapped : 0),
                PipeMode.PipeTypeByte | PipeMode.PipeRejectRemoteClients,
                nMaxInstances: 1,
                nOutBufferSize: PipeBufferSize,
                nInBufferSize: PipeBufferSize,
                nDefaultTimeout: 0,
                lpSecurityAttributes: IntPtr.Zero);

            if (writeHandle.IsInvalid)
            {
                throw new NativeWin32Exception(Marshal.GetLastWin32Error(), "CreateNamedPipeW failed");
            }

            var readSideFlags = FileFlagsAndAttributes.SecurityAnonymous;

            if ((flags & PipeFlags.ReadSideAsync) != 0)
            {
                readSideFlags |= FileFlagsAndAttributes.FileFlagOverlapped;
            }

            OpenFileResult openReadSideResult = FileUtilities.TryCreateOrOpenFile(
                pipeName,
                FileDesiredAccess.GenericRead,
                FileShare.None,
                FileMode.Open,
                readSideFlags,
                out readHandle);

            if (!openReadSideResult.Succeeded)
            {
                throw openReadSideResult.CreateExceptionForError();
            }

            if ((inheritance & PipeInheritance.InheritRead) != 0)
            {
                SetInheritable(readHandle);
            }

            if ((inheritance & PipeInheritance.InheritWrite) != 0)
            {
                SetInheritable(writeHandle);
            }
        }
Пример #5
0
        /// <inheritdoc />
        public Task <bool> CopyFileAsync(
            string source,
            string destination,
            Func <SafeFileHandle, SafeFileHandle, bool> predicate = null,
            Action <SafeFileHandle, SafeFileHandle> onCompletion  = null)
        {
            Contract.Requires(!string.IsNullOrEmpty(source));
            Contract.Requires(!string.IsNullOrEmpty(destination));

            return(ExceptionUtilities.HandleRecoverableIOExceptionAsync(
                       async() =>
            {
                using (FileStream sourceStream = CreateAsyncFileStream(
                           source,
                           FileMode.Open,
                           FileAccess.Read,
                           FileShare.Read | FileShare.Delete))
                {
                    if (predicate != null)
                    {
                        SafeFileHandle destinationHandle;
                        OpenFileResult predicateQueryOpenResult = m_fileSystem.TryCreateOrOpenFile(
                            destination,
                            FileDesiredAccess.GenericRead,
                            FileShare.Read | FileShare.Delete,
                            FileMode.OpenOrCreate,
                            FileFlagsAndAttributes.None,
                            out destinationHandle);
                        using (destinationHandle)
                        {
                            if (!predicateQueryOpenResult.Succeeded)
                            {
                                throw new BuildXLException(
                                    I($"Failed to open a copy destination '{destination}' to check its version"),
                                    predicateQueryOpenResult.CreateExceptionForError());
                            }

                            if (!predicate(sourceStream.SafeFileHandle, predicateQueryOpenResult.OpenedOrTruncatedExistingFile ? destinationHandle : null))
                            {
                                return false;
                            }
                        }
                    }

                    using (var destinationStream = CreateReplacementFile(destination, FileShare.Delete, openAsync: true))
                    {
                        await sourceStream.CopyToAsync(destinationStream);
                        var mode = GetFilePermissionsForFilePath(source, followSymlink: false);
                        var result = SetFilePermissionsForFilePath(destination, checked ((FilePermissions)mode), followSymlink: false);
                        if (result < 0)
                        {
                            throw new BuildXLException($"Failed to set permissions for file copy at '{destination}' - error: {Marshal.GetLastWin32Error()}");
                        }
                    }

                    if (onCompletion != null)
                    {
                        using (var dest = File.Open(destination, FileMode.Open, FileAccess.Read, FileShare.Delete))
                        {
                            onCompletion(sourceStream.SafeFileHandle, dest.SafeFileHandle);
                        }
                    }
                }

                return true;
            },
                       ex => { throw new BuildXLException(I($"File copy from '{source}' to '{destination}' failed"), ex); }));
        }
Пример #6
0
        /// <summary>
        /// Creates a pipe in which the specified ends are inheritable by child processes.
        /// In order for the pipe to be closable by the child, the inherited end should be closed after process creation.
        /// </summary>
        /// <remarks>
        /// This implementation creates a named pipe instance and immediately connects it. This is effectively the same implementation
        /// as <c>CreatePipe</c> (see %SDXROOT%\minkernel\kernelbase\pipe.c), but we compute a random pipe named rather than using the special
        /// 'anonymous' naming case of opening \\.\pipe or \Device\NamedPipe\ (without a subsequent path).
        /// We choose to not use <c>CreatePipe</c> since it opens both sides for synchronous I/O. Instead, for running build tools we want the
        /// BuildXL side opened for overlapped I/O and the build tool side opened for synchronous I/O (if the build tool side acts as a redirected
        /// console handle; recall that synchronous read and write attempts will fail on an async handle, which motivates the <c>CreatePipe</c> defaults
        /// in the first place). This is significant since many build tool console pipes are forever silent, and with sync handles we'd need N * 3 threads
        /// (stdin, stdout, stderr for N concurrent processes) to pessimistically drain them.
        /// </remarks>
        public static void CreateInheritablePipe(PipeInheritance inheritance, PipeFlags flags, out SafeFileHandle readHandle, out SafeFileHandle writeHandle)
        {
            string pipeName = @"\\.\pipe\BuildXL-" + Guid.NewGuid().ToString("N");

            writeHandle = ProcessUtilities.CreateNamedPipe(
                pipeName,
                PipeOpenMode.PipeAccessOutbound | (((flags & PipeFlags.WriteSideAsync) != 0) ? PipeOpenMode.FileFlagOverlapped : 0),
                PipeMode.PipeTypeByte | PipeMode.PipeRejectRemoteClients,
                nMaxInstances: 1,
                nOutBufferSize: PipeBufferSize,
                nInBufferSize: PipeBufferSize,
                nDefaultTimeout: 0,
                lpSecurityAttributes: IntPtr.Zero);

            if (writeHandle.IsInvalid)
            {
                throw new NativeWin32Exception(Marshal.GetLastWin32Error(), "CreateNamedPipeW failed");
            }

            var readSideFlags = FileFlagsAndAttributes.SecurityAnonymous;

            if ((flags & PipeFlags.ReadSideAsync) != 0)
            {
                readSideFlags |= FileFlagsAndAttributes.FileFlagOverlapped;
            }

            int maxRetry = 3;

            while (true)
            {
                OpenFileResult openReadSideResult = FileUtilities.TryCreateOrOpenFile(
                    pipeName,
                    FileDesiredAccess.GenericRead,
                    FileShare.None,
                    FileMode.Open,
                    readSideFlags,
                    out readHandle);

                if (openReadSideResult.Succeeded)
                {
                    break;
                }

                if (openReadSideResult.NativeErrorCode != NativeIOConstants.ErrorPipeBusy ||
                    maxRetry == 0)
                {
                    throw openReadSideResult.CreateExceptionForError();
                }

                bool success = false;

                // Wait for at most 5s.
                for (int i = 0; i < 10; ++i)
                {
                    success = ProcessUtilities.WaitNamedPipe(pipeName, 500);
                    if (success)
                    {
                        break;
                    }
                }

                if (!success)
                {
                    // After waiting for 5s, pipe is still not ready.
                    throw new NativeWin32Exception(Marshal.GetLastWin32Error(), "WaitNamedPipe");
                }

                --maxRetry;
            }

            if ((inheritance & PipeInheritance.InheritRead) != 0)
            {
                SetInheritable(readHandle);
            }

            if ((inheritance & PipeInheritance.InheritWrite) != 0)
            {
                SetInheritable(writeHandle);
            }
        }