From 72c186f9a98859cd239ddc4c4a4a2c78768b72a4 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 10 Nov 2013 17:12:45 -0600 Subject: [PATCH] Migrate to new app data directories. This commit makes use of the new btcutil.AppDataDir function which chooses appropriate data directories for each supported operating system. It also adds code to the upgrade path to properly migrate existing data from the old to new locations. This is part of work toward issue #30. --- config.go | 27 +++--------- sample-btcd.conf | 7 ++-- upgrade.go | 105 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 113 insertions(+), 26 deletions(-) diff --git a/config.go b/config.go index 35e6b552..819cda36 100644 --- a/config.go +++ b/config.go @@ -8,6 +8,7 @@ import ( "fmt" "github.com/conformal/btcdb" _ "github.com/conformal/btcdb/ldb" + "github.com/conformal/btcutil" "github.com/conformal/btcwire" "github.com/conformal/go-flags" "net" @@ -20,6 +21,7 @@ import ( const ( defaultConfigFilename = "btcd.conf" + defaultDataDirname = "data" defaultLogLevel = "info" defaultBtcnet = btcwire.MainNet defaultMaxPeers = 125 @@ -29,8 +31,9 @@ const ( ) var ( - defaultConfigFile = filepath.Join(btcdHomeDir(), defaultConfigFilename) - defaultDataDir = filepath.Join(btcdHomeDir(), "data") + btcdHomeDir = btcutil.AppDataDir("btcd", false) + defaultConfigFile = filepath.Join(btcdHomeDir, defaultConfigFilename) + defaultDataDir = filepath.Join(btcdHomeDir, defaultDataDirname) knownDbTypes = btcdb.SupportedDBs() ) @@ -65,30 +68,12 @@ type config struct { DebugLevel string `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}"` } -// btcdHomeDir returns an OS appropriate home directory for btcd. -func btcdHomeDir() string { - // Search for Windows APPDATA first. This won't exist on POSIX OSes. - appData := os.Getenv("APPDATA") - if appData != "" { - return filepath.Join(appData, "btcd") - } - - // Fall back to standard HOME directory that works for most POSIX OSes. - home := os.Getenv("HOME") - if home != "" { - return filepath.Join(home, ".btcd") - } - - // In the worst case, use the current directory. - return "." -} - // cleanAndExpandPath expands environement variables and leading ~ in the // passed path, cleans the result, and returns it. func cleanAndExpandPath(path string) string { // Expand initial ~ to OS specific home directory. if strings.HasPrefix(path, "~") { - homeDir := filepath.Dir(btcdHomeDir()) + homeDir := filepath.Dir(btcdHomeDir) path = strings.Replace(path, "~", homeDir, 1) } diff --git a/sample-btcd.conf b/sample-btcd.conf index 65eea08f..3ff6c4b6 100644 --- a/sample-btcd.conf +++ b/sample-btcd.conf @@ -6,10 +6,11 @@ ; The directory to store data such as the block chain and peer addresses. The ; block chain takes several GB, so this location must have a lot of free space. -; The default is ~/.btcd/data on POSIX OSes and $APPDATA/btcd/data on Windows. -; Environment variables are expanded so they may be used. NOTE: Windows +; The default is ~/.btcd/data on POSIX OSes, $LOCALAPPDATA/Btcd/data on Windows, +; ~/Library/Application Support/Btcd/data on Mac OS, and $home/btcd/data on +; Plan9. Environment variables are expanded so they may be used. NOTE: Windows ; environment variables are typically %VARIABLE%, but they must be accessed with -; $VARIABLE here. Also, ~ is expanded to $APPDATA on Windows. +; $VARIABLE here. Also, ~ is expanded to $LOCALAPPDATA on Windows. ; datadir=~/.btcd/data diff --git a/upgrade.go b/upgrade.go index dc5479ec..35379d22 100644 --- a/upgrade.go +++ b/upgrade.go @@ -5,10 +5,46 @@ package main import ( + "io" "os" "path/filepath" ) +// dirEmpty returns whether or not the specified directory path is empty. +func dirEmpty(dirPath string) (bool, error) { + f, err := os.Open(dirPath) + defer f.Close() + + // Read the names of a max of one entry from the directory. When the + // directory is empty, an io.EOF error will be returned, so allow it. + names, err := f.Readdirnames(1) + if err != nil && err != io.EOF { + return false, err + } + + return len(names) == 0, nil +} + +// oldBtcdHomeDir returns the OS specific home directory btcd used prior to +// version 0.3.3. This has since been replaced with btcutil.AppDataDir, but +// this function is still provided for the automatic upgrade path. +func oldBtcdHomeDir() string { + // Search for Windows APPDATA first. This won't exist on POSIX OSes. + appData := os.Getenv("APPDATA") + if appData != "" { + return filepath.Join(appData, "btcd") + } + + // Fall back to standard HOME directory that works for most POSIX OSes. + home := os.Getenv("HOME") + if home != "" { + return filepath.Join(home, ".btcd") + } + + // In the worst case, use the current directory. + return "." +} + // upgradeDBPathNet moves the database for a specific network from its // location prior to btcd version 0.2.0 and uses heuristics to ascertain the old // database type to rename to the new format. @@ -56,7 +92,7 @@ func upgradeDBPaths() error { // their names were suffixed by "testnet" and "regtest" for their // respective networks. Check for the old database and update it to the // new path introduced with version 0.2.0 accodingly. - oldDbRoot := filepath.Join(btcdHomeDir(), "db") + oldDbRoot := filepath.Join(oldBtcdHomeDir(), "db") upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd.db"), "mainnet") upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd_testnet.db"), "testnet") upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd_regtest.db"), "regtest") @@ -70,7 +106,72 @@ func upgradeDBPaths() error { return nil } +// upgradeDataPaths moves the application data from its location prior to btcd +// version 0.3.3 to its new location. +func upgradeDataPaths() error { + // No need to migrate if the old and new home paths are the same. + oldHomePath := oldBtcdHomeDir() + newHomePath := btcdHomeDir + if oldHomePath == newHomePath { + return nil + } + + // Only migrate if the old path exists and the new one doesn't. + if fileExists(oldHomePath) && !fileExists(newHomePath) { + // Create the new path. + log.Infof("Migrating application home path from '%s' to '%s'", + oldHomePath, newHomePath) + err := os.MkdirAll(newHomePath, 0700) + if err != nil { + return err + } + + // Move old btcd.conf into new location if needed. + oldConfPath := filepath.Join(oldHomePath, defaultConfigFilename) + newConfPath := filepath.Join(newHomePath, defaultConfigFilename) + if fileExists(oldConfPath) && !fileExists(newConfPath) { + err := os.Rename(oldConfPath, newConfPath) + if err != nil { + return err + } + } + + // Move old data directory into new location if needed. + oldDataPath := filepath.Join(oldHomePath, defaultDataDirname) + newDataPath := filepath.Join(newHomePath, defaultDataDirname) + if fileExists(oldDataPath) && !fileExists(newDataPath) { + err := os.Rename(oldDataPath, newDataPath) + if err != nil { + return err + } + } + + // Remove the old home if it is empty or show a warning if not. + ohpEmpty, err := dirEmpty(oldHomePath) + if err != nil { + return err + } + if ohpEmpty { + err := os.Remove(oldHomePath) + if err != nil { + return err + } + } else { + log.Warnf("Not removing '%s' since it contains files "+ + "not created by this application. You may "+ + "want to manually move them or delete them.", + oldHomePath) + } + } + + return nil +} + // doUpgrades performs upgrades to btcd as new versions require it. func doUpgrades() error { - return upgradeDBPaths() + err := upgradeDBPaths() + if err != nil { + return err + } + return upgradeDataPaths() }