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