protected void IgnoreOnMonoVersions(params string[] version_strings) { if (!PlatformInfo.IsMono) { return; } var current = PlatformInfo.GetVersion(); var versions = version_strings.Select(x => new Version(x)).ToList(); if (versions.Any(x => x.Major == current.Major && x.Minor == current.Minor)) { throw new IgnoreException($"Ignored on mono {PlatformInfo.GetVersion()}"); } }
public void SetUp() { if (PlatformInfo.IsMono && PlatformInfo.GetVersion() < new Version(5, 8)) { Assert.Inconclusive("Not supported on Mono < 5.8"); } Mocker.GetMock <IDiskProvider>() .Setup(v => v.FileExists(It.IsAny <string>())) .Returns <string>(s => File.Exists(s)); Mocker.GetMock <IDiskProvider>() .Setup(v => v.DeleteFile(It.IsAny <string>())) .Callback <string>(s => File.Delete(s)); Mocker.SetConstant <IPlatformInfo>(Mocker.Resolve <PlatformInfo>()); }
protected override void MoveFileInternal(string source, string destination) { var sourceInfo = UnixFileSystemInfo.GetFileSystemEntry(source); if (sourceInfo.IsSymbolicLink) { var isSameDir = UnixPath.GetDirectoryName(source) == UnixPath.GetDirectoryName(destination); var symlinkInfo = (UnixSymbolicLinkInfo)sourceInfo; var symlinkPath = symlinkInfo.ContentsPath; var newFile = new UnixSymbolicLinkInfo(destination); if (isSameDir) { // We're in the same dir, so we can preserve relative symlinks. newFile.CreateSymbolicLinkTo(symlinkInfo.ContentsPath); } else { var fullPath = UnixPath.Combine(UnixPath.GetDirectoryName(source), symlinkPath); newFile.CreateSymbolicLinkTo(fullPath); } try { // Finally remove the original symlink. symlinkInfo.Delete(); } catch { // Removing symlink failed, so rollback the new link and throw. newFile.Delete(); throw; } } else if ((PlatformInfo.Platform == PlatformType.Mono && PlatformInfo.GetVersion() >= new Version(6, 0)) || PlatformInfo.Platform == PlatformType.NetCore) { TransferFilePatched(source, destination, false, true); } else { base.MoveFileInternal(source, destination); } }
protected override void CopyFileInternal(string source, string destination, bool overwrite) { var sourceInfo = UnixFileSystemInfo.GetFileSystemEntry(source); if (sourceInfo.IsSymbolicLink) { var isSameDir = UnixPath.GetDirectoryName(source) == UnixPath.GetDirectoryName(destination); var symlinkInfo = (UnixSymbolicLinkInfo)sourceInfo; var symlinkPath = symlinkInfo.ContentsPath; var newFile = new UnixSymbolicLinkInfo(destination); if (FileExists(destination) && overwrite) { DeleteFile(destination); } if (isSameDir) { // We're in the same dir, so we can preserve relative symlinks. newFile.CreateSymbolicLinkTo(symlinkInfo.ContentsPath); } else { var fullPath = UnixPath.Combine(UnixPath.GetDirectoryName(source), symlinkPath); newFile.CreateSymbolicLinkTo(fullPath); } } else if (((PlatformInfo.Platform == PlatformType.Mono && PlatformInfo.GetVersion() >= new Version(6, 0)) || PlatformInfo.Platform == PlatformType.NetCore) && (!FileExists(destination) || overwrite)) { TransferFilePatched(source, destination, overwrite, false); } else { base.CopyFileInternal(source, destination, overwrite); } }
private void TransferFilePatched(string source, string destination, bool overwrite, bool move) { // Mono 6.x throws errors if permissions or timestamps cannot be set // - In 6.0 it'll leave a full length file // - In 6.6 it'll leave a zero length file // Catch the exception and attempt to handle these edgecases // Mono 6.x till 6.10 doesn't properly try use rename first. if (move && PlatformInfo.GetVersion() < new Version(6, 10)) { if (Syscall.lstat(source, out var sourcestat) == 0 && Syscall.lstat(destination, out var deststat) != 0 && Syscall.rename(source, destination) == 0) { _logger.Trace("Moved '{0}' -> '{1}' using Syscall.rename", source, destination); return; } } try { if (move) { base.MoveFileInternal(source, destination); } else { base.CopyFileInternal(source, destination, overwrite); } } catch (UnauthorizedAccessException) { var srcInfo = new FileInfo(source); var dstInfo = new FileInfo(destination); var exists = dstInfo.Exists && srcInfo.Exists; if (exists && dstInfo.Length == 0 && srcInfo.Length != 0) { // mono >=6.6 bug: zero length file since chmod happens at the start _logger.Debug("Mono failed to {2} file likely due to known mono bug, attempting to {2} directly. '{0}' -> '{1}'", source, destination, move ? "move" : "copy"); try { _logger.Trace("Copying content from {0} to {1} ({2} bytes)", source, destination, srcInfo.Length); using (var srcStream = new FileStream(source, FileMode.Open, FileAccess.Read)) using (var dstStream = new FileStream(destination, FileMode.Create, FileAccess.Write)) { srcStream.CopyTo(dstStream); } } catch { // If it fails again then bail throw; } } else if (exists && dstInfo.Length == srcInfo.Length) { // mono 6.0, 6.4 bug: full length file since utime and chmod happens at the end _logger.Debug("Mono failed to {2} file likely due to known mono bug, attempting to {2} directly. '{0}' -> '{1}'", source, destination, move ? "move" : "copy"); // Check at least part of the file since UnauthorizedAccess can happen due to legitimate reasons too var checkLength = (int)Math.Min(64 * 1024, dstInfo.Length); if (checkLength > 0) { var srcData = new byte[checkLength]; var dstData = new byte[checkLength]; _logger.Trace("Check last {0} bytes from {1}", checkLength, destination); using (var srcStream = new FileStream(source, FileMode.Open, FileAccess.Read)) using (var dstStream = new FileStream(destination, FileMode.Open, FileAccess.Read)) { srcStream.Position = srcInfo.Length - checkLength; dstStream.Position = dstInfo.Length - checkLength; srcStream.Read(srcData, 0, checkLength); dstStream.Read(dstData, 0, checkLength); } for (var i = 0; i < checkLength; i++) { if (srcData[i] != dstData[i]) { // Files aren't the same, the UnauthorizedAccess was unrelated _logger.Trace("Copy was incomplete, rethrowing original error"); throw; } } _logger.Trace("Copy was complete, finishing {0} operation", move ? "move" : "copy"); } } else { // Unrecognized situation, the UnauthorizedAccess was unrelated throw; } if (exists) { try { dstInfo.LastWriteTimeUtc = srcInfo.LastWriteTimeUtc; } catch { _logger.Debug("Unable to change last modified date for {0}, skipping.", destination); } if (move) { _logger.Trace("Removing source file {0}", source); File.Delete(source); } } } }