2025-01-07 09:40:39 +01:00
// Copyright (C) 2025, The Duplicati Team
2024-02-28 15:45:30 +01:00
// https://duplicati.com, hello@duplicati.com
2025-01-07 09:40:39 +01:00
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
2024-02-28 15:45:30 +01:00
// Software is furnished to do so, subject to the following conditions:
2025-01-07 09:40:39 +01:00
//
// The above copyright notice and this permission notice shall be included in
2024-02-28 15:45:30 +01:00
// all copies or substantial portions of the Software.
2025-01-07 09:40:39 +01:00
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2024-02-28 15:45:30 +01:00
// DEALINGS IN THE SOFTWARE.
2024-05-30 02:14:05 +02:00
using System ;
2019-12-29 12:11:47 -08:00
using System.Collections.Generic ;
using System.IO ;
2020-07-05 15:00:08 -07:00
using System.Linq ;
2020-07-01 16:32:07 -07:00
using Duplicati.Library.Common.IO ;
2020-07-19 19:51:05 -07:00
using Duplicati.Library.Interface ;
using Duplicati.Library.Main ;
using NUnit.Framework ;
2019-12-29 12:11:47 -08:00
namespace Duplicati.UnitTest
{
public class RestoreHandlerTests : BasicSetupHelper
{
2024-09-28 09:26:00 +02:00
[Test]
[Category("RestoreHandler")]
public void DisablePipedStreaming ()
{
string filePath = Path . Combine ( this . DATAFOLDER , "file" );
File . WriteAllBytes ( filePath , new byte [] { 0 });
Dictionary < string , string > options = new Dictionary < string , string >( this . TestOptions );
using ( Controller c = new Controller ( "file://" + this . TARGETFOLDER , options , null ))
{
c . Backup ( new [] { this . DATAFOLDER });
}
Dictionary < string , string > restoreOptions = new Dictionary < string , string >( this . TestOptions ) { [ "restore-path" ] = this . RESTOREFOLDER };
// This is now the default behavior, so we cannot explicitly disable it
//restoreOptions["disable-piped-streaming"] = "true";
using ( Controller c = new Controller ( "file://" + this . TARGETFOLDER , restoreOptions , null ))
{
IRestoreResults restoreResults = c . Restore ( new [] { filePath });
Assert . AreEqual ( 0 , restoreResults . Errors . Count ());
Assert . AreEqual ( 0 , restoreResults . Warnings . Count ());
}
string restoredFilePath = Path . Combine ( this . RESTOREFOLDER , "file" );
Assert . IsTrue ( File . Exists ( restoredFilePath ));
}
2020-07-01 16:32:07 -07:00
[Test]
[Category("RestoreHandler")]
public void RestoreEmptyFile ()
{
string folderPath = Path . Combine ( this . DATAFOLDER , "folder" );
Directory . CreateDirectory ( folderPath );
string filePath = Path . Combine ( folderPath , "empty_file" );
2020-07-19 19:51:05 -07:00
File . WriteAllBytes ( filePath , new byte [] { });
2020-07-01 16:32:07 -07:00
Dictionary < string , string > options = new Dictionary < string , string >( this . TestOptions );
using ( Controller c = new Controller ( "file://" + this . TARGETFOLDER , options , null ))
{
2024-09-28 09:26:00 +02:00
IBackupResults backupResults = c . Backup ( new [] { this . DATAFOLDER });
2020-07-19 19:51:05 -07:00
Assert . AreEqual ( 0 , backupResults . Errors . Count ());
Assert . AreEqual ( 0 , backupResults . Warnings . Count ());
2020-07-01 16:32:07 -07:00
}
// Issue #4148 described a situation where the folders containing the empty file were not recreated properly.
Dictionary < string , string > restoreOptions = new Dictionary < string , string >( this . TestOptions )
{
2020-07-20 20:08:14 -07:00
["restore-path"] = this . RESTOREFOLDER ,
2020-07-01 16:32:07 -07:00
["dont-compress-restore-paths"] = "true"
};
using ( Controller c = new Controller ( "file://" + this . TARGETFOLDER , restoreOptions , null ))
{
2024-09-28 09:26:00 +02:00
IRestoreResults restoreResults = c . Restore ( new [] { filePath });
2020-07-19 19:51:05 -07:00
Assert . AreEqual ( 0 , restoreResults . Errors . Count ());
2025-01-03 21:38:03 +01:00
// TODO The expected warning is expected, as the 'dont-compress-restore-paths' option results in a warning about a folder not being created before restoring a file.
Assert . AreEqual ( 1 , restoreResults . Warnings . Count ());
2020-07-02 15:39:56 -07:00
}
2020-07-01 16:32:07 -07:00
2024-07-26 00:08:45 -04:00
// We need to strip the root part of the path. Otherwise, Path.Combine will simply return the second argument
2020-07-02 15:39:56 -07:00
// if it's determined to be an absolute path.
string rootString = SystemIO . IO_OS . GetPathRoot ( filePath );
string newPathPart = filePath . Substring ( rootString . Length );
2024-05-30 02:14:05 +02:00
if ( OperatingSystem . IsWindows ())
2020-07-02 15:39:56 -07:00
{
2020-07-02 15:40:06 -07:00
// On Windows, the drive letter is included in the path when the dont-compress-restore-paths option is used.
2020-07-02 15:39:56 -07:00
// The drive letter is assumed to be the first character of the path root (e.g., C:\).
newPathPart = Path . Combine ( rootString . Substring ( 0 , 1 ), filePath . Substring ( rootString . Length ));
2020-07-01 16:32:07 -07:00
}
2020-07-02 15:39:56 -07:00
string restoredFilePath = Path . Combine ( restoreOptions [ "restore-path" ], newPathPart );
Assert . IsTrue ( File . Exists ( restoredFilePath ));
2020-07-01 16:32:07 -07:00
}
2019-12-29 12:11:47 -08:00
[Test]
[Category("RestoreHandler")]
public void RestoreInheritanceBreaks ()
{
2024-05-30 02:14:05 +02:00
if (! OperatingSystem . IsWindows ())
2019-12-29 12:11:47 -08:00
{
return ;
}
2020-05-31 14:38:14 +02:00
/* TODO-DNC
2019-12-29 12:11:47 -08:00
string folderPath = Path.Combine(this.DATAFOLDER, "folder");
Directory.CreateDirectory(folderPath);
string filePath = Path.Combine(folderPath, "file");
File.WriteAllBytes(filePath, new byte[] {0});
// Protect access rules on the file.
2024-03-03 15:41:49 +01:00
FileSecurity fileSecurity = new FileInfo(filePath).GetAccessControl();
2019-12-29 12:11:47 -08:00
fileSecurity.SetAccessRuleProtection(true, true);
2024-03-03 15:41:49 +01:00
new FileInfo(filePath).SetAccessControl(fileSecurity);
2019-12-29 12:11:47 -08:00
Dictionary<string, string> options = new Dictionary<string, string>(this.TestOptions);
using (Controller c = new Controller("file://" + this.TARGETFOLDER, options, null))
{
2020-07-05 15:00:08 -07:00
IBackupResults backupResults = c.Backup(new[] {this.DATAFOLDER});
Assert.AreEqual(0, backupResults.Errors.Count());
Assert.AreEqual(0, backupResults.Warnings.Count());
2019-12-29 12:11:47 -08:00
}
// First, restore without restoring permissions.
Dictionary<string, string> restoreOptions = new Dictionary<string, string>(this.TestOptions) {["restore-path"] = this.RESTOREFOLDER};
using (Controller c = new Controller("file://" + this.TARGETFOLDER, restoreOptions, null))
{
2020-07-05 15:00:08 -07:00
IRestoreResults restoreResults = c.Restore(new[] {filePath});
Assert.AreEqual(0, restoreResults.Errors.Count());
Assert.AreEqual(0, restoreResults.Warnings.Count());
2019-12-29 12:11:47 -08:00
string restoredFilePath = Path.Combine(this.RESTOREFOLDER, "file");
Assert.IsTrue(File.Exists(restoredFilePath));
2024-03-03 15:41:49 +01:00
FileSecurity restoredFileSecurity = new FileInfo(restoredFilePath).GetAccessControl();
2019-12-29 12:11:47 -08:00
Assert.IsFalse(restoredFileSecurity.AreAccessRulesProtected);
2020-07-06 19:31:54 -07:00
// Remove the restored file so that the later restore avoids the "Restore completed
// without errors but no files were restored" warning.
File.Delete(restoredFilePath);
2019-12-29 12:11:47 -08:00
}
// Restore with restoring permissions.
restoreOptions["overwrite"] = "true";
restoreOptions["restore-permissions"] = "true";
using (Controller c = new Controller("file://" + this.TARGETFOLDER, restoreOptions, null))
{
2020-07-05 15:00:08 -07:00
IRestoreResults restoreResults = c.Restore(new[] {filePath});
Assert.AreEqual(0, restoreResults.Errors.Count());
Assert.AreEqual(0, restoreResults.Warnings.Count());
2019-12-29 12:11:47 -08:00
string restoredFilePath = Path.Combine(this.RESTOREFOLDER, "file");
Assert.IsTrue(File.Exists(restoredFilePath));
2024-03-03 15:41:49 +01:00
FileSecurity restoredFileSecurity = new FileInfo(restoredFilePath).GetAccessControl();
2019-12-29 12:11:47 -08:00
Assert.IsTrue(restoredFileSecurity.AreAccessRulesProtected);
}
2020-05-31 14:38:14 +02:00
*/
2019-12-29 12:11:47 -08:00
}
}
}