diff --git a/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs b/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs index c91d54c46..02557152b 100644 --- a/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs +++ b/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs @@ -27,7 +27,8 @@ using System.Security.Authentication; using Duplicati.Library.Interface; using Uri = System.Uri; using CoreUtility = Duplicati.Library.Utility.Utility; - +using Duplicati.Library.IO; + namespace Duplicati.Library.Backend.AlternativeFTP { // ReSharper disable once RedundantExtendsListEntry diff --git a/Duplicati/Library/Backend/AzureBlob/AzureBlobWrapper.cs b/Duplicati/Library/Backend/AzureBlob/AzureBlobWrapper.cs index f54f2f578..7e7f74501 100644 --- a/Duplicati/Library/Backend/AzureBlob/AzureBlobWrapper.cs +++ b/Duplicati/Library/Backend/AzureBlob/AzureBlobWrapper.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Duplicati.Library.IO; using Duplicati.Library.Interface; using Duplicati.Library.Utility; using Microsoft.WindowsAzure.Storage; diff --git a/Duplicati/Library/Backend/AzureBlob/Duplicati.Library.Backend.AzureBlob.csproj b/Duplicati/Library/Backend/AzureBlob/Duplicati.Library.Backend.AzureBlob.csproj index e6a2c474c..74abcb7f4 100644 --- a/Duplicati/Library/Backend/AzureBlob/Duplicati.Library.Backend.AzureBlob.csproj +++ b/Duplicati/Library/Backend/AzureBlob/Duplicati.Library.Backend.AzureBlob.csproj @@ -79,6 +79,10 @@ {B68F2214-951F-4F78-8488-66E1ED3F50BF} Duplicati.Library.Localization + + {D63E53E4-A458-4C2F-914D-92F715F58ACF} + Duplicati.Library.IO + diff --git a/Duplicati/Library/Backend/CloudFiles/CloudFiles.cs b/Duplicati/Library/Backend/CloudFiles/CloudFiles.cs index c8d580be3..eba81377e 100644 --- a/Duplicati/Library/Backend/CloudFiles/CloudFiles.cs +++ b/Duplicati/Library/Backend/CloudFiles/CloudFiles.cs @@ -23,7 +23,8 @@ using System.Linq; using System.Text; using System.Net; using Duplicati.Library.Interface; - +using Duplicati.Library.IO; + namespace Duplicati.Library.Backend { public class CloudFiles : IBackend, IStreamingBackend diff --git a/Duplicati/Library/Backend/CloudFiles/Duplicati.Library.Backend.CloudFiles.csproj b/Duplicati/Library/Backend/CloudFiles/Duplicati.Library.Backend.CloudFiles.csproj index b1151e757..9a785568a 100644 --- a/Duplicati/Library/Backend/CloudFiles/Duplicati.Library.Backend.CloudFiles.csproj +++ b/Duplicati/Library/Backend/CloudFiles/Duplicati.Library.Backend.CloudFiles.csproj @@ -57,6 +57,10 @@ {B68F2214-951F-4F78-8488-66E1ED3F50BF} Duplicati.Library.Localization + + {D63E53E4-A458-4C2F-914D-92F715F58ACF} + Duplicati.Library.IO + diff --git a/Duplicati/Library/Backend/Dropbox/Dropbox.cs b/Duplicati/Library/Backend/Dropbox/Dropbox.cs index 27b88d9c1..9a4e055de 100644 --- a/Duplicati/Library/Backend/Dropbox/Dropbox.cs +++ b/Duplicati/Library/Backend/Dropbox/Dropbox.cs @@ -2,7 +2,8 @@ using System.Collections.Generic; using System.IO; using Duplicati.Library.Interface; - +using Duplicati.Library.IO; + namespace Duplicati.Library.Backend { public class Dropbox : IBackend, IStreamingBackend @@ -49,7 +50,7 @@ namespace Duplicati.Library.Backend get { return "dropbox"; } } - private FileEntry ParseEntry(MetaData md) + private IFileEntry ParseEntry(MetaData md) { var ife = new FileEntry(md.name); if (md.IsFile) diff --git a/Duplicati/Library/Backend/Dropbox/Duplicati.Library.Backend.Dropbox.csproj b/Duplicati/Library/Backend/Dropbox/Duplicati.Library.Backend.Dropbox.csproj index 780fc7a85..8fe7a70be 100644 --- a/Duplicati/Library/Backend/Dropbox/Duplicati.Library.Backend.Dropbox.csproj +++ b/Duplicati/Library/Backend/Dropbox/Duplicati.Library.Backend.Dropbox.csproj @@ -61,6 +61,10 @@ {B68F2214-951F-4F78-8488-66E1ED3F50BF} Duplicati.Library.Localization + + {D63E53E4-A458-4C2F-914D-92F715F58ACF} + Duplicati.Library.IO + diff --git a/Duplicati/Library/Backend/File/FileBackend.cs b/Duplicati/Library/Backend/File/FileBackend.cs index 56b1b3566..93eadccda 100644 --- a/Duplicati/Library/Backend/File/FileBackend.cs +++ b/Duplicati/Library/Backend/File/FileBackend.cs @@ -39,8 +39,9 @@ namespace Duplicati.Library.Backend private bool m_hasAutenticated; private readonly bool m_forceReauth; - private readonly byte[] m_copybuffer = new byte[Duplicati.Library.Utility.Utility.DEFAULT_BUFFER_SIZE]; + private readonly byte[] m_copybuffer = new byte[Utility.Utility.DEFAULT_BUFFER_SIZE]; + private static ISystemIO systemIO = SystemIO.IO_OS(Utility.Utility.IsClientWindows); public File() { @@ -61,12 +62,14 @@ namespace Duplicati.Library.Backend m_password = uri.Password; if (!System.IO.Path.IsPathRooted(m_path)) - m_path = System.IO.Path.GetFullPath(m_path); + m_path = systemIO.PathGetFullPath(m_path); if (options.ContainsKey(OPTION_ALTERNATE_PATHS)) { - List paths = new List(); - paths.Add(m_path); + List paths = new List + { + m_path + }; paths.AddRange(options[OPTION_ALTERNATE_PATHS].Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries)); //On windows we expand the drive letter * to all drives @@ -101,7 +104,7 @@ namespace Duplicati.Library.Backend { try { - if (System.IO.Directory.Exists(p) && (markerfile == null || System.IO.File.Exists(System.IO.Path.Combine(p, markerfile)))) + if (systemIO.DirectoryExists(p) && (markerfile == null || systemIO.FileExists(systemIO.PathCombine(p, markerfile)))) { m_path = p; break; @@ -125,13 +128,10 @@ namespace Duplicati.Library.Backend { try { - if (!string.IsNullOrEmpty(m_username) && m_password != null) + if (!string.IsNullOrEmpty(m_username) && m_password != null && !m_hasAutenticated) { - if (!m_hasAutenticated) - { - Win32.PreAuthenticate(m_path, m_username, m_password, m_forceReauth); - m_hasAutenticated = true; - } + Win32.PreAuthenticate(m_path, m_username, m_password, m_forceReauth); + m_hasAutenticated = true; } } catch @@ -142,10 +142,10 @@ namespace Duplicati.Library.Backend { PreAuthenticate(); - if (!System.IO.Directory.Exists(m_path)) + if (!systemIO.DirectoryExists(m_path)) throw new FolderMissingException(Strings.FileBackend.FolderMissingError(m_path)); - return SystemIO.IO_OS(Utility.Utility.IsClientWindows).PathCombine(m_path, remotename); + return systemIO.PathCombine(m_path, remotename); } #region IBackendInterface Members @@ -169,21 +169,17 @@ namespace Duplicati.Library.Backend { PreAuthenticate(); - if (!System.IO.Directory.Exists(m_path)) + if (!systemIO.DirectoryExists(m_path)) throw new FolderMissingException(Strings.FileBackend.FolderMissingError(m_path)); - foreach (string s in System.IO.Directory.EnumerateFiles(m_path)) + foreach (string s in systemIO.EnumerateFileSystemEntries(m_path)) { - System.IO.FileInfo fi = new System.IO.FileInfo(s); - yield return new FileEntry(fi.Name, fi.Length, fi.LastAccessTime, fi.LastWriteTime); + yield return systemIO.FileEntry(s); } - foreach (string s in System.IO.Directory.EnumerateDirectories(m_path)) + foreach (string s in systemIO.EnumerateDirectories(m_path)) { - System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(s); - FileEntry fe = new FileEntry(di.Name, 0, di.LastAccessTime, di.LastWriteTime); - fe.IsFolder = true; - yield return fe; + yield return systemIO.DirectoryEntry(s); } } @@ -191,7 +187,7 @@ namespace Duplicati.Library.Backend private static Random random = new Random(); public void Put(string remotename, System.IO.Stream stream) { - using(System.IO.FileStream writestream = System.IO.File.Open(GetRemoteName(remotename), System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None)) + using(System.IO.FileStream writestream = systemIO.FileOpenWrite(GetRemoteName(remotename))) { if (random.NextDouble() > 0.6666) throw new Exception("Random upload failure"); @@ -201,14 +197,15 @@ namespace Duplicati.Library.Backend #else public void Put(string remotename, System.IO.Stream stream) { - using(System.IO.FileStream writestream = System.IO.File.Open(GetRemoteName(remotename), System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None)) + using(System.IO.FileStream writestream = systemIO.FileOpenWrite(GetRemoteName(remotename))) Utility.Utility.CopyStream(stream, writestream, true, m_copybuffer); } #endif public void Get(string remotename, System.IO.Stream stream) { - using (System.IO.FileStream readstream = System.IO.File.Open(GetRemoteName(remotename), System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read)) + // FileOpenRead has flags System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read + using (System.IO.FileStream readstream = systemIO.FileOpenRead(GetRemoteName(remotename))) Utility.Utility.CopyStream(readstream, stream, true, m_copybuffer); } @@ -217,23 +214,23 @@ namespace Duplicati.Library.Backend string path = GetRemoteName(remotename); if (m_moveFile) { - if (System.IO.File.Exists(path)) - System.IO.File.Delete(path); + if (systemIO.FileExists(path)) + systemIO.FileDelete(path); - System.IO.File.Move(filename, path); + systemIO.FileMove(filename, path); } else - System.IO.File.Copy(filename, path, true); + systemIO.FileCopy(filename, path, true); } public void Get(string remotename, string filename) { - System.IO.File.Copy(GetRemoteName(remotename), filename, true); + systemIO.FileCopy(GetRemoteName(remotename), filename, true); } public void Delete(string remotename) { - System.IO.File.Delete(GetRemoteName(remotename)); + systemIO.FileDelete(GetRemoteName(remotename)); } public IList SupportedCommands @@ -267,10 +264,10 @@ namespace Duplicati.Library.Backend public void CreateFolder() { - if (System.IO.Directory.Exists(m_path)) + if (systemIO.DirectoryExists(m_path)) throw new FolderAreadyExistedException(); - System.IO.Directory.CreateDirectory(m_path); + systemIO.DirectoryCreate(m_path); } #endregion @@ -278,11 +275,9 @@ namespace Duplicati.Library.Backend #region IDisposable Members public void Dispose() - { - if (m_username != null) - m_username = null; - if (m_password != null) - m_password = null; + { + m_username = null; + m_password = null; } #endregion @@ -297,7 +292,7 @@ namespace Duplicati.Library.Backend string root; if (Utility.Utility.IsClientLinux) { - string path = Util.AppendDirSeparator(System.IO.Path.GetFullPath(m_path)); + string path = Util.AppendDirSeparator(systemIO.PathGetFullPath(m_path)); root = "/"; //Find longest common prefix from mounted devices @@ -309,7 +304,7 @@ namespace Duplicati.Library.Backend } else { - root = SystemIO.IO_OS(Utility.Utility.IsClientWindows).GetPathRoot(m_path); + root = systemIO.GetPathRoot(m_path); } // On Windows, DriveInfo is only valid for lettered drives. (e.g., not for UNC paths and shares) @@ -338,16 +333,15 @@ namespace Duplicati.Library.Backend { return new QuotaInfo(driveInfo.TotalSize, driveInfo.AvailableFreeSpace); } - else if (Utility.Utility.IsClientWindows) + + if (Utility.Utility.IsClientWindows) { // If we can't get the DriveInfo on Windows, fallback to GetFreeDiskSpaceEx // https://stackoverflow.com/questions/2050343/programmatically-determining-space-available-from-unc-path return GetDiskFreeSpace(m_path); } - else - { - return null; - } + + return null; } } @@ -360,9 +354,9 @@ namespace Duplicati.Library.Backend { var source = GetRemoteName(oldname); var target = GetRemoteName(newname); - if (System.IO.File.Exists(target)) - System.IO.File.Delete(target); - System.IO.File.Move(source, target); + if (systemIO.FileExists(target)) + systemIO.FileDelete(target); + systemIO.FileMove(source, target); } /// diff --git a/Duplicati/Library/Backend/Mega/Duplicati.Library.Backend.Mega.csproj b/Duplicati/Library/Backend/Mega/Duplicati.Library.Backend.Mega.csproj index 80da47791..3247ba94a 100644 --- a/Duplicati/Library/Backend/Mega/Duplicati.Library.Backend.Mega.csproj +++ b/Duplicati/Library/Backend/Mega/Duplicati.Library.Backend.Mega.csproj @@ -59,6 +59,10 @@ {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65} Duplicati.Library.Utility + + {D63E53E4-A458-4C2F-914D-92F715F58ACF} + Duplicati.Library.IO + diff --git a/Duplicati/Library/Backend/Mega/MegaBackend.cs b/Duplicati/Library/Backend/Mega/MegaBackend.cs index 694d72ba7..5e7358d40 100644 --- a/Duplicati/Library/Backend/Mega/MegaBackend.cs +++ b/Duplicati/Library/Backend/Mega/MegaBackend.cs @@ -19,7 +19,8 @@ using System.Linq; using System.Collections.Generic; using Duplicati.Library.Interface; using CG.Web.MegaApiClient; - +using Duplicati.Library.IO; + namespace Duplicati.Library.Backend.Mega { public class MegaBackend: IBackend, IStreamingBackend diff --git a/Duplicati/Library/Backend/OneDrive/MicrosoftGraphBackend.cs b/Duplicati/Library/Backend/OneDrive/MicrosoftGraphBackend.cs index 3205083b3..954b4872b 100644 --- a/Duplicati/Library/Backend/OneDrive/MicrosoftGraphBackend.cs +++ b/Duplicati/Library/Backend/OneDrive/MicrosoftGraphBackend.cs @@ -10,6 +10,7 @@ using System.Threading; using Duplicati.Library.Backend.MicrosoftGraph; using Duplicati.Library.Interface; +using Duplicati.Library.IO; using Duplicati.Library.Utility; using Newtonsoft.Json; diff --git a/Duplicati/Library/Backend/Rclone/Duplicati.Library.Backend.Rclone.csproj b/Duplicati/Library/Backend/Rclone/Duplicati.Library.Backend.Rclone.csproj index 7ff48865b..dfc3d2e2e 100644 --- a/Duplicati/Library/Backend/Rclone/Duplicati.Library.Backend.Rclone.csproj +++ b/Duplicati/Library/Backend/Rclone/Duplicati.Library.Backend.Rclone.csproj @@ -54,6 +54,10 @@ {C5899F45-B0FF-483C-9D38-24A9FCAAB237} Duplicati.Library.Interface + + {D63E53E4-A458-4C2F-914D-92F715F58ACF} + Duplicati.Library.IO + diff --git a/Duplicati/Library/Backend/Rclone/Rclone.cs b/Duplicati/Library/Backend/Rclone/Rclone.cs index dbdba63c7..9f73b324f 100644 --- a/Duplicati/Library/Backend/Rclone/Rclone.cs +++ b/Duplicati/Library/Backend/Rclone/Rclone.cs @@ -27,7 +27,8 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Globalization; using System.IO; - +using Duplicati.Library.IO; + namespace Duplicati.Library.Backend { public class Rclone : IBackend diff --git a/Duplicati/Library/Backend/S3/S3Wrapper.cs b/Duplicati/Library/Backend/S3/S3Wrapper.cs index 76df7ce71..5288dfbd4 100644 --- a/Duplicati/Library/Backend/S3/S3Wrapper.cs +++ b/Duplicati/Library/Backend/S3/S3Wrapper.cs @@ -169,7 +169,7 @@ namespace Duplicati.Library.Backend { if (alreadyReturned.Add(obj.Key)) { - yield return new FileEntry( + yield return new IO.FileEntry( obj.Key, obj.Size, obj.LastModified, diff --git a/Duplicati/Library/Backend/Sia/Duplicati.Library.Backend.Sia.csproj b/Duplicati/Library/Backend/Sia/Duplicati.Library.Backend.Sia.csproj index 4c67ec09a..d632c39a7 100644 --- a/Duplicati/Library/Backend/Sia/Duplicati.Library.Backend.Sia.csproj +++ b/Duplicati/Library/Backend/Sia/Duplicati.Library.Backend.Sia.csproj @@ -58,6 +58,10 @@ {C5899F45-B0FF-483C-9D38-24A9FCAAB237} Duplicati.Library.Interface + + {D63E53E4-A458-4C2F-914D-92F715F58ACF} + Duplicati.Library.IO + diff --git a/Duplicati/Library/Backend/Sia/Sia.cs b/Duplicati/Library/Backend/Sia/Sia.cs index 2576bdd0a..c23892317 100644 --- a/Duplicati/Library/Backend/Sia/Sia.cs +++ b/Duplicati/Library/Backend/Sia/Sia.cs @@ -6,6 +6,7 @@ using System.Net; using Duplicati.Library.Interface; using Duplicati.Library.Utility; using Newtonsoft.Json; +using Duplicati.Library.IO; namespace Duplicati.Library.Backend.Sia { @@ -296,9 +297,11 @@ namespace Duplicati.Library.Backend.Sia // in our target path if (f.Siapath.StartsWith(m_targetpath, StringComparison.Ordinal)) { - FileEntry fe = new FileEntry(f.Siapath.Substring(m_targetpath.Length + 1)); - fe.Size = f.Filesize; - fe.IsFolder = false; + FileEntry fe = new FileEntry(f.Siapath.Substring(m_targetpath.Length + 1)) + { + Size = f.Filesize, + IsFolder = false + }; yield return fe; } } diff --git a/Duplicati/Library/IO/Duplicati.Library.IO.csproj b/Duplicati/Library/IO/Duplicati.Library.IO.csproj index a93a16ff6..744b538f3 100644 --- a/Duplicati/Library/IO/Duplicati.Library.IO.csproj +++ b/Duplicati/Library/IO/Duplicati.Library.IO.csproj @@ -67,6 +67,7 @@ + @@ -87,6 +88,10 @@ {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798} Duplicati.Library.Logging + + {C5899F45-B0FF-483C-9D38-24A9FCAAB237} + Duplicati.Library.Interface + diff --git a/Duplicati/Library/Interface/FileEntry.cs b/Duplicati/Library/IO/FileEntry.cs similarity index 94% rename from Duplicati/Library/Interface/FileEntry.cs rename to Duplicati/Library/IO/FileEntry.cs index 5164a968d..aafeddf0f 100644 --- a/Duplicati/Library/Interface/FileEntry.cs +++ b/Duplicati/Library/IO/FileEntry.cs @@ -18,10 +18,9 @@ // #endregion using System; -using System.Collections.Generic; -using System.Text; +using Duplicati.Library.Interface; -namespace Duplicati.Library.Interface +namespace Duplicati.Library.IO { /// /// The primary implementation of the file interface diff --git a/Duplicati/Library/IO/ISystemIO.cs b/Duplicati/Library/IO/ISystemIO.cs index 7385dc291..e53d2339f 100644 --- a/Duplicati/Library/IO/ISystemIO.cs +++ b/Duplicati/Library/IO/ISystemIO.cs @@ -18,7 +18,8 @@ using System; using System.IO; using System.Collections.Generic; - +using Duplicati.Library.Interface; + namespace Duplicati.Library.IO { /// @@ -26,6 +27,7 @@ namespace Duplicati.Library.IO /// public interface ISystemIO { + IFileEntry DirectoryEntry(string path); void DirectoryDelete(string path); void DirectoryDelete(string path, bool recursive); void DirectoryCreate(string path); @@ -35,18 +37,21 @@ namespace Duplicati.Library.IO DateTime DirectoryGetLastWriteTimeUtc(string path); DateTime DirectoryGetCreationTimeUtc(string path); + + IFileEntry FileEntry(string path); void FileMove(string source, string target); void FileDelete(string path); + void FileCopy(string source, string target, bool overwrite); void FileSetLastWriteTimeUtc(string path, DateTime time); void FileSetCreationTimeUtc(string path, DateTime time); DateTime FileGetLastWriteTimeUtc(string path); DateTime FileGetCreationTimeUtc(string path); bool FileExists(string path); long FileLength(string path); - Stream FileOpenRead(string path); - Stream FileOpenWrite(string path); - Stream FileOpenReadWrite(string path); - Stream FileCreate(string path); + FileStream FileOpenRead(string path); + FileStream FileOpenWrite(string path); + FileStream FileOpenReadWrite(string path); + FileStream FileCreate(string path); FileAttributes GetFileAttributes(string path); void SetFileAttributes(string path, FileAttributes attributes); void CreateSymlink(string symlinkfile, string target, bool asDir); @@ -56,6 +61,7 @@ namespace Duplicati.Library.IO string PathGetExtension(string path); string PathChangeExtension(string path, string extension); string PathCombine(params string[] paths); + string PathGetFullPath(string path); string GetPathRoot(string path); string[] GetDirectories(string path); string[] GetFiles(string path); @@ -63,9 +69,11 @@ namespace Duplicati.Library.IO DateTime GetCreationTimeUtc(string path); DateTime GetLastWriteTimeUtc(string path); IEnumerable EnumerateFileSystemEntries(string path); + IEnumerable EnumerateDirectories(string path); void SetMetadata(string path, Dictionary metdata, bool restorePermissions); Dictionary GetMetadata(string path, bool isSymlink, bool followSymlink); } + } diff --git a/Duplicati/Library/IO/SystemIOLinux.cs b/Duplicati/Library/IO/SystemIOLinux.cs index 378376f1f..41586abe3 100644 --- a/Duplicati/Library/IO/SystemIOLinux.cs +++ b/Duplicati/Library/IO/SystemIOLinux.cs @@ -19,7 +19,8 @@ using System; using System.IO; using System.Linq; using System.Collections.Generic; - +using Duplicati.Library.Interface; + namespace Duplicati.Library.IO { public struct SystemIOLinux : ISystemIO @@ -70,22 +71,22 @@ namespace Duplicati.Library.IO return File.Exists(path); } - public Stream FileOpenRead(string path) + public FileStream FileOpenRead(string path) { return File.OpenRead(path); } - public Stream FileOpenWrite(string path) + public FileStream FileOpenWrite(string path) { return File.OpenWrite(path); } - public Stream FileOpenReadWrite(string path) + public FileStream FileOpenReadWrite(string path) { return File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); } - public Stream FileCreate(string path) + public FileStream FileCreate(string path) { return File.Create(path); } @@ -233,20 +234,6 @@ namespace Duplicati.Library.IO } } } - #endregion - - /// - /// Normalizes a path, by removing any trailing slash, before calling system methods - /// - /// The path to normalize. - /// The normalized path. - public static string NormalizePath(string path) - { - var p = Path.GetFullPath(path); - - // This should not be required, but some versions of Mono apperently do not strip the trailing slash - return p.Length > 1 && p[p.Length - 1] == Path.DirectorySeparatorChar ? p.Substring(0, p.Length - 1) : p; - } public string GetPathRoot(string path) { @@ -276,7 +263,52 @@ namespace Duplicati.Library.IO public DateTime GetLastWriteTimeUtc(string path) { return File.GetLastWriteTimeUtc(path); + } + + public IEnumerable EnumerateDirectories(string path) + { + return Directory.EnumerateDirectories(path); + } + + public void FileCopy(string source, string target, bool overwrite) + { + File.Copy(source, target, overwrite); } + + public string PathGetFullPath(string path) + { + return Path.GetFullPath(path); + } + + #endregion + + /// + /// Normalizes a path, by removing any trailing slash, before calling system methods + /// + /// The path to normalize. + /// The normalized path. + public static string NormalizePath(string path) + { + var p = Path.GetFullPath(path); + + // This should not be required, but some versions of Mono apperently do not strip the trailing slash + return p.Length > 1 && p[p.Length - 1] == Path.DirectorySeparatorChar ? p.Substring(0, p.Length - 1) : p; + } + + public IFileEntry DirectoryEntry(string path) + { + var dInfo = new DirectoryInfo(path); + return new FileEntry(dInfo.Name, 0, dInfo.LastAccessTime, dInfo.LastWriteTime) + { + IsFolder = true + }; + } + + public IFileEntry FileEntry(string path) + { + var fileInfo = new FileInfo(path); + return new FileEntry(fileInfo.Name, fileInfo.Length, fileInfo.LastAccessTime, fileInfo.LastWriteTime); + } } } diff --git a/Duplicati/Library/IO/SystemIOWindows.cs b/Duplicati/Library/IO/SystemIOWindows.cs index d6b0c8a61..17dae205f 100644 --- a/Duplicati/Library/IO/SystemIOWindows.cs +++ b/Duplicati/Library/IO/SystemIOWindows.cs @@ -22,7 +22,7 @@ using System.IO; using Alphaleonis.Win32.Vss; using AlphaFS = Alphaleonis.Win32.Filesystem; - +using Duplicati.Library.Interface; namespace Duplicati.Library.IO { @@ -180,7 +180,7 @@ namespace Duplicati.Library.IO return Alphaleonis.Win32.Filesystem.File.Exists(PrefixWithUNC(path)); } - public System.IO.Stream FileOpenRead(string path) + public System.IO.FileStream FileOpenRead(string path) { if (!IsPathTooLong(path)) try { return System.IO.File.Open(path, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite); } @@ -190,7 +190,7 @@ namespace Duplicati.Library.IO return Alphaleonis.Win32.Filesystem.File.Open(PrefixWithUNC(path), FileMode.Open, FileAccess.Read, FileShare.ReadWrite); } - public System.IO.Stream FileOpenWrite(string path) + public System.IO.FileStream FileOpenWrite(string path) { if (!IsPathTooLong(path)) try { return System.IO.File.OpenWrite(path); } @@ -203,7 +203,7 @@ namespace Duplicati.Library.IO return FileCreate(path); } - public System.IO.Stream FileOpenReadWrite(string path) + public System.IO.FileStream FileOpenReadWrite(string path) { if (!IsPathTooLong(path)) try { return System.IO.File.Open(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite, System.IO.FileShare.Read); } @@ -213,7 +213,7 @@ namespace Duplicati.Library.IO return Alphaleonis.Win32.Filesystem.File.Open(PrefixWithUNC(path), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); } - public System.IO.Stream FileCreate(string path) + public System.IO.FileStream FileCreate(string path) { if (!IsPathTooLong(path)) try { return System.IO.File.Create(path); } @@ -699,7 +699,84 @@ namespace Duplicati.Library.IO } return AlphaFS.File.GetLastWriteTimeUtc(path); - } + } + + public IEnumerable EnumerateDirectories(string path) + { + if (!IsPathTooLong(path)) + { + try { return Directory.EnumerateDirectories(path); } + catch (System.IO.PathTooLongException) { } + catch (System.ArgumentException) { } + } + + return AlphaFS.Directory.EnumerateDirectories(path); + } + + public void FileCopy(string source, string target, bool overwrite) + { + if (!IsPathTooLong(source) && !IsPathTooLong(target)) + { + try { File.Copy(source, target, overwrite); } + catch (System.IO.PathTooLongException) { } + catch (System.ArgumentException) { } + } + + AlphaFS.File.Copy(source, target, overwrite); + } + + public string PathGetFullPath(string path) + { + if (!IsPathTooLong(path)) + { + try { return System.IO.Path.GetFullPath(path); } + catch (System.IO.PathTooLongException) { } + catch (System.ArgumentException) { } + } + + return AlphaFS.Path.GetFullPath(path); + } + + public IFileEntry DirectoryEntry(string path) + { + if (!IsPathTooLong(path)) + { + try + { + var dInfo = new DirectoryInfo(path); + return new FileEntry(dInfo.Name, 0, dInfo.LastAccessTime, dInfo.LastWriteTime) + { + IsFolder = true + }; + } + catch (System.IO.PathTooLongException) { } + catch (System.ArgumentException) { } + } + + var dInfoAlphaFS = new AlphaFS.DirectoryInfo(path); + return new FileEntry(dInfoAlphaFS.Name, 0, dInfoAlphaFS.LastAccessTime, dInfoAlphaFS.LastWriteTime) + { + IsFolder = true + }; + } + + public IFileEntry FileEntry(string path) + { + if (!IsPathTooLong(path)) + { + try + { + var fileInfo = new FileInfo(path); + return new FileEntry(fileInfo.Name, fileInfo.Length, fileInfo.LastAccessTime, fileInfo.LastWriteTime); + } + catch (System.IO.PathTooLongException) { } + catch (System.ArgumentException) { } + } + + var fInfoAlphaFS = new AlphaFS.FileInfo(path); + return new FileEntry(fInfoAlphaFS.Name, fInfoAlphaFS.Length, fInfoAlphaFS.LastAccessTime, fInfoAlphaFS.LastWriteTime); + } + #endregion } } diff --git a/Duplicati/Library/Interface/Duplicati.Library.Interface.csproj b/Duplicati/Library/Interface/Duplicati.Library.Interface.csproj index 6c9bee0eb..a58fd641a 100644 --- a/Duplicati/Library/Interface/Duplicati.Library.Interface.csproj +++ b/Duplicati/Library/Interface/Duplicati.Library.Interface.csproj @@ -45,7 +45,6 @@ - diff --git a/Duplicati/Server/WebServer/RESTMethods/Filesystem.cs b/Duplicati/Server/WebServer/RESTMethods/Filesystem.cs index 375197de6..f407b641e 100644 --- a/Duplicati/Server/WebServer/RESTMethods/Filesystem.cs +++ b/Duplicati/Server/WebServer/RESTMethods/Filesystem.cs @@ -209,7 +209,7 @@ namespace Duplicati.Server.WebServer.RESTMethods var systemIO = SystemIO.IO_OS(Library.Utility.Utility.IsClientWindows); - foreach (var s in System.IO.Directory.EnumerateFileSystemEntries(entrypath)) + foreach (var s in systemIO.EnumerateFileSystemEntries(entrypath)) { Serializable.TreeNode tn = null; try diff --git a/Duplicati/UnitTest/SizeOmittingBackend.cs b/Duplicati/UnitTest/SizeOmittingBackend.cs index 9f3cb95bf..10480a530 100644 --- a/Duplicati/UnitTest/SizeOmittingBackend.cs +++ b/Duplicati/UnitTest/SizeOmittingBackend.cs @@ -19,7 +19,8 @@ using System.Linq; using Duplicati.Library.Interface; using System.Collections.Generic; using System.IO; - +using Duplicati.Library.IO; + namespace Duplicati.UnitTest { public class SizeOmittingBackend : IBackend, IStreamingBackend @@ -56,7 +57,7 @@ namespace Duplicati.UnitTest return from n in m_backend.List() where !n.IsFolder - select new Library.Interface.FileEntry(n.Name); + select new FileEntry(n.Name); } public void Put(string remotename, string filename) {