Refactor the BackupManager options interface some more

- Move the pref based (repository associated) options back
to init of the backup manager. This is still a bit of an unholy mess but
the existing setup is a little insane.
There are mostly runtime associated settings, like revision, tag, thread
count, quick mode, show stats. Then there are the more fuzzy ones,
like allow failures, patterns. However something like set-owner, mostly
likely that's going to be static for a "repository", same with
exclude by attribute. In any event things that make sense to be
associated with the repository preferences will stay in a backup mananger
initialization time options structure, and no more growing of the
arguments list - go doesn't look good there.

RestoreOptions for the more ephemeral run associated settings.
The Backup routine with it's large argument list stays for now, but
will get revamped if anything new gets added.
This commit is contained in:
2023-10-06 17:13:51 -05:00
parent c151b21f5c
commit 58d21eb17a
4 changed files with 112 additions and 109 deletions

View File

@@ -22,9 +22,7 @@ import (
"github.com/gilbertchen/cli"
"io/ioutil"
"github.com/gilbertchen/duplicacy/src"
duplicacy "github.com/gilbertchen/duplicacy/src"
)
const (
@@ -316,7 +314,7 @@ func configRepository(context *cli.Context, init bool) {
// write real path into .duplicacy file inside repository
duplicacyFileName := path.Join(repository, duplicacy.DUPLICACY_FILE)
d1 := []byte(preferencePath)
err = ioutil.WriteFile(duplicacyFileName, d1, 0644)
err = os.WriteFile(duplicacyFileName, d1, 0644)
if err != nil {
duplicacy.LOG_ERROR("REPOSITORY_PATH", "Failed to write %s file inside repository %v", duplicacyFileName, err)
return
@@ -705,7 +703,7 @@ func changePassword(context *cli.Context) {
}
configPath := path.Join(duplicacy.GetDuplicacyPreferencePath(), "config")
err = ioutil.WriteFile(configPath, description, 0600)
err = os.WriteFile(configPath, description, 0600)
if err != nil {
duplicacy.LOG_ERROR("CONFIG_SAVE", "Failed to save the old config to %s: %v", configPath, err)
return
@@ -789,7 +787,12 @@ func backupRepository(context *cli.Context) {
uploadRateLimit := context.Int("limit-rate")
enumOnly := context.Bool("enum-only")
storage.SetRateLimits(0, uploadRateLimit)
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, preference.NobackupFile, preference.FiltersFile, preference.ExcludeByAttribute)
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password,
&duplicacy.BackupManagerOptions{
NoBackupFile: preference.NobackupFile,
FiltersFile: preference.FiltersFile,
ExcludeByAttribute: preference.ExcludeByAttribute,
})
duplicacy.SavePassword(*preference, "password", password)
backupManager.SetupSnapshotCache(preference.Name)
@@ -850,20 +853,6 @@ func restoreRepository(context *cli.Context) {
password = duplicacy.GetPassword(*preference, "password", "Enter storage password:", false, false)
}
options := duplicacy.RestoreOptions{
InPlace: true,
QuickMode: !context.Bool("hash"),
Overwrite: context.Bool("overwrite"),
DeleteMode: context.Bool("delete"),
SetOwner: !context.Bool("ignore-owner"),
ShowStatistics: context.Bool("stats"),
AllowFailures: context.Bool("persist"),
ExcludeXattrs: preference.ExcludeXattrs,
NormalizeXattrs: preference.NormalizeXattrs,
IncludeSpecials: preference.IncludeSpecials,
FileFlagsMask: uint32(preference.FileFlagsMask),
}
var patterns []string
for _, pattern := range context.Args() {
@@ -880,20 +869,45 @@ func restoreRepository(context *cli.Context) {
patterns = append(patterns, pattern)
}
options.Patterns = duplicacy.ProcessFilterLines(patterns, make([]string, 0))
patterns = duplicacy.ProcessFilterLines(patterns, make([]string, 0))
duplicacy.LOG_DEBUG("REGEX_DEBUG", "There are %d compiled regular expressions stored", len(duplicacy.RegexMap))
duplicacy.LOG_INFO("SNAPSHOT_FILTER", "Loaded %d include/exclude pattern(s)", len(patterns))
storage.SetRateLimits(context.Int("limit-rate"), 0)
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, preference.NobackupFile, preference.FiltersFile, preference.ExcludeByAttribute)
excludeOwner := preference.ExcludeOwner
// TODO: for backward compat, eventually make them all overridable?
if context.IsSet("ignore-owner") {
excludeOwner = context.Bool("ignore-owner")
}
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password,
&duplicacy.BackupManagerOptions{
NoBackupFile: preference.NobackupFile,
FiltersFile: preference.FiltersFile,
ExcludeByAttribute: preference.ExcludeByAttribute,
SetOwner: excludeOwner,
ExcludeXattrs: preference.ExcludeXattrs,
NormalizeXattrs: preference.NormalizeXattrs,
IncludeSpecials: preference.IncludeSpecials,
FileFlagsMask: uint32(preference.FileFlagsMask),
})
duplicacy.SavePassword(*preference, "password", password)
loadRSAPrivateKey(context.String("key"), context.String("key-passphrase"), preference, backupManager, false)
backupManager.SetupSnapshotCache(preference.Name)
failed := backupManager.Restore(repository, revision, options)
failed := backupManager.Restore(repository, revision, &duplicacy.RestoreOptions{
InPlace: true,
QuickMode: !context.Bool("hash"),
Overwrite: context.Bool("overwrite"),
DeleteMode: context.Bool("delete"),
ShowStatistics: context.Bool("stats"),
AllowFailures: context.Bool("persist"),
})
if failed > 0 {
duplicacy.LOG_ERROR("RESTORE_FAIL", "%d file(s) were not restored correctly", failed)
return
@@ -933,7 +947,8 @@ func listSnapshots(context *cli.Context) {
tag := context.String("t")
revisions := getRevisions(context)
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, "", "", preference.ExcludeByAttribute)
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password,
&duplicacy.BackupManagerOptions{ExcludeByAttribute: preference.ExcludeByAttribute})
duplicacy.SavePassword(*preference, "password", password)
id := preference.SnapshotID
@@ -989,7 +1004,7 @@ func checkSnapshots(context *cli.Context) {
tag := context.String("t")
revisions := getRevisions(context)
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, "", "", false)
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, nil)
duplicacy.SavePassword(*preference, "password", password)
loadRSAPrivateKey(context.String("key"), context.String("key-passphrase"), preference, backupManager, false)
@@ -1049,8 +1064,7 @@ func printFile(context *cli.Context) {
snapshotID = context.String("id")
}
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, "", "", false)
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, nil)
duplicacy.SavePassword(*preference, "password", password)
loadRSAPrivateKey(context.String("key"), context.String("key-passphrase"), preference, backupManager, false)
@@ -1108,7 +1122,7 @@ func diff(context *cli.Context) {
}
compareByHash := context.Bool("hash")
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, "", "", false)
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, nil)
duplicacy.SavePassword(*preference, "password", password)
loadRSAPrivateKey(context.String("key"), context.String("key-passphrase"), preference, backupManager, false)
@@ -1153,7 +1167,7 @@ func showHistory(context *cli.Context) {
revisions := getRevisions(context)
showLocalHash := context.Bool("hash")
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, "", "", false)
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, nil)
duplicacy.SavePassword(*preference, "password", password)
backupManager.SetupSnapshotCache(preference.Name)
@@ -1216,7 +1230,7 @@ func pruneSnapshots(context *cli.Context) {
os.Exit(ArgumentExitCode)
}
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, "", "", false)
backupManager := duplicacy.CreateBackupManager(preference.SnapshotID, storage, repository, password, nil)
duplicacy.SavePassword(*preference, "password", password)
backupManager.SetupSnapshotCache(preference.Name)
@@ -1261,7 +1275,7 @@ func copySnapshots(context *cli.Context) {
sourcePassword = duplicacy.GetPassword(*source, "password", "Enter source storage password:", false, false)
}
sourceManager := duplicacy.CreateBackupManager(source.SnapshotID, sourceStorage, repository, sourcePassword, "", "", false)
sourceManager := duplicacy.CreateBackupManager(source.SnapshotID, sourceStorage, repository, sourcePassword, nil)
sourceManager.SetupSnapshotCache(source.Name)
duplicacy.SavePassword(*source, "password", sourcePassword)
@@ -1296,7 +1310,7 @@ func copySnapshots(context *cli.Context) {
destinationStorage.SetRateLimits(0, context.Int("upload-limit-rate"))
destinationManager := duplicacy.CreateBackupManager(destination.SnapshotID, destinationStorage, repository,
destinationPassword, "", "", false)
destinationPassword, nil)
duplicacy.SavePassword(*destination, "password", destinationPassword)
destinationManager.SetupSnapshotCache(destination.Name)
@@ -1570,7 +1584,6 @@ func main() {
Usage: "the maximum number of entries kept in memory (defaults to 1M)",
Argument: "<number>",
},
},
Usage: "Save a snapshot of the repository to the storage",
ArgsUsage: " ",

