mirror of
https://github.com/gilbertchen/duplicacy
synced 2025-12-10 13:23:17 +00:00
First steps -pref-dir
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.idea
|
||||
duplicacy_main
|
||||
|
||||
@@ -71,8 +71,9 @@ func CreateBackupManager(snapshotID string, storage Storage, top string, passwor
|
||||
// SetupSnapshotCache creates the snapshot cache, which is merely a local storage under the default .duplicacy
|
||||
// directory
|
||||
func (manager *BackupManager) SetupSnapshotCache(top string, storageName string) bool {
|
||||
|
||||
cacheDir := path.Join(top, DUPLICACY_DIRECTORY, "cache", storageName)
|
||||
|
||||
duplicacyDirectory := GetDotDuplicacyPathName(top)
|
||||
cacheDir := path.Join(duplicacyDirectory, "cache", storageName)
|
||||
|
||||
storage, err := CreateFileStorage(cacheDir, 1)
|
||||
if err != nil {
|
||||
@@ -600,6 +601,7 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
}
|
||||
}
|
||||
|
||||
// How will behave restore when repo created using -repo-dir ,??
|
||||
err = os.Mkdir(path.Join(top, DUPLICACY_DIRECTORY), 0744)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
LOG_ERROR("RESTORE_MKDIR", "Failed to create the preference directory: %v", err)
|
||||
@@ -978,8 +980,9 @@ func (manager *BackupManager) RestoreFile(chunkDownloader *ChunkDownloader, chun
|
||||
|
||||
var existingFile, newFile *os.File
|
||||
var err error
|
||||
|
||||
temporaryPath := path.Join(top, DUPLICACY_DIRECTORY, "temporary")
|
||||
|
||||
duplicacyDirectory := GetDotDuplicacyPathName(top)
|
||||
temporaryPath := path.Join(duplicacyDirectory, "temporary")
|
||||
fullPath := joinPath(top, entry.Path)
|
||||
|
||||
defer func() {
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
// This is the hidden directory in the repository for storing various files.
|
||||
var DUPLICACY_DIRECTORY = ".duplicacy"
|
||||
var DUPLICACY_FILE = ".duplicacy"
|
||||
|
||||
// Regex for matching 'StartChunk:StartOffset:EndChunk:EndOffset'
|
||||
var contentRegex = regexp.MustCompile(`^([0-9]+):([0-9]+):([0-9]+):([0-9]+)`)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"path"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Preference stores options for each storage.
|
||||
@@ -25,9 +26,52 @@ type Preference struct {
|
||||
|
||||
var Preferences [] Preference
|
||||
|
||||
func LoadPreferences(repository string) (bool) {
|
||||
// Compute .duplicacy directory path name:
|
||||
// - if .duplicacy is a directory -> compute absolute path name and return it
|
||||
// - if .duplicacy is a file -> assumed this file contains the real path name of .duplicacy
|
||||
// - if pointed directory does not exits... return error
|
||||
func GetDotDuplicacyPathName( repository string) (duplicacyDirectory string){
|
||||
|
||||
dotDuplicacy := path.Join(repository, DUPLICACY_DIRECTORY) //TOKEEP
|
||||
|
||||
stat, err := os.Stat(dotDuplicacy)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
LOG_ERROR("DOT_DUPLICACY_PATH", "Failed to retrieve the information about the directory %s: %v",
|
||||
repository, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
if stat != nil && stat.IsDir() {
|
||||
// $repository/.duplicacy exists and is a directory --> we found the .duplicacy directory
|
||||
return path.Clean(dotDuplicacy)
|
||||
}
|
||||
|
||||
if stat != nil && stat.Mode().IsRegular() {
|
||||
b, err := ioutil.ReadFile(dotDuplicacy) // just pass the file name
|
||||
if err != nil {
|
||||
LOG_ERROR("DOT_DUPLICACY_PATH", "Failed to read file %s: %v",
|
||||
dotDuplicacy, err)
|
||||
return ""
|
||||
}
|
||||
dot_duplicacy := string(b) // convert content to a 'string'
|
||||
stat, err := os.Stat(dot_duplicacy)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
LOG_ERROR("DOT_DUPLICACY_PATH", "Failed to retrieve the information about the directory %s: %v",
|
||||
repository, err)
|
||||
return ""
|
||||
}
|
||||
if stat != nil && stat.IsDir() {
|
||||
// If expression read from .duplicacy file is a directory --> we found the .duplicacy directory
|
||||
return path.Clean( dot_duplicacy)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
description, err := ioutil.ReadFile(path.Join(repository, DUPLICACY_DIRECTORY, "preferences"))
|
||||
func LoadPreferences(repository string) (bool) {
|
||||
|
||||
duplicacyDirectory := GetDotDuplicacyPathName(repository)
|
||||
description, err := ioutil.ReadFile(path.Join(duplicacyDirectory, "preferences"))
|
||||
if err != nil {
|
||||
LOG_ERROR("PREFERENCE_OPEN", "Failed to read the preference file from repository %s: %v", repository, err)
|
||||
return false
|
||||
@@ -53,8 +97,9 @@ func SavePreferences(repository string) (bool) {
|
||||
LOG_ERROR("PREFERENCE_MARSHAL", "Failed to marshal the repository preferences: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
preferenceFile := path.Join(repository, DUPLICACY_DIRECTORY, "/preferences")
|
||||
duplicacyDirectory := GetDotDuplicacyPathName(repository)
|
||||
preferenceFile := path.Join(duplicacyDirectory, "/preferences")
|
||||
|
||||
err = ioutil.WriteFile(preferenceFile, description, 0644)
|
||||
if err != nil {
|
||||
LOG_ERROR("PREFERENCE_WRITE", "Failed to save the preference file %s: %v", preferenceFile, err)
|
||||
|
||||
@@ -509,8 +509,11 @@ func CreateShadowCopy(top string, shadowCopy bool) (shadowTop string) {
|
||||
LOG_INFO("VSS_DONE", "Shadow copy %s created", SnapshotIDString)
|
||||
|
||||
snapshotPath := uint16ArrayToString(properties.SnapshotDeviceObject)
|
||||
|
||||
shadowLink = path.Join(top, DUPLICACY_DIRECTORY) + "\\shadow"
|
||||
|
||||
duplicacyDirectory := GetDotDuplicacyPathName(top)
|
||||
shadowLink = path.Join(duplicacyDirectory, "shadow")
|
||||
// FIXME: Not using path.Join : is this intentional ?
|
||||
//shadowLink = path.Join(top, DUPLICACY_DIRECTORY) + "\\shadow"
|
||||
os.Remove(shadowLink)
|
||||
err = os.Symlink(snapshotPath + "\\", shadowLink)
|
||||
if err != nil {
|
||||
|
||||
@@ -67,7 +67,9 @@ func CreateSnapshotFromDirectory(id string, top string) (snapshot *Snapshot, ski
|
||||
}
|
||||
|
||||
var patterns []string
|
||||
patternFile, err := ioutil.ReadFile(path.Join(top, DUPLICACY_DIRECTORY, "filters"))
|
||||
|
||||
duplicacyDirectory := GetDotDuplicacyPathName(top)
|
||||
patternFile, err := ioutil.ReadFile(path.Join(duplicacyDirectory, "filters"))
|
||||
if err == nil {
|
||||
for _, pattern := range strings.Split(string(patternFile), "\n") {
|
||||
pattern = strings.TrimSpace(pattern)
|
||||
|
||||
@@ -1510,8 +1510,9 @@ func (manager *SnapshotManager) PruneSnapshots(top string, selfID string, snapsh
|
||||
if len(revisionsToBeDeleted) > 0 && (len(tags) > 0 || len(retentions) > 0) {
|
||||
LOG_WARN("DELETE_OPTIONS", "Tags or retention policy will be ignored if at least one revision is specified")
|
||||
}
|
||||
|
||||
logDir := path.Join(top, DUPLICACY_DIRECTORY, "logs")
|
||||
|
||||
duplicacyDirectory := GetDotDuplicacyPathName(top)
|
||||
logDir := path.Join(duplicacyDirectory, "logs")
|
||||
os.Mkdir(logDir, 0700)
|
||||
logFileName := path.Join(logDir, time.Now().Format("prune-log-20060102-150405"))
|
||||
logFile, err := os.OpenFile(logFileName, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0600)
|
||||
|
||||
@@ -81,7 +81,7 @@ func checkHostKey(repository string, hostname string, remote net.Addr, key ssh.P
|
||||
return nil
|
||||
}
|
||||
|
||||
duplicacyDirectory := path.Join(repository, DUPLICACY_DIRECTORY)
|
||||
duplicacyDirectory := GetDotDuplicacyPathName(repository)
|
||||
hostFile := path.Join(duplicacyDirectory, "knowns_hosts")
|
||||
file, err := os.OpenFile(hostFile, os.O_RDWR | os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
|
||||
80
integration_tests/test.sh
Executable file
80
integration_tests/test.sh
Executable file
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
set -x
|
||||
get_abs_filename() {
|
||||
# $1 : relative filename
|
||||
echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
|
||||
}
|
||||
DUPLICACY=$(get_abs_filename ../duplicacy_main)
|
||||
|
||||
TEST_ZONE=$HOME/DUPLICACY_TEST_ZONE
|
||||
TEST_REPO=$TEST_ZONE/TEST_REPO
|
||||
TEST_STORAGE=$TEST_ZONE/TEST_STORAGE
|
||||
TEST_DOT_DUPLICACY=$TEST_ZONE/TEST_DOT_DUPLICACY
|
||||
TEST_RESTORE_POINT=$TEST_ZONE/RESTORE_POINT
|
||||
function fixture()
|
||||
{
|
||||
# clean TEST_RESTORE_POINT
|
||||
rm -rf $TEST_RESTORE_POINT
|
||||
mkdir -p $TEST_RESTORE_POINT
|
||||
|
||||
# clean TEST_STORAGE
|
||||
rm -rf $TEST_STORAGE
|
||||
mkdir -p $TEST_STORAGE
|
||||
|
||||
# clean TEST_DOT_DUPLICACY
|
||||
rm -rf $TEST_DOT_DUPLICACY
|
||||
mkdir -p $TEST_DOT_DUPLICACY
|
||||
|
||||
# Create test repository
|
||||
rm -rf $TEST_REPO
|
||||
mkdir -p $TEST_REPO
|
||||
pushd $TEST_REPO
|
||||
echo "file1" > file1
|
||||
mkdir dir1
|
||||
echo "file2 >dir1/file2"
|
||||
popd
|
||||
}
|
||||
|
||||
function init_repo()
|
||||
{
|
||||
pushd $TEST_REPO
|
||||
$DUPLICACY init integration-tests $TEST_STORAGE
|
||||
$DUPLICACY backup
|
||||
popd
|
||||
|
||||
}
|
||||
|
||||
function init_repo_pref_dir()
|
||||
{
|
||||
pushd $TEST_REPO
|
||||
$DUPLICACY init -pref-dir "$TEST_ZONE/TEST_DOT_DUPLICACY" integration-tests $TEST_STORAGE
|
||||
$DUPLICACY backup
|
||||
popd
|
||||
|
||||
}
|
||||
|
||||
function backup()
|
||||
{
|
||||
pushd $TEST_REPO
|
||||
NOW=`date`
|
||||
echo $NOW > "file-$NOW"
|
||||
$DUPLICACY backup
|
||||
popd
|
||||
}
|
||||
|
||||
|
||||
function restore()
|
||||
{
|
||||
pushd $TEST_REPO
|
||||
$DUPLICACY restore -r 2 -delete
|
||||
popd
|
||||
}
|
||||
|
||||
fixture
|
||||
init_repo_pref_dir
|
||||
|
||||
backup
|
||||
backup
|
||||
backup
|
||||
restore
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
"github.com/gilbertchen/cli"
|
||||
|
||||
"github.com/gilbertchen/duplicacy"
|
||||
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,6 +28,7 @@ const (
|
||||
|
||||
var ScriptEnabled bool
|
||||
|
||||
|
||||
func getRepositoryPreference(context *cli.Context, storageName string) (repository string,
|
||||
preference *duplicacy.Preference) {
|
||||
|
||||
@@ -34,16 +37,15 @@ func getRepositoryPreference(context *cli.Context, storageName string) (reposito
|
||||
duplicacy.LOG_ERROR("REPOSITORY_PATH", "Failed to retrieve the current working directory: %v", err)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
for {
|
||||
stat, err := os.Stat(path.Join(repository, duplicacy.DUPLICACY_DIRECTORY))
|
||||
stat, err := os.Stat(path.Join(repository, duplicacy.DUPLICACY_DIRECTORY)) //TOKEEP
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
duplicacy.LOG_ERROR("REPOSITORY_PATH", "Failed to retrieve the information about the directory %s: %v",
|
||||
repository, err)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if stat != nil && stat.IsDir() {
|
||||
if stat != nil && (stat.IsDir() || stat.Mode().IsRegular()) {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -54,10 +56,10 @@ func getRepositoryPreference(context *cli.Context, storageName string) (reposito
|
||||
}
|
||||
repository = parent
|
||||
}
|
||||
|
||||
duplicacy.LoadPreferences(repository)
|
||||
|
||||
duplicacy.SetKeyringFile(path.Join(repository, duplicacy.DUPLICACY_DIRECTORY, "keyring"))
|
||||
|
||||
duplicacyDirectory := duplicacy.GetDotDuplicacyPathName(repository)
|
||||
duplicacy.SetKeyringFile(path.Join(duplicacyDirectory, "keyring"))
|
||||
|
||||
if storageName == "" {
|
||||
storageName = context.String("storage")
|
||||
@@ -142,8 +144,9 @@ func runScript(context *cli.Context, repository string, storageName string, phas
|
||||
if !ScriptEnabled {
|
||||
return false
|
||||
}
|
||||
|
||||
scriptDir, _ := filepath.Abs(path.Join(repository, duplicacy.DUPLICACY_DIRECTORY, "scripts"))
|
||||
|
||||
duplicacyDirectory := duplicacy.GetDotDuplicacyPathName(repository)
|
||||
scriptDir, _ := filepath.Abs(path.Join(duplicacyDirectory, "scripts"))
|
||||
scriptName := phase + "-" + context.Command.Name
|
||||
|
||||
script := path.Join(scriptDir, scriptName)
|
||||
@@ -174,14 +177,14 @@ func runScript(context *cli.Context, repository string, storageName string, phas
|
||||
}
|
||||
|
||||
func initRepository(context *cli.Context) {
|
||||
configRespository(context, true)
|
||||
configRepository(context, true)
|
||||
}
|
||||
|
||||
func addStorage(context *cli.Context) {
|
||||
configRespository(context, false)
|
||||
configRepository(context, false)
|
||||
}
|
||||
|
||||
func configRespository(context *cli.Context, init bool) {
|
||||
func configRepository(context *cli.Context, init bool) {
|
||||
|
||||
setGlobalOptions(context)
|
||||
defer duplicacy.CatchLogException()
|
||||
@@ -220,8 +223,15 @@ func configRespository(context *cli.Context, init bool) {
|
||||
duplicacy.LOG_ERROR("REPOSITORY_PATH", "Failed to retrieve the current working directory: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
duplicacyDirectory := path.Join(repository, duplicacy.DUPLICACY_DIRECTORY)
|
||||
|
||||
duplicacyDirectory := context.String("pref-dir")
|
||||
if duplicacyDirectory == "" {
|
||||
|
||||
duplicacyDirectory = path.Join(repository, duplicacy.DUPLICACY_DIRECTORY) // TOKEEP
|
||||
}
|
||||
duplicacy.LOG_INFO("PREF_PATH", "-pref-dir value: --|%s|-- ", duplicacyDirectory)
|
||||
|
||||
|
||||
if stat, _ := os.Stat(path.Join(duplicacyDirectory, "preferences")); stat != nil {
|
||||
duplicacy.LOG_ERROR("REPOSITORY_INIT", "The repository %s has already been initialized", repository)
|
||||
return
|
||||
@@ -230,10 +240,20 @@ func configRespository(context *cli.Context, init bool) {
|
||||
err = os.Mkdir(duplicacyDirectory, 0744)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
duplicacy.LOG_ERROR("REPOSITORY_INIT", "Failed to create the directory %s: %v",
|
||||
duplicacy.DUPLICACY_DIRECTORY, err)
|
||||
duplicacyDirectory, err)
|
||||
return
|
||||
}
|
||||
|
||||
if context.String("pref-dir") != "" {
|
||||
// out of tree preference file
|
||||
// write real path into .duplicacy file inside repository
|
||||
duplicacyFileName := path.Join(repository, duplicacy.DUPLICACY_FILE)
|
||||
d1 := []byte(duplicacyDirectory)
|
||||
err = ioutil.WriteFile(duplicacyFileName, d1, 0644)
|
||||
if err != nil {
|
||||
duplicacy.LOG_ERROR("REPOSITORY_PATH", "Failed to write %s file inside repository %v", duplicacyFileName, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
duplicacy.SetKeyringFile(path.Join(duplicacyDirectory, "keyring"))
|
||||
|
||||
} else {
|
||||
@@ -547,7 +567,6 @@ func changePassword(context *cli.Context) {
|
||||
duplicacy.LOG_INFO("STORAGE_SET", "The password for storage %s has been changed", preference.StorageURL)
|
||||
}
|
||||
|
||||
|
||||
func backupRepository(context *cli.Context) {
|
||||
setGlobalOptions(context)
|
||||
defer duplicacy.CatchLogException()
|
||||
@@ -1071,7 +1090,8 @@ func infoStorage(context *cli.Context) {
|
||||
|
||||
repository := context.String("repository")
|
||||
if repository != "" {
|
||||
duplicacy.SetKeyringFile(path.Join(repository, duplicacy.DUPLICACY_DIRECTORY, "keyring"))
|
||||
duplicacyDirectory := duplicacy.GetDotDuplicacyPathName(repository)
|
||||
duplicacy.SetKeyringFile(path.Join(duplicacyDirectory, "keyring"))
|
||||
}
|
||||
|
||||
isEncrypted := context.Bool("e")
|
||||
@@ -1132,6 +1152,11 @@ func main() {
|
||||
Usage: "the minimum size of chunks (defaults to chunk-size / 4)",
|
||||
Argument: "1M",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pref-dir",
|
||||
Usage: "Specify alternate location for .duplicacy preferences directory (absolute or relative to current directory)",
|
||||
Argument: "<preference directory path>",
|
||||
},
|
||||
},
|
||||
Usage: "Initialize the storage if necessary and the current directory as the repository",
|
||||
ArgsUsage: "<snapshot id> <storage url>",
|
||||
|
||||
Reference in New Issue
Block a user