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); }
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)); }
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)); }
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)); }
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)); }
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; } }
// 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}"); } } }