View File

@@ -33,15 +33,21 @@ type BackupManager struct {
snapshotCache *FileStorage // for copies of chunks needed by snapshots
config *Config // contains a number of options
nobackupFile string // don't backup directory when this file name is found
filtersFile string // the path to the filters file
excludeByAttribute bool // don't backup file based on file attribute
options BackupManagerOptions
cachePath string
}
type BackupOptions struct {
type BackupManagerOptions struct {
NoBackupFile string // don't backup directory when this file name is found
FiltersFile string // the path to the filters file
ExcludeByAttribute bool // don't backup file based on file attribute
SetOwner bool
ExcludeXattrs bool
NormalizeXattrs bool
IncludeFileFlags bool
IncludeSpecials bool
FileFlagsMask uint32
}
type RestoreOptions struct {
@@ -51,13 +57,8 @@ type RestoreOptions struct {
QuickMode bool
Overwrite bool
DeleteMode bool
SetOwner bool
ShowStatistics bool
AllowFailures bool
ExcludeXattrs bool
NormalizeXattrs bool
IncludeSpecials bool
FileFlagsMask uint32
}
func (manager *BackupManager) SetDryRun(dryRun bool) {
@@ -71,7 +72,8 @@ func (manager *BackupManager) SetCompressionLevel(level int) {
// CreateBackupManager creates a backup manager using the specified 'storage'. 'snapshotID' is a unique id to
// identify snapshots created for this repository. 'top' is the top directory of the repository. 'password' is the
// master key which can be nil if encryption is not enabled.
func CreateBackupManager(snapshotID string, storage Storage, top string, password string, nobackupFile string, filtersFile string, excludeByAttribute bool) *BackupManager {
func CreateBackupManager(snapshotID string, storage Storage, top string, password string,
options *BackupManagerOptions) *BackupManager {
config, _, err := DownloadConfig(storage, password)
if err != nil {
@@ -92,12 +94,10 @@ func CreateBackupManager(snapshotID string, storage Storage, top string, passwor
SnapshotManager: snapshotManager,
config: config,
nobackupFile: nobackupFile,
filtersFile: filtersFile,
excludeByAttribute: excludeByAttribute,
options: *options,
}
if options != nil {
backupManager.options = *options
}
if IsDebugging() {
@@ -170,7 +170,7 @@ func (manager *BackupManager) Backup(top string, quickMode bool, threads int, ta
LOG_INFO("BACKUP_KEY", "RSA encryption is enabled")
}
if manager.excludeByAttribute {
if manager.options.ExcludeByAttribute {
LOG_INFO("BACKUP_EXCLUDE", "Exclude files with no-backup attributes")
}
@@ -255,7 +255,8 @@ func (manager *BackupManager) Backup(top string, quickMode bool, threads int, ta
go func() {
// List local files
defer CatchLogException()
localSnapshot.ListLocalFiles(shadowTop, manager.nobackupFile, manager.filtersFile, manager.excludeByAttribute, localListingChannel, &skippedDirectories, &skippedFiles)
localSnapshot.ListLocalFiles(shadowTop, manager.options.NoBackupFile, manager.options.FiltersFile,
manager.options.ExcludeByAttribute, localListingChannel, &skippedDirectories, &skippedFiles)
}()
go func() {
@@ -642,7 +643,7 @@ func (manager *BackupManager) Backup(top string, quickMode bool, threads int, ta
// Restore downloads the specified snapshot, compares it with what's on the repository, and then downloads
// files that are different.'QuickMode' will bypass files with unchanged sizes and timestamps. 'DeleteMode' will
// remove local files that don't exist in the snapshot. 'Patterns' is used to include/exclude certain files.
func (manager *BackupManager) Restore(top string, revision int, options RestoreOptions) int {
func (manager *BackupManager) Restore(top string, revision int, options *RestoreOptions) int {
if options.Threads < 1 {
options.Threads = 1
}
@@ -653,10 +654,10 @@ func (manager *BackupManager) Restore(top string, revision int, options RestoreO
allowFailures := options.AllowFailures
metadataOptions := RestoreMetadataOptions{
SetOwner: options.SetOwner,
ExcludeXattrs: options.ExcludeXattrs,
NormalizeXattrs: options.NormalizeXattrs,
FileFlagsMask: options.FileFlagsMask,
SetOwner: manager.options.SetOwner,
ExcludeXattrs: manager.options.ExcludeXattrs,
NormalizeXattrs: manager.options.NormalizeXattrs,
FileFlagsMask: manager.options.FileFlagsMask,
}
startTime := time.Now().Unix()
@@ -715,7 +716,8 @@ func (manager *BackupManager) Restore(top string, revision int, options RestoreO
go func() {
// List local files
defer CatchLogException()
localSnapshot.ListLocalFiles(top, manager.nobackupFile, manager.filtersFile, manager.excludeByAttribute, localListingChannel, nil, nil)
localSnapshot.ListLocalFiles(top, manager.options.NoBackupFile, manager.options.FiltersFile,
manager.options.ExcludeByAttribute, localListingChannel, nil, nil)
}()
remoteSnapshot := manager.SnapshotManager.DownloadSnapshot(manager.snapshotID, revision)
@@ -857,12 +859,12 @@ func (manager *BackupManager) Restore(top string, revision int, options RestoreO
return 0
}
}
err = remoteEntry.RestoreEarlyDirFlags(fullPath, options.FileFlagsMask)
err = remoteEntry.RestoreEarlyDirFlags(fullPath, manager.options.FileFlagsMask)
if err != nil {
LOG_WARN("DOWNLOAD_FLAGS", "Failed to set early file flags on %s: %v", fullPath, err)
}
directoryEntries = append(directoryEntries, remoteEntry)
} else if remoteEntry.IsSpecial() && options.IncludeSpecials {
} else if remoteEntry.IsSpecial() && manager.options.IncludeSpecials {
if stat, _ := os.Lstat(fullPath); stat != nil {
if remoteEntry.IsSameSpecial(stat) {
remoteEntry.RestoreMetadata(fullPath, nil, metadataOptions)

View File

@@ -251,21 +251,20 @@ func TestBackupManager(t *testing.T) {
time.Sleep(time.Duration(delay) * time.Second)
SetDuplicacyPreferencePath(testDir + "/repository1/.duplicacy")
backupManager := CreateBackupManager("host1", storage, testDir, password, "", "", false)
backupManager := CreateBackupManager("host1", storage, testDir, password, nil)
backupManager.SetupSnapshotCache("default")
SetDuplicacyPreferencePath(testDir + "/repository1/.duplicacy")
backupManager.Backup(testDir+"/repository1" /*quickMode=*/, true, threads, "first", false, false, 0, false, 1024, 1024)
time.Sleep(time.Duration(delay) * time.Second)
SetDuplicacyPreferencePath(testDir + "/repository2/.duplicacy")
failedFiles := backupManager.Restore(testDir+"/repository2", 1, RestoreOptions{
failedFiles := backupManager.Restore(testDir+"/repository2", 1, &RestoreOptions{
Threads: threads,
Patterns: nil,
InPlace: false,
QuickMode: false,
Overwrite: true,
DeleteMode: false,
SetOwner: false,
ShowStatistics: false,
AllowFailures: false,
})
@@ -292,14 +291,13 @@ func TestBackupManager(t *testing.T) {
backupManager.Backup(testDir+"/repository1" /*quickMode=*/, true, threads, "second", false, false, 0, false, 1024, 1024)
time.Sleep(time.Duration(delay) * time.Second)
SetDuplicacyPreferencePath(testDir + "/repository2/.duplicacy")
failedFiles = backupManager.Restore(testDir+"/repository2", 2, RestoreOptions{
failedFiles = backupManager.Restore(testDir+"/repository2", 2, &RestoreOptions{
Threads: threads,
Patterns: nil,
InPlace: true,
QuickMode: true,
Overwrite: true,
DeleteMode: false,
SetOwner: false,
ShowStatistics: false,
AllowFailures: false,
})
@@ -330,14 +328,13 @@ func TestBackupManager(t *testing.T) {
createRandomFile(testDir+"/repository2/dir5/file5", 100)
SetDuplicacyPreferencePath(testDir + "/repository2/.duplicacy")
failedFiles = backupManager.Restore(testDir+"/repository2", 3, RestoreOptions{
failedFiles = backupManager.Restore(testDir+"/repository2", 3, &RestoreOptions{
Threads: threads,
Patterns: nil,
InPlace: true,
QuickMode: false,
Overwrite: true,
DeleteMode: true,
SetOwner: false,
ShowStatistics: false,
AllowFailures: false,
})
@@ -367,14 +364,13 @@ func TestBackupManager(t *testing.T) {
os.Remove(testDir + "/repository1/file2")
os.Remove(testDir + "/repository1/dir1/file3")
SetDuplicacyPreferencePath(testDir + "/repository1/.duplicacy")
failedFiles = backupManager.Restore(testDir+"/repository1", 3, RestoreOptions{
failedFiles = backupManager.Restore(testDir+"/repository1", 3, &RestoreOptions{
Threads: threads,
Patterns: []string{"+file2", "+dir1/file3", "-*"},
InPlace: true,
QuickMode: false,
Overwrite: true,
DeleteMode: false,
SetOwner: false,
ShowStatistics: false,
AllowFailures: false,
})
@@ -564,7 +560,7 @@ func TestPersistRestore(t *testing.T) {
// do unencrypted backup
SetDuplicacyPreferencePath(testDir + "/repository1/.duplicacy")
unencBackupManager := CreateBackupManager("host1", unencStorage, testDir, "", "", "", false)
unencBackupManager := CreateBackupManager("host1", unencStorage, testDir, "", nil)
unencBackupManager.SetupSnapshotCache("default")
SetDuplicacyPreferencePath(testDir + "/repository1/.duplicacy")
@@ -573,7 +569,7 @@ func TestPersistRestore(t *testing.T) {
// do encrypted backup
SetDuplicacyPreferencePath(testDir + "/repository1/.duplicacy")
encBackupManager := CreateBackupManager("host1", storage, testDir, password, "", "", false)
encBackupManager := CreateBackupManager("host1", storage, testDir, password, nil)
encBackupManager.SetupSnapshotCache("default")
SetDuplicacyPreferencePath(testDir + "/repository1/.duplicacy")
@@ -651,14 +647,13 @@ func TestPersistRestore(t *testing.T) {
// test restore all uncorrupted to repository3
SetDuplicacyPreferencePath(testDir + "/repository3/.duplicacy")
failedFiles := unencBackupManager.Restore(testDir+"/repository3", 1, RestoreOptions{
failedFiles := unencBackupManager.Restore(testDir+"/repository3", 1, &RestoreOptions{
Threads: threads,
Patterns: nil,
InPlace: true,
QuickMode: false,
Overwrite: false,
DeleteMode: false,
SetOwner: false,
ShowStatistics: false,
AllowFailures: false,
})
@@ -706,14 +701,13 @@ func TestPersistRestore(t *testing.T) {
// test restore corrupted, inPlace = true, corrupted files will have hash failures
os.RemoveAll(testDir + "/repository2")
SetDuplicacyPreferencePath(testDir + "/repository2/.duplicacy")
failedFiles = unencBackupManager.Restore(testDir+"/repository2", 1, RestoreOptions{
failedFiles = unencBackupManager.Restore(testDir+"/repository2", 1, &RestoreOptions{
Threads: threads,
Patterns: nil,
InPlace: true,
QuickMode: false,
Overwrite: false,
DeleteMode: false,
SetOwner: false,
ShowStatistics: false,
AllowFailures: true,
})
@@ -724,14 +718,13 @@ func TestPersistRestore(t *testing.T) {
os.RemoveAll(testDir + "/repository2")
SetDuplicacyPreferencePath(testDir + "/repository2/.duplicacy")
failedFiles = encBackupManager.Restore(testDir+"/repository2", 1, RestoreOptions{
failedFiles = encBackupManager.Restore(testDir+"/repository2", 1, &RestoreOptions{
Threads: threads,
Patterns: nil,
InPlace: true,
QuickMode: false,
Overwrite: false,
DeleteMode: false,
SetOwner: false,
ShowStatistics: false,
AllowFailures: true,
})
@@ -744,14 +737,13 @@ func TestPersistRestore(t *testing.T) {
// test restore corrupted, inPlace = false, corrupted files will be missing
os.RemoveAll(testDir + "/repository2")
SetDuplicacyPreferencePath(testDir + "/repository2/.duplicacy")
failedFiles = unencBackupManager.Restore(testDir+"/repository2", 1, RestoreOptions{
failedFiles = unencBackupManager.Restore(testDir+"/repository2", 1, &RestoreOptions{
Threads: threads,
Patterns: nil,
InPlace: false,
QuickMode: false,
Overwrite: false,
DeleteMode: false,
SetOwner: false,
ShowStatistics: false,
AllowFailures: true,
})
@@ -762,14 +754,13 @@ func TestPersistRestore(t *testing.T) {
os.RemoveAll(testDir + "/repository2")
SetDuplicacyPreferencePath(testDir + "/repository2/.duplicacy")
failedFiles = encBackupManager.Restore(testDir+"/repository2", 1, RestoreOptions{
failedFiles = encBackupManager.Restore(testDir+"/repository2", 1, &RestoreOptions{
Threads: threads,
Patterns: nil,
InPlace: false,
QuickMode: false,
Overwrite: false,
DeleteMode: false,
SetOwner: false,
ShowStatistics: false,
AllowFailures: true,
})
@@ -782,27 +773,25 @@ func TestPersistRestore(t *testing.T) {
// with overwrite=true, corrupted file1 from unenc will be restored correctly from enc
// the latter will not touch the existing file3 with correct hash
os.RemoveAll(testDir + "/repository2")
failedFiles = unencBackupManager.Restore(testDir+"/repository2", 1, RestoreOptions{
failedFiles = unencBackupManager.Restore(testDir+"/repository2", 1, &RestoreOptions{
Threads: threads,
Patterns: nil,
InPlace: true,
QuickMode: false,
Overwrite: false,
DeleteMode: false,
SetOwner: false,
ShowStatistics: false,
AllowFailures: true,
})
assertRestoreFailures(t, failedFiles, 1)
failedFiles = encBackupManager.Restore(testDir+"/repository2", 1, RestoreOptions{
failedFiles = encBackupManager.Restore(testDir+"/repository2", 1, &RestoreOptions{
Threads: threads,
Patterns: nil,
InPlace: true,
QuickMode: false,
Overwrite: true,
DeleteMode: false,
SetOwner: false,
ShowStatistics: false,
AllowFailures: true,
})
@@ -812,28 +801,26 @@ func TestPersistRestore(t *testing.T) {
// restore to repository3, with overwrite and allowFailures (true/false), quickMode = false (use hashes)
// should always succeed as uncorrupted files already exist with correct hash, so these will be ignored
SetDuplicacyPreferencePath(testDir + "/repository3/.duplicacy")
failedFiles = unencBackupManager.Restore(testDir+"/repository3", 1, RestoreOptions{
failedFiles = unencBackupManager.Restore(testDir+"/repository3", 1, &RestoreOptions{
Threads: threads,
Patterns: nil,
InPlace: true,
QuickMode: false,
Overwrite: true,
DeleteMode: false,
SetOwner: false,
ShowStatistics: false,
AllowFailures: false,
})
assertRestoreFailures(t, failedFiles, 0)
checkAllUncorrupted("/repository3")
failedFiles = unencBackupManager.Restore(testDir+"/repository3", 1, RestoreOptions{
failedFiles = unencBackupManager.Restore(testDir+"/repository3", 1, &RestoreOptions{
Threads: threads,
Patterns: nil,
InPlace: true,
QuickMode: false,
Overwrite: true,
DeleteMode: false,
SetOwner: false,
ShowStatistics: false,
AllowFailures: true,
})

View File

@@ -50,6 +50,7 @@ type Preference struct {
NobackupFile string `json:"nobackup_file"`
Keys map[string]string `json:"keys"`
FiltersFile string `json:"filters"`
ExcludeOwner bool `json:"exclude_owner"`
ExcludeByAttribute bool `json:"exclude_by_attribute"`
ExcludeXattrs bool `json:"exclude_xattrs"`
NormalizeXattrs bool `json:"normalize_xattrs"`