public void AtomicWrite_ReplacingExistingReadOnlyFile_Throws() { var path = BaseDir.CreateFile("test.txt"); SafeFile.SetReadOnly(path); Should.Throw <UnauthorizedAccessException>(() => SafeFile.AtomicWrite(path, tmpPath => tmpPath.ToNPath().CreateFile())); }
public void AtomicWrite_WithEmptyAction_ShouldDoNothing() { var path = BaseDir.CreateFile("test.txt"); SafeFile.AtomicWrite(path, tmpPath => { }); path.FileExists().ShouldBeTrue(); (path + SafeFile.TmpExtension).ToNPath().FileExists().ShouldBeFalse(); (path + SafeFile.BakExtension).ToNPath().FileExists().ShouldBeFalse(); }
public void AtomicWrite_WithExistingReadOnlyTempAndBakFiles_OverwritesFilesAndOperatesNormally() { var path = BaseDir.Combine("test.txt").WriteAllText("test"); var temp = (path + SafeFile.TmpExtension).ToNPath().CreateFile(); var backup = (path + SafeFile.BakExtension).ToNPath().CreateFile(); SafeFile.SetReadOnly(temp); SafeFile.SetReadOnly(backup); SafeFile.AtomicWrite(path, tmpPath => tmpPath.ToNPath().WriteAllText("new")); temp.FileExists().ShouldBeFalse(); backup.FileExists().ShouldBeFalse(); path.ReadAllText().ShouldBe("new"); }
static bool OverwriteFileIfChanged(FormatItem formatItem, IEnumerable <string> lines, EndOfLine eol) { // write to a buffer so we can do an exact comparison vs the file already on disk. let's avoid generating io writes if // there is no actual change. but we need to have the raw bytes so that differences in encoding and EOL's are not masked. var newFileBuffer = new MemoryStream(); WriteLines(newFileBuffer, lines, formatItem.EditorConfig.Charset, eol); var newFileBytes = newFileBuffer.GetBuffer(); var newFileLength = (int)newFileBuffer.Length; // not newFileBytes.Length! var match = new FileInfo(formatItem.Path).Length == newFileLength; // do cheap length check first if (match) { var oldFileBytes = File.ReadAllBytes(formatItem.Path); // must do the byte compare vs disk for (var i = 0; i < newFileLength; ++i) { if (newFileBytes[i] != oldFileBytes[i]) { match = false; break; } } } // ok we have to write it if (!match) { // TODO: copy the permission bits over ($mode & 0777) to the new file // TODO: backup under ./Temp/Format (subfolders? replace folder names with _?) configurable via a new FormatContext.BackupRoot SafeFile.AtomicWrite(formatItem.Path, writePath => { using (var writeFile = File.OpenWrite(writePath)) writeFile.Write(newFileBytes, 0, newFileLength); }); } return(!match); }