private async Task <Errorable <Commit> > persistCommit(Commit cm) { FileInfo tmpFile = system.getTemporaryFile(); // Write the commit contents to the file: using (var fs = new FileStream(tmpFile.FullName, FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize: 16834, useAsync: true)) { await fs.WriteRawAsync(cm.WriteTo(new StringBuilder()).ToString()); } lock (FileSystem.SystemLock) { FileInfo fi = system.getPathByID(cm.ID); // NOTE: if the record already exists we can either error out or overwrite the existing file with contents known to be good in the case the existing file got corrupt. // Let's stick with the self-repair scenario since erroring out doesn't help anyone. if (fi.Exists) { Debug.WriteLine(String.Format("Self-repair scenario: overwriting old CommitID {0} with new contents", cm.ID)); fi.Delete(); } // Create directory if it doesn't exist: if (!fi.Directory.Exists) { Debug.WriteLine(String.Format("New DIR '{0}'", fi.Directory.FullName)); fi.Directory.Create(); } Debug.WriteLine(String.Format("New COMMIT '{0}'", fi.FullName)); File.Move(tmpFile.FullName, fi.FullName); } return(cm); }
public async Task <Errorable <TreePathStreamedBlob> > GetBlobByTreePath(TreeBlobPath treePath) { var etrm = await trrepo.GetTreeIDByPath(new TreeTreePath(treePath.RootTreeID, treePath.Path.Tree)).ConfigureAwait(continueOnCapturedContext: false); if (etrm.HasErrors) { return(etrm.Errors); } TreeIDPathMapping trm = etrm.Value; if (!trm.TreeID.HasValue) { return(new BlobNotFoundByPathError(treePath)); } // Get the tree: var etr = await trrepo.GetTree(trm.TreeID.Value).ConfigureAwait(continueOnCapturedContext: false); if (etr.HasErrors) { return(etr.Errors); } TreeNode tr = etr.Value; // Get the blob out of this tree: // TODO: standardize name comparison semantics: var trbl = tr.Blobs.SingleOrDefault(x => x.Name == treePath.Path.Name); if (trbl == null) { return(new BlobNotFoundByPathError(treePath)); } // Check for system inconsistency: if (!system.getPathByID(trbl.BlobID).Exists) { return(new BlobNotFoundByPathError(treePath)); } return(new TreePathStreamedBlob(treePath, new StreamedBlob(blrepo, trbl.BlobID))); }
private async Task <Errorable <IStreamedBlob> > persistBlob(PersistingBlob blob) { Debug.WriteLine(String.Format("Starting persistence of blob")); // Find a temporary filename: FileInfo tmpPath = system.getTemporaryFile(); long length = -1; BlobID blid; // Open a new stream to the source blob contents: using (var sr = blob.Stream) { length = sr.Length; // Create a new file and set its length so we can asynchronously write to it: using (var tmpFi = File.Open(tmpPath.FullName, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { Debug.WriteLine(String.Format("New BLOB temp '{0}' length {1}", tmpPath.FullName, length)); tmpFi.SetLength(length); tmpFi.Close(); } // Determine the best buffer size to use for writing contents: int bufSize = Math.Min(Math.Max((int)length, 8), largeBufferSize); // Open a new FileStream to asynchronously write the blob contents: using (var fs = new FileStream(tmpPath.FullName, FileMode.Open, FileAccess.Write, FileShare.Read, bufSize, useAsync: true)) using (var sha1 = new SHA1StreamWriter(fs)) { // Copy the contents asynchronously (expected copy in order): await sr.CopyToAsync(sha1, bufSize).ConfigureAwait(continueOnCapturedContext: false); // Create the BlobID from the SHA1 hash calculated during copy: blid = new BlobID(sha1.GetHash()); } } // Serialize access to the official blob file: lock (FileSystem.SystemLock) { // Create the blob's subdirectory under 'objects': FileInfo path = system.getPathByID(blid); path.Refresh(); if (!path.Directory.Exists) { Debug.WriteLine(String.Format("New DIR '{0}'", path.Directory.FullName)); path.Directory.Create(); } // Don't recreate an existing blob: if (path.Exists) { Debug.WriteLine(String.Format("Blob already exists at path '{0}', deleting temporary...", path.FullName)); tmpPath.Delete(); return(new Errorable <IStreamedBlob>((IStreamedBlob) new StreamedBlob(this, blid, length))); } // Move the temp file to the final blob filename: File.Move(tmpPath.FullName, path.FullName); } return(new Errorable <IStreamedBlob>((IStreamedBlob) new StreamedBlob(this, blid, length))); }
private async Task <Errorable <TreeNode> > getTree(TreeID id) { FileInfo fi = system.getPathByID(id); if (!fi.Exists) { return(new TreeIDRecordDoesNotExistError(id)); } byte[] buf; int nr = 0; using (var fs = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, 16384, true)) { // TODO: implement an async buffered Stream: buf = new byte[16384]; nr = await fs.ReadAsync(buf, 0, 16384).ConfigureAwait(continueOnCapturedContext: false); if (nr >= 16384) { // My, what a large tree you have! throw new NotSupportedException(); } } TreeNode.Builder tb = new TreeNode.Builder(new List <TreeTreeReference>(), new List <TreeBlobReference>()); // Parse the Tree: using (var ms = new MemoryStream(buf, 0, nr, false)) using (var sr = new StreamReader(ms, Encoding.UTF8)) { string line; while ((line = sr.ReadLine()) != null) { if (line.StartsWith("tree ")) { string linked_treeid = line.Substring(5, (TreeID.ByteArrayLength * 2)); string name = line.Substring(6 + (TreeID.ByteArrayLength * 2)); // Attempt to parse the TreeID and verify its existence: Errorable <TreeID> trid = TreeID.TryParse(linked_treeid); if (trid.HasErrors) { return(trid.Errors); } if (!system.getPathByID(trid.Value).Exists) { return(new TreeIDRecordDoesNotExistError(trid.Value)); } tb.Trees.Add(new TreeTreeReference.Builder(name, trid.Value)); } else if (line.StartsWith("blob ")) { string linked_blobid = line.Substring(5, (TreeID.ByteArrayLength * 2)); string name = line.Substring(6 + (TreeID.ByteArrayLength * 2)); // Attempt to parse the BlobID and verify its existence: Errorable <BlobID> blid = BlobID.TryParse(linked_blobid); if (blid.HasErrors) { return(blid.Errors); } if (!system.getPathByID(blid.Value).Exists) { return(new BlobIDRecordDoesNotExistError(blid.Value)); } tb.Blobs.Add(new TreeBlobReference.Builder(name, blid.Value)); } } } // Create the immutable Tree from the Builder: TreeNode tr = tb; // Validate the computed TreeID: if (tr.ID != id) { return(new ComputedTreeIDMismatchError(tr.ID, id)); } return(tr); }
private async Task <Errorable <Tag> > persistTag(Tag tg) { // Write the commit contents to the file: FileInfo tmpFile = system.getTemporaryFile(); using (var fs = new FileStream(tmpFile.FullName, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { await fs.WriteRawAsync(tg.WriteTo(new StringBuilder()).ToString()); } lock (FileSystem.SystemLock) { FileInfo fi = system.getPathByID(tg.ID); // NOTE: if the record already exists we can either error out or overwrite the existing file with contents known to be good in the case the existing file got corrupt. // Let's stick with the self-repair scenario since erroring out doesn't help anyone. if (fi.Exists) { Debug.WriteLine(String.Format("Self-repair scenario: overwriting old TagID {0} with new contents", tg.ID)); fi.Delete(); } // Create directory if it doesn't exist: if (!fi.Directory.Exists) { Debug.WriteLine(String.Format("New DIR '{0}'", fi.Directory.FullName)); fi.Directory.Create(); } Debug.WriteLine(String.Format("New TAG '{0}'", fi.FullName)); File.Move(tmpFile.FullName, fi.FullName); } // Now keep track of the tag by its name: tmpFile = system.getTemporaryFile(); using (var fs = new FileStream(tmpFile.FullName, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { await fs.WriteRawAsync(tg.ID.ToString()); } lock (FileSystem.SystemLock) { FileInfo fiTracker = system.getTagPathByTagName(tg.Name); // Does this tag name exist already? if (fiTracker.Exists) { tmpFile.Delete(); return(new TagNameAlreadyExistsError(tg.Name)); } // Create directory if it doesn't exist: if (!fiTracker.Directory.Exists) { Debug.WriteLine(String.Format("New DIR '{0}'", fiTracker.Directory.FullName)); fiTracker.Directory.Create(); } Debug.WriteLine(String.Format("New TAG '{0}'", fiTracker.FullName)); File.Move(tmpFile.FullName, fiTracker.FullName); } return(tg); }