예제 #1
0
파일: Format.cs 프로젝트: romkatv/ChunkIO
        public static bool VerifyHash(byte[] array, ref int offset)
        {
            ulong expected = SipHash.ComputeHash(array, 0, offset);
            ulong actual   = UInt64LE.Read(array, ref offset);

            return(expected == actual);
        }
예제 #2
0
파일: Format.cs 프로젝트: romkatv/ChunkIO
        public void WriteTo(byte[] array)
        {
            Debug.Assert(array.Length >= Size);
            Debug.Assert(Format.IsValidPosition(ChunkBeginPosition));
            int offset = 0;

            UInt64LE.Write(array, ref offset, (ulong)ChunkBeginPosition);
            UInt64LE.Write(array, ref offset, SipHash.ComputeHash(array, 0, offset));
        }
예제 #3
0
파일: Format.cs 프로젝트: romkatv/ChunkIO
        public void WriteTo(byte[] array)
        {
            Debug.Assert(array.Length >= Size);
            Debug.Assert(Format.IsValidContentLength(ContentLength));
            int offset = 0;

            UserData.WriteTo(array, ref offset);
            UInt64LE.Write(array, ref offset, (ulong)ContentLength);
            UInt64LE.Write(array, ref offset, ContentHash);
            UInt64LE.Write(array, ref offset, SipHash.ComputeHash(array, 0, offset));
        }
예제 #4
0
파일: Format.cs 프로젝트: romkatv/ChunkIO
        public bool ReadFrom(byte[] array)
        {
            Debug.Assert(array.Length >= Size);
            int   offset = 0;
            ulong pos    = UInt64LE.Read(array, ref offset);

            if (!Format.IsValidPosition(pos))
            {
                return(false);
            }
            ChunkBeginPosition = (long)pos;
            return(Format.VerifyHash(array, ref offset));
        }
예제 #5
0
파일: Format.cs 프로젝트: romkatv/ChunkIO
        public bool ReadFrom(byte[] array)
        {
            Debug.Assert(array.Length >= Size);
            int offset = 0;

            UserData = UserData.ReadFrom(array, ref offset);
            ulong len = UInt64LE.Read(array, ref offset);

            if (!Format.IsValidContentLength(len))
            {
                return(false);
            }
            ContentLength = (int)len;
            ContentHash   = UInt64LE.Read(array, ref offset);
            return(Format.VerifyHash(array, ref offset));
        }
예제 #6
0
            public Listener(IReadOnlyCollection <byte> fileId, Func <bool, Task <long> > flush)
            {
                // This mutex serves as a marker of the existence of flush listener. RemoteFLush.FlushAsync() looks at it.
                // We allow all authenticated users to access the mutex.
                var security = new MutexSecurity();

                security.AddAccessRule(
                    new MutexAccessRule(
                        new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null),
                        MutexRights.Synchronize,
                        AccessControlType.Allow));
                _mutex = new Mutex(true, MutexName(fileId), out bool createdNew, security);
                try {
                    _srv = new PipeServer(PipeName(fileId), 2, async(Stream strm, CancellationToken cancel) => {
                        var buf = new byte[UInt64LE.Size];
                        if (await strm.ReadAsync(buf, 0, 1, cancel) != 1)
                        {
                            throw new Exception("Empty Flush request");
                        }
                        if (buf[0] != 0 && buf[0] != 1)
                        {
                            throw new Exception("Invalid Flush request");
                        }
                        bool flushToDisk = buf[0] == 1;
                        try {
                            long len = await flush.Invoke(flushToDisk);
                            Debug.Assert(len >= 0);
                            int offset = 0;
                            UInt64LE.Write(buf, ref offset, (ulong)len);
                        } catch {
                            int offset = 0;
                            UInt64LE.Write(buf, ref offset, ulong.MaxValue);
                        }
                        await strm.WriteAsync(buf, 0, UInt64LE.Size, cancel);
                        await strm.FlushAsync(cancel);
                    });
                } catch {
                    _mutex.Dispose();
                    throw;
                }
            }
예제 #7
0
        // Returns:
        //
        //   * null if there is no listener associated with the file.
        //   * file length immediately after flushing (the flushing and the grabbing of the file length are
        //     done atomically) if there is a listener and the file was successfully flushed.
        //
        // Throws in all other cases. For example:
        //
        //   * The remote writer failed to flush because disk is full.
        //   * The remote writer sent invalid response to our request.
        //   * Pipe permission error.
        public static async Task <long?> FlushAsync(IReadOnlyCollection <byte> fileId, bool flushToDisk)
        {
            if (fileId == null)
            {
                throw new ArgumentNullException(nameof(fileId));
            }
            while (true)
            {
                using (var pipe = new NamedPipeClientStream(".", PipeName(fileId), PipeDirection.InOut,
                                                            PipeOptions.Asynchronous | PipeOptions.WriteThrough)) {
                    await s_connect.LockAsync();

                    try {
                        // NamedPipeClientStream has an awful API. It doesn't allow us to bail early if there is no pipe and
                        // to wait indefinitely if there is (this can be done with Win32 API). So we do it manually with
                        // a mutex existence check and a loop. We create this mutex together with the pipe to serve as
                        // a marker of the pipe's existence. It's difficult to use the pipe itself as such marker because
                        // server pipes disappear after accepting and serving a request and there may be a short gap before
                        // we create new server connections.
                        if (Mutex.TryOpenExisting(MutexName(fileId), MutexRights.Synchronize, out Mutex mutex))
                        {
                            mutex.Dispose();
                        }
                        else
                        {
                            return(null);
                        }
                        // The timeout is meant to avoid waiting forever if the pipe disappears between the moment
                        // we have verified its existence and our attempt to connect to it. We use a mutex to
                        // restrict the number of simultaneous ConnectAsync() because ConnectAsync() blocks a task
                        // thread for the whole duration of its execution. Without the mutex, WaitAsync() calls
                        // could block all task threads.
                        await pipe.ConnectAsync(100);
                    } catch (TimeoutException) {
                        continue;
                    } catch (IOException) {
                        continue;
                    } finally {
                        s_connect.Unlock(runNextSynchronously: false);
                    }
                    var buf = new byte[UInt64LE.Size];
                    if (flushToDisk)
                    {
                        buf[0] = 1;
                    }
                    try {
                        await pipe.WriteAsync(buf, 0, 1);

                        await pipe.FlushAsync();

                        if (await pipe.ReadAsync(buf, 0, UInt64LE.Size) != UInt64LE.Size)
                        {
                            continue;
                        }
                    } catch {
                        continue;
                    }
                    int   offset = 0;
                    ulong res    = UInt64LE.Read(buf, ref offset);
                    if (res < long.MaxValue)
                    {
                        return((long)res);
                    }
                    if (res == ulong.MaxValue)
                    {
                        throw new IOException("Remote writer failed to flush");
                    }
                    throw new Exception($"Invalid Flush response: {res}");
                }
            }
        }