public void CreateRoot(ref Context context) { context.Storage.CreateDirectory(ObjectsDir); BaseDir baseDir = new Core.BaseDir { Eyecatcher = EyeCatcher }; // Serialize to a temp file using (var path = new LocalPath(LocalUtils.GetTempFileName(suffix: ".dir"))) using (var encodedPath = new LocalPath(LocalUtils.GetTempFileName(suffix: ".edir"))) { FileStream output = File.OpenWrite(path.Path); // Write the directory to a file baseDir.WriteTo(output); output.Close(); byte[] hash = new byte[32]; CodecHelper.EncodeFile(ref context, path.Path, encodedPath.Path, ref hash); // Upload the file. It's removed by Upload. Upload(ref context, encodedPath.Path, RootId); } // Write the config using (var configPath = new LocalPath(LocalUtils.GetTempFileName(suffix: ".config"))) { context.RepoCfg.Save(configPath.Path); Upload(ref context, configPath.Path, RepoConfig.ConfigFile); } }
public void Write() { using (var rawContent = new LocalPath(LocalUtils.GetTempFileName(suffix: ".dir"))) { using (FileStream fs = File.OpenWrite(rawContent.Path)) { // Serialize the content _data.WriteTo(fs); } if (!File.Exists(rawContent.Path)) { throw new Exception($"The temporary file {rawContent.Path} disappeared!"); } // Use another temp file to encode the content using (var encodedPath = new LocalPath(LocalUtils.GetTempFileName(prefix: "cync", suffix: ".edir"))) { byte[] hash = new byte[32]; CodecHelper.EncodeFile(ref _context, rawContent.Path, encodedPath.Path, ref hash); // Make sure the target directory exists _context.Storage.CreateDirectory(_fid.DirectoryPath); // Upload the encoded file _context.Storage.Upload(encodedPath.Path, _fid.FullPath, true); _dirty = false; } } }
public bool PullFile(string src, string dest, bool force) { bool exists = TryGetEntry(src, out DirEntry de); // Check whether the destination file exists. // TODO Check whether the destination is newer var ve = de.Versions[de.Latest]; if (!force) { if (File.Exists(dest)) { if (File.GetLastWriteTimeUtc(dest).Ticks > ve.LastWriteTime) { return(false); } var fi = new FileInfo(dest); if ((ulong)fi.Length == ve.Length) { if (_context.UseChecksums) { // If the hashes are also the same, skip the file byte[] destHash = new byte[32]; CodecHelper.ComputeHash(dest, ref destHash); if (destHash.SequenceEqual(ve.Checksum)) { return(false); } } else { return(false); } } } } var fid = new FileId(ve.Uuid); var encoded = _context.Storage.Download(fid.FullPath); byte[] hash = new byte[32]; byte[] expectedHash = new byte[32]; CodecHelper.DecodeFile(ref _context, encoded.Path, dest, ref hash, ref expectedHash); if (!expectedHash.SequenceEqual(hash)) { throw new Exception("The data signature seems wrong. It's possible that file content is damaged."); } // Restore the file times File.SetCreationTimeUtc(dest, new DateTime(ve.CreationTime)); File.SetLastAccessTimeUtc(dest, new DateTime(ve.LastAccessTime)); File.SetLastWriteTimeUtc(dest, new DateTime(ve.LastWriteTime)); return(true); }
public void Read() { // Download the directory file using (var localPath = _context.Storage.Download(_fid.FullPath)) using (var decodedPath = new LocalPath(LocalUtils.GetTempFileName(suffix: ".dir"))) { var fi = new FileInfo(localPath.Path); if (fi.Length < 4) { throw new Exception($"The downloaded file is too short, only {fi.Length} bytes. Minimum is 4."); } byte[] hash = new byte[32]; byte[] expectedHash = new byte[32]; CodecHelper.DecodeFile(ref _context, localPath.Path, decodedPath.Path, ref hash, ref expectedHash); // Read the directory content using (Stream ss = OpenTempFileForReading(decodedPath.Path)) { MessageParser <BaseDir> parser = new MessageParser <BaseDir>(() => new BaseDir()); _data = parser.ParseFrom(ss); } } }
public bool PushFile(string src, string dest, bool force) { if (dest == null || dest.Length == 0) { dest = Path.GetFileName(src); } DirEntry de; FileInfo fi = new FileInfo(src); bool exists = TryGetEntry(dest, out de); if (exists) { bool changeDetected = force; if (!changeDetected) { VersionEntry ve = de.Versions[de.Latest]; // Compare the file time if (fi.LastWriteTimeUtc.Ticks > ve.LastWriteTime) { // Compare the size if ((ulong)fi.Length != ve.Length) { changeDetected = true; } else if (_context.UseChecksums) { // Checksum check byte[] sha256 = new byte[32]; ComputeHashParams bb = new ComputeHashParams { }; bb.Hash = "sha256"; bb.Path = src; using (MemoryStream ms = new MemoryStream()) { bb.WriteTo(ms); CodecDll.ComputeHash(ms.GetBuffer(), (uint)ms.Length, sha256, (uint)sha256.Length); } // Compare the hash changeDetected = !sha256.SequenceEqual(ve.Checksum); } if (!changeDetected) { // Update the timestamp only de.Versions[de.Latest].LastWriteTime = fi.LastWriteTimeUtc.Ticks; return(true); } } } if (!changeDetected) { return(false); } } else { de = new DirEntry { Type = DirEntryType.File, Latest = 0 }; AddEntry(dest, de); } VersionEntry versionToRemove = null; // Encode the file to a temp location using (var lp = new LocalPath(LocalUtils.GetTempFileName())) { var hash = new byte[32]; CodecHelper.EncodeFile(ref _context, src, lp.Path, ref hash); var newVersionEntry = new VersionEntry { Uuid = Guid.NewGuid().ToString("N"), LastWriteTime = fi.LastWriteTimeUtc.Ticks, LastAccessTime = fi.LastAccessTimeUtc.Ticks, CreationTime = fi.CreationTimeUtc.Ticks, Attributes = (int)fi.Attributes, Length = (ulong)fi.Length }; newVersionEntry.Checksum = ByteString.CopyFrom(hash); if (de.Versions.Count == 0) { // The very first version addition de.Versions.Add(newVersionEntry); Debug.Assert(de.Latest == 0); } else if (de.Versions.Count > 0 && de.Versions.Count >= _context.RepoCfg.MaxVersions) { de.Latest = (de.Latest + 1) % _context.RepoCfg.MaxVersions; versionToRemove = de.Versions[de.Latest]; de.Versions[de.Latest] = newVersionEntry; } else { de.Versions.Add(newVersionEntry); ++de.Latest; } // The in-memory directory is updated, perform the file operations FileId fid = new FileId(newVersionEntry.Uuid); _context.Storage.CreateDirectory(fid.DirectoryPath); _context.Storage.Upload(lp.Path, fid.FullPath); } // Remove the previous version if necessary if (versionToRemove != null) { _context.Storage.RemoveFile((new FileId(versionToRemove.Uuid)).FullPath); } Write(); return(true); }